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} from '../../../src/initialization/ConfigResolver';
36import { PropCollections, renameFileNameModule } from '../../../src/ArkObfuscator';
37import { nameCacheMap } from '../../../src/initialization/CommonObject';
38import path from 'path';
39import fs from 'fs';
40import { FileUtils } from '../../../src/utils/FileUtils';
41import sinon from 'sinon';
42import { UnobfuscationCollections } from '../../../src/utils/CommonCollections';
43
44const OBFUSCATE_TESTDATA_DIR = path.resolve(__dirname, '../../testData/obfuscation/system_api_obfuscation');
45const projectConfig = {
46  obfuscationOptions: { option1: 'value1' },
47  compileHar: true
48};
49const logger = console;
50const isTerser = false;
51let newObConfigResolver = new ObConfigResolver(projectConfig, logger, isTerser);
52
53describe('test for ConfigResolve', function() {
54  describe('ObOptions', () => {
55    it('should merge options correctly', () => {
56      const ob1 = new ObOptionsForTest();
57      ob1.disableObfuscation = true;
58      ob1.enablePropertyObfuscation = true;
59  
60      const ob2 = new ObOptionsForTest();
61      ob2.enableToplevelObfuscation = true;
62      ob2.printNameCache = 'test';
63      ob2.printKeptNamesPath = './test/ut/initialization/printKeptNamesPath.txt';
64      ob2.applyNameCache = 'test case';
65  
66      ob1.merge(ob2);
67  
68      expect(ob1.disableObfuscation).to.be.true;
69      expect(ob1.enablePropertyObfuscation).to.be.true;
70      expect(ob1.enableToplevelObfuscation).to.be.true;
71      expect(ob1.printNameCache).to.equal('test');
72      expect(ob1.printKeptNamesPath).to.equal('./test/ut/initialization/printKeptNamesPath.txt');
73      expect(ob1.applyNameCache).to.equal('test case');
74    });
75  });
76  
77  describe('MergedConfig', () => {
78    it('should merge two MergedConfig instances correctly', () => {
79      const config1 = new MergedConfig();
80      config1.reservedPropertyNames = ['prop1'];
81      config1.reservedGlobalNames = ['global1'];
82      config1.keepComments = ['comment1'];
83      config1.excludePathSet.add('path1');
84  
85      const config2 = new MergedConfig();
86      config2.reservedPropertyNames = ['prop2'];
87      config2.reservedGlobalNames = ['global2'];
88      config2.keepComments = ['comment2'];
89      config2.excludePathSet.add('path2');
90  
91      config1.merge(config2);
92  
93      expect(config1.reservedPropertyNames).to.deep.equal(['prop1', 'prop2']);
94      expect(config1.reservedGlobalNames).to.deep.equal(['global1', 'global2']);
95      expect(config1.keepComments).to.deep.equal(['comment1', 'comment2']);
96      expect(config1.excludePathSet).to.deep.equal(new Set(['path1', 'path2']));
97    });
98  
99    it('should sort and deduplicate arrays correctly', () => {
100      const config = new MergedConfig();
101      config.reservedPropertyNames = ['prop2', 'prop1', 'prop1'];
102      config.reservedGlobalNames = ['global2', 'global1', 'global1'];
103      config.keepComments = ['comment2', 'comment1', 'comment1'];
104  
105      config.sortAndDeduplicate();
106  
107      expect(config.reservedPropertyNames).to.deep.equal(['prop1', 'prop2']);
108      expect(config.reservedGlobalNames).to.deep.equal(['global1', 'global2']);
109      expect(config.keepComments).to.deep.equal(['comment1', 'comment2']);
110    });
111  
112    it('should serialize merged config correctly', () => {
113      let resultStr: string = '';
114      const config = new MergedConfig();
115      config.options = new ObOptionsForTest();
116      config.options['option1'] = true;
117      config.options['option2'] = false;
118      config.options['disableObfuscation'] = true;
119      config.options['enableStringPropertyObfuscation'] = true;
120      config.reservedGlobalNames = ['global1'];
121      config.reservedPropertyNames = ['prop1'];
122  
123      const serialized = config.serializeMergedConfig();
124      const serializedExportSwitchMap = new Map([
125        ['disableObfuscation', ObConfigResolver.KEEP_DTS],
126        ['enablePropertyObfuscation', ObConfigResolver.ENABLE_PROPERTY_OBFUSCATION],
127        ['enableStringPropertyObfuscation', ObConfigResolver.ENABLE_STRING_PROPERTY_OBFUSCATION],
128        ['enableToplevelObfuscation', ObConfigResolver.ENABLE_TOPLEVEL_OBFUSCATION],
129        ['compact', ObConfigResolver.COMPACT],
130        ['removeLog', ObConfigResolver.REMOVE_LOG],
131      ]);
132  
133      expect(serialized).to.include(ObConfigResolver.KEEP_GLOBAL_NAME);
134      expect(serialized).to.include('global1');
135      expect(serialized).to.include(ObConfigResolver.KEEP_PROPERTY_NAME);
136      expect(serialized).to.include('prop1');
137      expect(serialized).to.not.include('option2');
138  
139      expect(ObConfigResolver.exportedSwitchMap.has(String('disableObfuscation'))).to.be.true;
140      expect(ObConfigResolver.exportedSwitchMap.has(String('enableStringPropertyObfuscation'))).to.be.true;
141      expect(resultStr).to.equal('');
142    });
143  });
144  
145  describe('ObConfigResolver', () => {
146    describe('constructor', () => {
147      it('should create an instance with the correct properties', () => {
148        const projectConfig = {
149          obfuscationOptions: { option1: 'value1' },
150          compileHar: true
151        };
152        const logger = console;
153        const isTerser = false;
154        const myInstance = new ObConfigResolver(projectConfig, logger, isTerser);
155    
156        expect(myInstance).to.be.an('object');
157        expect(myInstance.sourceObConfig).to.deep.equal({ option1: 'value1' });
158        expect(myInstance.logger).to.equal(console);
159        expect(myInstance.isHarCompiled).to.be.true;
160        expect(myInstance.isTerser).to.be.false;
161      });
162    
163      it('should create an instance with Terser enabled', () => {
164        const projectConfig = {
165          obfuscationOptions: { option1: 'value1' },
166          compileHar: true
167        };
168        const logger = console;
169        const myInstance = new ObConfigResolver(projectConfig, logger, true);
170    
171        expect(myInstance).to.be.an('object');
172        expect(myInstance.sourceObConfig).to.deep.equal({ option1: 'value1' });
173        expect(myInstance.logger).to.equal(console);
174        expect(myInstance.isHarCompiled).to.be.true;
175        expect(myInstance.isTerser).to.be.true;
176      });
177    });
178
179    describe('resolveObfuscationConfigs', () => {
180      const projectConfig = {
181        obfuscationOptions: { option1: 'value1' },
182        compileHar: true
183      };
184      const logger = console;
185      const isTerser = false;
186      let newObConfigResolver = new ObConfigResolver(projectConfig, logger, isTerser);
187      let testClass: ObConfigResolver & {
188        performancePrinter?: any;
189      };
190      testClass = newObConfigResolver;
191
192      it('should return a MergedConfig object when sourceObConfig is undefined', () => {
193        testClass.sourceObConfig = undefined;
194        testClass.isHarCompiled = true;
195        testClass.performancePrinter = {};
196        const result = testClass.resolveObfuscationConfigs();
197        generateConsumerObConfigFile(testClass.sourceObConfig, undefined);
198
199        expect(testClass.sourceObConfig).to.be.an.undefined;
200        expect(result).to.deep.equal(new MergedConfig());
201        expect(result).to.be.an('object');
202        expect(result).to.have.property('options');
203      });
204
205      it('should return a MergedConfig object when sourceObConfig is null', () => {
206        testClass.sourceObConfig = null;
207        testClass.isHarCompiled = true;
208        testClass.performancePrinter = {};
209        const result = testClass.resolveObfuscationConfigs();
210        generateConsumerObConfigFile(null, undefined);
211
212        expect(testClass.sourceObConfig).to.be.a.null;
213        expect(result).to.deep.equal(new MergedConfig());
214        expect(result).to.be.an('object');
215        expect(result).to.have.property('options');
216      });
217
218      it('should handle the case when sourceObConfig.selfConfig.ruleOptions.enable is false', function () {
219        testClass.sourceObConfig = {
220          selfConfig: {
221            ruleOptions: { enable: false },
222          }, 
223          dependencies: { libraries: [], hars: [] },
224        };
225        generateConsumerObConfigFile(testClass.sourceObConfig, undefined);
226        const result = testClass.resolveObfuscationConfigs();
227
228        expect(result).to.be.an('object');
229        expect(result).to.have.property('options');
230        expect(result.options.disableObfuscation).to.be.true;
231      });
232
233      it('should handle the case when sourceObConfig.selfConfig.ruleOptions.enable is true', function () {
234        testClass.sourceObConfig = {
235          selfConfig: {
236            ruleOptions: { enable: true },
237          },
238          dependencies: { libraries: [], hars: [] },
239        };
240        generateConsumerObConfigFile(testClass.sourceObConfig, undefined);
241        const result = testClass.resolveObfuscationConfigs();
242
243        expect(result).to.be.an('object');
244        expect(result).to.have.property('options');
245        expect(result.options.disableObfuscation).to.be.false;
246      });
247
248      it('should handle the case when enableObfuscation is false', () => {
249        const sourceObConfig = {
250          selfConfig: {
251            ruleOptions: { enable: false },
252            consumerRules: ['./test/testData/obfuscation/keepDts/obfuscation-template.txt'],
253          },
254          dependencies: { libraries: [], hars: [] },
255          obfuscationCacheDir: './test/testData/cache',
256          options: {
257            disableObfuscation: true
258          }
259        };
260        const isHarCompiled = true;
261        let enableObfuscation = sourceObConfig.selfConfig.ruleOptions.enable;
262        let selfConfig = new MergedConfig();
263        enableObfuscation = !selfConfig.options.disableObfuscation;
264        let needConsumerConfigs = isHarCompiled && sourceObConfig.selfConfig.consumerRules &&
265          sourceObConfig.selfConfig.consumerRules.length > 0;
266        let needDependencyConfigs = enableObfuscation || needConsumerConfigs;
267        let dependencyConfigs = new MergedConfig();
268        const dependencyMaxLength = Math.max(
269          sourceObConfig.dependencies.libraries.length,
270          sourceObConfig.dependencies.hars.length,
271        );
272        newObConfigResolver.getDependencyConfigsForTest(sourceObConfig, dependencyConfigs);
273
274        expect(needConsumerConfigs).to.be.true;
275        expect(!dependencyMaxLength).to.be.true;
276        expect(needDependencyConfigs).to.be.true;
277        expect(dependencyConfigs).to.deep.equal(new MergedConfig());
278        expect(enableObfuscation).to.be.true;
279      });
280
281      it('should handle the case when enableObfuscation is false', () => {
282        const sourceObConfig = {
283          selfConfig: {
284            options: {
285              disableObfuscation: false,
286              enablePropertyObfuscation: true,
287              enableExportObfuscation: true,
288              enableToplevelObfuscation:true
289            },
290            ruleOptions: { enable: true },
291            consumerRules: ['./test/testData/obfuscation/keepDts/obfuscation-template.txt'],
292          },
293          dependencies: { libraries: [1, 2, 3], hars: [] },
294          obfuscationCacheDir: './test/testData/cache',
295        };
296        const isHarCompiled = true;
297        let enableObfuscation = sourceObConfig.selfConfig.ruleOptions.enable;
298        let selfConfig = new MergedConfig();
299        enableObfuscation = !selfConfig.options.disableObfuscation;
300        let needConsumerConfigs = isHarCompiled && sourceObConfig.selfConfig.consumerRules &&
301          sourceObConfig.selfConfig.consumerRules.length > 0;
302        let needDependencyConfigs = enableObfuscation || needConsumerConfigs;
303        let dependencyConfigs = new MergedConfig();
304        const dependencyMaxLength = Math.max(
305          sourceObConfig.dependencies.libraries.length,
306          sourceObConfig.dependencies.hars.length,
307        );
308        const mergedConfigs = newObConfigResolver.getMergedConfigsForTest(selfConfig, dependencyConfigs);
309        newObConfigResolver.handleReservedArrayForTest(mergedConfigs);
310        let needKeepSystemApi = enableObfuscation &&
311          (mergedConfigs.options.enablePropertyObfuscation ||
312            (mergedConfigs.options.enableExportObfuscation && mergedConfigs.options.enableToplevelObfuscation));
313        
314        newObConfigResolver.getDependencyConfigsForTest(sourceObConfig, dependencyConfigs);
315
316        expect(needConsumerConfigs).to.be.true;
317        expect(!dependencyMaxLength).to.be.false;
318        expect(needDependencyConfigs).to.be.true;
319        expect(dependencyConfigs).to.deep.equal(new MergedConfig());
320        expect(enableObfuscation).to.be.true;
321        expect(needKeepSystemApi).to.be.false;
322        expect(needConsumerConfigs).to.be.true;
323      });
324
325      it('should handle the case when enableObfuscation is true', () => {
326        testClass.sourceObConfig = {
327          selfConfig: {
328            ruleOptions: { enable: true },
329            consumerRules: ['./test/testData/obfuscation/keepDts/obfuscation-template.txt'],
330          },
331          dependencies: { libraries: [], hars: [] },
332          obfuscationCacheDir: './test/testData/cache',
333          options: {
334            disableObfuscation: true
335          }
336        };
337        testClass.isHarCompiled = true;
338        testClass.performancePrinter = {};
339        let enableObfuscation = testClass.sourceObConfig.selfConfig.ruleOptions.enable;
340        let selfConfig = new MergedConfig();
341        newObConfigResolver.getSelfConfigsForTest(selfConfig);
342        enableObfuscation = !selfConfig.options.disableObfuscation;
343        enableObfuscation = false;
344        let needConsumerConfigs = testClass.isHarCompiled && testClass.sourceObConfig.selfConfig.consumerRules &&
345          testClass.sourceObConfig.selfConfig.consumerRules.length > 0;
346        let needDependencyConfigs = enableObfuscation || needConsumerConfigs;
347        let dependencyConfigs = new MergedConfig();
348        const dependencyMaxLength = Math.max(
349          testClass.sourceObConfig.dependencies.libraries.length,
350          testClass.sourceObConfig.dependencies.hars.length,
351        );
352        newObConfigResolver.getDependencyConfigsForTest(testClass.sourceObConfig, dependencyConfigs);
353
354        expect(needConsumerConfigs).to.be.true;
355        expect(!dependencyMaxLength).to.be.true;
356        expect(needDependencyConfigs).to.be.true;
357        expect(dependencyConfigs).to.deep.equal(new MergedConfig());
358        expect(enableObfuscation).to.be.false;
359      });
360    });
361
362    describe('getSelfConfigs', () => {
363      it('should create an instance with the correct properties', () => {
364        const projectConfig = {
365          obfuscationOptions: {
366            selfConfig: {
367              ruleOptions: {
368                rules: []
369              }
370            }
371          },
372          compileHar: true
373        };
374        const logger = console;
375        const isTerser = false;
376        const resolver = new ObConfigResolver(projectConfig, logger, isTerser);
377
378        expect(resolver).to.be.an('object');
379        expect(resolver.sourceObConfig).to.deep.equal(projectConfig.obfuscationOptions);
380        expect(resolver.logger).to.equal(logger);
381        expect(resolver.isHarCompiled).to.equal(projectConfig.compileHar);
382        expect(resolver.isTerser).to.equal(isTerser);
383      });
384
385      it('should create an instance with the correct properties', () => {
386        const projectConfig = {
387          obfuscationOptions: {
388            selfConfig: {
389              ruleOptions: {
390                rules: ['./test/ut/initialization/tempNameCache.json']
391              }
392            }
393          },
394          compileHar: true
395        };
396        const logger = console;
397        const isTerser = false;
398        const resolver = new ObConfigResolver(projectConfig, logger, isTerser);
399
400        expect(resolver).to.be.an('object');
401        expect(resolver.sourceObConfig).to.deep.equal(projectConfig.obfuscationOptions);
402        expect(resolver.logger).to.equal(logger);
403        expect(resolver.isHarCompiled).to.equal(projectConfig.compileHar);
404        expect(resolver.isTerser).to.equal(isTerser);
405      });
406    
407      it('should get self configs correctly', () => {
408        const projectConfig = {
409          obfuscationOptions: {
410            selfConfig: {
411              ruleOptions: {
412                rules: ['./test/ut/initialization/tempNameCache.json']
413              }
414            }
415          },
416          compileHar: true
417        };
418        const logger = console;
419        const isTerser = false;
420        const resolver = new ObConfigResolver(projectConfig, logger, isTerser);
421        const selfConfigs = new MergedConfig();
422    
423        resolver.getSelfConfigsForTest(selfConfigs);
424
425        expect(resolver).to.be.an('object');
426        expect(resolver.sourceObConfig).to.deep.equal(projectConfig.obfuscationOptions);
427        expect(resolver.logger).to.equal(logger);
428        expect(resolver.isHarCompiled).to.equal(projectConfig.compileHar);
429        expect(resolver.isTerser).to.equal(isTerser);
430      });
431    });
432
433    describe('getConfigByPath', () => {
434      const projectConfig = {
435        obfuscationOptions: {
436          selfConfig: {
437            ruleOptions: {
438              rules: ['./test/testData/obfuscation/filename_obf/getConfigByPath.json']
439            }
440          }
441        },
442        compileHar: true
443      };
444      const logger = console;
445      let sandbox;
446      let instance;
447    
448      beforeEach(() => {
449        sandbox = sinon.createSandbox();
450        instance = new ObConfigResolver(projectConfig, logger, true);
451        instance.logger = { error: sandbox.stub() };
452      });
453    
454      afterEach(() => {
455        sandbox.restore();
456      });
457
458      it('should read file content and handle config content', () => {
459        const path = './test/testData/obfuscation/filename_obf/getConfigByPath.json';
460        let fileContent;
461        const expectedContent = '{"key": "value"}';
462        const configs = new MergedConfig();
463
464        fileContent = fs.readFileSync(path, 'utf-8');
465        instance.getConfigByPathForTest(path, configs);
466        instance.handleConfigContent(fileContent, configs, path);
467
468        expect(fileContent).to.equal(expectedContent);
469        expect(configs).to.be.an.instanceOf(MergedConfig);
470      });
471    
472      it('should log error and throw when failed to open file', () => {
473        const path = './test/testData/obfuscation/filename_obf/non-existent-file.json';
474        const configs = new MergedConfig();
475        const errorMessage = `Failed to open ${path}. Error message: ENOENT: no such file or directory, open '${path}'`;
476
477        sandbox.stub(fs, 'readFileSync').throws(new Error(errorMessage));
478    
479        expect(() => instance.getConfigByPath(path, configs)).to.throw(Error, errorMessage);
480        expect(instance.logger.error.calledWith(errorMessage)).to.be.false;
481      });
482    });
483   
484    it('should return a new MergedConfig instance when sourceObConfig is not defined', function() {
485      const projectConfig = {
486        sourceObConfig: null,
487        getMergedConfig: function() {
488          let sourceObConfig = this.sourceObConfig;
489          if (!sourceObConfig) {
490            return new MergedConfig();
491          }
492        }
493      };
494  
495      const result = projectConfig.getMergedConfig();
496      expect(result).to.be.an.instanceOf(MergedConfig);
497    });
498
499    describe('resolveKeepConfig', () => {
500      it('should resolve keep config correctly, starts with "!" and contains "*", not starts with "!"', () => {
501        const keepConfigs = ['!test/*/exclude.js', 'test/include.js'];
502        const configs = new MergedConfig();
503        const configPath = './test/testData/obfuscation/keepDts/obfuscation-template.txt';
504    
505        newObConfigResolver.resolveKeepConfig(keepConfigs, configs, configPath);
506    
507        expect(configs.excludeUniversalPaths).to.have.lengthOf(1);
508        expect(configs.keepUniversalPaths).to.have.lengthOf(0);
509        expect(configs.excludePathSet).to.have.lengthOf(0);
510        expect(configs.keepSourceOfPaths).to.have.lengthOf(0);
511      });
512
513      it('should resolve keep config correctly, starts with "!" and contains "*", not starts with "!" and "?"', () => {
514        const keepConfigs = ['!test/*/exclude.js', 'test/?/include.js'];
515        const configs = new MergedConfig();
516        const configPath = './test/testData/obfuscation/keepDts/obfuscation-template.txt';
517    
518        newObConfigResolver.resolveKeepConfig(keepConfigs, configs, configPath);
519    
520        expect(configs.excludeUniversalPaths).to.have.lengthOf(1);
521        expect(configs.keepUniversalPaths).to.have.lengthOf(1);
522        expect(configs.excludePathSet).to.have.lengthOf(0);
523        expect(configs.keepSourceOfPaths).to.have.lengthOf(0);
524      });
525
526      it('should resolve keep config correctly, both start with "!", contains "*" and "?"', () => {
527        const keepConfigs = ['!test/*/exclude.js', '!test/?/include.js'];
528        const configs = new MergedConfig();
529        const configPath = './test/testData/obfuscation/keepDts/obfuscation-template.txt';
530    
531        newObConfigResolver.resolveKeepConfig(keepConfigs, configs, configPath);
532    
533        expect(configs.excludeUniversalPaths).to.have.lengthOf(2);
534        expect(configs.keepUniversalPaths).to.have.lengthOf(0);
535        expect(configs.excludePathSet).to.have.lengthOf(0);
536        expect(configs.keepSourceOfPaths).to.have.lengthOf(0);
537      });
538
539      it('should resolve keep config correctly, not starts with "!" and contains "*" and "?"', () => {
540        const keepConfigs = ['test/*/exclude.js', 'test/?/include.js'];
541        const configs = new MergedConfig();
542        const configPath = './test/testData/obfuscation/keepDts/obfuscation-template.txt';
543    
544        newObConfigResolver.resolveKeepConfig(keepConfigs, configs, configPath);
545    
546        expect(configs.excludeUniversalPaths).to.have.lengthOf(0);
547        expect(configs.keepUniversalPaths).to.have.lengthOf(2);
548        expect(configs.excludePathSet).to.have.lengthOf(0);
549        expect(configs.keepSourceOfPaths).to.have.lengthOf(0);
550      });
551
552      it('should resolve keep config correctly, starts with "!", not starts with "!" and contains "*"', () => {
553        const keepConfigs = ['!test/exclude.js', 'test/*/include.js'];
554        const configs = new MergedConfig();
555        const configPath = './test/testData/obfuscation/keepDts/obfuscation-template.txt';
556    
557        newObConfigResolver.resolveKeepConfig(keepConfigs, configs, configPath);
558    
559        expect(configs.excludeUniversalPaths).to.have.lengthOf(0);
560        expect(configs.keepUniversalPaths).to.have.lengthOf(1);
561        expect(configs.excludePathSet).to.have.lengthOf(1);
562        expect(configs.keepSourceOfPaths).to.have.lengthOf(0);
563      });
564
565      it('should resolve keep config correctly, starts with "!" and not starts with "!"', () => {
566        const keepConfigs = ['!test/exclude.js', 'test/include.js'];
567        const configs = new MergedConfig();
568        const configPath = './test/testData/obfuscation/keepDts/obfuscation-template.txt';
569    
570        newObConfigResolver.resolveKeepConfig(keepConfigs, configs, configPath);
571    
572        expect(configs.excludeUniversalPaths).to.have.lengthOf(0);
573        expect(configs.keepUniversalPaths).to.have.lengthOf(0);
574        expect(configs.excludePathSet).to.have.lengthOf(1);
575        expect(configs.keepSourceOfPaths).to.have.lengthOf(0);
576      });
577
578      it('should resolve keep config correctly, both start with "!"', () => {
579        const keepConfigs = ['!test/exclude.js', '!test/include.js'];
580        const configs = new MergedConfig();
581        const configPath = './test/testData/obfuscation/keepDts/obfuscation-template.txt';
582    
583        newObConfigResolver.resolveKeepConfig(keepConfigs, configs, configPath);
584    
585        expect(configs.excludeUniversalPaths).to.have.lengthOf(0);
586        expect(configs.keepUniversalPaths).to.have.lengthOf(0);
587        expect(configs.excludePathSet).to.have.lengthOf(2);
588        expect(configs.keepSourceOfPaths).to.have.lengthOf(0);
589      });
590    });
591
592    describe('resolvePath', () => {
593      it('should return the absolute path if token is already an absolute path', () => {
594        const configPath = '/home/user/config.json';
595        const token = '/home/user/data.txt';
596        const result = newObConfigResolver.resolvePathForTest(configPath, token);
597        expect(result).to.equal(token);
598      });
599    
600      it('should resolve the relative path based on the config file directory', () => {
601        const configPath = '/home/user/config.json';
602        const token = 'data.txt';
603        const expectedResult = '/home/user/data.txt';
604        const result = newObConfigResolver.resolvePathForTest(configPath, token);
605        expect(result).to.equal(expectedResult);
606      });
607    });
608
609    describe('getTokenType', () => {
610      it('should return the correct OptionType for each token', () => {
611        const tokens = [
612          ObConfigResolver.KEEP_DTS,
613          ObConfigResolver.KEEP_GLOBAL_NAME,
614          ObConfigResolver.KEEP_PROPERTY_NAME,
615          ObConfigResolver.KEEP_FILE_NAME,
616          ObConfigResolver.KEEP_COMMENTS,
617          ObConfigResolver.DISABLE_OBFUSCATION,
618          ObConfigResolver.ENABLE_PROPERTY_OBFUSCATION,
619          ObConfigResolver.ENABLE_STRING_PROPERTY_OBFUSCATION,
620          ObConfigResolver.ENABLE_TOPLEVEL_OBFUSCATION,
621          ObConfigResolver.ENABLE_FILENAME_OBFUSCATION,
622          ObConfigResolver.ENABLE_EXPORT_OBFUSCATION,
623          ObConfigResolver.REMOVE_COMMENTS,
624          ObConfigResolver.COMPACT,
625          ObConfigResolver.REMOVE_LOG,
626          ObConfigResolver.PRINT_NAMECACHE,
627          ObConfigResolver.PRINT_KEPT_NAMES,
628          ObConfigResolver.APPLY_NAMECACHE,
629          ObConfigResolver.KEEP,
630          'unknown_token'
631        ];
632    
633        const expectedResults = [
634          OptionTypeForTest.KEEP_DTS,
635          OptionTypeForTest.KEEP_GLOBAL_NAME,
636          OptionTypeForTest.KEEP_PROPERTY_NAME,
637          OptionTypeForTest.KEEP_FILE_NAME,
638          OptionTypeForTest.KEEP_COMMENTS,
639          OptionTypeForTest.DISABLE_OBFUSCATION,
640          OptionTypeForTest.ENABLE_PROPERTY_OBFUSCATION,
641          OptionTypeForTest.ENABLE_STRING_PROPERTY_OBFUSCATION,
642          OptionTypeForTest.ENABLE_TOPLEVEL_OBFUSCATION,
643          OptionTypeForTest.ENABLE_FILENAME_OBFUSCATION,
644          OptionTypeForTest.ENABLE_EXPORT_OBFUSCATION,
645          OptionTypeForTest.REMOVE_COMMENTS,
646          OptionTypeForTest.COMPACT,
647          OptionTypeForTest.REMOVE_LOG,
648          OptionTypeForTest.PRINT_NAMECACHE,
649          OptionTypeForTest.PRINT_KEPT_NAMES,
650          OptionTypeForTest.APPLY_NAMECACHE,
651          OptionTypeForTest.KEEP,
652          OptionTypeForTest.NONE
653        ];
654    
655        for (let i = 0; i < tokens.length; i++) {
656          const result = newObConfigResolver.getTokenTypeForTest(tokens[i]);
657          expect(result).to.equal(expectedResults[i]);
658        }
659      });
660    });
661
662    describe('handleConfigContent', () => {
663      it('should handle config content correctly', () => {
664        const configs: MergedConfig = new MergedConfig();
665        configs.options = new ObOptionsForTest();
666
667        const configPath = './test/testData/obfuscation/keepDts/obfuscation-template.txt';
668        const data = `
669          #This is a comment
670          -none,
671          -keep obfuscation-template.txt,
672          -keep-dts,
673          -keep-global-name,
674          -keep-property-name,
675          -keep-file-name,
676          -keep-comments,
677          -disable-obfuscation,
678          -enable-property-obfuscation,
679          -enable-string-property-obfuscation,
680          -enable-toplevel-obfuscation,
681          -enable-filename-obfuscation,
682          -enable-export-obfuscation,
683          -remove-comments,
684          -compact,
685          -remove-log,
686          -print-namecache obfuscation-template.txt,
687          -print-kept-names,
688          -apply-namecache obfuscation-template.txt
689        `;
690    
691        newObConfigResolver.handleConfigContentForTest(data, configs, configPath);
692    
693        expect(configs.options.disableObfuscation).to.be.true;
694        expect(configs.options.enablePropertyObfuscation).to.be.true;
695        expect(configs.options.enableStringPropertyObfuscation).to.be.true;
696        expect(configs.options.enableToplevelObfuscation).to.be.true;
697        expect(configs.options.removeComments).to.be.true;
698        expect(configs.options.enableFileNameObfuscation).to.be.true;
699        expect(configs.options.enableExportObfuscation).to.be.true;
700        expect(configs.options.compact).to.be.true;
701        expect(configs.options.removeLog).to.be.true;
702      });
703    });
704
705    describe('1: test Api getSystemApiCache', function() {
706      it('1-1: test getSystemApiCache: -enable-property-obfuscation', function () {
707        let obfuscationCacheDir = path.join(OBFUSCATE_TESTDATA_DIR, 'property');
708        let obfuscationOptions = {
709          'selfConfig': {
710            'ruleOptions': {
711              'enable': true,
712              'rules': [ 
713                path.join(OBFUSCATE_TESTDATA_DIR, 'property/property.txt')
714              ]
715            },
716            'consumerRules': [],
717          },
718          'dependencies': {
719            'libraries': [],
720            'hars': []
721          },
722          'obfuscationCacheDir': obfuscationCacheDir,
723          'sdkApis': [
724            path.join(OBFUSCATE_TESTDATA_DIR, 'system_api.d.ts')
725          ]
726        };
727        let projectConfig = {
728          obfuscationOptions,
729          compileHar: false
730        };
731        const obConfig: ObConfigResolver =  new ObConfigResolver(projectConfig, undefined);
732        obConfig.resolveObfuscationConfigs();
733        const reservedSdkApiForProp = UnobfuscationCollections.reservedSdkApiForProp;
734        const reservedSdkApiForGlobal = UnobfuscationCollections.reservedSdkApiForGlobal;
735    
736        expect(reservedSdkApiForProp.size == 12).to.be.true;
737        expect(reservedSdkApiForProp.has('TestClass')).to.be.true;
738        expect(reservedSdkApiForProp.has('para1')).to.be.true;
739        expect(reservedSdkApiForProp.has('para2')).to.be.true;
740        expect(reservedSdkApiForProp.has('foo')).to.be.true;
741        expect(reservedSdkApiForProp.has('TestFunction')).to.be.true;
742        expect(reservedSdkApiForProp.has('funcPara1')).to.be.true;
743        expect(reservedSdkApiForProp.has('funcPara2')).to.be.true;
744        expect(reservedSdkApiForProp.has('ns')).to.be.true;
745        expect(reservedSdkApiForProp.has('NavigationBuilderRegister')).to.be.true;
746        expect(reservedSdkApiForProp.has('ViewV2')).to.be.true;
747        expect(reservedSdkApiForProp.has('initParam')).to.be.true;
748        expect(reservedSdkApiForProp.has('updateParam')).to.be.true;
749        expect(reservedSdkApiForGlobal.size == 0).to.be.true;
750        UnobfuscationCollections.clear();
751    
752        let systemApiPath = obfuscationCacheDir + '/systemApiCache.json';
753        const data = fs.readFileSync(systemApiPath, 'utf8');
754        const systemApiContent = JSON.parse(data);
755    
756        expect(systemApiContent.ReservedPropertyNames.length == 12).to.be.true;
757        expect(systemApiContent.ReservedPropertyNames.includes('TestClass')).to.be.true;
758        expect(systemApiContent.ReservedPropertyNames.includes('para1')).to.be.true;
759        expect(systemApiContent.ReservedPropertyNames.includes('para2')).to.be.true;
760        expect(systemApiContent.ReservedPropertyNames.includes('foo')).to.be.true;
761        expect(systemApiContent.ReservedPropertyNames.includes('TestFunction')).to.be.true;
762        expect(systemApiContent.ReservedPropertyNames.includes('funcPara1')).to.be.true;
763        expect(systemApiContent.ReservedPropertyNames.includes('funcPara2')).to.be.true;
764        expect(systemApiContent.ReservedPropertyNames.includes('ns')).to.be.true;
765        expect(systemApiContent.ReservedPropertyNames.includes('NavigationBuilderRegister')).to.be.true;
766        expect(systemApiContent.ReservedPropertyNames.includes('ViewV2')).to.be.true;
767        expect(systemApiContent.ReservedPropertyNames.includes('initParam')).to.be.true;
768        expect(systemApiContent.ReservedPropertyNames.includes('updateParam')).to.be.true;
769        expect(systemApiContent.ReservedGlobalNames == undefined).to.be.true;
770    
771        fs.unlinkSync(systemApiPath);
772      });
773     
774      it('1-2: test getSystemApiCache: -enable-export-obfuscation', function () {
775        let obfuscationCacheDir = path.join(OBFUSCATE_TESTDATA_DIR, 'export');
776        let obfuscationOptions = {
777          'selfConfig': {
778            'ruleOptions': {
779              'enable': true,
780              'rules': [ 
781                path.join(OBFUSCATE_TESTDATA_DIR, 'export/export.txt')
782              ]
783            },
784            'consumerRules': [],
785          },
786          'dependencies': {
787            'libraries': [],
788            'hars': []
789          },
790          'obfuscationCacheDir': obfuscationCacheDir,
791          'sdkApis': [
792            path.join(OBFUSCATE_TESTDATA_DIR, 'system_api.d.ts')
793          ]
794        };
795        let projectConfig = {
796          obfuscationOptions,
797          compileHar: false
798        };
799        const obConfig: ObConfigResolver =  new ObConfigResolver(projectConfig, undefined);
800        obConfig.resolveObfuscationConfigs();
801        const reservedSdkApiForProp = UnobfuscationCollections.reservedSdkApiForProp;
802        const reservedSdkApiForGlobal = UnobfuscationCollections.reservedSdkApiForGlobal;
803    
804        expect(reservedSdkApiForProp.size == 0).to.be.true;
805        expect(reservedSdkApiForGlobal.size == 0).to.be.true;
806        UnobfuscationCollections.clear();
807    
808        let systemApiPath = obfuscationCacheDir + '/systemApiCache.json';
809        const noSystemApi = fs.existsSync(systemApiPath);
810    
811        expect(noSystemApi).to.be.false;
812      });
813     
814      it('1-3: test getSystemApiCache: -enable-export-obfuscation -enable-toplevel-obfuscation', function () {
815        let obfuscationCacheDir = path.join(OBFUSCATE_TESTDATA_DIR, 'export_toplevel');
816        let obfuscationOptions = {
817          'selfConfig': {
818            'ruleOptions': {
819              'enable': true,
820              'rules': [ 
821                path.join(OBFUSCATE_TESTDATA_DIR, 'export_toplevel/export_toplevel.txt')
822              ]
823            },
824            'consumerRules': [],
825          },
826          'dependencies': {
827            'libraries': [],
828            'hars': []
829          },
830          'obfuscationCacheDir': obfuscationCacheDir,
831          'sdkApis': [
832            path.join(OBFUSCATE_TESTDATA_DIR, 'system_api.d.ts')
833          ]
834        };
835        let projectConfig = {
836          obfuscationOptions,
837          compileHar: false
838        };
839        const obConfig: ObConfigResolver =  new ObConfigResolver(projectConfig, undefined);
840        obConfig.resolveObfuscationConfigs();
841        const reservedSdkApiForProp = UnobfuscationCollections.reservedSdkApiForProp;
842        const reservedSdkApiForGlobal = UnobfuscationCollections.reservedSdkApiForGlobal;
843    
844        expect(reservedSdkApiForProp.size == 0).to.be.true;
845        expect(reservedSdkApiForGlobal.size == 3).to.be.true;
846        expect(reservedSdkApiForGlobal.has('TestClass')).to.be.true;
847        expect(reservedSdkApiForGlobal.has('TestFunction')).to.be.true;
848        expect(reservedSdkApiForGlobal.has('ns')).to.be.true;
849        UnobfuscationCollections.clear();
850    
851        let systemApiPath = obfuscationCacheDir + '/systemApiCache.json';
852        const data = fs.readFileSync(systemApiPath, 'utf8');
853        const systemApiContent = JSON.parse(data);
854    
855        expect(systemApiContent.ReservedPropertyNames == undefined).to.be.true;
856        expect(systemApiContent.ReservedGlobalNames.length == 3).to.be.true;
857        expect(systemApiContent.ReservedGlobalNames.includes('TestClass')).to.be.true;
858        expect(systemApiContent.ReservedGlobalNames.includes('TestFunction')).to.be.true;
859        expect(systemApiContent.ReservedGlobalNames.includes('ns')).to.be.true;
860    
861        fs.unlinkSync(systemApiPath);
862      });
863    });
864  });
865  
866  describe('collectResevedFileNameInIDEConfig', () => {
867    let collectPathReservedStringStub;
868  
869    beforeEach(function() {
870      collectPathReservedStringStub = sinon.stub(FileUtils, 'collectPathReservedString');
871      collectPathReservedStringStub.callsFake((filePath, reservedArray) => reservedArray.push(filePath));
872    });
873  
874    afterEach(function() {
875      collectPathReservedStringStub.restore();
876      projectConfig.compileShared = false;
877      projectConfig.byteCodeHar = false;
878      projectConfig.aceModuleJsonPath = '';
879    });
880  
881    const ohPackagePath = './test/ut/initialization/testOhPackagePath.json';
882    let projectConfig = {
883      aceModuleJsonPath: '',
884      projectPath: 'path/to/project',
885      cachePath: 'path/to/cache',
886      compileShared: false,
887      byteCodeHar: false,
888      aceModuleBuild: 'path/to/build',
889    }
890    const entryArray = ['path/to/entry1', 'path/to/entry2'];
891    const modulePathMap = {
892      module1: 'path/to/module1',
893      module2: 'path/to/module2',
894    };
895  
896    it('should collect reserved file names from entryArray and projectConfig', () => {
897      const result = collectResevedFileNameInIDEConfig('', projectConfig, undefined, entryArray);
898      expect(result).to.deep.equal(['path/to/entry1', 'path/to/entry2','path/to/project','path/to/cache']);
899    });
900    it('should collect reserved file names from modulePathMap and projectConfig', () => {
901      projectConfig.compileShared = true;
902      const result = collectResevedFileNameInIDEConfig('', projectConfig, modulePathMap, []);
903      expect(result).to.deep.equal(['path/to/module1', 'path/to/module2','module1','module2','path/to/build','etsFortgz','path/to/project','path/to/cache']);
904    });
905    it('should collect reserved file names from ohPackagePath and projectConfig', () => {
906      projectConfig.byteCodeHar = true;
907      const result = collectResevedFileNameInIDEConfig(ohPackagePath, projectConfig, undefined, []);
908      expect(result).to.deep.equal(['path/to/main', 'path/to/types','path/to/build','etsFortgz','path/to/project','path/to/cache']);
909    });
910    it('should collect reserved file names from moduleJsonPath and projectConfig', () => {
911      projectConfig.aceModuleJsonPath = "./test/ut/initialization/testModuleJsonPath_0.json";
912      const hasSrcEntry = collectResevedFileNameInIDEConfig('', projectConfig, undefined, []);
913      expect(hasSrcEntry).to.deep.equal(['path/to/srcEntry','path/to/project','path/to/cache']);
914
915      projectConfig.aceModuleJsonPath = "./test/ut/initialization/testModuleJsonPath_1.json";
916      const noSrcEntry = collectResevedFileNameInIDEConfig('', projectConfig, undefined, []);
917      expect(noSrcEntry).to.deep.equal(['path/to/project','path/to/cache']);
918    });
919  });
920  
921  describe('readNameCache', () => {
922    let tempFilePath;
923    let testData;
924    let fsWriteFileSyncStub;
925    let fsUnlinkSyncStub;
926    let logger = {
927      error: (message: string) => console.error(message),
928    };
929    let PropCollections;
930    let renameFileNameModule;
931  
932    beforeEach(() => {
933      PropCollections = {
934        historyMangledTable: {},
935      };
936      renameFileNameModule = {
937        historyFileNameMangledTable: {},
938      };
939      
940      tempFilePath = './test/ut/initialization/tempNameCache.json';
941      testData = {
942        compileSdkVersion: '1.0.0',
943        PropertyCache: { key1: 'value1', key2: 'value2' },
944        FileNameCache: { key3: 'value3', key4: 'value4' },
945        extraKey: '',
946      };
947  
948      fsWriteFileSyncStub = sinon.stub(fs, 'writeFileSync').callsFake((path, data) => {});
949      fsUnlinkSyncStub = sinon.stub(fs, 'unlinkSync').callsFake((path) => {});
950    });
951  
952    afterEach(() => {
953      fsWriteFileSyncStub.restore();
954      fsUnlinkSyncStub.restore();
955    });
956  
957    it('should read and parse the name cache file correctly', () => {
958      readNameCache(tempFilePath, logger);
959
960      expect(nameCacheMap.get('extraKey')).to.equal('');
961    });
962  });
963  
964  describe('readNameCache', () => {
965    let fsReadFileSyncStub;
966    let logger; 
967    const testPath = './test/ut/initialization/tempNameCache.json';
968  
969    beforeEach(() => {
970      fsReadFileSyncStub = sinon.stub(fs, 'readFileSync').returns('{"compileSdkVersion":"1.0","PropertyCache":{},"FileNameCache":{}}');
971      logger = { error: sinon.spy() };
972    });
973  
974    afterEach(() => {
975      fsReadFileSyncStub.restore();
976    });
977  
978    it('should read the name cache file and parse its content', () => {
979      readNameCache(testPath, logger);
980      expect(PropCollections.historyMangledTable).to.deep.equal(new Map());
981      expect(renameFileNameModule.historyFileNameMangledTable).to.deep.equal(new Map());
982      expect(nameCacheMap.get('compileSdkVersion')).to.be.undefined;
983      expect(logger.error.called).to.be.false;
984    });
985  
986    it('should handle errors when reading the file', () => {
987      const mockLogger = {
988        error: (message: string) => console.error(message),
989      };
990
991      const nonExistentFilePath = './test/ut/initialization/nonExistentFile.json';
992      readNameCache(nonExistentFilePath, mockLogger);
993      fsReadFileSyncStub.throws(new Error('Test error'));
994      readNameCache(testPath, logger);
995      expect(logger.error.calledWith(`Failed to open ${nonExistentFilePath}. Error message: Test error`)).to.be.false;
996    });
997  });
998  
999  describe('handleUniversalPathInObf', () => {
1000    it('should handle universal paths correctly', () => {
1001      const mergedObConfig: MergedConfig = {
1002        options: new ObOptionsForTest(),
1003        reservedPropertyNames: [],
1004        reservedGlobalNames: [],
1005        reservedNames: [],
1006        reservedFileNames: [],
1007        keepComments: [],
1008        keepSourceOfPaths: [],
1009        universalReservedPropertyNames: [],
1010        universalReservedGlobalNames: [],
1011        keepUniversalPaths: [/test\.js$/],
1012        excludeUniversalPaths: [/exclude\.js$/],
1013        excludePathSet: new Set(),
1014        merge: () => {},
1015        sortAndDeduplicate: () => {},
1016        serializeMergedConfig: () => {
1017          return JSON.stringify(this);
1018        }
1019      };
1020      const allSourceFilePaths = new Set([
1021        'test.js',
1022        'exclude.js',
1023        'other.js',
1024      ]);
1025  
1026      handleUniversalPathInObf(mergedObConfig, allSourceFilePaths);
1027      expect(mergedObConfig.keepSourceOfPaths).to.deep.equal(['test.js']);
1028      expect(mergedObConfig.excludePathSet).to.deep.equal(new Set(['exclude.js']));
1029    });
1030  
1031    it('should return early if mergedObConfig is not provided or both keepUniversalPaths and excludeUniversalPaths are empty', () => {
1032      const mergedObConfig: MergedConfig = {
1033        options: new ObOptionsForTest(),
1034        reservedPropertyNames: [],
1035        reservedGlobalNames: [],
1036        reservedNames: [],
1037        reservedFileNames: [],
1038        keepComments: [],
1039        keepSourceOfPaths: [],
1040        universalReservedPropertyNames: [],
1041        universalReservedGlobalNames: [],
1042        keepUniversalPaths: [],
1043        excludeUniversalPaths: [],
1044        excludePathSet: new Set(),
1045        merge: () => {},
1046        sortAndDeduplicate: () => {},
1047        serializeMergedConfig: () => {
1048          return JSON.stringify(this);
1049        }
1050      };
1051      const allSourceFilePaths = new Set([]);
1052      const result = handleUniversalPathInObf(mergedObConfig, allSourceFilePaths);
1053  
1054      expect(result).to.be.undefined;
1055    });
1056  });
1057  
1058  describe('getArkguardNameCache', () => {
1059    it('should return a JSON string with the correct structure', () => {
1060      const enablePropertyObfuscation = true;
1061      const enableFileNameObfuscation = true;
1062      const sdkVersion = '1.0.0';
1063      const entryPackageInfo = 'packageInfo';
1064  
1065      const result = getArkguardNameCache(enablePropertyObfuscation, enableFileNameObfuscation, false, sdkVersion, entryPackageInfo);
1066  
1067      try {
1068        JSON.parse(result);
1069        // If no error is thrown, the result is a valid JSON string
1070        console.log('Test passed: getArkguardNameCache returns a valid JSON string');
1071      } catch (error) {
1072        console.error('Test failed: getArkguardNameCache does not return a valid JSON string');
1073      }
1074    });
1075  
1076    it('should include the correct compileSdkVersion and entryPackageInfo', () => {
1077      const enablePropertyObfuscation = false;
1078      const enableFileNameObfuscation = false;
1079      const sdkVersion = '2.0.0';
1080      const entryPackageInfo = 'anotherPackageInfo';
1081  
1082      const result = getArkguardNameCache(enablePropertyObfuscation, enableFileNameObfuscation, false, sdkVersion, entryPackageInfo);
1083      const parsedResult = JSON.parse(result);
1084  
1085      expect(parsedResult.compileSdkVersion).to.equal(sdkVersion);
1086      expect(parsedResult.entryPackageInfo).to.equal(entryPackageInfo);
1087    });
1088
1089    it('PropertyCache exists when enable export obfuscation', () => {
1090      const enablePropertyObfuscation = false;
1091      const enableFileNameObfuscation = false;
1092      const enableExportObfuscation = true;
1093      const sdkVersion = '2.0.0';
1094      const entryPackageInfo = 'anotherPackageInfo';
1095  
1096      PropCollections.historyMangledTable.set("key1", "value1");
1097      PropCollections.historyMangledTable.set("key2", "value2");
1098      PropCollections.globalMangledTable.set("key3", "value3");
1099      PropCollections.globalMangledTable.set("key4", "value4");
1100      const result = getArkguardNameCache(enablePropertyObfuscation, enableFileNameObfuscation, enableExportObfuscation, sdkVersion, entryPackageInfo);
1101      const parsedResult = JSON.parse(result);
1102      PropCollections.historyMangledTable.clear();
1103      PropCollections.globalMangledTable.clear();
1104      expect('PropertyCache' in parsedResult).to.be.true;
1105      expect(parsedResult.PropertyCache.key1 === "value1").to.be.true;
1106      expect(parsedResult.PropertyCache.key2 === "value2").to.be.true;
1107      expect(parsedResult.PropertyCache.key3 === "value3").to.be.true;
1108      expect(parsedResult.PropertyCache.key4 === "value4").to.be.true;
1109    });
1110  });
1111  
1112  describe('fillNameCache', function() {
1113    it('should correctly fill the name cache with the given table entries', function() {
1114      const table = new Map([
1115        ['key1', 'value1'],
1116        ['key2', 'value2'],
1117        ['key3', 'value3']
1118      ]);
1119      const nameCache = new Map();
1120  
1121      fillNameCache(table, nameCache);
1122  
1123      assert.deepEqual(nameCache, table);
1124    });
1125  
1126    it('should handle empty tables gracefully', function() {
1127      const table = new Map();
1128      const nameCache = new Map();
1129  
1130      fillNameCache(table, nameCache);
1131  
1132      assert.deepEqual(nameCache, table);
1133    });
1134  });
1135  
1136  describe('writeObfuscationNameCache', () => {
1137    let existsSyncSpy;
1138    let mkdirSyncSpy;
1139    let writeFileSyncSpy;
1140  
1141    beforeEach(function() {
1142      existsSyncSpy = sinon.spy(fs, 'existsSync');
1143      mkdirSyncSpy = sinon.spy(fs, 'mkdirSync');
1144      writeFileSyncSpy = sinon.spy(fs, 'writeFileSync');
1145    });
1146  
1147    afterEach(function() {
1148      existsSyncSpy.restore();
1149      mkdirSyncSpy.restore();
1150      writeFileSyncSpy.restore();
1151    });
1152  
1153    it('should not write cache if projectConfig.arkObfuscator is false', () => {
1154      const projectConfig = {
1155        arkObfuscator: false,
1156        obfuscationMergedObConfig: {
1157          options: {
1158            enablePropertyObfuscation: true,
1159            enableFileNameObfuscation: true,
1160          },
1161        },
1162        etsLoaderVersion: '1.0.0',
1163      };
1164      const entryPackageInfo = 'testEntryPackageInfo';
1165      const obfuscationCacheDir = 'testCacheDir';
1166      const printNameCache = 'testPrintNameCache';
1167  
1168      writeObfuscationNameCache(projectConfig, entryPackageInfo, obfuscationCacheDir, printNameCache);
1169  
1170      expect(existsSyncSpy.called).to.be.false;
1171      expect(mkdirSyncSpy.called).to.be.false;
1172      expect(writeFileSyncSpy.called).to.be.false;
1173    });
1174    it('should write cache to obfuscationCacheDir if provided', () => {
1175      const projectConfig = {
1176        arkObfuscator: true,
1177        obfuscationMergedObConfig: {
1178          options: {
1179            enablePropertyObfuscation: true,
1180            enableFileNameObfuscation: true,
1181            enableExportObfusaction: true
1182          },
1183        },
1184        etsLoaderVersion: '1.0.0',
1185      };
1186      const entryPackageInfo = 'testEntryPackageInfo';
1187      const obfuscationCacheDir = './test/ut/initialization/testObfuscationCacheDir';
1188      const printNameCache = '';
1189      writeObfuscationNameCache(projectConfig, entryPackageInfo, obfuscationCacheDir, printNameCache);
1190      const defaultNameCachePath: string = path.join(obfuscationCacheDir, 'nameCache.json');
1191      const expectedContent = {
1192        "extraKey": "",
1193        "compileSdkVersion": "1.0.0",
1194        "entryPackageInfo": "testEntryPackageInfo",
1195        "PropertyCache": {},
1196        "FileNameCache": {}
1197      }
1198      const jsonData = fs.readFileSync(defaultNameCachePath,'utf-8');
1199      const result = JSON.parse(jsonData);
1200      expect(result).to.deep.equal(expectedContent);
1201      fs.unlinkSync(defaultNameCachePath);
1202      fs.rmdirSync(path.dirname(defaultNameCachePath));
1203    });
1204    it('should write cache to printNameCache if provided', () => {
1205      const projectConfig = {
1206        arkObfuscator: true,
1207        obfuscationMergedObConfig: {
1208          options: {
1209            enablePropertyObfuscation: true,
1210            enableFileNameObfuscation: true,
1211          },
1212        },
1213        etsLoaderVersion: '1.0.0',
1214      };
1215      const entryPackageInfo = 'testEntryPackageInfo';
1216      const obfuscationCacheDir = '';
1217      const printNameCache = './test/ut/initialization/printNameCache.json';
1218      writeObfuscationNameCache(projectConfig, entryPackageInfo, obfuscationCacheDir, printNameCache);
1219      const expectedContent = {
1220        "extraKey": "",
1221        "compileSdkVersion": "1.0.0",
1222        "entryPackageInfo": "testEntryPackageInfo",
1223        "PropertyCache": {},
1224        "FileNameCache": {}
1225      }
1226      const jsonData = fs.readFileSync(printNameCache,'utf-8');
1227      const result = JSON.parse(jsonData);
1228      expect(result).to.deep.equal(expectedContent);
1229      expect(existsSyncSpy.called).to.be.false;
1230      expect(mkdirSyncSpy.called).to.be.false;
1231      fs.unlinkSync(printNameCache);
1232    });
1233  });
1234  
1235  describe('enableObfuscatedFilePathConfig', () => {
1236    it('should return false if in debug mode or no obfuscation config', () => {
1237      const projectConfig = {
1238        obfuscationMergedObConfig: null,
1239        buildMode: 'debug'
1240      };
1241      const result = enableObfuscatedFilePathConfig(true, projectConfig);
1242      expect(result).to.be.false;
1243    });
1244  
1245    it('should return false if obfuscation is disabled or file name obfuscation is not enabled', () => {
1246      const projectConfig = {
1247        obfuscationMergedObConfig: {
1248          options: {
1249            disableObfuscation: true,
1250            enableFileNameObfuscation: false,
1251          },
1252        },
1253        buildMode: 'debug'
1254      };
1255      const result = enableObfuscatedFilePathConfig(false, projectConfig);
1256      expect(result).to.be.false;
1257    });
1258  
1259    it('should return true if all conditions are met', () => {
1260      const projectConfig = {
1261        obfuscationMergedObConfig: {
1262          options: {
1263            disableObfuscation: false,
1264            enableFileNameObfuscation: true,
1265          },
1266        },
1267        buildMode: 'not debug'
1268      };
1269      const result = enableObfuscatedFilePathConfig(false, projectConfig);
1270      expect(result).to.be.true;
1271    });
1272  });
1273  
1274  describe('handleObfuscatedFilePath', () => {
1275    it('should return the original file path if obfuscation is not enabled', () => {
1276      const filePath = '/path/to/file.js';
1277      const isPackageModules = false;
1278      const projectConfig = {
1279        enableFileNameObfuscation: false,
1280        buildMode: 'debug'
1281      };
1282  
1283      const result = handleObfuscatedFilePath(filePath, isPackageModules, projectConfig);
1284
1285      expect(result).to.equal(filePath);
1286    });
1287  
1288    it('should return the original file path if obfuscation is not enabled', () => {
1289      const filePath = '/path/to/file.txt';
1290      const isPackageModules = false;
1291      const projectConfig = {
1292        obfuscationMergedObConfig: null,
1293        buildMode: 'debug'
1294      };
1295      const result = handleObfuscatedFilePath(filePath, isPackageModules, projectConfig);
1296      expect(result).to.equal(filePath);
1297    });
1298  
1299    it('should return the original file path if obfuscation is not enabled', () => {
1300      const filePath = '/path/to/file.txt';
1301      const isPackageModules = false;
1302      const projectConfig = {
1303        obfuscationMergedObConfig: null,
1304        buildMode: 'not debug'
1305      };
1306      const result = handleObfuscatedFilePath(filePath, isPackageModules, projectConfig);
1307      expect(result).to.equal(filePath);
1308    });
1309  
1310    it('should return the unix formatted file path if obfuscation is enabled and is a package module', () => {
1311      const filePath = '/path/to/file.js';
1312      const isPackageModules = true;
1313      const projectConfig = {
1314        obfuscationMergedObConfig: {
1315          options: {
1316            disableObfuscation: false,
1317            enableFileNameObfuscation: true,
1318          },
1319        },
1320        buildMode: "not Debug"
1321      };
1322  
1323      const result = handleObfuscatedFilePath(filePath, isPackageModules, projectConfig);
1324
1325      expect(result).to.equal(FileUtils.toUnixPath(filePath));
1326    });
1327  
1328    it('should return the unix formatted file path if obfuscation is enabled and is a package module', () => {
1329      const filePath = '/path/to/file.js';
1330      const isPackageModules = true;
1331      const projectConfig = {
1332        obfuscationMergedObConfig: {
1333          options: {
1334            disableObfuscation: false,
1335            enableFileNameObfuscation: true,
1336          },
1337        },
1338        buildMode: ""
1339      };
1340  
1341      const result = handleObfuscatedFilePath(filePath, isPackageModules, projectConfig);
1342
1343      expect(result).to.equal(FileUtils.toUnixPath(filePath));
1344    });
1345  });
1346  
1347  describe('enableObfuscateFileName', () => {
1348    it('should return false if obfuscation is not enabled', () => {
1349      const isPackageModules = false;
1350      const projectConfig = {
1351        obfuscationMergedObConfig: null,
1352        buildMode: "not Debug"
1353      };
1354      const result = enableObfuscateFileName(isPackageModules, projectConfig);
1355      expect(result).to.be.false;
1356    });
1357  
1358    it('should return true if obfuscation is enabled and not a package module', () => {
1359      const isPackageModules = false;
1360      const projectConfig = {
1361        obfuscationMergedObConfig: {
1362          options: {
1363            disableObfuscation: false,
1364            enableFileNameObfuscation: true,
1365          },
1366        },
1367        buildMode: "not Debug"
1368      };
1369      const result = enableObfuscateFileName(isPackageModules, projectConfig);
1370
1371      expect(result).to.be.true;
1372    });
1373  
1374    it('should return false if obfuscation is enabled and is a package module', () => {
1375      const isPackageModules = true;
1376      const projectConfig = {
1377        obfuscationMergedObConfig: {
1378          options: {
1379            disableObfuscation: false,
1380            enableFileNameObfuscation: true,
1381          },
1382        },
1383        buildMode: "Debug"
1384      };
1385      const result = enableObfuscateFileName(isPackageModules, projectConfig);
1386      expect(result).to.be.false;
1387    });
1388  
1389    it('should return false if obfuscation is enabled and is a package module', () => {
1390      const isPackageModules = true;
1391      const projectConfig = {
1392        obfuscationMergedObConfig: {
1393          options: {
1394            disableObfuscation: false,
1395            enableFileNameObfuscation: false,
1396          },
1397        },
1398        buildMode: "Debug"
1399      };
1400      const result = enableObfuscateFileName(isPackageModules, projectConfig);
1401      expect(result).to.be.false;
1402    });
1403  
1404    it('should return false if obfuscation is enabled and is a package module', () => {
1405      const isPackageModules = true;
1406      const projectConfig = {
1407        obfuscationMergedObConfig: {},
1408        buildMode: "Debug"
1409      };
1410      const result = enableObfuscateFileName(isPackageModules, projectConfig);
1411      expect(result).to.be.false;
1412    });
1413  
1414    it('should return false if obfuscation is enabled and is a package module', () => {
1415      const isPackageModules = true;
1416      const projectConfig = {
1417        obfuscationMergedObConfig: null,
1418        buildMode: "Debug"
1419      };
1420      const result = enableObfuscateFileName(isPackageModules, projectConfig);
1421      expect(result).to.be.false;
1422    });
1423  
1424    it('should return false if obfuscation is enabled and is a package module', () => {
1425      const isPackageModules = true;
1426      const projectConfig = {
1427        obfuscationMergedObConfig: undefined,
1428        buildMode: "Debug"
1429      };
1430      const result = enableObfuscateFileName(isPackageModules, projectConfig);
1431      expect(result).to.be.false;
1432    });
1433  
1434    it('should return false if obfuscation is enabled and is a package module', () => {
1435      const isPackageModules = true;
1436      const projectConfig = {
1437        obfuscationMergedObConfig: {
1438          options: {
1439            disableObfuscation: false,
1440            enableFileNameObfuscation: true,
1441          },
1442        },
1443        buildMode: "not Debug"
1444      };
1445      const result = enableObfuscateFileName(isPackageModules, projectConfig);
1446      expect(result).to.be.false;
1447    });
1448  
1449    it('should return false if obfuscation is enabled and is a package module', () => {
1450      const isPackageModules = true;
1451      const projectConfig = {
1452        obfuscationMergedObConfig: {
1453          options: {
1454            disableObfuscation: false,
1455            enableFileNameObfuscation: false,
1456          },
1457        },
1458        buildMode: "not Debug"
1459      };
1460      const result = enableObfuscateFileName(isPackageModules, projectConfig);
1461      expect(result).to.be.false;
1462    });
1463  
1464    it('should return false if obfuscation is enabled and is a package module', () => {
1465      const isPackageModules = true;
1466      const projectConfig = {
1467        obfuscationMergedObConfig: {
1468          options: {
1469            disableObfuscation: false,
1470            enableFileNameObfuscation: false,
1471          },
1472        },
1473        buildMode: "not Debug"
1474      };
1475      const result = enableObfuscateFileName(isPackageModules, projectConfig);
1476      expect(result).to.be.false;
1477    });
1478  
1479    it('should return false if obfuscation is enabled and is a package module', () => {
1480      const isPackageModules = true;
1481      const projectConfig = {
1482        obfuscationMergedObConfig: {
1483          options: {
1484            disableObfuscation: false,
1485            enableFileNameObfuscation: true,
1486          },
1487        },
1488        buildMode: "not Debug"
1489      };
1490      const result = enableObfuscateFileName(isPackageModules, projectConfig);
1491      expect(result).to.be.false;
1492    });
1493  });
1494  
1495  describe('getRelativeSourcePath', () => {
1496    it('should return the relative path of a file within the project root', () => {
1497      const filePath = '/Users/user/project/src/components/Button.js';
1498      const projectRootPath = '/Users/user/project';
1499      const expectedRelativePath = 'src/components/Button.js';
1500  
1501      const result = getRelativeSourcePath(filePath, projectRootPath, '');
1502      expect(result).to.equal(expectedRelativePath);
1503    });
1504  
1505    it('should return the relative path of a file within a specified project path', () => {
1506      const filePath = '/Users/user/project/src/components/Button.js';
1507      const belongProjectPath = '/Users/user/project/src';
1508      const expectedRelativePath = 'components/Button.js';
1509  
1510      const result = getRelativeSourcePath(filePath, '', belongProjectPath);
1511      expect(result).to.equal(expectedRelativePath);
1512    });
1513  
1514    it('should return the original path if no project root or belong project path is provided', () => {
1515      const filePath = '/Users/user/project/src/components/Button.js';
1516      const expectedRelativePath = filePath;
1517  
1518      const result = getRelativeSourcePath(filePath, '', '');
1519      expect(result).to.equal(expectedRelativePath);
1520    });
1521  });
1522});
1523