• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import { describe, it } from 'mocha';
17import { assert, expect } from 'chai';
18import {
19  ObOptionsForTest,
20  ObConfigResolver,
21  MergedConfig,
22  collectResevedFileNameInIDEConfig,
23  readNameCache,
24  handleUniversalPathInObf,
25  getArkguardNameCache,
26  fillNameCache,
27  writeObfuscationNameCache,
28  generateConsumerObConfigFile,
29  mangleFilePath,
30  enableObfuscatedFilePathConfig,
31  handleObfuscatedFilePath,
32  enableObfuscateFileName,
33  getRelativeSourcePath,
34  OptionTypeForTest,
35  SourceObConfig,
36  Obfuscation,
37  printUnobfuscationReasons,
38  clearNameCache
39} from '../../../src/initialization/ConfigResolver';
40import { HvigorErrorInfo, PropCollections, renameFileNameModule } from '../../../src/ArkObfuscator';
41import {
42  clearUnobfuscationNamesObj,
43  nameCacheMap,
44  unobfuscationNamesObj
45} from '../../../src/initialization/CommonObject';
46import path from 'path';
47import fs from 'fs';
48import { FileUtils } from '../../../src/utils/FileUtils';
49import sinon from 'sinon';
50import { AtKeepCollections, UnobfuscationCollections } from '../../../src/utils/CommonCollections';
51import {
52  clearHistoryUnobfuscatedMap,
53  historyAllUnobfuscatedNamesMap,
54  historyUnobfuscatedPropMap
55} from '../../../src/initialization/Initializer';
56
57const OBFUSCATE_TESTDATA_DIR = path.resolve(__dirname, '../../testData/obfuscation/system_api_obfuscation');
58const projectConfig = {
59  obfuscationOptions: { option1: 'value1' },
60  compileHar: true
61};
62
63function printObfLogger(errorInfo: string, errorCodeInfo: HvigorErrorInfo | string, level: string): void {
64  switch (level) {
65    case 'warn':
66      console.warn(errorInfo);
67      break;
68    case 'error':
69      console.error(errorInfo);
70      break;
71    default:
72      break;
73  }
74}
75
76function printObfHvigorLogger(errorInfo: string, errorCodeInfo: HvigorErrorInfo | string, level: string): void {
77  switch (level) {
78    case 'warn':
79      console.warn(errorCodeInfo);
80      break;
81    case 'error':
82      console.error(errorCodeInfo);
83      break;
84    default:
85      break;
86  }
87}
88
89const logger = console;
90const isTerser = false;
91let newObConfigResolver = new ObConfigResolver(projectConfig, printObfLogger, isTerser);
92
93describe('test for ConfigResolve', function() {
94  describe('ObOptions', () => {
95    it('should merge options correctly', () => {
96      const ob1 = new ObOptionsForTest();
97      ob1.disableObfuscation = true;
98      ob1.enablePropertyObfuscation = true;
99
100      const ob2 = new ObOptionsForTest();
101      ob2.enableToplevelObfuscation = true;
102      ob2.printNameCache = 'test';
103      ob2.printKeptNamesPath = './test/ut/initialization/printKeptNamesPath.txt';
104      ob2.applyNameCache = 'test case';
105
106      ob1.mergeObOptions(ob2);
107
108      expect(ob1.disableObfuscation).to.be.true;
109      expect(ob1.enablePropertyObfuscation).to.be.true;
110      expect(ob1.enableToplevelObfuscation).to.be.true;
111      expect(ob1.printNameCache).to.equal('test');
112      expect(ob1.printKeptNamesPath).to.equal('./test/ut/initialization/printKeptNamesPath.txt');
113      expect(ob1.applyNameCache).to.equal('test case');
114    });
115  });
116
117  describe('MergedConfig', () => {
118    describe('should merge two MergedConfig instances correctly', () => {
119      it('merge arrays and sets in MergeConfig', () => {
120        const config1 = new MergedConfig();
121        config1.reservedPropertyNames = ['prop1'];
122        config1.reservedGlobalNames = ['global1'];
123        config1.keepComments = ['comment1'];
124        config1.excludePathSet.add('path1');
125
126        const config2 = new MergedConfig();
127        config2.reservedPropertyNames = ['prop2'];
128        config2.reservedGlobalNames = ['global2'];
129        config2.keepComments = ['comment2'];
130        config2.excludePathSet.add('path2');
131
132        config1.mergeAllRules(config2);
133
134        expect(config1.reservedPropertyNames).to.deep.equal(['prop1', 'prop2']);
135        expect(config1.reservedGlobalNames).to.deep.equal(['global1', 'global2']);
136        expect(config1.keepComments).to.deep.equal(['comment1', 'comment2']);
137        expect(config1.excludePathSet).to.deep.equal(new Set(['path1', 'path2']));
138      });
139
140      it('merge options in MergeConfig', () => {
141        const config1 = new MergedConfig();
142        config1.options.enableExportObfuscation = false;
143        config1.options.stripLanguageDefault = true;
144        config1.options.stripSystemApiArgs = true;
145
146        const config2 = new MergedConfig();
147        config2.options.enableExportObfuscation = true;
148        config2.options.stripLanguageDefault = false;
149        config2.options.stripSystemApiArgs = true;
150        config1.mergeAllRules(config2);
151
152        expect(config1.options.disableObfuscation).to.be.false;
153        expect(config1.options.enablePropertyObfuscation).to.be.false;
154        expect(config1.options.enableToplevelObfuscation).to.be.false;
155        expect(config1.options.enableStringPropertyObfuscation).to.be.false;
156        expect(config1.options.removeLog).to.be.false;
157        expect(config1.options.compact).to.be.false;
158        expect(config1.options.removeComments).to.be.false;
159        expect(config1.options.enableFileNameObfuscation).to.be.false;
160        expect(config1.options.enableExportObfuscation).to.be.true;
161        expect(config1.options.stripLanguageDefault).to.be.true;
162        expect(config1.options.stripSystemApiArgs).to.be.true;
163      });
164    });
165
166    it('should sort and deduplicate arrays correctly', () => {
167      const config = new MergedConfig();
168      config.reservedPropertyNames = ['prop2', 'prop1', 'prop1'];
169      config.reservedGlobalNames = ['global2', 'global1', 'global1'];
170      config.keepComments = ['comment2', 'comment1', 'comment1'];
171
172      config.sortAndDeduplicate();
173
174      expect(config.reservedPropertyNames).to.deep.equal(['prop1', 'prop2']);
175      expect(config.reservedGlobalNames).to.deep.equal(['global1', 'global2']);
176      expect(config.keepComments).to.deep.equal(['comment1', 'comment2']);
177    });
178
179    it('should serialize merged config correctly', () => {
180      let resultStr: string = '';
181      const config = new MergedConfig();
182      config.options = new ObOptionsForTest();
183      config.options['option1'] = true;
184      config.options['option2'] = false;
185      config.options['disableObfuscation'] = true;
186      config.options['enableStringPropertyObfuscation'] = true;
187      config.reservedGlobalNames = ['global1'];
188      config.reservedPropertyNames = ['prop1'];
189
190      const serialized = config.serializeMergedConfig();
191      const serializedExportSwitchMap = new Map([
192        ['disableObfuscation', ObConfigResolver.KEEP_DTS],
193        ['enablePropertyObfuscation', ObConfigResolver.ENABLE_PROPERTY_OBFUSCATION],
194        ['enableStringPropertyObfuscation', ObConfigResolver.ENABLE_STRING_PROPERTY_OBFUSCATION],
195        ['enableToplevelObfuscation', ObConfigResolver.ENABLE_TOPLEVEL_OBFUSCATION],
196        ['compact', ObConfigResolver.COMPACT],
197        ['removeLog', ObConfigResolver.REMOVE_LOG],
198      ]);
199
200      expect(serialized).to.include(ObConfigResolver.KEEP_GLOBAL_NAME);
201      expect(serialized).to.include('global1');
202      expect(serialized).to.include(ObConfigResolver.KEEP_PROPERTY_NAME);
203      expect(serialized).to.include('prop1');
204      expect(serialized).to.not.include('option2');
205
206      expect(ObConfigResolver.exportedSwitchMap.has(String('disableObfuscation'))).to.be.false;
207      expect(ObConfigResolver.exportedSwitchMap.has(String('enableStringPropertyObfuscation'))).to.be.true;
208      expect(resultStr).to.equal('');
209    });
210  });
211
212  describe('ObConfigResolver', () => {
213    describe('constructor', () => {
214      it('should create an instance with the correct properties', () => {
215        const projectConfig = {
216          obfuscationOptions: { option1: 'value1' },
217          compileHar: true
218        };
219        const isTerser = false;
220        const myInstance = new ObConfigResolver(projectConfig, printObfLogger, isTerser);
221
222        expect(myInstance).to.be.an('object');
223        expect(myInstance.sourceObConfig).to.deep.equal({ option1: 'value1' });
224        expect(myInstance.isHarCompiled).to.be.true;
225        expect(myInstance.isTerser).to.be.false;
226      });
227
228      it('should create an instance with Terser enabled', () => {
229        const projectConfig = {
230          obfuscationOptions: { option1: 'value1' },
231          compileHar: true
232        };
233        const myInstance = new ObConfigResolver(projectConfig, printObfLogger, true);
234
235        expect(myInstance).to.be.an('object');
236        expect(myInstance.sourceObConfig).to.deep.equal({ option1: 'value1' });
237        expect(myInstance.isHarCompiled).to.be.true;
238        expect(myInstance.isTerser).to.be.true;
239      });
240    });
241
242    describe('resolveObfuscationConfigs', () => {
243      const projectConfig = {
244        obfuscationOptions: { option1: 'value1' },
245        compileHar: true
246      };
247      const logger = console;
248      const isTerser = false;
249      let newObConfigResolver = new ObConfigResolver(projectConfig, printObfLogger, isTerser);
250      let testClass: ObConfigResolver & {
251        performancePrinter?: any;
252      };
253      testClass = newObConfigResolver;
254
255      it('should return a MergedConfig object when sourceObConfig is undefined', () => {
256        testClass.sourceObConfig = undefined;
257        testClass.isHarCompiled = true;
258        testClass.performancePrinter = {};
259        const result = testClass.resolveObfuscationConfigs();
260        generateConsumerObConfigFile(testClass.sourceObConfig, undefined);
261
262        expect(testClass.sourceObConfig).to.be.an.undefined;
263        expect(result).to.deep.equal(new MergedConfig());
264        expect(result).to.be.an('object');
265        expect(result).to.have.property('options');
266      });
267
268      it('should return a MergedConfig object when sourceObConfig is null', () => {
269        testClass.sourceObConfig = null;
270        testClass.isHarCompiled = true;
271        testClass.performancePrinter = {};
272        const result = testClass.resolveObfuscationConfigs();
273        generateConsumerObConfigFile(null, undefined);
274
275        expect(testClass.sourceObConfig).to.be.a.null;
276        expect(result).to.deep.equal(new MergedConfig());
277        expect(result).to.be.an('object');
278        expect(result).to.have.property('options');
279      });
280
281      it('should handle the case when sourceObConfig.selfConfig.ruleOptions.enable is false', function () {
282        testClass.sourceObConfig = {
283          selfConfig: {
284            ruleOptions: { enable: false },
285          },
286          dependencies: { libraries: [], hars: [] },
287        };
288        generateConsumerObConfigFile(testClass.sourceObConfig, undefined);
289        const result = testClass.resolveObfuscationConfigs();
290
291        expect(result).to.be.an('object');
292        expect(result).to.have.property('options');
293        expect(result.options.disableObfuscation).to.be.true;
294      });
295
296      it('should handle the case when sourceObConfig.selfConfig.ruleOptions.enable is true', function () {
297        testClass.sourceObConfig = {
298          selfConfig: {
299            ruleOptions: { enable: true },
300          },
301          dependencies: { libraries: [], hars: [] },
302        };
303        generateConsumerObConfigFile(testClass.sourceObConfig, undefined);
304        const result = testClass.resolveObfuscationConfigs();
305
306        expect(result).to.be.an('object');
307        expect(result).to.have.property('options');
308        expect(result.options.disableObfuscation).to.be.false;
309      });
310
311      it('should handle the case when enableObfuscation is false', () => {
312        const sourceObConfig = {
313          selfConfig: {
314            ruleOptions: { enable: false },
315            consumerRules: ['./test/testData/obfuscation/keepDts/obfuscation-template.txt'],
316          },
317          dependencies: { libraries: [], hars: [] },
318          obfuscationCacheDir: './test/testData/cache',
319          options: {
320            disableObfuscation: true
321          },
322          exportRulePath: '',
323          sdkApis: []
324        };
325        const isHarCompiled = true;
326        let enableObfuscation = sourceObConfig.selfConfig.ruleOptions.enable;
327        let selfConfig = new MergedConfig();
328        enableObfuscation = !selfConfig.options.disableObfuscation;
329        let needConsumerConfigs = isHarCompiled && sourceObConfig.selfConfig.consumerRules &&
330          sourceObConfig.selfConfig.consumerRules.length > 0;
331        let needDependencyConfigs = enableObfuscation || needConsumerConfigs;
332        let dependencyConfigs = new MergedConfig();
333        const dependencyMaxLength = Math.max(
334          sourceObConfig.dependencies.libraries.length,
335          sourceObConfig.dependencies.hars.length,
336        );
337        newObConfigResolver.getDependencyConfigsForTest(sourceObConfig, dependencyConfigs);
338
339        expect(needConsumerConfigs).to.be.true;
340        expect(!dependencyMaxLength).to.be.true;
341        expect(needDependencyConfigs).to.be.true;
342        expect(dependencyConfigs).to.deep.equal(new MergedConfig());
343        expect(enableObfuscation).to.be.true;
344      });
345
346      it('should handle the case when enableObfuscation is false', () => {
347        const localHarConfig = {
348          selfConfig: {
349            ruleOptions: {
350              enable: true,
351              rules: ['./test/testData/obfuscation/Configs/localHar/obfuscation-rules.txt']
352            },
353            consumerRules: [],
354            libDir: "",
355            consumerFiles: ""
356          },
357          dependencies: { libraries: [], hars: [] },
358          sdkApis: [],
359          obfuscationCacheDir: '',
360          exportRulePath: ''
361        };
362
363        const sourceObConfig = {
364          selfConfig: {
365            options: {
366              disableObfuscation: false,
367              enablePropertyObfuscation: true,
368              enableExportObfuscation: true,
369              enableToplevelObfuscation: true
370            },
371            ruleOptions: { enable: true },
372            consumerRules: ['./test/testData/obfuscation/keepDts/obfuscation-template.txt'],
373            libDir: ''
374          },
375          dependencies: { libraries: [localHarConfig.selfConfig], hars: [] },
376          obfuscationCacheDir: './test/testData/cache',
377          sdkApis: [],
378          exportRulePath: ''
379        };
380        const isHarCompiled = true;
381        let enableObfuscation = sourceObConfig.selfConfig.ruleOptions.enable;
382        let selfConfig = new MergedConfig();
383        enableObfuscation = !selfConfig.options.disableObfuscation;
384        let needConsumerConfigs = isHarCompiled && sourceObConfig.selfConfig.consumerRules &&
385          sourceObConfig.selfConfig.consumerRules.length > 0;
386        let needDependencyConfigs = enableObfuscation || needConsumerConfigs;
387        let dependencyConfigs = new MergedConfig();
388        const dependencyMaxLength = Math.max(
389          sourceObConfig.dependencies.libraries.length,
390          sourceObConfig.dependencies.hars.length,
391        );
392        const mergedConfigs = newObConfigResolver.getMergedConfigsForTest(selfConfig, dependencyConfigs);
393        newObConfigResolver.handleReservedArrayForTest(mergedConfigs);
394        let needKeepSystemApi = enableObfuscation &&
395          (mergedConfigs.options.enablePropertyObfuscation ||
396            (mergedConfigs.options.enableExportObfuscation && mergedConfigs.options.enableToplevelObfuscation));
397
398        newObConfigResolver.getDependencyConfigsForTest(sourceObConfig, dependencyConfigs);
399
400        expect(needConsumerConfigs).to.be.true;
401        expect(!dependencyMaxLength).to.be.false;
402        expect(needDependencyConfigs).to.be.true;
403        expect(dependencyConfigs).to.deep.equal(new MergedConfig());
404        expect(enableObfuscation).to.be.true;
405        expect(needKeepSystemApi).to.be.false;
406        expect(needConsumerConfigs).to.be.true;
407      });
408
409      it('should handle the case when enableObfuscation is true', () => {
410        testClass.sourceObConfig = {
411          selfConfig: {
412            ruleOptions: { enable: true },
413            consumerRules: ['./test/testData/obfuscation/keepDts/obfuscation-template.txt'],
414          },
415          dependencies: { libraries: [], hars: [] },
416          obfuscationCacheDir: './test/testData/cache',
417          options: {
418            disableObfuscation: true
419          }
420        };
421        testClass.isHarCompiled = true;
422        testClass.performancePrinter = {};
423        let enableObfuscation = testClass.sourceObConfig.selfConfig.ruleOptions.enable;
424        let selfConfig = new MergedConfig();
425        newObConfigResolver.getSelfConfigsForTest(selfConfig);
426        enableObfuscation = !selfConfig.options.disableObfuscation;
427        enableObfuscation = false;
428        let needConsumerConfigs = testClass.isHarCompiled && testClass.sourceObConfig.selfConfig.consumerRules &&
429          testClass.sourceObConfig.selfConfig.consumerRules.length > 0;
430        let needDependencyConfigs = enableObfuscation || needConsumerConfigs;
431        let dependencyConfigs = new MergedConfig();
432        const dependencyMaxLength = Math.max(
433          testClass.sourceObConfig.dependencies.libraries.length,
434          testClass.sourceObConfig.dependencies.hars.length,
435          testClass.sourceObConfig.dependencies.hsps?.length ?? 0,
436          testClass.sourceObConfig.dependencies.hspLibraries?.length ?? 0
437        );
438        newObConfigResolver.getDependencyConfigsForTest(testClass.sourceObConfig, dependencyConfigs);
439
440        expect(needConsumerConfigs).to.be.true;
441        expect(!dependencyMaxLength).to.be.true;
442        expect(needDependencyConfigs).to.be.true;
443        expect(dependencyConfigs).to.deep.equal(new MergedConfig());
444        expect(enableObfuscation).to.be.false;
445      });
446    });
447
448    describe('getSelfConfigs', () => {
449      it('should create an instance with the correct properties', () => {
450        const projectConfig = {
451          obfuscationOptions: {
452            selfConfig: {
453              ruleOptions: {
454                rules: []
455              }
456            }
457          },
458          compileHar: true
459        };
460        const isTerser = false;
461        const resolver = new ObConfigResolver(projectConfig, printObfLogger, isTerser);
462
463        expect(resolver).to.be.an('object');
464        expect(resolver.sourceObConfig).to.deep.equal(projectConfig.obfuscationOptions);
465        expect(resolver.isHarCompiled).to.equal(projectConfig.compileHar);
466        expect(resolver.isTerser).to.equal(isTerser);
467      });
468
469      it('should create an instance with the correct properties', () => {
470        const projectConfig = {
471          obfuscationOptions: {
472            selfConfig: {
473              ruleOptions: {
474                rules: ['./test/ut/initialization/tempNameCache.json']
475              }
476            }
477          },
478          compileHar: true
479        };
480        const isTerser = false;
481        const resolver = new ObConfigResolver(projectConfig, printObfLogger, isTerser);
482
483        expect(resolver).to.be.an('object');
484        expect(resolver.sourceObConfig).to.deep.equal(projectConfig.obfuscationOptions);
485        expect(resolver.isHarCompiled).to.equal(projectConfig.compileHar);
486        expect(resolver.isTerser).to.equal(isTerser);
487      });
488
489      it('should get self configs correctly', () => {
490        const projectConfig = {
491          obfuscationOptions: {
492            selfConfig: {
493              ruleOptions: {
494                rules: ['./test/ut/initialization/tempNameCache.json']
495              }
496            }
497          },
498          compileHar: true
499        };
500        const isTerser = false;
501        const resolver = new ObConfigResolver(projectConfig, printObfLogger, isTerser);
502        const selfConfigs = new MergedConfig();
503
504        resolver.getSelfConfigsForTest(selfConfigs);
505
506        expect(resolver).to.be.an('object');
507        expect(resolver.sourceObConfig).to.deep.equal(projectConfig.obfuscationOptions);
508        expect(resolver.isHarCompiled).to.equal(projectConfig.compileHar);
509        expect(resolver.isTerser).to.equal(isTerser);
510      });
511    });
512
513    describe('getConfigByPath', () => {
514      const projectConfig = {
515        obfuscationOptions: {
516          selfConfig: {
517            ruleOptions: {
518              rules: ['./test/testData/obfuscation/filename_obf/getConfigByPath.json']
519            }
520          }
521        },
522        compileHar: true
523      };
524      let sandbox;
525      let instance;
526
527      beforeEach(() => {
528        sandbox = sinon.createSandbox();
529        instance = new ObConfigResolver(projectConfig, printObfLogger, true);
530        instance.logger = { error: sandbox.stub() };
531      });
532
533      afterEach(() => {
534        sandbox.restore();
535      });
536
537      it('should read file content and handle config content', () => {
538        const path = './test/testData/obfuscation/filename_obf/getConfigByPath.json';
539        let fileContent;
540        const expectedContent = '{"key": "value"}';
541        const configs = new MergedConfig();
542
543        fileContent = fs.readFileSync(path, 'utf-8');
544        instance.getConfigByPathForTest(path, configs);
545        instance.handleConfigContent(fileContent, configs, path);
546
547        expect(fileContent).to.equal(expectedContent);
548        expect(configs).to.be.an.instanceOf(MergedConfig);
549      });
550
551      it('should log error and throw when failed to open file', () => {
552        const path = './test/testData/obfuscation/filename_obf/non-existent-file.json';
553        const configs = new MergedConfig();
554        const stub = sinon.stub(console, 'error');
555        const errorMessage = `Failed to open ${path}. Error message: Error: ENOENT: no such file or directory, open ${path}`;
556        sandbox.stub(fs, 'readFileSync').throws(new Error(`ENOENT: no such file or directory, open ${path}`));
557        try {
558          instance.getConfigByPath(path, configs);
559        } catch (err) {
560        }
561        expect(stub.calledWith(errorMessage)).to.be.true;
562        stub.restore();
563      });
564
565      it('should log error and throw when failed to open file with hvigor errorCode', () => {
566        const path = './test/testData/obfuscation/filename_obf/non-existent-file.json';
567        const configs = new MergedConfig();
568        const stub = sinon.stub(console, 'error');
569        const errorMessage = {
570          code: '10804001',
571          description: 'ArkTS compiler Error',
572          cause: `Failed to open obfuscation config file from ${path}. Error message: Error: ENOENT: no such file or directory, open ${path}`,
573          position: path,
574          solutions: [`Please check whether ${path} exists.`],
575        };
576        instance = new ObConfigResolver(projectConfig, printObfHvigorLogger, true);
577        sandbox.stub(fs, 'readFileSync').throws(new Error(`ENOENT: no such file or directory, open ${path}`));
578        try {
579          instance.getConfigByPath(path, configs);
580        } catch (err) {
581        }
582        expect(stub.calledWith(errorMessage)).to.be.true;
583        stub.restore();
584      });
585    });
586
587    it('should return a new MergedConfig instance when sourceObConfig is not defined', function() {
588      const projectConfig = {
589        sourceObConfig: null,
590        getMergedConfig: function() {
591          let sourceObConfig = this.sourceObConfig;
592          if (!sourceObConfig) {
593            return new MergedConfig();
594          }
595        }
596      };
597
598      const result = projectConfig.getMergedConfig();
599      expect(result).to.be.an.instanceOf(MergedConfig);
600    });
601
602    describe('resolveKeepConfig', () => {
603      it('should resolve keep config correctly, starts with "!" and contains "*", not starts with "!"', () => {
604        const keepConfigs = ['!test/*/exclude.js', 'test/include.js'];
605        const configs = new MergedConfig();
606        const configPath = './test/testData/obfuscation/keepDts/obfuscation-template.txt';
607
608        newObConfigResolver.resolveKeepConfig(keepConfigs, configs, configPath);
609
610        expect(configs.excludeUniversalPaths).to.have.lengthOf(1);
611        expect(configs.keepUniversalPaths).to.have.lengthOf(0);
612        expect(configs.excludePathSet).to.have.lengthOf(0);
613        expect(configs.keepSourceOfPaths).to.have.lengthOf(0);
614      });
615
616      it('should resolve keep config correctly, starts with "!" and contains "*", not starts with "!" and "?"', () => {
617        const keepConfigs = ['!test/*/exclude.js', 'test/?/include.js'];
618        const configs = new MergedConfig();
619        const configPath = './test/testData/obfuscation/keepDts/obfuscation-template.txt';
620
621        newObConfigResolver.resolveKeepConfig(keepConfigs, configs, configPath);
622
623        expect(configs.excludeUniversalPaths).to.have.lengthOf(1);
624        expect(configs.keepUniversalPaths).to.have.lengthOf(1);
625        expect(configs.excludePathSet).to.have.lengthOf(0);
626        expect(configs.keepSourceOfPaths).to.have.lengthOf(0);
627      });
628
629      it('should resolve keep config correctly, both start with "!", contains "*" and "?"', () => {
630        const keepConfigs = ['!test/*/exclude.js', '!test/?/include.js'];
631        const configs = new MergedConfig();
632        const configPath = './test/testData/obfuscation/keepDts/obfuscation-template.txt';
633
634        newObConfigResolver.resolveKeepConfig(keepConfigs, configs, configPath);
635
636        expect(configs.excludeUniversalPaths).to.have.lengthOf(2);
637        expect(configs.keepUniversalPaths).to.have.lengthOf(0);
638        expect(configs.excludePathSet).to.have.lengthOf(0);
639        expect(configs.keepSourceOfPaths).to.have.lengthOf(0);
640      });
641
642      it('should resolve keep config correctly, not starts with "!" and contains "*" and "?"', () => {
643        const keepConfigs = ['test/*/exclude.js', 'test/?/include.js'];
644        const configs = new MergedConfig();
645        const configPath = './test/testData/obfuscation/keepDts/obfuscation-template.txt';
646
647        newObConfigResolver.resolveKeepConfig(keepConfigs, configs, configPath);
648
649        expect(configs.excludeUniversalPaths).to.have.lengthOf(0);
650        expect(configs.keepUniversalPaths).to.have.lengthOf(2);
651        expect(configs.excludePathSet).to.have.lengthOf(0);
652        expect(configs.keepSourceOfPaths).to.have.lengthOf(0);
653      });
654
655      it('should resolve keep config correctly, starts with "!", not starts with "!" and contains "*"', () => {
656        const keepConfigs = ['!test/exclude.js', 'test/*/include.js'];
657        const configs = new MergedConfig();
658        const configPath = './test/testData/obfuscation/keepDts/obfuscation-template.txt';
659
660        newObConfigResolver.resolveKeepConfig(keepConfigs, configs, configPath);
661
662        expect(configs.excludeUniversalPaths).to.have.lengthOf(0);
663        expect(configs.keepUniversalPaths).to.have.lengthOf(1);
664        expect(configs.excludePathSet).to.have.lengthOf(1);
665        expect(configs.keepSourceOfPaths).to.have.lengthOf(0);
666      });
667
668      it('should resolve keep config correctly, starts with "!" and not starts with "!"', () => {
669        const keepConfigs = ['!test/exclude.js', 'test/include.js'];
670        const configs = new MergedConfig();
671        const configPath = './test/testData/obfuscation/keepDts/obfuscation-template.txt';
672
673        newObConfigResolver.resolveKeepConfig(keepConfigs, configs, configPath);
674
675        expect(configs.excludeUniversalPaths).to.have.lengthOf(0);
676        expect(configs.keepUniversalPaths).to.have.lengthOf(0);
677        expect(configs.excludePathSet).to.have.lengthOf(1);
678        expect(configs.keepSourceOfPaths).to.have.lengthOf(0);
679      });
680
681      it('should resolve keep config correctly, both start with "!"', () => {
682        const keepConfigs = ['!test/exclude.js', '!test/include.js'];
683        const configs = new MergedConfig();
684        const configPath = './test/testData/obfuscation/keepDts/obfuscation-template.txt';
685
686        newObConfigResolver.resolveKeepConfig(keepConfigs, configs, configPath);
687
688        expect(configs.excludeUniversalPaths).to.have.lengthOf(0);
689        expect(configs.keepUniversalPaths).to.have.lengthOf(0);
690        expect(configs.excludePathSet).to.have.lengthOf(2);
691        expect(configs.keepSourceOfPaths).to.have.lengthOf(0);
692      });
693    });
694
695    describe('resolvePath', () => {
696      it('should return the absolute path if token is already an absolute path', () => {
697        const configPath = '/home/user/config.json';
698        const token = '/home/user/data.txt';
699        const result = newObConfigResolver.resolvePathForTest(configPath, token);
700        expect(result).to.equal(token);
701      });
702
703      it('should resolve the relative path based on the config file directory', () => {
704        const configPath = '/home/user/config.json';
705        const token = 'data.txt';
706        const expectedResult = '/home/user/data.txt';
707        const result = newObConfigResolver.resolvePathForTest(configPath, token);
708        expect(result).to.equal(expectedResult);
709      });
710    });
711
712    describe('getTokenType', () => {
713      it('should return the correct OptionType for each token', () => {
714        const tokens = [
715          ObConfigResolver.KEEP_DTS,
716          ObConfigResolver.KEEP_GLOBAL_NAME,
717          ObConfigResolver.KEEP_PROPERTY_NAME,
718          ObConfigResolver.KEEP_FILE_NAME,
719          ObConfigResolver.KEEP_COMMENTS,
720          ObConfigResolver.DISABLE_OBFUSCATION,
721          ObConfigResolver.ENABLE_PROPERTY_OBFUSCATION,
722          ObConfigResolver.ENABLE_STRING_PROPERTY_OBFUSCATION,
723          ObConfigResolver.ENABLE_TOPLEVEL_OBFUSCATION,
724          ObConfigResolver.ENABLE_FILENAME_OBFUSCATION,
725          ObConfigResolver.ENABLE_EXPORT_OBFUSCATION,
726          ObConfigResolver.ENABLE_LIB_OBFUSCATION_OPTIONS,
727          ObConfigResolver.REMOVE_COMMENTS,
728          ObConfigResolver.COMPACT,
729          ObConfigResolver.REMOVE_LOG,
730          ObConfigResolver.PRINT_NAMECACHE,
731          ObConfigResolver.PRINT_KEPT_NAMES,
732          ObConfigResolver.APPLY_NAMECACHE,
733          ObConfigResolver.KEEP,
734          ObConfigResolver.EXTRA_OPTIONS,
735          ObConfigResolver.STRIP_LANGUAGE_DEFAULT,
736          ObConfigResolver.STRIP_SYSTEM_API_ARGS,
737          'unknown_token'
738        ];
739
740        const expectedResults = [
741          OptionTypeForTest.KEEP_DTS,
742          OptionTypeForTest.KEEP_GLOBAL_NAME,
743          OptionTypeForTest.KEEP_PROPERTY_NAME,
744          OptionTypeForTest.KEEP_FILE_NAME,
745          OptionTypeForTest.KEEP_COMMENTS,
746          OptionTypeForTest.DISABLE_OBFUSCATION,
747          OptionTypeForTest.ENABLE_PROPERTY_OBFUSCATION,
748          OptionTypeForTest.ENABLE_STRING_PROPERTY_OBFUSCATION,
749          OptionTypeForTest.ENABLE_TOPLEVEL_OBFUSCATION,
750          OptionTypeForTest.ENABLE_FILENAME_OBFUSCATION,
751          OptionTypeForTest.ENABLE_EXPORT_OBFUSCATION,
752          OptionTypeForTest.ENABLE_LIB_OBFUSCATION_OPTIONS,
753          OptionTypeForTest.REMOVE_COMMENTS,
754          OptionTypeForTest.COMPACT,
755          OptionTypeForTest.REMOVE_LOG,
756          OptionTypeForTest.PRINT_NAMECACHE,
757          OptionTypeForTest.PRINT_KEPT_NAMES,
758          OptionTypeForTest.APPLY_NAMECACHE,
759          OptionTypeForTest.KEEP,
760          OptionTypeForTest.EXTRA_OPTIONS,
761          OptionTypeForTest.STRIP_LANGUAGE_DEFAULT,
762          OptionTypeForTest.STRIP_SYSTEM_API_ARGS,
763          OptionTypeForTest.NONE
764        ];
765
766        for (let i = 0; i < tokens.length; i++) {
767          const result = newObConfigResolver.getTokenTypeForTest(tokens[i]);
768          expect(result).to.equal(expectedResults[i]);
769        }
770      });
771    });
772
773    describe('handleConfigContent', () => {
774      it('should handle config content correctly', () => {
775        const configs: MergedConfig = new MergedConfig();
776        configs.options = new ObOptionsForTest();
777
778        const configPath = './test/testData/obfuscation/keepDts/obfuscation-template.txt';
779        const data = `
780          #This is a comment
781          -none,
782          -keep obfuscation-template.txt,
783          -keep-dts,
784          -keep-global-name,
785          -keep-property-name,
786          -keep-file-name,
787          -keep-comments,
788          -disable-obfuscation,
789          -enable-property-obfuscation,
790          -enable-string-property-obfuscation,
791          -enable-toplevel-obfuscation,
792          -enable-filename-obfuscation,
793          -enable-export-obfuscation,
794          -enable-lib-obfuscation-options,
795          -use-keep-in-source,
796          -extra-options strip-language-default,
797          -extra-options strip-system-api-args,
798          -keep-parameter-names
799          -remove-comments,
800          -compact,
801          -remove-log,
802          -print-namecache obfuscation-template.txt,
803          -print-kept-names,
804          -apply-namecache obfuscation-template.txt
805        `;
806
807        newObConfigResolver.handleConfigContentForTest(data, configs, configPath);
808
809        expect(configs.options.disableObfuscation).to.be.true;
810        expect(configs.options.enablePropertyObfuscation).to.be.true;
811        expect(configs.options.enableStringPropertyObfuscation).to.be.true;
812        expect(configs.options.enableToplevelObfuscation).to.be.true;
813        expect(configs.options.removeComments).to.be.true;
814        expect(configs.options.enableFileNameObfuscation).to.be.true;
815        expect(configs.options.enableExportObfuscation).to.be.true;
816        expect(configs.options.enableLibObfuscationOptions).to.be.true;
817        expect(configs.options.enableAtKeep).to.be.true;
818        expect(configs.options.compact).to.be.true;
819        expect(configs.options.removeLog).to.be.true;
820        expect(configs.options.stripLanguageDefault).to.be.true;
821        expect(configs.options.stripSystemApiArgs).to.be.true;
822        expect(configs.options.keepParameterNames).to.be.true;
823      });
824
825      it('should handle config content correctly when diffenent combinations use line break splitting', () => {
826        const configs: MergedConfig = new MergedConfig();
827        configs.options = new ObOptionsForTest();
828
829        const configPath = './test/testData/obfuscation/keepDts/obfuscation-template.txt';
830        const data = `
831          #This is a comment
832          -extra-options
833          strip-language-default
834          -extra-options
835          strip-system-api-args
836        `;
837
838        newObConfigResolver.handleConfigContentForTest(data, configs, configPath);
839
840        expect(configs.options.stripLanguageDefault).to.be.true;
841        expect(configs.options.stripSystemApiArgs).to.be.true;
842      });
843
844      it('should handle config content correctly when diffenent combinations use line break splitting', () => {
845        const configs: MergedConfig = new MergedConfig();
846        configs.options = new ObOptionsForTest();
847
848        const configPath = './test/testData/obfuscation/keepDts/obfuscation-template.txt';
849        const data = `
850          #This is a comment
851          -extra-options strip-language-default
852          -extra-options strip-system-api-args
853        `;
854
855        newObConfigResolver.handleConfigContentForTest(data, configs, configPath);
856
857        expect(configs.options.stripLanguageDefault).to.be.true;
858        expect(configs.options.stripSystemApiArgs).to.be.true;
859      });
860
861      it('should handle config content correctly when diffenent combinations use space separation', () => {
862        const configs: MergedConfig = new MergedConfig();
863        configs.options = new ObOptionsForTest();
864
865        const configPath = './test/testData/obfuscation/keepDts/obfuscation-template.txt';
866        const data = `
867          #This is a comment
868          -extra-options strip-language-default -extra-options strip-system-api-args
869        `;
870
871        newObConfigResolver.handleConfigContentForTest(data, configs, configPath);
872
873        expect(configs.options.stripLanguageDefault).to.be.true;
874        expect(configs.options.stripSystemApiArgs).to.be.true;
875      });
876
877      it('should handle config content correctly when diffenent combinations only use one "-extra-options"', () => {
878        const configs: MergedConfig = new MergedConfig();
879        configs.options = new ObOptionsForTest();
880
881        const configPath = './test/testData/obfuscation/keepDts/obfuscation-template.txt';
882        const data = `
883          #This is a comment
884          -extra-options strip-language-default, strip-system-api-args
885        `;
886
887        newObConfigResolver.handleConfigContentForTest(data, configs, configPath);
888
889        expect(configs.options.stripLanguageDefault).to.be.true;
890        expect(configs.options.stripSystemApiArgs).to.be.true;
891      });
892
893      it('should handle config content correctly when no "-extra-options"', () => {
894        const configs: MergedConfig = new MergedConfig();
895        configs.options = new ObOptionsForTest();
896
897        const configPath = './test/testData/obfuscation/keepDts/obfuscation-template.txt';
898        const data = `
899          #This is a comment
900          strip-language-default, strip-system-api-args
901        `;
902
903        newObConfigResolver.handleConfigContentForTest(data, configs, configPath);
904
905        expect(configs.options.stripLanguageDefault).to.be.false;
906        expect(configs.options.stripSystemApiArgs).to.be.false;
907      });
908
909      it('should handle config content correctly when have invalid token between "-extra-options" and "strip-language-default"', () => {
910        const configs: MergedConfig = new MergedConfig();
911        configs.options = new ObOptionsForTest();
912
913        const configPath = './test/testData/obfuscation/keepDts/obfuscation-template.txt';
914        const data = `
915          #This is a comment
916          -extra-options
917          invalid-token
918          strip-system-api-args
919          strip-language-default
920        `;
921
922        newObConfigResolver.handleConfigContentForTest(data, configs, configPath);
923
924        expect(configs.options.stripLanguageDefault).to.be.false;
925        expect(configs.options.stripSystemApiArgs).to.be.false;
926      });
927
928      it('should handle config content correctly when have valid token between "-extra-options" and "strip-language-default"', () => {
929        const configs: MergedConfig = new MergedConfig();
930        configs.options = new ObOptionsForTest();
931
932        const configPath = './test/testData/obfuscation/keepDts/obfuscation-template.txt';
933        const data = `
934          #This is a comment
935          -extra-options
936          -enable-property-obfuscation
937          strip-language-default
938          strip-system-api-args
939        `;
940
941        newObConfigResolver.handleConfigContentForTest(data, configs, configPath);
942
943        expect(configs.options.enablePropertyObfuscation).to.be.true;
944        expect(configs.options.stripLanguageDefault).to.be.false;
945        expect(configs.options.stripSystemApiArgs).to.be.false;
946      });
947
948      it('should handle config content correctly when enable lib obfuscation options', () => {
949        const configs: MergedConfig = new MergedConfig();
950        configs.options = new ObOptionsForTest();
951
952        const configPath = './test/testData/obfuscation/enable_lib_obfuscation_options/obfuscation-rule.txt';
953        const data = fs.readFileSync(configPath, 'utf-8');
954
955        newObConfigResolver.handleConfigContentForTest(data, configs, configPath);
956
957        expect(configs.options.enableLibObfuscationOptions).to.be.true;
958      });
959    });
960
961    describe('1: test Api collectSystemApiWhitelist', function() {
962      it('1-1: test collectSystemApiWhitelist: -enable-property-obfuscation', function () {
963        let obfuscationCacheDir = path.join(OBFUSCATE_TESTDATA_DIR, 'property');
964        let obfuscationOptions = {
965          'selfConfig': {
966            'ruleOptions': {
967              'enable': true,
968              'rules': [
969                path.join(OBFUSCATE_TESTDATA_DIR, 'property/property.txt')
970              ]
971            },
972            'consumerRules': [],
973          },
974          'dependencies': {
975            'libraries': [],
976            'hars': []
977          },
978          'obfuscationCacheDir': obfuscationCacheDir,
979          'sdkApis': [
980            path.join(OBFUSCATE_TESTDATA_DIR, 'system_api.d.ts')
981          ]
982        };
983        let projectConfig = {
984          obfuscationOptions,
985          compileHar: false
986        };
987        const obConfig: ObConfigResolver =  new ObConfigResolver(projectConfig, undefined);
988        obConfig.resolveObfuscationConfigs();
989        const reservedSdkApiForProp = UnobfuscationCollections.reservedSdkApiForProp;
990        const reservedSdkApiForGlobal = UnobfuscationCollections.reservedSdkApiForGlobal;
991        const reservedSdkApiForLocal = UnobfuscationCollections.reservedSdkApiForLocal;
992
993        expect(reservedSdkApiForProp.size == 12).to.be.true;
994        expect(reservedSdkApiForProp.has('TestClass')).to.be.true;
995        expect(reservedSdkApiForProp.has('para1')).to.be.true;
996        expect(reservedSdkApiForProp.has('para2')).to.be.true;
997        expect(reservedSdkApiForProp.has('foo')).to.be.true;
998        expect(reservedSdkApiForProp.has('TestFunction')).to.be.true;
999        expect(reservedSdkApiForProp.has('funcPara1')).to.be.true;
1000        expect(reservedSdkApiForProp.has('funcPara2')).to.be.true;
1001        expect(reservedSdkApiForProp.has('ns')).to.be.true;
1002        expect(reservedSdkApiForProp.has('NavigationBuilderRegister')).to.be.true;
1003        expect(reservedSdkApiForProp.has('ViewV2')).to.be.true;
1004        expect(reservedSdkApiForProp.has('initParam')).to.be.true;
1005        expect(reservedSdkApiForProp.has('updateParam')).to.be.true;
1006        expect(reservedSdkApiForProp.has('NavigationBuilderRegister')).to.be.true;
1007        expect(reservedSdkApiForProp.has('__Recycle__')).to.be.false;
1008        expect(reservedSdkApiForGlobal.size === 0).to.be.true;
1009        expect(reservedSdkApiForLocal.size === 8).to.be.true;
1010        UnobfuscationCollections.clear();
1011
1012        let systemApiPath = obfuscationCacheDir + '/systemApiCache.json';
1013        const data = fs.readFileSync(systemApiPath, 'utf8');
1014        const systemApiContent = JSON.parse(data);
1015
1016        expect(systemApiContent.ReservedPropertyNames.length == 12).to.be.true;
1017        expect(systemApiContent.ReservedPropertyNames.includes('TestClass')).to.be.true;
1018        expect(systemApiContent.ReservedPropertyNames.includes('para1')).to.be.true;
1019        expect(systemApiContent.ReservedPropertyNames.includes('para2')).to.be.true;
1020        expect(systemApiContent.ReservedPropertyNames.includes('foo')).to.be.true;
1021        expect(systemApiContent.ReservedPropertyNames.includes('TestFunction')).to.be.true;
1022        expect(systemApiContent.ReservedPropertyNames.includes('funcPara1')).to.be.true;
1023        expect(systemApiContent.ReservedPropertyNames.includes('funcPara2')).to.be.true;
1024        expect(systemApiContent.ReservedPropertyNames.includes('ns')).to.be.true;
1025        expect(systemApiContent.ReservedPropertyNames.includes('NavigationBuilderRegister')).to.be.true;
1026        expect(systemApiContent.ReservedPropertyNames.includes('ViewV2')).to.be.true;
1027        expect(systemApiContent.ReservedPropertyNames.includes('initParam')).to.be.true;
1028        expect(systemApiContent.ReservedPropertyNames.includes('updateParam')).to.be.true;
1029        expect(systemApiContent.ReservedPropertyNames.includes('NavigationBuilderRegister')).to.be.true;
1030        expect(systemApiContent.ReservedPropertyNames.includes('__Recycle__')).to.be.false;
1031        expect(systemApiContent.ReservedGlobalNames === undefined).to.be.true;
1032        expect(systemApiContent.ReservedLocalNames.size === 8).to.be.false;
1033
1034        fs.unlinkSync(systemApiPath);
1035      });
1036
1037      it('1-2: test collectSystemApiWhitelist: -enable-export-obfuscation', function () {
1038        let obfuscationCacheDir = path.join(OBFUSCATE_TESTDATA_DIR, 'export');
1039        let obfuscationOptions = {
1040          'selfConfig': {
1041            'ruleOptions': {
1042              'enable': true,
1043              'rules': [
1044                path.join(OBFUSCATE_TESTDATA_DIR, 'export/export.txt')
1045              ]
1046            },
1047            'consumerRules': [],
1048          },
1049          'dependencies': {
1050            'libraries': [],
1051            'hars': []
1052          },
1053          'obfuscationCacheDir': obfuscationCacheDir,
1054          'sdkApis': [
1055            path.join(OBFUSCATE_TESTDATA_DIR, 'system_api.d.ts')
1056          ]
1057        };
1058        let projectConfig = {
1059          obfuscationOptions,
1060          compileHar: false
1061        };
1062        const obConfig: ObConfigResolver =  new ObConfigResolver(projectConfig, undefined);
1063        obConfig.resolveObfuscationConfigs();
1064        const reservedSdkApiForProp = UnobfuscationCollections.reservedSdkApiForProp;
1065        const reservedSdkApiForGlobal = UnobfuscationCollections.reservedSdkApiForGlobal;
1066
1067        expect(reservedSdkApiForProp.size == 0).to.be.true;
1068        expect(reservedSdkApiForGlobal.size == 0).to.be.true;
1069        UnobfuscationCollections.clear();
1070
1071        let systemApiPath = obfuscationCacheDir + '/systemApiCache.json';
1072        const noSystemApi = fs.existsSync(systemApiPath);
1073
1074        expect(noSystemApi).to.be.false;
1075      });
1076
1077      it('1-3: test collectSystemApiWhitelist: -enable-export-obfuscation -enable-toplevel-obfuscation', function () {
1078        let obfuscationCacheDir = path.join(OBFUSCATE_TESTDATA_DIR, 'export_toplevel');
1079        let obfuscationOptions = {
1080          'selfConfig': {
1081            'ruleOptions': {
1082              'enable': true,
1083              'rules': [
1084                path.join(OBFUSCATE_TESTDATA_DIR, 'export_toplevel/export_toplevel.txt')
1085              ]
1086            },
1087            'consumerRules': [],
1088          },
1089          'dependencies': {
1090            'libraries': [],
1091            'hars': []
1092          },
1093          'obfuscationCacheDir': obfuscationCacheDir,
1094          'sdkApis': [
1095            path.join(OBFUSCATE_TESTDATA_DIR, 'system_api.d.ts')
1096          ]
1097        };
1098        let projectConfig = {
1099          obfuscationOptions,
1100          compileHar: false
1101        };
1102        const obConfig: ObConfigResolver =  new ObConfigResolver(projectConfig, undefined);
1103        obConfig.resolveObfuscationConfigs();
1104        const reservedSdkApiForProp = UnobfuscationCollections.reservedSdkApiForProp;
1105        const reservedSdkApiForGlobal = UnobfuscationCollections.reservedSdkApiForGlobal;
1106
1107        expect(reservedSdkApiForProp.size == 0).to.be.true;
1108        expect(reservedSdkApiForGlobal.size == 3).to.be.true;
1109        expect(reservedSdkApiForGlobal.has('TestClass')).to.be.true;
1110        expect(reservedSdkApiForGlobal.has('TestFunction')).to.be.true;
1111        expect(reservedSdkApiForGlobal.has('ns')).to.be.true;
1112        UnobfuscationCollections.clear();
1113
1114        let systemApiPath = obfuscationCacheDir + '/systemApiCache.json';
1115        const data = fs.readFileSync(systemApiPath, 'utf8');
1116        const systemApiContent = JSON.parse(data);
1117
1118        expect(systemApiContent.ReservedPropertyNames == undefined).to.be.true;
1119        expect(systemApiContent.ReservedGlobalNames.length == 3).to.be.true;
1120        expect(systemApiContent.ReservedGlobalNames.includes('TestClass')).to.be.true;
1121        expect(systemApiContent.ReservedGlobalNames.includes('TestFunction')).to.be.true;
1122        expect(systemApiContent.ReservedGlobalNames.includes('ns')).to.be.true;
1123
1124        fs.unlinkSync(systemApiPath);
1125      });
1126
1127      it('1-4: test collectSystemApiWhitelist: system api when -enable-property-obfuscation and -extra-options strip-system-api-args', function () {
1128        let obfuscationCacheDir = path.join(OBFUSCATE_TESTDATA_DIR, 'system_api_optimize');
1129        let obfuscationOptions = {
1130          'selfConfig': {
1131            'ruleOptions': {
1132              'enable': true,
1133              'rules': [
1134                path.join(OBFUSCATE_TESTDATA_DIR, 'system_api_optimize/system_api_optimize.txt')
1135              ]
1136            },
1137            'consumerRules': [],
1138          },
1139          'dependencies': {
1140            'libraries': [],
1141            'hars': []
1142          },
1143          'obfuscationCacheDir': obfuscationCacheDir,
1144          'sdkApis': [
1145            path.join(OBFUSCATE_TESTDATA_DIR, 'system_api.d.ts')
1146          ]
1147        };
1148        let projectConfig = {
1149          obfuscationOptions,
1150          compileHar: false
1151        };
1152        const obConfig: ObConfigResolver =  new ObConfigResolver(projectConfig, undefined);
1153        obConfig.resolveObfuscationConfigs();
1154        const reservedSdkApiForProp = UnobfuscationCollections.reservedSdkApiForProp;
1155        const reservedSdkApiForGlobal = UnobfuscationCollections.reservedSdkApiForGlobal;
1156        const reservedSdkApiForLocal = UnobfuscationCollections.reservedSdkApiForLocal;
1157
1158        expect(reservedSdkApiForProp.has('TestClass')).to.be.true;
1159        expect(reservedSdkApiForProp.has('para1')).to.be.true;
1160        expect(reservedSdkApiForProp.has('para2')).to.be.true;
1161        expect(reservedSdkApiForProp.has('foo')).to.be.true;
1162        expect(reservedSdkApiForProp.has('TestFunction')).to.be.true;
1163        expect(reservedSdkApiForProp.has('funcPara1')).to.be.false;
1164        expect(reservedSdkApiForProp.has('funcPara2')).to.be.false;
1165        expect(reservedSdkApiForProp.has('ns')).to.be.true;
1166        expect(reservedSdkApiForProp.has('NavigationBuilderRegister')).to.be.true;
1167        expect(reservedSdkApiForProp.has('__Recycle__')).to.be.true;
1168        expect(reservedSdkApiForGlobal.size == 0).to.be.true;
1169        expect(reservedSdkApiForLocal.size == 0).to.be.true;
1170        UnobfuscationCollections.clear();
1171
1172        let systemApiPath = obfuscationCacheDir + '/systemApiCache.json';
1173        const data = fs.readFileSync(systemApiPath, 'utf8');
1174        const systemApiContent = JSON.parse(data);
1175
1176        expect(systemApiContent.ReservedPropertyNames.includes('TestClass')).to.be.true;
1177        expect(systemApiContent.ReservedPropertyNames.includes('para1')).to.be.true;
1178        expect(systemApiContent.ReservedPropertyNames.includes('para2')).to.be.true;
1179        expect(systemApiContent.ReservedPropertyNames.includes('foo')).to.be.true;
1180        expect(systemApiContent.ReservedPropertyNames.includes('TestFunction')).to.be.true;
1181        expect(systemApiContent.ReservedPropertyNames.includes('funcPara1')).to.be.false;
1182        expect(systemApiContent.ReservedPropertyNames.includes('funcPara2')).to.be.false;
1183        expect(systemApiContent.ReservedPropertyNames.includes('ns')).to.be.true;
1184        expect(systemApiContent.ReservedPropertyNames.includes('NavigationBuilderRegister')).to.be.true;
1185        expect(systemApiContent.ReservedPropertyNames.includes('__Recycle__')).to.be.true;
1186        expect(systemApiContent.ReservedGlobalNames === undefined).to.be.true;
1187        expect(systemApiContent.ReservedLocalNames === undefined).to.be.true;
1188
1189        fs.unlinkSync(systemApiPath);
1190      });
1191    });
1192  });
1193
1194  describe('getMergedConfigs', function() {
1195    it('should merge all configs', () => {
1196      const config1 = new MergedConfig();
1197      config1.options.enableLibObfuscationOptions = true;
1198
1199      const config2 = new MergedConfig();
1200      config2.options.enablePropertyObfuscation = true;
1201      config2.options.enableStringPropertyObfuscation = true;
1202      config2.options.enableToplevelObfuscation = true;
1203      config2.options.compact = true;
1204      config2.options.removeLog = true;
1205      config2.reservedPropertyNames = ['prop2'];
1206      config2.reservedGlobalNames = ['global2'];
1207      config2.keepComments = ['comment2'];
1208      config2.excludePathSet.add('path2');
1209
1210      const projectConfig = {
1211        obfuscationOptions: { option1: 'value1' },
1212        compileHar: false
1213      };
1214      const logger = console;
1215      const isTerser = false;
1216      let newObConfigResolver = new ObConfigResolver(projectConfig, printObfLogger, isTerser);
1217      const res: MergedConfig = newObConfigResolver.getMergedConfigsForTest(config1, config2);
1218
1219      expect(res.options.enablePropertyObfuscation).to.be.true;
1220      expect(res.options.enableStringPropertyObfuscation).to.be.true;
1221      expect(res.options.enableToplevelObfuscation).to.be.true;
1222      expect(res.options.compact).to.be.true;
1223      expect(res.options.removeLog).to.be.true;
1224      expect(res.reservedPropertyNames).to.deep.equal(['prop2']);
1225      expect(res.reservedGlobalNames).to.deep.equal(['global2']);
1226      expect(res.keepComments).to.deep.equal(['comment2']);
1227      expect(res.excludePathSet).to.deep.equal(new Set(['path2']));
1228    });
1229
1230    it('should merge only keep configs', () => {
1231      const config1 = new MergedConfig();
1232      config1.options.enableLibObfuscationOptions = false;
1233
1234      const config2 = new MergedConfig();
1235      config2.options.enablePropertyObfuscation = true;
1236      config2.options.enableStringPropertyObfuscation = true;
1237      config2.options.enableToplevelObfuscation = true;
1238      config2.options.compact = true;
1239      config2.options.removeLog = true;
1240      config2.reservedPropertyNames = ['prop2'];
1241      config2.reservedGlobalNames = ['global2'];
1242      config2.keepComments = ['comment2'];
1243      config2.excludePathSet.add('path2');
1244
1245      const projectConfig = {
1246        obfuscationOptions: { option1: 'value1' },
1247        compileHar: false
1248      };
1249      const logger = console;
1250      const isTerser = false;
1251      let newObConfigResolver = new ObConfigResolver(projectConfig, printObfLogger, isTerser);
1252      const res: MergedConfig = newObConfigResolver.getMergedConfigsForTest(config1, config2);
1253
1254      expect(res.options.enablePropertyObfuscation).to.be.false;
1255      expect(res.options.enableStringPropertyObfuscation).to.be.false;
1256      expect(res.options.enableToplevelObfuscation).to.be.false;
1257      expect(res.options.compact).to.be.false;
1258      expect(res.options.removeLog).to.be.false;
1259      expect(res.reservedPropertyNames).to.deep.equal(['prop2']);
1260      expect(res.reservedGlobalNames).to.deep.equal(['global2']);
1261      expect(res.keepComments).to.deep.equal(['comment2']);
1262      expect(res.excludePathSet).to.deep.equal(new Set(['path2']));
1263    });
1264  });
1265
1266  describe('genConsumerConfigFilesForTest', function (){
1267    it('should merge all configs: compileHar is true', () => {
1268      const config1 = new MergedConfig();
1269      config1.options.enableLibObfuscationOptions = true;
1270
1271      const config2 = new MergedConfig();
1272      config2.options.enablePropertyObfuscation = true;
1273      config2.options.enableStringPropertyObfuscation = true;
1274      config2.options.enableToplevelObfuscation = true;
1275      config2.options.compact = true;
1276      config2.options.removeLog = true;
1277      config2.reservedPropertyNames = ['prop2'];
1278      config2.reservedGlobalNames = ['global2'];
1279      config2.keepComments = ['comment2'];
1280      config2.excludePathSet.add('path2');
1281
1282      const projectConfig = {
1283        obfuscationOptions: { option1: 'value1' },
1284        compileHar: true
1285      };
1286      const logger = console;
1287      const isTerser = false;
1288      let newObConfigResolver = new ObConfigResolver(projectConfig, printObfLogger, isTerser);
1289      const sourceObConfig: SourceObConfig = {
1290        selfConfig: {} as Obfuscation,
1291        sdkApis: [],
1292        obfuscationCacheDir: '',
1293        exportRulePath: './test/ut/initialization/obfuscation.txt',
1294        dependencies: {
1295          libraries: [],
1296          hars: [],
1297          hsps: [],
1298          hspLibraries: [],
1299        },
1300      };
1301      newObConfigResolver.genConsumerConfigFilesForTest(sourceObConfig, config1, config2);
1302
1303      let res: string = fs.readFileSync(sourceObConfig.exportRulePath, 'utf-8');
1304      expect(res.indexOf('-enable-lib-obfuscation-options') === -1).to.be.true;
1305      expect(res.indexOf('-enable-property-obfuscation') !== -1).to.be.true;
1306      expect(res.indexOf('-enable-string-property-obfuscation') !== -1).to.be.true;
1307      expect(res.indexOf('-enable-toplevel-obfuscation') !== -1).to.be.true;
1308      expect(res.indexOf('-compact') !== -1).to.be.true;
1309      expect(res.indexOf('-remove-log') !== -1).to.be.true;
1310      expect(res.indexOf('-keep-global-name') !== -1).to.be.true;
1311      expect(res.indexOf('global2') !== -1).to.be.true;
1312      expect(res.indexOf('-keep-property-name') !== -1).to.be.true;
1313      expect(res.indexOf('prop2') !== -1).to.be.true;
1314
1315      fs.unlinkSync(sourceObConfig.exportRulePath);
1316    });
1317
1318    it('should merge all configs: compileHar is false', () => {
1319      const config1 = new MergedConfig();
1320      config1.options.enableLibObfuscationOptions = false;
1321
1322      const config2 = new MergedConfig();
1323      config2.options.enablePropertyObfuscation = true;
1324      config2.options.enableStringPropertyObfuscation = true;
1325      config2.options.enableToplevelObfuscation = true;
1326      config2.options.compact = true;
1327      config2.options.removeLog = true;
1328      config2.reservedPropertyNames = ['prop2'];
1329      config2.reservedGlobalNames = ['global2'];
1330      config2.keepComments = ['comment2'];
1331      config2.excludePathSet.add('path2');
1332
1333      const projectConfig = {
1334        obfuscationOptions: { option1: 'value1' },
1335        compileHar: false
1336      };
1337      const logger = console;
1338      const isTerser = false;
1339      let newObConfigResolver = new ObConfigResolver(projectConfig, printObfLogger, isTerser);
1340      const sourceObConfig: SourceObConfig = {
1341        selfConfig: {} as Obfuscation,
1342        sdkApis: [],
1343        obfuscationCacheDir: '',
1344        exportRulePath: './test/ut/initialization/obfuscation.txt',
1345        dependencies: {
1346          libraries: [],
1347          hars: [],
1348          hsps: [],
1349          hspLibraries: [],
1350        },
1351      };
1352      newObConfigResolver.genConsumerConfigFilesForTest(sourceObConfig, config1, config2);
1353
1354      let res: string = fs.readFileSync(sourceObConfig.exportRulePath, 'utf-8');
1355
1356      expect(res.indexOf('-enable-lib-obfuscation-options') === -1).to.be.true;
1357      expect(res.indexOf('-enable-property-obfuscation') === -1).to.be.true;
1358      expect(res.indexOf('-enable-string-property-obfuscation') === -1).to.be.true;
1359      expect(res.indexOf('-enable-toplevel-obfuscation') === -1).to.be.true;
1360      expect(res.indexOf('-compact') === -1).to.be.true;
1361      expect(res.indexOf('-remove-log') === -1).to.be.true;
1362      expect(res.indexOf('-keep-global-name') === -1).to.be.true;
1363      expect(res.indexOf('global2') === -1).to.be.true;
1364      expect(res.indexOf('-keep-property-name') === -1).to.be.true;
1365      expect(res.indexOf('prop2') === -1).to.be.true;
1366
1367      fs.unlinkSync(sourceObConfig.exportRulePath);
1368    });
1369  });
1370
1371  describe('collectResevedFileNameInIDEConfig', () => {
1372    let collectPathReservedStringStub;
1373
1374    beforeEach(function() {
1375      collectPathReservedStringStub = sinon.stub(FileUtils, 'collectPathReservedString');
1376      collectPathReservedStringStub.callsFake((filePath, reservedArray) => reservedArray.push(filePath));
1377    });
1378
1379    afterEach(function() {
1380      collectPathReservedStringStub.restore();
1381      projectConfig.compileShared = false;
1382      projectConfig.byteCodeHar = false;
1383      projectConfig.aceModuleJsonPath = '';
1384    });
1385
1386    const ohPackagePath = './test/ut/initialization/testOhPackagePath.json';
1387    let projectConfig = {
1388      aceModuleJsonPath: '',
1389      projectPath: 'path/to/project',
1390      cachePath: 'path/to/cache',
1391      compileShared: false,
1392      byteCodeHar: false,
1393      aceModuleBuild: 'path/to/build',
1394    }
1395    const entryArray = ['path/to/entry1', 'path/to/entry2'];
1396    const modulePathMap = {
1397      module1: 'path/to/module1',
1398      module2: 'path/to/module2',
1399    };
1400
1401    it('should collect reserved file names from entryArray and projectConfig', () => {
1402      const result = collectResevedFileNameInIDEConfig('', projectConfig, undefined, entryArray);
1403      expect(result).to.deep.equal(['path/to/entry1', 'path/to/entry2','path/to/project','path/to/cache']);
1404    });
1405    it('should collect reserved file names from modulePathMap and projectConfig', () => {
1406      projectConfig.compileShared = true;
1407      const result = collectResevedFileNameInIDEConfig('', projectConfig, modulePathMap, []);
1408      expect(result).to.deep.equal(['path/to/module1', 'path/to/module2','module1','module2','path/to/build','etsFortgz','path/to/project','path/to/cache']);
1409    });
1410    it('should collect reserved file names from ohPackagePath and projectConfig', () => {
1411      projectConfig.byteCodeHar = true;
1412      const result = collectResevedFileNameInIDEConfig(ohPackagePath, projectConfig, undefined, []);
1413      expect(result).to.deep.equal(['path/to/main', 'path/to/types','path/to/build','etsFortgz','path/to/project','path/to/cache']);
1414    });
1415    it('should collect reserved file names from moduleJsonPath and projectConfig', () => {
1416      projectConfig.aceModuleJsonPath = "./test/ut/initialization/testModuleJsonPath_0.json";
1417      const hasSrcEntry = collectResevedFileNameInIDEConfig('', projectConfig, undefined, []);
1418      expect(hasSrcEntry).to.deep.equal(['path/to/srcEntry','path/to/project','path/to/cache']);
1419
1420      projectConfig.aceModuleJsonPath = "./test/ut/initialization/testModuleJsonPath_1.json";
1421      const noSrcEntry = collectResevedFileNameInIDEConfig('', projectConfig, undefined, []);
1422      expect(noSrcEntry).to.deep.equal(['path/to/project','path/to/cache']);
1423    });
1424  });
1425
1426  describe('readNameCache', () => {
1427    let tempFilePath;
1428    let testData;
1429    let fsWriteFileSyncStub;
1430    let fsUnlinkSyncStub;
1431    let PropCollections;
1432    let renameFileNameModule;
1433
1434    beforeEach(() => {
1435      PropCollections = {
1436        historyMangledTable: {},
1437      };
1438      renameFileNameModule = {
1439        historyFileNameMangledTable: {},
1440      };
1441
1442      tempFilePath = './test/ut/initialization/tempNameCache.json';
1443      testData = {
1444        compileSdkVersion: '1.0.0',
1445        PropertyCache: { key1: 'value1', key2: 'value2' },
1446        FileNameCache: { key3: 'value3', key4: 'value4' },
1447        extraKey: '',
1448      };
1449
1450      fsWriteFileSyncStub = sinon.stub(fs, 'writeFileSync').callsFake((path, data) => {});
1451      fsUnlinkSyncStub = sinon.stub(fs, 'unlinkSync').callsFake((path) => {});
1452    });
1453
1454    afterEach(() => {
1455      fsWriteFileSyncStub.restore();
1456      fsUnlinkSyncStub.restore();
1457    });
1458
1459    it('should read and parse the name cache file correctly', () => {
1460      readNameCache(tempFilePath, printObfLogger);
1461
1462      expect(nameCacheMap.get('extraKey')).to.equal('');
1463    });
1464  });
1465
1466  describe('readNameCache', () => {
1467    let fsReadFileSyncStub;
1468    let logger;
1469    const testPath = './test/ut/initialization/tempNameCache.json';
1470
1471    beforeEach(() => {
1472      fsReadFileSyncStub = sinon.stub(fs, 'readFileSync').returns('{"compileSdkVersion":"1.0","PropertyCache":{},"FileNameCache":{}}');
1473      logger = { error: sinon.spy(), printError: (msg)=>{}};
1474    });
1475
1476    afterEach(() => {
1477      fsReadFileSyncStub.restore();
1478    });
1479
1480    it('should read the name cache file and parse its content', () => {
1481      readNameCache(testPath, printObfLogger);
1482      expect(PropCollections.historyMangledTable).to.deep.equal(new Map());
1483      expect(renameFileNameModule.historyFileNameMangledTable).to.deep.equal(new Map());
1484      expect(nameCacheMap.get('compileSdkVersion')).to.be.undefined;
1485      expect(logger.error.called).to.be.false;
1486    });
1487
1488    it('should handle errors when reading the file', () => {
1489      const mockLogger = {
1490        error: (message: string) => console.error(message),
1491      };
1492
1493      const nonExistentFilePath = './test/ut/initialization/nonExistentFile.json';
1494      try {
1495        readNameCache(nonExistentFilePath, printObfLogger);
1496      } catch (err) {
1497      }
1498      expect(logger.error.calledWith(`Failed to open ${nonExistentFilePath}. Error message: Test error`)).to.be.false;
1499    });
1500
1501    it('should handle errors when reading the file with hvigor errorCode', () => {
1502      const mockLogger = {
1503        error: (message: string) => console.error(message),
1504      };
1505
1506      const nonExistentFilePath = './test/ut/initialization/nonExistentFile.json';
1507      try {
1508        readNameCache(nonExistentFilePath, printObfHvigorLogger);
1509      } catch (err) {
1510      }
1511      const errorMessage = {
1512        code: '10804002',
1513        description: 'ArkTS compiler Error',
1514        cause: `Failed to open namecache file from ${nonExistentFilePath}, Error message: Test error`,
1515        position: nonExistentFilePath,
1516        solutions: [`Please check ${nonExistentFilePath} as error message suggested.`],
1517      };
1518      expect(logger.error.calledWith(errorMessage)).to.be.false;
1519    });
1520  });
1521
1522  describe('handleUniversalPathInObf', () => {
1523    it('should handle universal paths correctly', () => {
1524      const mergedObConfig: MergedConfig = {
1525        options: new ObOptionsForTest(),
1526        reservedPropertyNames: [],
1527        reservedGlobalNames: [],
1528        reservedNames: [],
1529        reservedFileNames: [],
1530        keepComments: [],
1531        keepSourceOfPaths: [],
1532        universalReservedPropertyNames: [],
1533        universalReservedGlobalNames: [],
1534        keepUniversalPaths: [/test\.js$/],
1535        excludeUniversalPaths: [/exclude\.js$/],
1536        excludePathSet: new Set(),
1537        mergeKeepOptions: () => {},
1538        mergeAllRules: () => {},
1539        sortAndDeduplicate: () => {},
1540        serializeMergedConfig: () => {
1541          return JSON.stringify(this);
1542        }
1543      };
1544      const allSourceFilePaths = new Set([
1545        'test.js',
1546        'exclude.js',
1547        'other.js',
1548      ]);
1549
1550      handleUniversalPathInObf(mergedObConfig, allSourceFilePaths);
1551      expect(mergedObConfig.keepSourceOfPaths).to.deep.equal(['test.js']);
1552      expect(mergedObConfig.excludePathSet).to.deep.equal(new Set(['exclude.js']));
1553    });
1554
1555    it('should return early if mergedObConfig is not provided or both keepUniversalPaths and excludeUniversalPaths are empty', () => {
1556      const mergedObConfig: MergedConfig = {
1557        options: new ObOptionsForTest(),
1558        reservedPropertyNames: [],
1559        reservedGlobalNames: [],
1560        reservedNames: [],
1561        reservedFileNames: [],
1562        keepComments: [],
1563        keepSourceOfPaths: [],
1564        universalReservedPropertyNames: [],
1565        universalReservedGlobalNames: [],
1566        keepUniversalPaths: [],
1567        excludeUniversalPaths: [],
1568        excludePathSet: new Set(),
1569        mergeKeepOptions: () => {},
1570        mergeAllRules: () => {},
1571        sortAndDeduplicate: () => {},
1572        serializeMergedConfig: () => {
1573          return JSON.stringify(this);
1574        }
1575      };
1576      const allSourceFilePaths = new Set([]);
1577      const result = handleUniversalPathInObf(mergedObConfig, allSourceFilePaths);
1578
1579      expect(result).to.be.undefined;
1580    });
1581  });
1582
1583  describe('getArkguardNameCache', () => {
1584    it('should return a JSON string with the correct structure', () => {
1585      const enablePropertyObfuscation = true;
1586      const enableFileNameObfuscation = true;
1587      const sdkVersion = '1.0.0';
1588      const entryPackageInfo = 'packageInfo';
1589
1590      const result = getArkguardNameCache(enablePropertyObfuscation, enableFileNameObfuscation, false, sdkVersion, entryPackageInfo);
1591
1592      try {
1593        JSON.parse(result);
1594        // If no error is thrown, the result is a valid JSON string
1595        console.log('Test passed: getArkguardNameCache returns a valid JSON string');
1596      } catch (error) {
1597        console.error('Test failed: getArkguardNameCache does not return a valid JSON string');
1598      }
1599    });
1600
1601    it('should include the correct compileSdkVersion and entryPackageInfo', () => {
1602      const enablePropertyObfuscation = false;
1603      const enableFileNameObfuscation = false;
1604      const sdkVersion = '2.0.0';
1605      const entryPackageInfo = 'anotherPackageInfo';
1606
1607      const result = getArkguardNameCache(enablePropertyObfuscation, enableFileNameObfuscation, false, sdkVersion, entryPackageInfo);
1608      const parsedResult = JSON.parse(result);
1609
1610      expect(parsedResult.compileSdkVersion).to.equal(sdkVersion);
1611      expect(parsedResult.entryPackageInfo).to.equal(entryPackageInfo);
1612    });
1613
1614    it('PropertyCache exists when enable export obfuscation', () => {
1615      const enablePropertyObfuscation = false;
1616      const enableFileNameObfuscation = false;
1617      const enableExportObfuscation = true;
1618      const sdkVersion = '2.0.0';
1619      const entryPackageInfo = 'anotherPackageInfo';
1620
1621      PropCollections.historyMangledTable.set("key1", "value1");
1622      PropCollections.historyMangledTable.set("key2", "value2");
1623      PropCollections.globalMangledTable.set("key3", "value3");
1624      PropCollections.globalMangledTable.set("key4", "value4");
1625      const result = getArkguardNameCache(enablePropertyObfuscation, enableFileNameObfuscation, enableExportObfuscation, sdkVersion, entryPackageInfo);
1626      const parsedResult = JSON.parse(result);
1627      PropCollections.historyMangledTable.clear();
1628      PropCollections.globalMangledTable.clear();
1629      expect('PropertyCache' in parsedResult).to.be.true;
1630      expect(parsedResult.PropertyCache.key1 === "value1").to.be.true;
1631      expect(parsedResult.PropertyCache.key2 === "value2").to.be.true;
1632      expect(parsedResult.PropertyCache.key3 === "value3").to.be.true;
1633      expect(parsedResult.PropertyCache.key4 === "value4").to.be.true;
1634    });
1635  });
1636
1637  describe('fillNameCache', function() {
1638    it('should correctly fill the name cache with the given table entries', function() {
1639      const table = new Map([
1640        ['key1', 'value1'],
1641        ['key2', 'value2'],
1642        ['key3', 'value3']
1643      ]);
1644      const nameCache = new Map();
1645
1646      fillNameCache(table, nameCache);
1647
1648      assert.deepEqual(nameCache, table);
1649    });
1650
1651    it('should handle empty tables gracefully', function() {
1652      const table = new Map();
1653      const nameCache = new Map();
1654
1655      fillNameCache(table, nameCache);
1656
1657      assert.deepEqual(nameCache, table);
1658    });
1659  });
1660
1661  describe('writeObfuscationNameCache', () => {
1662    let existsSyncSpy;
1663    let mkdirSyncSpy;
1664    let writeFileSyncSpy;
1665
1666    beforeEach(function() {
1667      existsSyncSpy = sinon.spy(fs, 'existsSync');
1668      mkdirSyncSpy = sinon.spy(fs, 'mkdirSync');
1669      writeFileSyncSpy = sinon.spy(fs, 'writeFileSync');
1670    });
1671
1672    afterEach(function() {
1673      existsSyncSpy.restore();
1674      mkdirSyncSpy.restore();
1675      writeFileSyncSpy.restore();
1676    });
1677
1678    it('should not write cache if projectConfig.arkObfuscator is false', () => {
1679      const projectConfig = {
1680        arkObfuscator: false,
1681        obfuscationMergedObConfig: {
1682          options: {
1683            enablePropertyObfuscation: true,
1684            enableFileNameObfuscation: true,
1685          },
1686        },
1687        etsLoaderVersion: '1.0.0',
1688      };
1689      const entryPackageInfo = 'testEntryPackageInfo';
1690      const obfuscationCacheDir = 'testCacheDir';
1691      const printNameCache = 'testPrintNameCache';
1692
1693      writeObfuscationNameCache(projectConfig, entryPackageInfo, obfuscationCacheDir, printNameCache);
1694
1695      expect(existsSyncSpy.called).to.be.false;
1696      expect(mkdirSyncSpy.called).to.be.false;
1697      expect(writeFileSyncSpy.called).to.be.false;
1698    });
1699    it('should write cache to obfuscationCacheDir if provided', () => {
1700      const projectConfig = {
1701        arkObfuscator: true,
1702        obfuscationMergedObConfig: {
1703          options: {
1704            enablePropertyObfuscation: true,
1705            enableFileNameObfuscation: true,
1706            enableExportObfusaction: true
1707          },
1708        },
1709        etsLoaderVersion: '1.0.0',
1710      };
1711      const entryPackageInfo = 'testEntryPackageInfo';
1712      const obfuscationCacheDir = './test/ut/initialization/testObfuscationCacheDir';
1713      const printNameCache = '';
1714      writeObfuscationNameCache(projectConfig, entryPackageInfo, obfuscationCacheDir, printNameCache);
1715      const defaultNameCachePath: string = path.join(obfuscationCacheDir, 'nameCache.json');
1716      const expectedContent = {
1717        "extraKey": "",
1718        "compileSdkVersion": "1.0.0",
1719        "entryPackageInfo": "testEntryPackageInfo",
1720        "PropertyCache": {},
1721        "FileNameCache": {}
1722      }
1723      const jsonData = fs.readFileSync(defaultNameCachePath,'utf-8');
1724      const result = JSON.parse(jsonData);
1725      expect(result).to.deep.equal(expectedContent);
1726      fs.unlinkSync(defaultNameCachePath);
1727      fs.rmdirSync(path.dirname(defaultNameCachePath));
1728    });
1729    it('should write cache to printNameCache if provided', () => {
1730      const projectConfig = {
1731        arkObfuscator: true,
1732        obfuscationMergedObConfig: {
1733          options: {
1734            enablePropertyObfuscation: true,
1735            enableFileNameObfuscation: true,
1736          },
1737        },
1738        etsLoaderVersion: '1.0.0',
1739      };
1740      const entryPackageInfo = 'testEntryPackageInfo';
1741      const obfuscationCacheDir = '';
1742      const printNameCache = './test/ut/initialization/printNameCache.json';
1743      writeObfuscationNameCache(projectConfig, entryPackageInfo, obfuscationCacheDir, printNameCache);
1744      const expectedContent = {
1745        "extraKey": "",
1746        "compileSdkVersion": "1.0.0",
1747        "entryPackageInfo": "testEntryPackageInfo",
1748        "PropertyCache": {},
1749        "FileNameCache": {}
1750      }
1751      const jsonData = fs.readFileSync(printNameCache,'utf-8');
1752      const result = JSON.parse(jsonData);
1753      expect(result).to.deep.equal(expectedContent);
1754      expect(existsSyncSpy.called).to.be.false;
1755      expect(mkdirSyncSpy.called).to.be.false;
1756      fs.unlinkSync(printNameCache);
1757    });
1758  });
1759
1760  describe('enableObfuscatedFilePathConfig', () => {
1761    it('should return false if in debug mode or no obfuscation config', () => {
1762      const projectConfig = {
1763        obfuscationMergedObConfig: null,
1764        buildMode: 'debug'
1765      };
1766      const result = enableObfuscatedFilePathConfig(true, projectConfig);
1767      expect(result).to.be.false;
1768    });
1769
1770    it('should return false if obfuscation is disabled or file name obfuscation is not enabled', () => {
1771      const projectConfig = {
1772        obfuscationMergedObConfig: {
1773          options: {
1774            disableObfuscation: true,
1775            enableFileNameObfuscation: false,
1776          },
1777        },
1778        buildMode: 'debug'
1779      };
1780      const result = enableObfuscatedFilePathConfig(false, projectConfig);
1781      expect(result).to.be.false;
1782    });
1783
1784    it('should return true if all conditions are met', () => {
1785      const projectConfig = {
1786        obfuscationMergedObConfig: {
1787          options: {
1788            disableObfuscation: false,
1789            enableFileNameObfuscation: true,
1790          },
1791        },
1792        buildMode: 'not debug'
1793      };
1794      const result = enableObfuscatedFilePathConfig(false, projectConfig);
1795      expect(result).to.be.true;
1796    });
1797  });
1798
1799  describe('handleObfuscatedFilePath', () => {
1800    it('should return the original file path if obfuscation is not enabled', () => {
1801      const filePath = '/path/to/file.js';
1802      const isPackageModules = false;
1803      const projectConfig = {
1804        enableFileNameObfuscation: false,
1805        buildMode: 'debug'
1806      };
1807
1808      const result = handleObfuscatedFilePath(filePath, isPackageModules, projectConfig);
1809
1810      expect(result).to.equal(filePath);
1811    });
1812
1813    it('should return the original file path if obfuscation is not enabled', () => {
1814      const filePath = '/path/to/file.txt';
1815      const isPackageModules = false;
1816      const projectConfig = {
1817        obfuscationMergedObConfig: null,
1818        buildMode: 'debug'
1819      };
1820      const result = handleObfuscatedFilePath(filePath, isPackageModules, projectConfig);
1821      expect(result).to.equal(filePath);
1822    });
1823
1824    it('should return the original file path if obfuscation is not enabled', () => {
1825      const filePath = '/path/to/file.txt';
1826      const isPackageModules = false;
1827      const projectConfig = {
1828        obfuscationMergedObConfig: null,
1829        buildMode: 'not debug'
1830      };
1831      const result = handleObfuscatedFilePath(filePath, isPackageModules, projectConfig);
1832      expect(result).to.equal(filePath);
1833    });
1834
1835    it('should return the unix formatted file path if obfuscation is enabled and is a package module', () => {
1836      const filePath = '/path/to/file.js';
1837      const isPackageModules = true;
1838      const projectConfig = {
1839        obfuscationMergedObConfig: {
1840          options: {
1841            disableObfuscation: false,
1842            enableFileNameObfuscation: true,
1843          },
1844        },
1845        buildMode: "not Debug"
1846      };
1847
1848      const result = handleObfuscatedFilePath(filePath, isPackageModules, projectConfig);
1849
1850      expect(result).to.equal(FileUtils.toUnixPath(filePath));
1851    });
1852
1853    it('should return the unix formatted file path if obfuscation is enabled and is a package module', () => {
1854      const filePath = '/path/to/file.js';
1855      const isPackageModules = true;
1856      const projectConfig = {
1857        obfuscationMergedObConfig: {
1858          options: {
1859            disableObfuscation: false,
1860            enableFileNameObfuscation: true,
1861          },
1862        },
1863        buildMode: ""
1864      };
1865
1866      const result = handleObfuscatedFilePath(filePath, isPackageModules, projectConfig);
1867
1868      expect(result).to.equal(FileUtils.toUnixPath(filePath));
1869    });
1870  });
1871
1872  describe('enableObfuscateFileName', () => {
1873    it('should return false if obfuscation is not enabled', () => {
1874      const isPackageModules = false;
1875      const projectConfig = {
1876        obfuscationMergedObConfig: null,
1877        buildMode: "not Debug"
1878      };
1879      const result = enableObfuscateFileName(isPackageModules, projectConfig);
1880      expect(result).to.be.false;
1881    });
1882
1883    it('should return true if obfuscation is enabled and not a package module', () => {
1884      const isPackageModules = false;
1885      const projectConfig = {
1886        obfuscationMergedObConfig: {
1887          options: {
1888            disableObfuscation: false,
1889            enableFileNameObfuscation: true,
1890          },
1891        },
1892        buildMode: "not Debug"
1893      };
1894      const result = enableObfuscateFileName(isPackageModules, projectConfig);
1895
1896      expect(result).to.be.true;
1897    });
1898
1899    it('should return false if obfuscation is enabled and is a package module', () => {
1900      const isPackageModules = true;
1901      const projectConfig = {
1902        obfuscationMergedObConfig: {
1903          options: {
1904            disableObfuscation: false,
1905            enableFileNameObfuscation: true,
1906          },
1907        },
1908        buildMode: "Debug"
1909      };
1910      const result = enableObfuscateFileName(isPackageModules, projectConfig);
1911      expect(result).to.be.false;
1912    });
1913
1914    it('should return false if obfuscation is enabled and is a package module', () => {
1915      const isPackageModules = true;
1916      const projectConfig = {
1917        obfuscationMergedObConfig: {
1918          options: {
1919            disableObfuscation: false,
1920            enableFileNameObfuscation: false,
1921          },
1922        },
1923        buildMode: "Debug"
1924      };
1925      const result = enableObfuscateFileName(isPackageModules, projectConfig);
1926      expect(result).to.be.false;
1927    });
1928
1929    it('should return false if obfuscation is enabled and is a package module', () => {
1930      const isPackageModules = true;
1931      const projectConfig = {
1932        obfuscationMergedObConfig: {},
1933        buildMode: "Debug"
1934      };
1935      const result = enableObfuscateFileName(isPackageModules, projectConfig);
1936      expect(result).to.be.false;
1937    });
1938
1939    it('should return false if obfuscation is enabled and is a package module', () => {
1940      const isPackageModules = true;
1941      const projectConfig = {
1942        obfuscationMergedObConfig: null,
1943        buildMode: "Debug"
1944      };
1945      const result = enableObfuscateFileName(isPackageModules, projectConfig);
1946      expect(result).to.be.false;
1947    });
1948
1949    it('should return false if obfuscation is enabled and is a package module', () => {
1950      const isPackageModules = true;
1951      const projectConfig = {
1952        obfuscationMergedObConfig: undefined,
1953        buildMode: "Debug"
1954      };
1955      const result = enableObfuscateFileName(isPackageModules, projectConfig);
1956      expect(result).to.be.false;
1957    });
1958
1959    it('should return false if obfuscation is enabled and is a package module', () => {
1960      const isPackageModules = true;
1961      const projectConfig = {
1962        obfuscationMergedObConfig: {
1963          options: {
1964            disableObfuscation: false,
1965            enableFileNameObfuscation: true,
1966          },
1967        },
1968        buildMode: "not Debug"
1969      };
1970      const result = enableObfuscateFileName(isPackageModules, projectConfig);
1971      expect(result).to.be.false;
1972    });
1973
1974    it('should return false if obfuscation is enabled and is a package module', () => {
1975      const isPackageModules = true;
1976      const projectConfig = {
1977        obfuscationMergedObConfig: {
1978          options: {
1979            disableObfuscation: false,
1980            enableFileNameObfuscation: false,
1981          },
1982        },
1983        buildMode: "not Debug"
1984      };
1985      const result = enableObfuscateFileName(isPackageModules, projectConfig);
1986      expect(result).to.be.false;
1987    });
1988
1989    it('should return false if obfuscation is enabled and is a package module', () => {
1990      const isPackageModules = true;
1991      const projectConfig = {
1992        obfuscationMergedObConfig: {
1993          options: {
1994            disableObfuscation: false,
1995            enableFileNameObfuscation: false,
1996          },
1997        },
1998        buildMode: "not Debug"
1999      };
2000      const result = enableObfuscateFileName(isPackageModules, projectConfig);
2001      expect(result).to.be.false;
2002    });
2003
2004    it('should return false if obfuscation is enabled and is a package module', () => {
2005      const isPackageModules = true;
2006      const projectConfig = {
2007        obfuscationMergedObConfig: {
2008          options: {
2009            disableObfuscation: false,
2010            enableFileNameObfuscation: true,
2011          },
2012        },
2013        buildMode: "not Debug"
2014      };
2015      const result = enableObfuscateFileName(isPackageModules, projectConfig);
2016      expect(result).to.be.false;
2017    });
2018  });
2019
2020  describe('getRelativeSourcePath', () => {
2021    it('should return the relative path of a file within the project root', () => {
2022      const filePath = '/Users/user/project/src/components/Button.js';
2023      const projectRootPath = '/Users/user/project';
2024      const expectedRelativePath = 'src/components/Button.js';
2025
2026      const result = getRelativeSourcePath(filePath, projectRootPath, '');
2027      expect(result).to.equal(expectedRelativePath);
2028    });
2029
2030    it('should return the relative path of a file within a specified project path', () => {
2031      const filePath = '/Users/user/project/src/components/Button.js';
2032      const belongProjectPath = '/Users/user/project/src';
2033      const expectedRelativePath = 'components/Button.js';
2034
2035      const result = getRelativeSourcePath(filePath, '', belongProjectPath);
2036      expect(result).to.equal(expectedRelativePath);
2037    });
2038
2039    it('should return the original path if no project root or belong project path is provided', () => {
2040      const filePath = '/Users/user/project/src/components/Button.js';
2041      const expectedRelativePath = filePath;
2042
2043      const result = getRelativeSourcePath(filePath, '', '');
2044      expect(result).to.equal(expectedRelativePath);
2045    });
2046  });
2047
2048  describe('printUnobfuscationReasons', () => {
2049    it('historyAllUnobfuscatedNamesMap is empty', () => {
2050      unobfuscationNamesObj['Index.ets'] = { 'abc': ['sdk'] };
2051      unobfuscationNamesObj['Test.ets'] = { 'abc_test': ['conf'] };
2052      printUnobfuscationReasons('', './keptNames.json');
2053      let keptNamesObj = {
2054        keptReasons: {},
2055        keptNames: {}
2056      };
2057      let keptNameString = fs.readFileSync('./keptNames.json', 'utf-8');
2058      const parsedObj = JSON.parse(keptNameString);
2059      keptNamesObj.keptReasons = parsedObj.keptReasons;
2060      keptNamesObj.keptNames = parsedObj.keptNames;
2061      expect(keptNamesObj.keptNames['Index.ets']['abc'][0]).to.equal('sdk');
2062      expect(keptNamesObj.keptNames['Test.ets']['abc_test'][0]).to.equal('conf');
2063      clearHistoryUnobfuscatedMap();
2064      clearUnobfuscationNamesObj();
2065    });
2066
2067    it('historyAllUnobfuscatedNamesMap is not empty', () => {
2068      historyAllUnobfuscatedNamesMap.set('Index.ets', { 'abc': ['sdk'] });
2069      historyAllUnobfuscatedNamesMap.set('Test.ets', { 'abc_test': ['conf'] });
2070      unobfuscationNamesObj['Index.ets'] = { 'abc': ['conf'] };
2071      unobfuscationNamesObj['Test2.ets'] = { 'abc_test2': ['lang'] };
2072      printUnobfuscationReasons('', './keptNames.json');
2073      let keptNamesObj = {
2074        keptReasons: {},
2075        keptNames: {}
2076      };
2077      let keptNameString = fs.readFileSync('./keptNames.json', 'utf-8');
2078      const parsedObj = JSON.parse(keptNameString);
2079      keptNamesObj.keptReasons = parsedObj.keptReasons;
2080      keptNamesObj.keptNames = parsedObj.keptNames;
2081      expect(keptNamesObj.keptNames['Index.ets']['abc'][0]).to.equal('conf');
2082      expect(keptNamesObj.keptNames['Test.ets']['abc_test'][0]).to.equal('conf');
2083      expect(keptNamesObj.keptNames['Test2.ets']['abc_test2'][0]).to.equal('lang');
2084      clearHistoryUnobfuscatedMap();
2085      clearUnobfuscationNamesObj();
2086    });
2087  });
2088
2089  describe('clearNameCache', () => {
2090    it('should clear historyMangledTable and nameCacheMap', () => {
2091      PropCollections.historyMangledTable.set('name1','name2');
2092      nameCacheMap.set('name1','name2');
2093      historyAllUnobfuscatedNamesMap.set('key', {'prop1': 'aaa'});
2094      historyUnobfuscatedPropMap.set('key', ['value']);
2095      clearNameCache();
2096      expect(PropCollections.historyMangledTable.size).to.be.equal(0);
2097      expect(nameCacheMap.size).to.be.equal(0);
2098      expect(historyAllUnobfuscatedNamesMap.size).to.equal(0);
2099      expect(historyUnobfuscatedPropMap.size).to.equal(0);
2100    });
2101  });
2102
2103  describe('emitConsumerConfigFiles', () => {
2104    it('should also collect atKeepNames when atKeep is enabled', () => {
2105      const atKeepExportedPath = path.join(__dirname, '../../testData/output/atKeep_obfuscation01.txt');
2106      const sourceObConfig = {
2107        selfConfig: {
2108          ruleOptions: {
2109            enable: true,
2110            rules: ['./test/testData/obfuscation/testAtKeep/obfuscation-rules-atKeep.txt']
2111          },
2112          consumerRules: ['./test/testData/obfuscation/testAtKeep/consumer-rules.txt'],
2113        },
2114        dependencies: { libraries: [], hars: [] },
2115        obfuscationCacheDir: './test/testData/cache',
2116        options: {
2117          disableObfuscation: false
2118        },
2119        exportRulePath: atKeepExportedPath,
2120        sdkApis: []
2121      };
2122      const projectConfig = {
2123        obfuscationOptions: sourceObConfig,
2124        compileHar: false,
2125        compileShared: true
2126      }
2127      const obConfigResolver = new ObConfigResolver(projectConfig, printObfLogger);
2128      obConfigResolver.resolveObfuscationConfigs();
2129      AtKeepCollections.clear();
2130      AtKeepCollections.keepAsConsumer.globalNames.add('globalName1');
2131      AtKeepCollections.keepAsConsumer.propertyNames.add('propertyName1');
2132      AtKeepCollections.keepSymbol.globalNames.add('globalName2');
2133      AtKeepCollections.keepSymbol.propertyNames.add('propertyName2');
2134      obConfigResolver.emitConsumerConfigFiles();
2135      const atKeepContent = fs.readFileSync(sourceObConfig.exportRulePath, 'utf-8');
2136      expect(atKeepContent).to.include('-enable-property-obfuscation');
2137      expect(atKeepContent).to.include('-keep-global-name');
2138      expect(atKeepContent).to.include('globalName00');
2139      expect(atKeepContent).to.include('globalName1');
2140      expect(atKeepContent).to.include('-keep-property-name');
2141      expect(atKeepContent).to.include('propertyName00');
2142      expect(atKeepContent).to.include('propertyName1');
2143      expect(atKeepContent).not.to.include('globalName2');
2144      expect(atKeepContent).not.to.include('propertyName2');
2145      FileUtils.deleteFile(atKeepExportedPath);
2146    });
2147
2148    it('should not collect atKeepNames when atKeep is not enabled', () => {
2149      const atKeepExportedPath = path.join(__dirname, '../../testData/output/atKeep_obfuscation02.txt');
2150      const sourceObConfig = {
2151        selfConfig: {
2152          ruleOptions: {
2153            enable: true,
2154            rules: ['./test/testData/obfuscation/testAtKeep/obfuscation-rules.txt']
2155          },
2156          consumerRules: ['./test/testData/obfuscation/testAtKeep/consumer-rules.txt'],
2157        },
2158        dependencies: { libraries: [], hars: [] },
2159        obfuscationCacheDir: './test/testData/cache',
2160        options: {
2161          disableObfuscation: false
2162        },
2163        exportRulePath: atKeepExportedPath,
2164        sdkApis: []
2165      };
2166      const projectConfig = {
2167        obfuscationOptions: sourceObConfig,
2168        compileHar: false,
2169        compileShared: true
2170      }
2171      AtKeepCollections.clear();
2172      const obConfigResolver = new ObConfigResolver(projectConfig, printObfLogger);
2173      obConfigResolver.resolveObfuscationConfigs();
2174      const atKeepContent = fs.readFileSync(sourceObConfig.exportRulePath, 'utf-8');
2175      expect(atKeepContent).to.include('-enable-property-obfuscation');
2176      expect(atKeepContent).to.include('-keep-global-name');
2177      expect(atKeepContent).to.include('globalName00');
2178      expect(atKeepContent).not.to.include('globalName1');
2179      expect(atKeepContent).to.include('-keep-property-name');
2180      expect(atKeepContent).to.include('propertyName00');
2181      expect(atKeepContent).not.to.include('propertyName1');
2182      expect(atKeepContent).not.to.include('globalName2');
2183      expect(atKeepContent).not.to.include('propertyName2');
2184      FileUtils.deleteFile(atKeepExportedPath);
2185    });
2186  });
2187});
2188