13af6ab5fSopenharmony_ci#!/usr/bin/env python3 23af6ab5fSopenharmony_ci# -*- coding: utf-8 -*- 33af6ab5fSopenharmony_ci 43af6ab5fSopenharmony_ci""" 53af6ab5fSopenharmony_ciCopyright (c) 2024 Huawei Device Co., Ltd. 63af6ab5fSopenharmony_ciLicensed under the Apache License, Version 2.0 (the "License"); 73af6ab5fSopenharmony_ciyou may not use this file except in compliance with the License. 83af6ab5fSopenharmony_ciYou may obtain a copy of the License at 93af6ab5fSopenharmony_ci 103af6ab5fSopenharmony_ci http://www.apache.org/licenses/LICENSE-2.0 113af6ab5fSopenharmony_ci 123af6ab5fSopenharmony_ciUnless required by applicable law or agreed to in writing, software 133af6ab5fSopenharmony_cidistributed under the License is distributed on an "AS IS" BASIS, 143af6ab5fSopenharmony_ciWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 153af6ab5fSopenharmony_ciSee the License for the specific language governing permissions and 163af6ab5fSopenharmony_cilimitations under the License. 173af6ab5fSopenharmony_ci""" 183af6ab5fSopenharmony_ci 193af6ab5fSopenharmony_cifrom grammar_test import Runner, parse_args, Task 203af6ab5fSopenharmony_ciimport os 213af6ab5fSopenharmony_ciimport json 223af6ab5fSopenharmony_ciimport copy 233af6ab5fSopenharmony_ciimport shutil 243af6ab5fSopenharmony_ciimport stat 253af6ab5fSopenharmony_ci 263af6ab5fSopenharmony_cicombination_config_path = os.path.join(os.path.dirname(__file__), 'combination_config.json') 273af6ab5fSopenharmony_ciconfig_temp_dir = os.path.join(os.path.dirname(__file__), '../test/local/temp_configs') 283af6ab5fSopenharmony_ciINDENTATION = 2 293af6ab5fSopenharmony_ciTEST_TYPE = 'combinations' 303af6ab5fSopenharmony_ci 313af6ab5fSopenharmony_ciDEFAULT_CONFIG = { 323af6ab5fSopenharmony_ci 'mCompact': False, 333af6ab5fSopenharmony_ci 'mRemoveComments': False, 343af6ab5fSopenharmony_ci 'mOutputDir': '', 353af6ab5fSopenharmony_ci 'mDisableConsole': False, 363af6ab5fSopenharmony_ci 'mSimplify': False, 373af6ab5fSopenharmony_ci 'mNameObfuscation': { 383af6ab5fSopenharmony_ci 'mEnable': True, 393af6ab5fSopenharmony_ci 'mNameGeneratorType': 1, 403af6ab5fSopenharmony_ci 'mDictionaryList': [], 413af6ab5fSopenharmony_ci 'mRenameProperties': False, 423af6ab5fSopenharmony_ci 'mKeepStringProperty': True, 433af6ab5fSopenharmony_ci 'mTopLevel': False 443af6ab5fSopenharmony_ci }, 453af6ab5fSopenharmony_ci 'mExportObfuscation': False, 463af6ab5fSopenharmony_ci 'mEnableSourceMap': False, 473af6ab5fSopenharmony_ci 'mEnableNameCache': False, 483af6ab5fSopenharmony_ci 'mKeepFileSourceCode': { 493af6ab5fSopenharmony_ci 'mKeepSourceOfPaths': [], 503af6ab5fSopenharmony_ci 'mkeepFilesAndDependencies': [] 513af6ab5fSopenharmony_ci }, 523af6ab5fSopenharmony_ci 'mRenameFileName': { 533af6ab5fSopenharmony_ci 'mEnable': False, 543af6ab5fSopenharmony_ci 'mNameGeneratorType': 1, 553af6ab5fSopenharmony_ci 'mReservedFileNames': [], 563af6ab5fSopenharmony_ci 'mOhmUrlUseNormalized': False 573af6ab5fSopenharmony_ci } 583af6ab5fSopenharmony_ci} 593af6ab5fSopenharmony_ci 603af6ab5fSopenharmony_ci# This alias is used as the output directory for the current obfuscation options. 613af6ab5fSopenharmony_ciCONFIG_ALIAS = { 623af6ab5fSopenharmony_ci 'mCompact': 'comp', 633af6ab5fSopenharmony_ci 'mRemoveComments': 'rmComments', 643af6ab5fSopenharmony_ci 'mDisableConsole': 'con', 653af6ab5fSopenharmony_ci 'mNameObfuscation': { 663af6ab5fSopenharmony_ci 'mEnable': 'localVar', 673af6ab5fSopenharmony_ci 'mRenameProperties': 'prop', 683af6ab5fSopenharmony_ci 'mKeepStringProperty': 'strProp', 693af6ab5fSopenharmony_ci 'mTopLevel': 'top' 703af6ab5fSopenharmony_ci }, 713af6ab5fSopenharmony_ci 'mExportObfuscation': 'export', 723af6ab5fSopenharmony_ci 'mEnableSourceMap': 'sourcemap', 733af6ab5fSopenharmony_ci 'mEnableNameCache': 'namecache' 743af6ab5fSopenharmony_ci} 753af6ab5fSopenharmony_ci 763af6ab5fSopenharmony_ci 773af6ab5fSopenharmony_ciclass CombinationRunner(Runner): 783af6ab5fSopenharmony_ci def __init__(self, test_filter, root_dir, test_type): 793af6ab5fSopenharmony_ci super().__init__(test_filter, root_dir, test_type) 803af6ab5fSopenharmony_ci self.configs = [] 813af6ab5fSopenharmony_ci self.combinations = [] 823af6ab5fSopenharmony_ci self.temp_files = [] 833af6ab5fSopenharmony_ci self.obfscated_cache_root_dir = os.path.normpath(root_dir) 843af6ab5fSopenharmony_ci if not os.path.exists(config_temp_dir): 853af6ab5fSopenharmony_ci os.makedirs(config_temp_dir) 863af6ab5fSopenharmony_ci 873af6ab5fSopenharmony_ci def prepare_task(self, input_dirs, config_path): 883af6ab5fSopenharmony_ci for input_dir in input_dirs: 893af6ab5fSopenharmony_ci input_abs_dir = os.path.normpath(os.path.join(os.path.dirname(__file__), input_dir)) 903af6ab5fSopenharmony_ci for one_case_path in os.listdir(input_abs_dir): 913af6ab5fSopenharmony_ci input_path = os.path.join(input_abs_dir, one_case_path) 923af6ab5fSopenharmony_ci task = Task(input_path, config_path, TEST_TYPE) 933af6ab5fSopenharmony_ci self.obfuscation_tasks.append(task) 943af6ab5fSopenharmony_ci 953af6ab5fSopenharmony_ci def recursive_search(self, config_part, input_part, result): 963af6ab5fSopenharmony_ci for key in input_part: 973af6ab5fSopenharmony_ci if key not in config_part: 983af6ab5fSopenharmony_ci continue 993af6ab5fSopenharmony_ci input_val = input_part[key] 1003af6ab5fSopenharmony_ci config_val = config_part[key] 1013af6ab5fSopenharmony_ci 1023af6ab5fSopenharmony_ci if isinstance(input_val, dict): 1033af6ab5fSopenharmony_ci self.recursive_search(config_val, input_val, result) 1043af6ab5fSopenharmony_ci else: 1053af6ab5fSopenharmony_ci result.append(config_val) 1063af6ab5fSopenharmony_ci 1073af6ab5fSopenharmony_ci def get_alias_from_config(self, config, input_data): 1083af6ab5fSopenharmony_ci result = [] 1093af6ab5fSopenharmony_ci self.recursive_search(config, input_data, result) 1103af6ab5fSopenharmony_ci return '+'.join(result) if result else 'default' 1113af6ab5fSopenharmony_ci 1123af6ab5fSopenharmony_ci def merge_deep(self, target, source): 1133af6ab5fSopenharmony_ci for key, value in source.items(): 1143af6ab5fSopenharmony_ci if isinstance(value, dict) and key in target: 1153af6ab5fSopenharmony_ci target[key] = self.merge_deep(target.get(key, {}), value) 1163af6ab5fSopenharmony_ci else: 1173af6ab5fSopenharmony_ci target[key] = value 1183af6ab5fSopenharmony_ci return target 1193af6ab5fSopenharmony_ci 1203af6ab5fSopenharmony_ci def merge_config(self, options, options_alias, whitelist, output_dir): 1213af6ab5fSopenharmony_ci whitelist_config = whitelist.get(options_alias, {}) 1223af6ab5fSopenharmony_ci output_config = {'mOutputDir': output_dir} 1233af6ab5fSopenharmony_ci option_config = self.merge_deep(copy.deepcopy(whitelist_config), copy.deepcopy(options)) 1243af6ab5fSopenharmony_ci option_config = self.merge_deep(option_config, output_config) 1253af6ab5fSopenharmony_ci merged_config = self.merge_deep(copy.deepcopy(DEFAULT_CONFIG), option_config) 1263af6ab5fSopenharmony_ci return merged_config 1273af6ab5fSopenharmony_ci 1283af6ab5fSopenharmony_ci def generate_config(self, combinations, input_dirs, output_dir, whitelist): 1293af6ab5fSopenharmony_ci output_abs_dir = os.path.join(os.path.dirname(combination_config_path), output_dir) 1303af6ab5fSopenharmony_ci for combination in combinations: 1313af6ab5fSopenharmony_ci alias_str = self.get_alias_from_config(CONFIG_ALIAS, combination) 1323af6ab5fSopenharmony_ci output_dir_for_current_option = os.path.normpath(os.path.join(output_abs_dir, alias_str)) 1333af6ab5fSopenharmony_ci temp_config_path = os.path.normpath(os.path.join(config_temp_dir, alias_str + '_config.json')) 1343af6ab5fSopenharmony_ci merged_config = self.merge_config(combination, alias_str, whitelist, str(output_dir_for_current_option)) 1353af6ab5fSopenharmony_ci flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC 1363af6ab5fSopenharmony_ci modes = stat.S_IRUSR | stat.S_IWUSR 1373af6ab5fSopenharmony_ci # write temp config file 1383af6ab5fSopenharmony_ci with os.fdopen(os.open(temp_config_path, flags, modes), 'w') as config_file: 1393af6ab5fSopenharmony_ci json.dump(merged_config, config_file, indent=INDENTATION) 1403af6ab5fSopenharmony_ci self.temp_files.append(temp_config_path) 1413af6ab5fSopenharmony_ci self.prepare_task(input_dirs, temp_config_path) 1423af6ab5fSopenharmony_ci 1433af6ab5fSopenharmony_ci 1443af6ab5fSopenharmony_ci def delete_temp_config_files(self): 1453af6ab5fSopenharmony_ci for path in self.temp_files: 1463af6ab5fSopenharmony_ci os.remove(path) 1473af6ab5fSopenharmony_ci self.temp_files.clear() 1483af6ab5fSopenharmony_ci 1493af6ab5fSopenharmony_ci 1503af6ab5fSopenharmony_ci def combine(self, current, remaining_keys, configs, result): 1513af6ab5fSopenharmony_ci if not remaining_keys: 1523af6ab5fSopenharmony_ci return 1533af6ab5fSopenharmony_ci 1543af6ab5fSopenharmony_ci key = remaining_keys[0] 1553af6ab5fSopenharmony_ci value = configs[key] 1563af6ab5fSopenharmony_ci 1573af6ab5fSopenharmony_ci if isinstance(value, dict): 1583af6ab5fSopenharmony_ci sub_keys = value.keys() 1593af6ab5fSopenharmony_ci sub_combinations = [] 1603af6ab5fSopenharmony_ci 1613af6ab5fSopenharmony_ci for sub_key in sub_keys: 1623af6ab5fSopenharmony_ci sub_value = value[sub_key] 1633af6ab5fSopenharmony_ci current_sub_combinations = sub_combinations[0:] 1643af6ab5fSopenharmony_ci for comb in current_sub_combinations: 1653af6ab5fSopenharmony_ci new_comb = {**comb, sub_key: sub_value} 1663af6ab5fSopenharmony_ci sub_combinations.append(new_comb) 1673af6ab5fSopenharmony_ci sub_combinations.append({sub_key: sub_value}) 1683af6ab5fSopenharmony_ci 1693af6ab5fSopenharmony_ci for sub_comb in sub_combinations: 1703af6ab5fSopenharmony_ci new_comb = {**current, key: sub_comb} 1713af6ab5fSopenharmony_ci result.append(new_comb) 1723af6ab5fSopenharmony_ci self.combine(new_comb, remaining_keys[1:], configs, result) 1733af6ab5fSopenharmony_ci else: 1743af6ab5fSopenharmony_ci new_comb = {**current, key: value} 1753af6ab5fSopenharmony_ci result.append(new_comb) 1763af6ab5fSopenharmony_ci self.combine(new_comb, remaining_keys[1:], configs, result) 1773af6ab5fSopenharmony_ci 1783af6ab5fSopenharmony_ci self.combine(current, remaining_keys[1:], configs, result) 1793af6ab5fSopenharmony_ci 1803af6ab5fSopenharmony_ci def generate_combinations(self, configs): 1813af6ab5fSopenharmony_ci result = [{}] # Initialize with the empty object 1823af6ab5fSopenharmony_ci keys = list(configs.keys()) 1833af6ab5fSopenharmony_ci self.combine({}, keys, configs, result) 1843af6ab5fSopenharmony_ci return result 1853af6ab5fSopenharmony_ci 1863af6ab5fSopenharmony_ci def parse_configs_and_execute(self): 1873af6ab5fSopenharmony_ci with open(combination_config_path, 'r', encoding='utf-8') as file: 1883af6ab5fSopenharmony_ci configs = json.load(file) 1893af6ab5fSopenharmony_ci # Parse each configuration in combination_config.json and then execute the test. 1903af6ab5fSopenharmony_ci for key, config in configs.items(): 1913af6ab5fSopenharmony_ci enable_options = config.get('enableOptions', {}) 1923af6ab5fSopenharmony_ci input_dirs = config.get('inputDirs', []) 1933af6ab5fSopenharmony_ci output_dir = config.get('outputDir', '') 1943af6ab5fSopenharmony_ci whitelist = config.get('whitelist', {}) 1953af6ab5fSopenharmony_ci combinations = self.generate_combinations(enable_options) 1963af6ab5fSopenharmony_ci print('Obfuscation option combinations count:', len(combinations)) 1973af6ab5fSopenharmony_ci self.generate_config(combinations, input_dirs, output_dir, whitelist) 1983af6ab5fSopenharmony_ci self.obfuscate() 1993af6ab5fSopenharmony_ci self.delete_temp_config_files() 2003af6ab5fSopenharmony_ci 2013af6ab5fSopenharmony_ci def try_remove_cache(self): 2023af6ab5fSopenharmony_ci if not self.has_failed_cases(): 2033af6ab5fSopenharmony_ci shutil.rmtree(self.obfscated_cache_root_dir) 2043af6ab5fSopenharmony_ci 2053af6ab5fSopenharmony_ci 2063af6ab5fSopenharmony_cidef main(): 2073af6ab5fSopenharmony_ci args = parse_args() 2083af6ab5fSopenharmony_ci local_root_dir = os.path.join(os.path.dirname(__file__), "../test/local/combinations") 2093af6ab5fSopenharmony_ci runner = CombinationRunner(args.test_filter, local_root_dir, TEST_TYPE) 2103af6ab5fSopenharmony_ci runner.parse_configs_and_execute() 2113af6ab5fSopenharmony_ci runner.run_with_node() 2123af6ab5fSopenharmony_ci runner.content_compare() 2133af6ab5fSopenharmony_ci runner.print_summary() 2143af6ab5fSopenharmony_ci 2153af6ab5fSopenharmony_ci runner.try_remove_cache() 2163af6ab5fSopenharmony_ci 2173af6ab5fSopenharmony_ci 2183af6ab5fSopenharmony_ciif __name__ == '__main__': 2193af6ab5fSopenharmony_ci main() 220