• 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 { expect } from 'chai';
18import {
19  ArkObfuscator,
20  clearGlobalCaches,
21  collectReservedNameForObf,
22  collectResevedFileNameInIDEConfig,
23  deleteLineInfoForNameString,
24  enableObfuscatedFilePathConfig,
25  enableObfuscateFileName,
26  generateConsumerObConfigFile,
27  getRelativeSourcePath,
28  handleObfuscatedFilePath,
29  handleUniversalPathInObf,
30  initObfuscationConfig,
31  mangleFilePath,
32  MemoryUtils,
33  MergedConfig,
34  nameCacheMap,
35  ObConfigResolver,
36  ObfuscationResultType,
37  orignalFilePathForSearching,
38  PropCollections,
39  readNameCache,
40  readProjectPropertiesByCollectedPaths,
41  renameFileNameModule,
42  ReseverdSetForArkguard,
43  TimeSumPrinter,
44  UnobfuscationCollections,
45  writeObfuscationNameCache,
46  writeUnobfuscationContent,
47  unobfuscationNamesObj,
48  FileUtils,
49} from '../../../src/ArkObfuscator';
50
51import {
52  createSourceFile,
53  createObfTextSingleLineWriter,
54  RawSourceMap,
55  SourceFile,
56  ScriptTarget,
57} from 'typescript';
58
59import { IOptions } from '../../../src/configs/IOptions';
60import { getSourceMapGenerator } from '../../../src/utils/SourceMapUtil';
61import {
62  globalFileNameMangledTable,
63  historyFileNameMangledTable,
64} from '../../../src/transformers/rename/RenameFileNameTransformer';
65import { LocalVariableCollections } from '../../../src/utils/CommonCollections';
66import { SOURCE_FILE_PATHS, projectWhiteListManager } from '../../../src/utils/ProjectCollections';
67import { FilePathObj } from '../../../src/common/type';
68import { historyAllUnobfuscatedNamesMap } from '../../../src/initialization/Initializer';
69import path from 'path';
70import { nameCache } from '../../../src/transformers/rename/RenameIdentifierTransformer';
71import {
72  decodeSourcemap,
73  MappingsNameTypeForTest,
74  Source,
75  SourceMapLink
76} from '../../../src/utils/SourceMapMergingUtil';
77
78describe('Tester Cases for <ArkObfuscator>', function () {
79  let obfuscator: ArkObfuscator;
80  let defaultConfig: IOptions;
81
82  const sourceFilePathObj: FilePathObj = {
83    buildFilePath: 'demo.ts',
84    relativeFilePath: 'demo',
85  };
86  let sourceFile: SourceFile;
87  let sourceFileContent: string = `class Person {
88  constructor(public name: string, public age: number) {
89      this.name = name;
90      this.age = age;
91  }
92}`;
93
94  const jsSourceFilePathObj: FilePathObj = {
95    buildFilePath: 'demo.js',
96    relativeFilePath: '',
97  };
98  let jsSourceFile: SourceFile;
99  let jsSourceFileContent: string = `//This is a comment
100//This is a comment
101function subtract(a, b) {
102    return a - b;
103}`;
104
105  const declSourceFilePathObj: FilePathObj = {
106    buildFilePath: 'demo.d.ts',
107    relativeFilePath: '',
108  };
109  let declSourceFile: SourceFile;
110  let declSourceFileContent: string = `//This is a comment
111//This is a comment
112export declare function add(num1: number, num2: number): number;
113export declare function findElement<T>(arr: T[], callback: (item: T) => boolean): T | undefined;`;
114
115  beforeEach(() => {
116    obfuscator = new ArkObfuscator();
117
118    // Clear the collection to ensure test isolation
119    PropCollections.clearPropsCollections();
120    UnobfuscationCollections.clear();
121    LocalVariableCollections.clear();
122
123    defaultConfig = {
124      mRemoveComments: true,
125      mNameObfuscation: {
126        mEnable: true,
127        mNameGeneratorType: 1,
128        mReservedNames: [],
129        mRenameProperties: true,
130        mReservedProperties: [],
131        mTopLevel: true,
132        mReservedToplevelNames: [],
133      },
134      mEnableSourceMap: true,
135      mEnableNameCache: true,
136    };
137
138    sourceFile = createSourceFile(sourceFilePathObj.buildFilePath, sourceFileContent, ScriptTarget.ES2015, true);
139    jsSourceFile = createSourceFile(jsSourceFilePathObj.buildFilePath, jsSourceFileContent, ScriptTarget.ES2015, true);
140    declSourceFile = createSourceFile(declSourceFilePathObj.buildFilePath, declSourceFileContent, ScriptTarget.ES2015, true);
141  });
142
143  describe('test for ArkObfuscator.init', () => {
144    it('should return false if config is undefined', () => {
145      const result = obfuscator.init(undefined);
146      expect(result).to.be.false;
147    });
148
149    it('should return true if config is valid', () => {
150      const result = obfuscator.init(defaultConfig);
151      expect(result).to.be.true;
152    });
153
154    it('source code should be compacted into one line', () => {
155      const config: IOptions = {
156        mCompact: true,
157        mEnableSourceMap: true,
158        mRemoveComments: true
159      };
160      obfuscator.init(config);
161
162      let sourceMapGenerator = getSourceMapGenerator(jsSourceFilePathObj.buildFilePath);
163      const textWriter = createObfTextSingleLineWriter();
164      obfuscator.createObfsPrinter(jsSourceFile.isDeclarationFile).writeFile(jsSourceFile, textWriter, sourceMapGenerator);
165      const actualContent = textWriter.getText();
166      const expectContent = `function subtract(a, b) {return a - b;}`;
167      expect(actualContent === expectContent).to.be.true;
168    });
169
170    it('should not init incremental cache when cachePath is not passed', () => {
171      const config: IOptions = {
172        mNameObfuscation: {
173          mEnable: true,
174          mRenameProperties: true,
175          mReservedProperties: [],
176        },
177        mCompact: true,
178        mEnableSourceMap: true,
179        mRemoveComments: true
180      };
181      obfuscator.init(config);
182      expect(obfuscator.fileContentManager).to.be.undefined;
183      expect(obfuscator.filePathManager).to.be.undefined;
184      expect(projectWhiteListManager).to.be.undefined;
185    });
186
187    it('should init incremental cache when cachePath is passed', () => {
188      const config: IOptions = {
189        mNameObfuscation: {
190          mEnable: true,
191          mRenameProperties: true,
192          mReservedProperties: [],
193        },
194        mCompact: true,
195        mEnableSourceMap: true,
196        mRemoveComments: true
197      };
198      const cachePath = 'test/ut/utils/obfuscation';
199      obfuscator.init(config, cachePath);
200      expect(obfuscator.fileContentManager).to.not.be.undefined;
201      expect(obfuscator.filePathManager).to.not.be.undefined;
202      expect(projectWhiteListManager).to.not.be.undefined;
203    });
204
205    it('should set is incremental flag if is incremental', () => {
206      const config: IOptions = {
207        mNameObfuscation: {
208          mEnable: true,
209          mRenameProperties: true,
210          mReservedProperties: [],
211        },
212        mCompact: true,
213        mEnableSourceMap: true,
214        mRemoveComments: true
215      };
216      const cachePath = 'test/ut/utils/obfuscation';
217      const filePathsCache = path.join(cachePath, SOURCE_FILE_PATHS);
218      let content = 'hello';
219      FileUtils.writeFile(filePathsCache, content);
220      obfuscator.init(config, cachePath);
221      expect(obfuscator.isIncremental).to.be.true;
222      FileUtils.deleteFile(filePathsCache);
223    });
224  });
225
226  describe('test for ArkObfuscator.obfuscate', () => {
227    it('should return empty result for ignored files', async () => {
228      const sourceFilePathObj: FilePathObj = {
229        buildFilePath: 'ignoredFile.cpp',
230        relativeFilePath: '',
231      };
232      const result: ObfuscationResultType = await obfuscator.obfuscate(
233        'hello world',
234        sourceFilePathObj,
235      );
236      expect(result).to.deep.equal({ content: undefined });
237    });
238
239    it('should return empty result for empty AST', async () => {
240      const sourceFilePathObj: FilePathObj = {
241        buildFilePath: 'emptyFile.js',
242        relativeFilePath: '',
243      };
244      const result: ObfuscationResultType = await obfuscator.obfuscate('', sourceFilePathObj);
245      expect(result).to.deep.equal({ content: undefined });
246    });
247
248    it('should be correctly obfuscated for valid AST', async () => {
249      obfuscator.init(defaultConfig);
250      const result: ObfuscationResultType = await obfuscator.obfuscate(
251        sourceFile,
252        sourceFilePathObj,
253      );
254      const expectResult = `class g {
255    constructor(public name: string, public h: number) {
256        this.name = name;
257        this.h = h;
258    }
259}
260`;
261      expect(result.content === expectResult).to.be.true;
262    });
263
264    it('comments in declaration file should be removed when enable -remove-comments', async () => {
265      const config: IOptions = {
266        mRemoveDeclarationComments: {
267          mEnable: true,
268          mReservedComments: [],
269          mUniversalReservedComments: [],
270        },
271      };
272
273      obfuscator.init(config);
274      const result: ObfuscationResultType = await obfuscator.obfuscate(declSourceFile, declSourceFilePathObj);
275      const expectResult = `export declare function add(num1: number, num2: number): number;
276export declare function findElement<T>(arr: T[], callback: (item: T) => boolean): T | undefined;
277`;
278      expect(result.content === expectResult).to.be.true;
279    });
280
281    it('comments in declaration file should not be removed when enable -remove-comments', async () => {
282      const config: IOptions = {
283        mRemoveDeclarationComments: {
284          mEnable: false,
285          mReservedComments: [],
286          mUniversalReservedComments: [],
287        },
288      };
289      obfuscator.init(config);
290      const result: ObfuscationResultType = await obfuscator.obfuscate(declSourceFile, declSourceFilePathObj);
291      const expectResult = `//This is a comment
292//This is a comment
293export declare function add(num1: number, num2: number): number;
294export declare function findElement<T>(arr: T[], callback: (item: T) => boolean): T | undefined;
295`;
296      expect(result.content === expectResult).to.be.true;
297    });
298
299    it('unobfuscationNameMap should be Map type when enable -print-kept-names', async () => {
300      const config = {
301        mUnobfuscationOption: {
302          mPrintKeptNames: true,
303          mPrintPath: 'local.json',
304        },
305      };
306      historyAllUnobfuscatedNamesMap.set('demo', {'key': ['value']});
307      obfuscator.init(config);
308      const result: ObfuscationResultType = await obfuscator.obfuscate(
309        sourceFile,
310        sourceFilePathObj,
311      );
312      expect(result.unobfuscationNameMap instanceof Map).to.be.true;
313    });
314
315    it('unobfuscationNameMap should be undefined when disable -print-kept-names, ', async () => {
316      obfuscator.init(defaultConfig);
317      const result: ObfuscationResultType = await obfuscator.obfuscate(
318        sourceFile,
319        sourceFilePathObj,
320      );
321      expect(result.unobfuscationNameMap === undefined).to.be.true;
322    });
323
324    it('historyNameCache should be used when provide historyNameCache', async () => {
325      obfuscator.init(defaultConfig);
326      const historyNameCache = new Map([['#Person', 'm'], ['Person:2:5', 'm']]);
327      const result: ObfuscationResultType = await obfuscator.obfuscate(
328        sourceFile,
329        sourceFilePathObj,
330        undefined,
331        historyNameCache,
332      );
333      const expectResult = `
334        class m {
335          constructor(public name: string, public g: number) {
336            this.name = name;
337            this.g = g;
338          }
339        }
340      `;
341      expect(compareStringsIgnoreNewlines(result.content, expectResult)).to.be.true;
342    });
343
344    it('fileName should be obfuscated when enable -enable-filename-obfuscation', async () => {
345      const config: IOptions = {
346        mRenameFileName: {
347          mEnable: true,
348          mNameGeneratorType: 1,
349          mReservedFileNames: [],
350        },
351      };
352      obfuscator.init(config);
353      const result: ObfuscationResultType = await obfuscator.obfuscate(
354        sourceFile,
355        sourceFilePathObj,
356      );
357
358      expect(orignalFilePathForSearching === 'demo.ts').to.be.true;
359      expect(result.filePath === 'a.ts').to.be.true;
360    });
361
362    it('PropCollections shoule be cleared when only enable toplevel option', async () => {
363      PropCollections.globalMangledTable.set('test', 'obfuscated');
364      const config: IOptions = {
365        mNameObfuscation: {
366          mEnable: true,
367          mNameGeneratorType: 1,
368          mRenameProperties: false,
369          mReservedProperties: [],
370          mTopLevel: true,
371        },
372        mExportObfuscation: false,
373      };
374      obfuscator.init(config);
375      await obfuscator.obfuscate(sourceFile, sourceFilePathObj);
376      expect(PropCollections.globalMangledTable.size).to.equal(0);
377    });
378
379    it('PropCollections shoule not be cleared when enable toplevel、property and export option', async () => {
380      PropCollections.globalMangledTable.set('test', 'obfuscated');
381      const config: IOptions = {
382        mNameObfuscation: {
383          mEnable: true,
384          mNameGeneratorType: 1,
385          mRenameProperties: true,
386          mReservedProperties: [],
387          mTopLevel: true,
388        },
389        mExportObfuscation: true,
390      };
391      obfuscator.init(config);
392      await obfuscator.obfuscate(sourceFile, sourceFilePathObj);
393      expect(PropCollections.globalMangledTable.get('test') === 'obfuscated').to.be.true;
394    });
395
396    it('test for use sourcemap mapping', async () => {
397      obfuscator.init(defaultConfig);
398      const previousStageSourceMap = {
399        "version":3,
400        "file":"demo.js",
401        "sourceRoot":"",
402        "sources":["demo.js"],
403        "names":[],
404        "mappings":"AAAA,mBAAmB;AACnB,mBAAmB;AACnB,SAAS,QAAQ,CAAC,CAAC,EAAE,CAAC;IAClB,OAAO,CAAC,GAAG,CAAC,CAAC;AACjB,CAAC"
405      } as RawSourceMap;
406      const result: ObfuscationResultType = await obfuscator.obfuscate(
407        jsSourceFile,
408        jsSourceFilePathObj,
409        previousStageSourceMap,
410      );
411      const actualSourceMap = JSON.stringify(result.sourceMap);
412      const expectSourceMap = `{"version":3,"file":"demo.js","sources":["demo.js"],"names":[],
413        "mappings":"AAEA,WAAkB,CAAC,EAAE,CAAC;IAClB,OAAO,KAAK,CAAC;AACjB,CAAC","sourceRoot":""}`;
414      expect(compareStringsIgnoreNewlines(actualSourceMap, expectSourceMap)).to.be.true;
415    });
416
417    it('test for not use sourcemap mapping', async () => {
418      obfuscator.init(defaultConfig);
419      const result: ObfuscationResultType = await obfuscator.obfuscate(
420        jsSourceFile,
421        jsSourceFilePathObj,
422      );
423      const actualSourceMap = JSON.stringify(result.sourceMap);
424      const expectSourceMap = `{"version":3,"file":"demo.js","sourceRoot":"","sources":["demo.js"],
425        "names":[],"mappings":"AAEA,WAAkB,CAAC,EAAE,CAAC;IAClB,OAAO,KAAK,CAAC;AACjB,CAAC"}`;
426      expect(compareStringsIgnoreNewlines(actualSourceMap, expectSourceMap)).to.be.true;
427    });
428  });
429
430  describe('test for ArkObfuscator.setWriteOriginalFile', () => {
431    it('should set writeOriginalFile to true', () => {
432      obfuscator.setWriteOriginalFile(true);
433      expect(obfuscator.getWriteOriginalFileForTest()).to.be.true;
434    });
435
436    it('should set writeOriginalFile to false', () => {
437      obfuscator.setWriteOriginalFile(false);
438      expect(obfuscator.getWriteOriginalFileForTest()).to.be.false;
439    });
440  });
441
442  describe('test for ArkObfuscator.addReservedSetForPropertyObf', () => {
443    it('should add reserved sets correctly', () => {
444      const properties: ReseverdSetForArkguard = {
445        structPropertySet: new Set(['struct1']),
446        stringPropertySet: new Set(['string1']),
447        exportNameAndPropSet: new Set(['export1']),
448        exportNameSet: undefined,
449        enumPropertySet: new Set(['enum1']),
450      };
451
452      obfuscator.addReservedSetForPropertyObf(properties);
453
454      expect(UnobfuscationCollections.reservedStruct.has('struct1')).to.be.true;
455      expect(UnobfuscationCollections.reservedStrProp.has('string1')).to.be.true;
456      expect(UnobfuscationCollections.reservedExportNameAndProp.has('export1')).to.be.true;
457      expect(UnobfuscationCollections.reservedEnum.has('enum1')).to.be.true;
458    });
459
460    it('should not add empty sets', () => {
461      const properties: ReseverdSetForArkguard = {
462        structPropertySet: new Set(),
463        stringPropertySet: new Set(),
464        exportNameAndPropSet: new Set(),
465        exportNameSet: undefined,
466        enumPropertySet: new Set(),
467      };
468
469      obfuscator.addReservedSetForPropertyObf(properties);
470
471      expect(UnobfuscationCollections.reservedStruct.size).to.equal(0);
472      expect(UnobfuscationCollections.reservedStrProp.size).to.equal(0);
473      expect(UnobfuscationCollections.reservedExportNameAndProp.size).to.equal(0);
474      expect(UnobfuscationCollections.reservedEnum.size).to.equal(0);
475    });
476  });
477
478  describe('test for ArkObfuscator.addReservedSetForDefaultObf', () => {
479    it('should add reserved export name set correctly', () => {
480      const properties: ReseverdSetForArkguard = {
481        structPropertySet: undefined,
482        stringPropertySet: undefined,
483        exportNameAndPropSet: undefined,
484        exportNameSet: new Set(['exportName1']),
485        enumPropertySet: undefined,
486      };
487
488      obfuscator.addReservedSetForDefaultObf(properties);
489      expect(UnobfuscationCollections.reservedExportName.has('exportName1')).to.be.true;
490    });
491  });
492
493  describe('test for ArkObfuscator.setKeepSourceOfPaths', () => {
494    it('should set the keep source of paths correctly', () => {
495      const config: IOptions = {
496        mKeepFileSourceCode: {
497          mKeepSourceOfPaths: new Set(),
498          mkeepFilesAndDependencies: new Set(),
499        },
500      };
501      obfuscator.init(config);
502
503      const paths = new Set(['path1', 'path2']);
504      obfuscator.setKeepSourceOfPaths(paths);
505      expect(obfuscator.customProfiles.mKeepFileSourceCode?.mKeepSourceOfPaths).to.equal(paths);
506    });
507  });
508
509  describe('test for ArkObfuscator.isCurrentFileInKeepPaths', () => {
510    it('should return false if mKeepSourceOfPaths is empty', () => {
511      const customProfiles: IOptions = {
512        mKeepFileSourceCode: {
513          mKeepSourceOfPaths: new Set(),
514          mkeepFilesAndDependencies: new Set(),
515        },
516      };
517      const result = obfuscator.isCurrentFileInKeepPathsForTest(customProfiles, 'some/file/path.js');
518      expect(result).to.be.false;
519    });
520
521    it('should return true if originalFilePath is in mKeepSourceOfPaths', () => {
522      const keepPaths = new Set(['some/file/path.js']);
523      const customProfiles: IOptions = {
524        mKeepFileSourceCode: {
525          mKeepSourceOfPaths: keepPaths,
526          mkeepFilesAndDependencies: new Set(),
527        }
528      };
529      const result = obfuscator.isCurrentFileInKeepPathsForTest(customProfiles, 'some/file/path.js');
530      expect(result).to.be.true;
531    });
532  });
533
534  describe('test for ArkObfuscator.convertLineBasedOnSourceMap', () => {
535    it('there is no location but the value is obfuscated, it should be recorded in nameacahe', () => {
536      const identifierCache = new Map<string, string>([
537        ['#Person', 'Person'],
538        ['obfsucationValue:31:5:35:6', 'h'],
539        ['getName:75:5:77:6', 'getName']
540      ]);
541      nameCache.set('IdentifierCache', identifierCache);
542      const source: Source = new Source('utils.ets', null);
543      const previousStageSourceMap: RawSourceMap = {
544        version: 3,
545        file: 'utils.ets',
546        sourceRoot: '',
547        sources: [ 'entry/src/main/ets/pages/utils/utils.ets'],
548        names: [],
549        mappings: '',
550        sourcesContent: undefined,
551      }
552      const decodedSourceMap = decodeSourcemap(previousStageSourceMap as RawSourceMap);
553      const sourceMapLink: SourceMapLink = new SourceMapLink(decodedSourceMap as MappingsNameTypeForTest, [source]);
554
555      const result = obfuscator.convertLineBasedOnSourceMapForTest('IdentifierCache', sourceMapLink);
556      const expectedResult= {
557        '#Person': 'Person',
558        'obfsucationValue': 'h'
559      };
560      const compareResult = JSON.stringify(result) === JSON.stringify(expectedResult);
561      expect(compareResult).to.be.true;
562    });
563
564    it('there is start and end location, it should be recorded in nameacahe', () => {
565      const identifierCache = new Map<string, string>([
566        ['#Person', 'Person'],
567        ['#foo:1:1:3:2', 'foo'],
568        ['getName:75:5:77:6', 'getName']
569      ]);
570      nameCache.set('IdentifierCache', identifierCache);
571      const source: Source = new Source('file.ts', null);
572      const previousStageSourceMap: RawSourceMap = {
573        "version": 3,
574        "file": "file.ts",
575        "sources": [
576          "entry/src/main/ets/pages/file.ts"
577        ],
578        "names": [],
579        "mappings": "AAAA,MAAM,UAAU,GAAG;IACjB,OAAO,CAAC,CAAC;AACX,CAAC",
580        "sourceRoot": ""
581      }
582      const decodedSourceMap = decodeSourcemap(previousStageSourceMap as RawSourceMap);
583      const sourceMapLink: SourceMapLink = new SourceMapLink(decodedSourceMap as MappingsNameTypeForTest, [source]);
584
585      const result = obfuscator.convertLineBasedOnSourceMapForTest('IdentifierCache', sourceMapLink);
586      const expectedResult= {
587        '#Person': 'Person',
588        '#foo:1:3': 'foo'
589      };
590      const compareResult = JSON.stringify(result) === JSON.stringify(expectedResult);
591      expect(compareResult).to.be.true;
592    });
593  });
594
595  describe('test for clearGlobalCaches', () => {
596    beforeEach(() => {
597      PropCollections.globalMangledTable.set('test1', 'obfuscated1');
598      PropCollections.historyMangledTable = new Map([['test2', 'obfuscated2']]);
599      PropCollections.reservedProperties.add('reserved1');
600      PropCollections.universalReservedProperties.push(/universal\d+/);
601      globalFileNameMangledTable.set('key1', 'value1');
602      renameFileNameModule.historyFileNameMangledTable = new Map([['keyA', 'valueA']]);
603      UnobfuscationCollections.reservedSdkApiForProp.add('api1');
604      UnobfuscationCollections.reservedSdkApiForGlobal.add('globalApi1');
605      UnobfuscationCollections.reservedSdkApiForLocal.add('localApi1');
606      UnobfuscationCollections.reservedStruct.add('struct1');
607      UnobfuscationCollections.reservedLangForProperty.add('lang1');
608      UnobfuscationCollections.reservedExportName.add('exportName1');
609      UnobfuscationCollections.reservedExportNameAndProp.add('exportNameAndProp1');
610      UnobfuscationCollections.reservedStrProp.add('stringProp1');
611      UnobfuscationCollections.reservedEnum.add('enum1');
612      UnobfuscationCollections.unobfuscatedPropMap.set('age', new Set(['key', 'value']));
613      UnobfuscationCollections.unobfuscatedNamesMap.set('name1', new Set(['key1', 'value2']));
614      LocalVariableCollections.reservedConfig.add('localConfig1');
615    });
616
617    it('should clear all global caches', () => {
618      clearGlobalCaches();
619
620      expect(PropCollections.globalMangledTable.size).to.equal(0);
621      expect(PropCollections.historyMangledTable.size).to.equal(0);
622      expect(PropCollections.reservedProperties.size).to.equal(0);
623      expect(PropCollections.universalReservedProperties.length).to.equal(0);
624      expect(globalFileNameMangledTable.size).to.equal(0);
625      expect(historyFileNameMangledTable.size).to.equal(0);
626      expect(UnobfuscationCollections.reservedSdkApiForProp.size).to.equal(0);
627      expect(UnobfuscationCollections.reservedStruct.size).to.equal(0);
628      expect(UnobfuscationCollections.reservedExportName.size).to.equal(0);
629      expect(LocalVariableCollections.reservedConfig.size).to.equal(0);
630    });
631  });
632
633  describe('test whether the methods exported from the ArkObfuscator file exist', () => {
634    it('The ArkObfuscator export the collectReservedNameForObf method', () => {
635      expect(collectReservedNameForObf).to.exist;
636    });
637
638    it('The ArkObfuscator export the collectResevedFileNameInIDEConfig method', () => {
639      expect(collectResevedFileNameInIDEConfig).to.exist;
640    });
641
642    it('The ArkObfuscator export the deleteLineInfoForNameString method', () => {
643      expect(deleteLineInfoForNameString).to.exist;
644    });
645
646    it('The ArkObfuscator export the enableObfuscatedFilePathConfig method', () => {
647      expect(enableObfuscatedFilePathConfig).to.exist;
648    });
649
650    it('The ArkObfuscator export the enableObfuscateFileName method', () => {
651      expect(enableObfuscateFileName).to.exist;
652    });
653
654    it('The ArkObfuscator export the generateConsumerObConfigFile method', () => {
655      expect(generateConsumerObConfigFile).to.exist;
656    });
657
658    it('The ArkObfuscator export the getRelativeSourcePath method', () => {
659      expect(getRelativeSourcePath).to.exist;
660    });
661
662    it('The ArkObfuscator export the handleObfuscatedFilePath method', () => {
663      expect(handleObfuscatedFilePath).to.exist;
664    });
665
666    it('The ArkObfuscator export the handleUniversalPathInObf method', () => {
667      expect(handleUniversalPathInObf).to.exist;
668    });
669
670    it('The ArkObfuscator export the initObfuscationConfig method', () => {
671      expect(initObfuscationConfig).to.exist;
672    });
673
674    it('The ArkObfuscator export the mangleFilePath method', () => {
675      expect(mangleFilePath).to.exist;
676    });
677
678    it('The ArkObfuscator export the MemoryUtils method', () => {
679      expect(MemoryUtils).to.exist;
680    });
681
682    it('The ArkObfuscator export the MergedConfig method', () => {
683      expect(MergedConfig).to.exist;
684    });
685
686    it('The ArkObfuscator export the nameCacheMap method', () => {
687      expect(nameCacheMap).to.exist;
688    });
689
690    it('The ArkObfuscator export the ObConfigResolver method', () => {
691      expect(ObConfigResolver).to.exist;
692    });
693
694    it('The ArkObfuscator export the readNameCache method', () => {
695      expect(readNameCache).to.exist;
696    });
697
698    it('The ArkObfuscator export the readProjectPropertiesByCollectedPaths method', () => {
699      expect(readProjectPropertiesByCollectedPaths).to.exist;
700    });
701
702    it('The ArkObfuscator export the TimeSumPrinter method', () => {
703      expect(TimeSumPrinter).to.exist;
704    });
705
706    it('The ArkObfuscator export the writeObfuscationNameCache method', () => {
707      expect(writeObfuscationNameCache).to.exist;
708    });
709
710    it('The ArkObfuscator export the writeUnobfuscationContent method', () => {
711      expect(writeUnobfuscationContent).to.exist;
712    });
713
714    it('The ArkObfuscator export the unobfuscationNamesObj method', () => {
715      expect(unobfuscationNamesObj).to.exist;
716    });
717  });
718});
719
720function compareStringsIgnoreNewlines(str1: string, str2: string): boolean {
721  const normalize = (str: string) => str.replace(/[\n\r\s]+/g, '');
722  return normalize(str1) === normalize(str2);
723}
724