1/* 2 * Copyright (c) 2023 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use rollupObject 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 16// Execute this file first to avoid circular dependency problems 17 18import { expect } from 'chai'; 19import mocha from 'mocha'; 20import fs from "fs"; 21import path from "path"; 22import MagicString from 'magic-string'; 23import sinon from 'sinon'; 24 25import { 26 getBuildModeInLowerCase, 27 getPackageInfo, 28 genSourceMapFileName, 29 isOhModules, 30 isEs2Abc, 31 writeArkguardObfuscatedSourceCode, 32 writeMinimizedSourceCode, 33 tryMangleFileName 34} from '../../lib/ark_utils'; 35import { 36 DEBUG, 37 RELEASE, 38 OH_MODULES, 39 EXTNAME_TS, 40 EXTNAME_JS, 41 EXTNAME_ETS, 42 OBFUSCATION_TOOL 43} from '../../lib/fast_build/ark_compiler/common/ark_define'; 44import RollUpPluginMock from './mock/rollup_mock/rollup_plugin_mock'; 45import { 46 BUNDLE_NAME_DEFAULT, 47 ENTRY_MODULE_NAME_DEFAULT, 48 EXTNAME_MAP, 49 ENTRYABILITY_JS 50} from './mock/rollup_mock/common'; 51import projectConfig from './utils/processProjectConfig'; 52import { 53 ES2ABC, 54 TS2ABC 55} from '../../lib/pre_define'; 56import { changeFileExtension } from '../../lib/fast_build/ark_compiler/utils'; 57import ModuleSourceFileMock from './mock/class_mock/module_source_files_mock'; 58import { 59 genTemporaryPath, 60 toUnixPath 61} from '../../lib/utils'; 62import { 63 ObConfigResolver, 64 MergedConfig 65} from '../../lib/fast_build/ark_compiler/common/ob_config_resolver'; 66import { 67 utProcessArkConfig 68} from '../../lib/fast_build/ark_compiler/common/process_ark_config'; 69import { ModuleSourceFile } from '../../lib/fast_build/ark_compiler/module/module_source_file'; 70import { PROJECT_ROOT, TERSER_PROCESSED_EXPECTED_CODE } from './mock/rollup_mock/path_config'; 71import { GEN_ABC_PLUGIN_NAME } from '../../lib/fast_build/ark_compiler/common/ark_define'; 72import { SourceMapGenerator } from '../../lib/fast_build/ark_compiler/generate_sourcemap'; 73import { ArkObfuscator } from 'arkguard'; 74 75mocha.describe('test ark_utils file api', function () { 76 mocha.before(function () { 77 this.rollup = new RollUpPluginMock(); 78 }); 79 80 mocha.after(() => { 81 delete this.rollup; 82 }); 83 84 mocha.it('1-1: test getBuildModeInLowerCase under build debug', function () { 85 this.rollup.build(); 86 const buildMode = getBuildModeInLowerCase(this.rollup.share.projectConfig); 87 expect(buildMode === DEBUG).to.be.true; 88 }); 89 90 mocha.it('1-2: test getBuildModeInLowerCase under build release', function () { 91 this.rollup.build(RELEASE); 92 const buildMode = getBuildModeInLowerCase(this.rollup.share.projectConfig); 93 expect(buildMode === RELEASE).to.be.true; 94 }); 95 96 mocha.it('1-3: test getBuildModeInLowerCase under preview debug', function () { 97 this.rollup.preview(); 98 const buildMode = getBuildModeInLowerCase(this.rollup.share.projectConfig); 99 expect(buildMode === DEBUG).to.be.true; 100 }); 101 102 mocha.it('1-4: test getBuildModeInLowerCase under hot reload debug', function () { 103 this.rollup.hotReload(); 104 const buildMode = getBuildModeInLowerCase(this.rollup.share.projectConfig); 105 expect(buildMode === DEBUG).to.be.true; 106 }); 107 108 mocha.it('2-1: test getPackageInfo under build debug', function () { 109 this.rollup.build(); 110 const returnInfo = getPackageInfo(this.rollup.share.projectConfig.aceModuleJsonPath); 111 expect(returnInfo[0] === BUNDLE_NAME_DEFAULT).to.be.true; 112 expect(returnInfo[1] === ENTRY_MODULE_NAME_DEFAULT).to.be.true; 113 }); 114 115 mocha.it('2-2: test getPackageInfo under build release', function () { 116 this.rollup.build(RELEASE); 117 const returnInfo = getPackageInfo(this.rollup.share.projectConfig.aceModuleJsonPath); 118 expect(returnInfo[0] === BUNDLE_NAME_DEFAULT).to.be.true; 119 expect(returnInfo[1] === ENTRY_MODULE_NAME_DEFAULT).to.be.true; 120 }); 121 122 mocha.it('2-3: test getPackageInfo under preview debug', function () { 123 this.rollup.preview(); 124 const returnInfo = getPackageInfo(this.rollup.share.projectConfig.aceModuleJsonPath); 125 expect(returnInfo[0] === BUNDLE_NAME_DEFAULT).to.be.true; 126 expect(returnInfo[1] === ENTRY_MODULE_NAME_DEFAULT).to.be.true; 127 }); 128 129 mocha.it('2-4: test getPackageInfo under hot reload debug', function () { 130 this.rollup.hotReload(); 131 const returnInfo = getPackageInfo(this.rollup.share.projectConfig.aceModuleJsonPath); 132 expect(returnInfo[0] === BUNDLE_NAME_DEFAULT).to.be.true; 133 expect(returnInfo[1] === ENTRY_MODULE_NAME_DEFAULT).to.be.true; 134 }); 135 136 mocha.it('3-1: test genSourceMapFileName under build debug', function () { 137 this.rollup.build(); 138 for (const filePath of this.rollup.share.allFiles) { 139 if (filePath.endsWith(EXTNAME_TS) || filePath.endsWith(EXTNAME_JS)) { 140 const originPath = genSourceMapFileName(filePath); 141 const expectedPath = `${filePath}${EXTNAME_MAP}`; 142 expect(originPath === expectedPath).to.be.true; 143 } else if (filePath.endsWith(EXTNAME_ETS)) { 144 const originPath = genSourceMapFileName(filePath); 145 expect(originPath === filePath).to.be.true; 146 } 147 } 148 }); 149 150 mocha.it('3-2: test genSourceMapFileName under build release', function () { 151 this.rollup.build(RELEASE); 152 for (const filePath of this.rollup.share.allFiles) { 153 if (filePath.endsWith(EXTNAME_TS) || filePath.endsWith(EXTNAME_JS)) { 154 const originPath = genSourceMapFileName(filePath); 155 const expectedPath = `${filePath}${EXTNAME_MAP}`; 156 expect(originPath === expectedPath).to.be.true; 157 } 158 } 159 }); 160 161 mocha.it('3-3: test genSourceMapFileName under preview debug', function () { 162 this.rollup.preview(); 163 for (const filePath of this.rollup.share.allFiles) { 164 if (filePath.endsWith(EXTNAME_TS) || filePath.endsWith(EXTNAME_JS)) { 165 const originPath = genSourceMapFileName(filePath); 166 const expectedPath = `${filePath}${EXTNAME_MAP}`; 167 expect(originPath === expectedPath).to.be.true; 168 } 169 } 170 }); 171 172 mocha.it('3-4: test genSourceMapFileName under hot reload debug', function () { 173 this.rollup.hotReload(); 174 for (const filePath of this.rollup.share.allFiles) { 175 if (filePath.endsWith(EXTNAME_TS) || filePath.endsWith(EXTNAME_JS)) { 176 const originPath = genSourceMapFileName(filePath); 177 const expectedPath = `${filePath}${EXTNAME_MAP}`; 178 expect(originPath === expectedPath).to.be.true; 179 } 180 } 181 }); 182 183 mocha.it('4-1: test isOhModules under build debug', function () { 184 this.rollup.build(); 185 const returnInfo = isOhModules(this.rollup.share.projectConfig); 186 expect(returnInfo === false).to.be.true; 187 this.rollup.share.projectConfig.packageDir = OH_MODULES; 188 const returnInfoOh = isOhModules(this.rollup.share.projectConfig); 189 expect(returnInfoOh).to.be.true; 190 }); 191 192 mocha.it('4-2: test isOhModules under build release', function () { 193 this.rollup.build(RELEASE); 194 const returnInfo = isOhModules(this.rollup.share.projectConfig); 195 expect(returnInfo === false).to.be.true; 196 this.rollup.share.projectConfig.packageDir = OH_MODULES; 197 const returnInfoOh = isOhModules(this.rollup.share.projectConfig); 198 expect(returnInfoOh).to.be.true; 199 }); 200 201 mocha.it('4-3: test isOhModules under preview debug', function () { 202 this.rollup.preview(); 203 const returnInfo = isOhModules(this.rollup.share.projectConfig); 204 expect(returnInfo === false).to.be.true; 205 this.rollup.share.projectConfig.packageDir = OH_MODULES; 206 const returnInfoOh = isOhModules(this.rollup.share.projectConfig); 207 expect(returnInfoOh).to.be.true; 208 }); 209 210 mocha.it('4-4: test isOhModules under hot reload debug', function () { 211 this.rollup.hotReload(); 212 const returnInfo = isOhModules(this.rollup.share.projectConfig); 213 expect(returnInfo === false).to.be.true; 214 this.rollup.share.projectConfig.packageDir = OH_MODULES; 215 const returnInfoOh = isOhModules(this.rollup.share.projectConfig); 216 expect(returnInfoOh).to.be.true; 217 }); 218 219 mocha.it('4-5: test isOhModules under hot fix debug', function () { 220 projectConfig.buildMode = DEBUG; 221 const returnInfo = isOhModules(projectConfig); 222 expect(returnInfo).to.be.true; 223 }); 224 225 mocha.it('4-6: test isOhModules under hot fix release', function () { 226 projectConfig.buildMode = RELEASE; 227 const returnInfo = isOhModules(projectConfig); 228 expect(returnInfo).to.be.true; 229 }); 230 231 mocha.it('5-1: test isEs2Abc under build debug', function () { 232 this.rollup.build(); 233 this.rollup.share.projectConfig.pandaMode = ES2ABC; 234 const returnInfo = isEs2Abc(this.rollup.share.projectConfig); 235 expect(returnInfo).to.be.true; 236 237 this.rollup.share.projectConfig.pandaMode = TS2ABC; 238 const returnInfoTS2ABC = isEs2Abc(this.rollup.share.projectConfig); 239 expect(returnInfoTS2ABC === false).to.be.true; 240 241 this.rollup.share.projectConfig.pandaMode = "undefined"; 242 const returnInfoUndef = isEs2Abc(this.rollup.share.projectConfig); 243 expect(returnInfoUndef).to.be.true; 244 245 this.rollup.share.projectConfig.pandaMode = undefined; 246 const returnInfoUndefined = isEs2Abc(this.rollup.share.projectConfig); 247 expect(returnInfoUndefined).to.be.true; 248 }); 249 250 mocha.it('5-2: test isEs2Abc under build release', function () { 251 this.rollup.build(RELEASE); 252 this.rollup.share.projectConfig.pandaMode = ES2ABC; 253 const returnInfo = isEs2Abc(this.rollup.share.projectConfig); 254 expect(returnInfo).to.be.true; 255 256 this.rollup.share.projectConfig.pandaMode = TS2ABC; 257 const returnInfoTS2ABC = isEs2Abc(this.rollup.share.projectConfig); 258 expect(returnInfoTS2ABC).to.be.false; 259 }); 260 261 mocha.it('5-3: test isEs2Abc under preview debug', function () { 262 this.rollup.preview(); 263 this.rollup.share.projectConfig.pandaMode = ES2ABC; 264 const returnInfo = isEs2Abc(this.rollup.share.projectConfig); 265 expect(returnInfo).to.be.true; 266 267 this.rollup.share.projectConfig.pandaMode = TS2ABC; 268 const returnInfoTS2ABC = isEs2Abc(this.rollup.share.projectConfig); 269 expect(returnInfoTS2ABC).to.be.false; 270 }); 271 272 mocha.it('5-4: test isEs2Abc under hot reload debug', function () { 273 this.rollup.hotReload(); 274 this.rollup.share.projectConfig.pandaMode = ES2ABC; 275 const returnInfo = isEs2Abc(this.rollup.share.projectConfig); 276 expect(returnInfo).to.be.true; 277 278 this.rollup.share.projectConfig.pandaMode = TS2ABC; 279 const returnInfoTS2ABC = isEs2Abc(this.rollup.share.projectConfig); 280 expect(returnInfoTS2ABC).to.be.false; 281 }); 282 283 mocha.it('5-5: test isEs2Abc under hot fix debug', function () { 284 projectConfig.buildMode = DEBUG; 285 projectConfig.pandaMode = ES2ABC; 286 const returnInfo = isEs2Abc(projectConfig); 287 expect(returnInfo).to.be.true; 288 289 projectConfig.pandaMode = TS2ABC; 290 const returnInfoTS2ABC = isEs2Abc(projectConfig); 291 expect(returnInfoTS2ABC).to.be.false; 292 }); 293 294 mocha.it('5-6: test isEs2Abc under hot fix release', function () { 295 projectConfig.buildMode = RELEASE; 296 projectConfig.pandaMode = ES2ABC; 297 const returnInfo = isEs2Abc(projectConfig); 298 expect(returnInfo).to.be.true; 299 300 projectConfig.pandaMode = TS2ABC; 301 const returnInfoTS2ABC = isEs2Abc(projectConfig); 302 expect(returnInfoTS2ABC).to.be.false; 303 }); 304 305 mocha.it('6-1: test the error message of writeArkguardObfuscatedSourceCode', async function () { 306 this.rollup.build(RELEASE); 307 SourceMapGenerator.initInstance(this.rollup); 308 const logger = this.rollup.share.getLogger(GEN_ABC_PLUGIN_NAME); 309 const stub = sinon.stub(logger, 'error'); 310 const red: string = '\x1B[31m'; 311 try { 312 await writeArkguardObfuscatedSourceCode( 313 {content: undefined, buildFilePath: '', relativeSourceFilePath: '', originSourceFilePath: ''}, 314 logger, this.rollup.share.projectConfig, {}); 315 } catch (e) { 316 } 317 expect(stub.calledWith(red, 318 `ArkTS:INTERNAL ERROR: Failed to obfuscate file '' with arkguard. TypeError: Cannot read properties of undefined (reading 'obfuscate')` 319 )).to.be.true; 320 stub.restore(); 321 SourceMapGenerator.cleanSourceMapObject(); 322 }); 323 324 mocha.it('7-1: test the error message of writeMinimizedSourceCode', async function () { 325 this.rollup.build(RELEASE); 326 const logger = this.rollup.share.getLogger(GEN_ABC_PLUGIN_NAME); 327 const stub = sinon.stub(logger, 'error'); 328 const red: string = '\x1B[31m'; 329 const reset: string = '\x1B[39m'; 330 try { 331 await writeMinimizedSourceCode(undefined, '', logger); 332 } catch (e) { 333 } 334 expect(stub.calledWith(red, 335 'ArkTS:INTERNAL ERROR: Failed to obfuscate source code for ', reset 336 )).to.be.true; 337 stub.restore(); 338 }); 339 340 mocha.it('8-1: test tryMangleFileName when obfuscation is disabled', async function () { 341 const filePath = '/mnt/application/entry/build/default/cache/default/default@CompileArkTS/esmodule/release/src/main/ets/entryability/EntryAbility.ts'; 342 const originalFilePath = '/mnt/application/entry/src/main/ets/entryability/EntryAbility.ets'; 343 const projectConfig = { 344 projectRootPath: '/mnt/application', 345 packageDir: 'oh_modules', 346 modulePathMap: { 347 entry: '/mnt/application/entry' 348 }, 349 obfuscationMergedObConfig: { 350 options: { 351 enableFileNameObfuscation: false 352 } 353 } 354 } 355 const result = tryMangleFileName(filePath, projectConfig, originalFilePath); 356 expect(result === filePath).to.be.true; 357 }); 358 359 mocha.it('8-2: test tryMangleFileName when obfuscation is enabled', async function () { 360 const filePath = '/mnt/application/entry/build/default/cache/default/default@CompileArkTS/esmodule/release/src/main/ets/entryability/EntryAbility.ts'; 361 const originalFilePath = '/mnt/application/entry/src/main/ets/entryability/EntryAbility.ets'; 362 const newFilePath = '/mnt/application/entry/build/default/cache/default/default@CompileArkTS/esmodule/release/src/main/ets/a/b.ts'; 363 const projectConfig = { 364 projectRootPath: '/mnt/application', 365 packageDir: 'oh_modules', 366 modulePathMap: { 367 entry: '/mnt/application/entry' 368 }, 369 obfuscationMergedObConfig: { 370 options: { 371 enableFileNameObfuscation: true 372 } 373 } 374 } 375 376 const printerConfig = { 377 //Print obfuscation time&memory usage of all files and obfuscation processes 378 mFilesPrinter: false, 379 //Print time&memory usage of a single file obfuscation in transform processes 380 mSingleFilePrinter: false, 381 //Print sum up time of transform processes during obfuscation 382 mSumPrinter: false, 383 //Output path of printer 384 mOutputPath: "" 385 } 386 387 const arkguardConfig = { 388 mRenameFileName: { 389 mEnable: true, 390 mNameGeneratorType: 1, 391 mReservedFileNames: [ 392 'entry', 393 'mnt', 394 'application', 395 'entry', 396 'src', 397 'main', 398 'ets', 399 'build', 400 'default', 401 'cache', 402 'default', 403 'default@CompileArkTS', 404 'esmodule', 405 'release' 406 ], 407 }, 408 mPerformancePrinter: printerConfig 409 } 410 411 let arkObfuscator: ArkObfuscator = new ArkObfuscator(); 412 arkObfuscator.init(arkguardConfig); 413 const content = `function foo() {}` 414 const obfuscateResult = arkObfuscator.obfuscate(content, filePath, undefined); 415 obfuscateResult.then(result => { 416 const afterObfuscateFilePath = result.filePath; 417 expect(afterObfuscateFilePath === newFilePath).to.be.true; 418 }) 419 420 const result = tryMangleFileName(filePath, projectConfig, originalFilePath); 421 expect(result === newFilePath).to.be.true; 422 }); 423 424 mocha.it('9-1: test writeArkguardObfuscatedSourceCode when obfuscation is enabled', async function () { 425 this.rollup.build(RELEASE); 426 const logger = this.rollup.share.getLogger(GEN_ABC_PLUGIN_NAME); 427 const arkguardConfig = { 428 mCompact: false, 429 mDisableConsole: false, 430 mSimplify: false, 431 mRemoveComments: true, 432 mNameObfuscation: { 433 mEnable: true, 434 mNameGeneratorType: 1, 435 mReservedNames: [], 436 mRenameProperties: false, 437 mReservedProperties: [], 438 mKeepStringProperty: true, 439 mTopLevel: false, 440 mReservedToplevelNames: [], 441 mUniversalReservedProperties: [], 442 mUniversalReservedToplevelNames: [], 443 }, 444 mRemoveDeclarationComments: { 445 mEnable: true, 446 mReservedComments: [] 447 }, 448 mEnableSourceMap: true, 449 mEnableNameCache: true, 450 mRenameFileName: { 451 mEnable: false, 452 mNameGeneratorType: 1, 453 mReservedFileNames: [], 454 }, 455 mExportObfuscation: false, 456 mPerformancePrinter: { 457 mFilesPrinter: false, 458 mSingleFilePrinter: false, 459 mSumPrinter: false, 460 mOutputPath: "" 461 }, 462 mKeepFileSourceCode: { 463 mKeepSourceOfPaths: new Set(), 464 mkeepFilesAndDependencies: new Set(), 465 }, 466 }; 467 468 const sourceMapGenerator: SourceMapGenerator = SourceMapGenerator.initInstance(this.rollup); 469 sourceMapGenerator.setNewSoureMaps(false); 470 const arkObfuscator: ArkObfuscator = new ArkObfuscator(); 471 arkObfuscator.init(arkguardConfig); 472 473 const projectConfig = { 474 projectRootPath: PROJECT_ROOT, 475 arkObfuscator: arkObfuscator, 476 packageDir: 'oh_modules', 477 localPackageSet: new Set(), 478 useTsHar: false, 479 useNormalized: false 480 }; 481 482 const sourceFileName = 'sourceFile.ts'; 483 const originalSourceFilePath = path.join(__dirname, '../../test/ark_compiler_ut/testdata/har_obfuscation', sourceFileName); 484 const moduleInfo = { 485 content: fs.readFileSync(originalSourceFilePath, 'utf-8'), 486 buildFilePath: `${PROJECT_ROOT}/har_obfuscation/build/default/cache/${sourceFileName}`, 487 relativeSourceFilePath: `har_obfuscation/${sourceFileName}`, 488 originSourceFilePath: originalSourceFilePath, 489 rollupModuleId: originalSourceFilePath 490 }; 491 492 try { 493 await writeArkguardObfuscatedSourceCode(moduleInfo, logger, projectConfig, {}); 494 } catch (e) { 495 } 496 const expectedResult = `export function add(d: number, e: number): number { 497 return d + e; 498} 499export function findElement<a>(b: a[], c: (item: a) => boolean): a | undefined { 500 return b.find(c); 501} 502`; 503 const result = fs.readFileSync(moduleInfo.buildFilePath, 'utf-8'); 504 expect(result === expectedResult).to.be.true; 505 506 const declFileName = 'sourceFile.d.ts'; 507 const originalDeclFilePath = path.join(__dirname, '../../test/ark_compiler_ut/testdata/har_obfuscation/', declFileName); 508 509 const declModuleInfo = { 510 content: fs.readFileSync(originalDeclFilePath, 'utf-8'), 511 buildFilePath: `${PROJECT_ROOT}/har_obfuscation/build/default/cache/${declFileName}`, 512 relativeSourceFilePath: `har_obfuscation/build/default/cache/${declFileName}`, 513 originSourceFilePath: originalSourceFilePath, 514 rollupModuleId: originalSourceFilePath 515 }; 516 517 try { 518 await writeArkguardObfuscatedSourceCode(declModuleInfo, logger, projectConfig, {}); 519 } catch (e) { 520 } 521 522 const expectedDeclResult = `export declare function add(d: number, e: number): number; 523export declare function findElement<a>(b: a[], c: (item: a) => boolean): a | undefined; 524`; 525 const declResult = fs.readFileSync(declModuleInfo.buildFilePath, 'utf-8'); 526 expect(declResult === expectedDeclResult).to.be.true; 527 }); 528});