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