/* * Copyright (c) 2023 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use rollupObject file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // Execute this file first to avoid circular dependency problems import { expect } from 'chai'; import mocha from 'mocha'; import fs from "fs"; import path from "path"; import MagicString from 'magic-string'; import sinon from 'sinon'; import { getBuildModeInLowerCase, getPackageInfo, genSourceMapFileName, isOhModules, isEs2Abc, writeArkguardObfuscatedSourceCode, writeMinimizedSourceCode, tryMangleFileName } from '../../lib/ark_utils'; import { DEBUG, RELEASE, OH_MODULES, EXTNAME_TS, EXTNAME_JS, EXTNAME_ETS, OBFUSCATION_TOOL } from '../../lib/fast_build/ark_compiler/common/ark_define'; import RollUpPluginMock from './mock/rollup_mock/rollup_plugin_mock'; import { BUNDLE_NAME_DEFAULT, ENTRY_MODULE_NAME_DEFAULT, EXTNAME_MAP, ENTRYABILITY_JS } from './mock/rollup_mock/common'; import projectConfig from './utils/processProjectConfig'; import { ES2ABC, TS2ABC } from '../../lib/pre_define'; import { changeFileExtension } from '../../lib/fast_build/ark_compiler/utils'; import ModuleSourceFileMock from './mock/class_mock/module_source_files_mock'; import { genTemporaryPath, toUnixPath } from '../../lib/utils'; import { ObConfigResolver, MergedConfig } from '../../lib/fast_build/ark_compiler/common/ob_config_resolver'; import { utProcessArkConfig } from '../../lib/fast_build/ark_compiler/common/process_ark_config'; import { ModuleSourceFile } from '../../lib/fast_build/ark_compiler/module/module_source_file'; import { PROJECT_ROOT, TERSER_PROCESSED_EXPECTED_CODE } from './mock/rollup_mock/path_config'; import { GEN_ABC_PLUGIN_NAME } from '../../lib/fast_build/ark_compiler/common/ark_define'; import { SourceMapGenerator } from '../../lib/fast_build/ark_compiler/generate_sourcemap'; import { ArkObfuscator } from 'arkguard'; mocha.describe('test ark_utils file api', function () { mocha.before(function () { this.rollup = new RollUpPluginMock(); }); mocha.after(() => { delete this.rollup; }); mocha.it('1-1: test getBuildModeInLowerCase under build debug', function () { this.rollup.build(); const buildMode = getBuildModeInLowerCase(this.rollup.share.projectConfig); expect(buildMode === DEBUG).to.be.true; }); mocha.it('1-2: test getBuildModeInLowerCase under build release', function () { this.rollup.build(RELEASE); const buildMode = getBuildModeInLowerCase(this.rollup.share.projectConfig); expect(buildMode === RELEASE).to.be.true; }); mocha.it('1-3: test getBuildModeInLowerCase under preview debug', function () { this.rollup.preview(); const buildMode = getBuildModeInLowerCase(this.rollup.share.projectConfig); expect(buildMode === DEBUG).to.be.true; }); mocha.it('1-4: test getBuildModeInLowerCase under hot reload debug', function () { this.rollup.hotReload(); const buildMode = getBuildModeInLowerCase(this.rollup.share.projectConfig); expect(buildMode === DEBUG).to.be.true; }); mocha.it('2-1: test getPackageInfo under build debug', function () { this.rollup.build(); const returnInfo = getPackageInfo(this.rollup.share.projectConfig.aceModuleJsonPath); expect(returnInfo[0] === BUNDLE_NAME_DEFAULT).to.be.true; expect(returnInfo[1] === ENTRY_MODULE_NAME_DEFAULT).to.be.true; }); mocha.it('2-2: test getPackageInfo under build release', function () { this.rollup.build(RELEASE); const returnInfo = getPackageInfo(this.rollup.share.projectConfig.aceModuleJsonPath); expect(returnInfo[0] === BUNDLE_NAME_DEFAULT).to.be.true; expect(returnInfo[1] === ENTRY_MODULE_NAME_DEFAULT).to.be.true; }); mocha.it('2-3: test getPackageInfo under preview debug', function () { this.rollup.preview(); const returnInfo = getPackageInfo(this.rollup.share.projectConfig.aceModuleJsonPath); expect(returnInfo[0] === BUNDLE_NAME_DEFAULT).to.be.true; expect(returnInfo[1] === ENTRY_MODULE_NAME_DEFAULT).to.be.true; }); mocha.it('2-4: test getPackageInfo under hot reload debug', function () { this.rollup.hotReload(); const returnInfo = getPackageInfo(this.rollup.share.projectConfig.aceModuleJsonPath); expect(returnInfo[0] === BUNDLE_NAME_DEFAULT).to.be.true; expect(returnInfo[1] === ENTRY_MODULE_NAME_DEFAULT).to.be.true; }); mocha.it('3-1: test genSourceMapFileName under build debug', function () { this.rollup.build(); for (const filePath of this.rollup.share.allFiles) { if (filePath.endsWith(EXTNAME_TS) || filePath.endsWith(EXTNAME_JS)) { const originPath = genSourceMapFileName(filePath); const expectedPath = `${filePath}${EXTNAME_MAP}`; expect(originPath === expectedPath).to.be.true; } else if (filePath.endsWith(EXTNAME_ETS)) { const originPath = genSourceMapFileName(filePath); expect(originPath === filePath).to.be.true; } } }); mocha.it('3-2: test genSourceMapFileName under build release', function () { this.rollup.build(RELEASE); for (const filePath of this.rollup.share.allFiles) { if (filePath.endsWith(EXTNAME_TS) || filePath.endsWith(EXTNAME_JS)) { const originPath = genSourceMapFileName(filePath); const expectedPath = `${filePath}${EXTNAME_MAP}`; expect(originPath === expectedPath).to.be.true; } } }); mocha.it('3-3: test genSourceMapFileName under preview debug', function () { this.rollup.preview(); for (const filePath of this.rollup.share.allFiles) { if (filePath.endsWith(EXTNAME_TS) || filePath.endsWith(EXTNAME_JS)) { const originPath = genSourceMapFileName(filePath); const expectedPath = `${filePath}${EXTNAME_MAP}`; expect(originPath === expectedPath).to.be.true; } } }); mocha.it('3-4: test genSourceMapFileName under hot reload debug', function () { this.rollup.hotReload(); for (const filePath of this.rollup.share.allFiles) { if (filePath.endsWith(EXTNAME_TS) || filePath.endsWith(EXTNAME_JS)) { const originPath = genSourceMapFileName(filePath); const expectedPath = `${filePath}${EXTNAME_MAP}`; expect(originPath === expectedPath).to.be.true; } } }); mocha.it('4-1: test isOhModules under build debug', function () { this.rollup.build(); const returnInfo = isOhModules(this.rollup.share.projectConfig); expect(returnInfo === false).to.be.true; this.rollup.share.projectConfig.packageDir = OH_MODULES; const returnInfoOh = isOhModules(this.rollup.share.projectConfig); expect(returnInfoOh).to.be.true; }); mocha.it('4-2: test isOhModules under build release', function () { this.rollup.build(RELEASE); const returnInfo = isOhModules(this.rollup.share.projectConfig); expect(returnInfo === false).to.be.true; this.rollup.share.projectConfig.packageDir = OH_MODULES; const returnInfoOh = isOhModules(this.rollup.share.projectConfig); expect(returnInfoOh).to.be.true; }); mocha.it('4-3: test isOhModules under preview debug', function () { this.rollup.preview(); const returnInfo = isOhModules(this.rollup.share.projectConfig); expect(returnInfo === false).to.be.true; this.rollup.share.projectConfig.packageDir = OH_MODULES; const returnInfoOh = isOhModules(this.rollup.share.projectConfig); expect(returnInfoOh).to.be.true; }); mocha.it('4-4: test isOhModules under hot reload debug', function () { this.rollup.hotReload(); const returnInfo = isOhModules(this.rollup.share.projectConfig); expect(returnInfo === false).to.be.true; this.rollup.share.projectConfig.packageDir = OH_MODULES; const returnInfoOh = isOhModules(this.rollup.share.projectConfig); expect(returnInfoOh).to.be.true; }); mocha.it('4-5: test isOhModules under hot fix debug', function () { projectConfig.buildMode = DEBUG; const returnInfo = isOhModules(projectConfig); expect(returnInfo).to.be.true; }); mocha.it('4-6: test isOhModules under hot fix release', function () { projectConfig.buildMode = RELEASE; const returnInfo = isOhModules(projectConfig); expect(returnInfo).to.be.true; }); mocha.it('5-1: test isEs2Abc under build debug', function () { this.rollup.build(); this.rollup.share.projectConfig.pandaMode = ES2ABC; const returnInfo = isEs2Abc(this.rollup.share.projectConfig); expect(returnInfo).to.be.true; this.rollup.share.projectConfig.pandaMode = TS2ABC; const returnInfoTS2ABC = isEs2Abc(this.rollup.share.projectConfig); expect(returnInfoTS2ABC === false).to.be.true; this.rollup.share.projectConfig.pandaMode = "undefined"; const returnInfoUndef = isEs2Abc(this.rollup.share.projectConfig); expect(returnInfoUndef).to.be.true; this.rollup.share.projectConfig.pandaMode = undefined; const returnInfoUndefined = isEs2Abc(this.rollup.share.projectConfig); expect(returnInfoUndefined).to.be.true; }); mocha.it('5-2: test isEs2Abc under build release', function () { this.rollup.build(RELEASE); this.rollup.share.projectConfig.pandaMode = ES2ABC; const returnInfo = isEs2Abc(this.rollup.share.projectConfig); expect(returnInfo).to.be.true; this.rollup.share.projectConfig.pandaMode = TS2ABC; const returnInfoTS2ABC = isEs2Abc(this.rollup.share.projectConfig); expect(returnInfoTS2ABC).to.be.false; }); mocha.it('5-3: test isEs2Abc under preview debug', function () { this.rollup.preview(); this.rollup.share.projectConfig.pandaMode = ES2ABC; const returnInfo = isEs2Abc(this.rollup.share.projectConfig); expect(returnInfo).to.be.true; this.rollup.share.projectConfig.pandaMode = TS2ABC; const returnInfoTS2ABC = isEs2Abc(this.rollup.share.projectConfig); expect(returnInfoTS2ABC).to.be.false; }); mocha.it('5-4: test isEs2Abc under hot reload debug', function () { this.rollup.hotReload(); this.rollup.share.projectConfig.pandaMode = ES2ABC; const returnInfo = isEs2Abc(this.rollup.share.projectConfig); expect(returnInfo).to.be.true; this.rollup.share.projectConfig.pandaMode = TS2ABC; const returnInfoTS2ABC = isEs2Abc(this.rollup.share.projectConfig); expect(returnInfoTS2ABC).to.be.false; }); mocha.it('5-5: test isEs2Abc under hot fix debug', function () { projectConfig.buildMode = DEBUG; projectConfig.pandaMode = ES2ABC; const returnInfo = isEs2Abc(projectConfig); expect(returnInfo).to.be.true; projectConfig.pandaMode = TS2ABC; const returnInfoTS2ABC = isEs2Abc(projectConfig); expect(returnInfoTS2ABC).to.be.false; }); mocha.it('5-6: test isEs2Abc under hot fix release', function () { projectConfig.buildMode = RELEASE; projectConfig.pandaMode = ES2ABC; const returnInfo = isEs2Abc(projectConfig); expect(returnInfo).to.be.true; projectConfig.pandaMode = TS2ABC; const returnInfoTS2ABC = isEs2Abc(projectConfig); expect(returnInfoTS2ABC).to.be.false; }); mocha.it('6-1: test the error message of writeArkguardObfuscatedSourceCode', async function () { this.rollup.build(RELEASE); SourceMapGenerator.initInstance(this.rollup); const logger = this.rollup.share.getLogger(GEN_ABC_PLUGIN_NAME); const stub = sinon.stub(logger, 'error'); const red: string = '\x1B[31m'; try { await writeArkguardObfuscatedSourceCode( {content: undefined, buildFilePath: '', relativeSourceFilePath: '', originSourceFilePath: ''}, logger, this.rollup.share.projectConfig, {}); } catch (e) { } expect(stub.calledWith(red, `ArkTS:INTERNAL ERROR: Failed to obfuscate file '' with arkguard. TypeError: Cannot read properties of undefined (reading 'obfuscate')` )).to.be.true; stub.restore(); SourceMapGenerator.cleanSourceMapObject(); }); mocha.it('7-1: test the error message of writeMinimizedSourceCode', async function () { this.rollup.build(RELEASE); const logger = this.rollup.share.getLogger(GEN_ABC_PLUGIN_NAME); const stub = sinon.stub(logger, 'error'); const red: string = '\x1B[31m'; const reset: string = '\x1B[39m'; try { await writeMinimizedSourceCode(undefined, '', logger); } catch (e) { } expect(stub.calledWith(red, 'ArkTS:INTERNAL ERROR: Failed to obfuscate source code for ', reset )).to.be.true; stub.restore(); }); mocha.it('8-1: test tryMangleFileName when obfuscation is disabled', async function () { const filePath = '/mnt/application/entry/build/default/cache/default/default@CompileArkTS/esmodule/release/src/main/ets/entryability/EntryAbility.ts'; const originalFilePath = '/mnt/application/entry/src/main/ets/entryability/EntryAbility.ets'; const projectConfig = { projectRootPath: '/mnt/application', packageDir: 'oh_modules', modulePathMap: { entry: '/mnt/application/entry' }, obfuscationMergedObConfig: { options: { enableFileNameObfuscation: false } } } const result = tryMangleFileName(filePath, projectConfig, originalFilePath); expect(result === filePath).to.be.true; }); mocha.it('8-2: test tryMangleFileName when obfuscation is enabled', async function () { const filePath = '/mnt/application/entry/build/default/cache/default/default@CompileArkTS/esmodule/release/src/main/ets/entryability/EntryAbility.ts'; const originalFilePath = '/mnt/application/entry/src/main/ets/entryability/EntryAbility.ets'; const newFilePath = '/mnt/application/entry/build/default/cache/default/default@CompileArkTS/esmodule/release/src/main/ets/a/b.ts'; const projectConfig = { projectRootPath: '/mnt/application', packageDir: 'oh_modules', modulePathMap: { entry: '/mnt/application/entry' }, obfuscationMergedObConfig: { options: { enableFileNameObfuscation: true } } } const printerConfig = { //Print obfuscation time&memory usage of all files and obfuscation processes mFilesPrinter: false, //Print time&memory usage of a single file obfuscation in transform processes mSingleFilePrinter: false, //Print sum up time of transform processes during obfuscation mSumPrinter: false, //Output path of printer mOutputPath: "" } const arkguardConfig = { mRenameFileName: { mEnable: true, mNameGeneratorType: 1, mReservedFileNames: [ 'entry', 'mnt', 'application', 'entry', 'src', 'main', 'ets', 'build', 'default', 'cache', 'default', 'default@CompileArkTS', 'esmodule', 'release' ], }, mPerformancePrinter: printerConfig } let arkObfuscator: ArkObfuscator = new ArkObfuscator(); arkObfuscator.init(arkguardConfig); const content = `function foo() {}` const obfuscateResult = arkObfuscator.obfuscate(content, filePath, undefined); obfuscateResult.then(result => { const afterObfuscateFilePath = result.filePath; expect(afterObfuscateFilePath === newFilePath).to.be.true; }) const result = tryMangleFileName(filePath, projectConfig, originalFilePath); expect(result === newFilePath).to.be.true; }); mocha.it('9-1: test writeArkguardObfuscatedSourceCode when obfuscation is enabled', async function () { this.rollup.build(RELEASE); const logger = this.rollup.share.getLogger(GEN_ABC_PLUGIN_NAME); const arkguardConfig = { mCompact: false, mDisableConsole: false, mSimplify: false, mRemoveComments: true, mNameObfuscation: { mEnable: true, mNameGeneratorType: 1, mReservedNames: [], mRenameProperties: false, mReservedProperties: [], mKeepStringProperty: true, mTopLevel: false, mReservedToplevelNames: [], mUniversalReservedProperties: [], mUniversalReservedToplevelNames: [], }, mRemoveDeclarationComments: { mEnable: true, mReservedComments: [] }, mEnableSourceMap: true, mEnableNameCache: true, mRenameFileName: { mEnable: false, mNameGeneratorType: 1, mReservedFileNames: [], }, mExportObfuscation: false, mPerformancePrinter: { mFilesPrinter: false, mSingleFilePrinter: false, mSumPrinter: false, mOutputPath: "" }, mKeepFileSourceCode: { mKeepSourceOfPaths: new Set(), mkeepFilesAndDependencies: new Set(), }, }; const sourceMapGenerator: SourceMapGenerator = SourceMapGenerator.initInstance(this.rollup); sourceMapGenerator.setNewSoureMaps(false); const arkObfuscator: ArkObfuscator = new ArkObfuscator(); arkObfuscator.init(arkguardConfig); const projectConfig = { projectRootPath: PROJECT_ROOT, arkObfuscator: arkObfuscator, packageDir: 'oh_modules', localPackageSet: new Set(), useTsHar: false, useNormalized: false }; const sourceFileName = 'sourceFile.ts'; const originalSourceFilePath = path.join(__dirname, '../../test/ark_compiler_ut/testdata/har_obfuscation', sourceFileName); const moduleInfo = { content: fs.readFileSync(originalSourceFilePath, 'utf-8'), buildFilePath: `${PROJECT_ROOT}/har_obfuscation/build/default/cache/${sourceFileName}`, relativeSourceFilePath: `har_obfuscation/${sourceFileName}`, originSourceFilePath: originalSourceFilePath, rollupModuleId: originalSourceFilePath }; try { await writeArkguardObfuscatedSourceCode(moduleInfo, logger, projectConfig, {}); } catch (e) { } const expectedResult = `export function add(d: number, e: number): number { return d + e; } export function findElement(b: a[], c: (item: a) => boolean): a | undefined { return b.find(c); } `; const result = fs.readFileSync(moduleInfo.buildFilePath, 'utf-8'); expect(result === expectedResult).to.be.true; const declFileName = 'sourceFile.d.ts'; const originalDeclFilePath = path.join(__dirname, '../../test/ark_compiler_ut/testdata/har_obfuscation/', declFileName); const declModuleInfo = { content: fs.readFileSync(originalDeclFilePath, 'utf-8'), buildFilePath: `${PROJECT_ROOT}/har_obfuscation/build/default/cache/${declFileName}`, relativeSourceFilePath: `har_obfuscation/build/default/cache/${declFileName}`, originSourceFilePath: originalSourceFilePath, rollupModuleId: originalSourceFilePath }; try { await writeArkguardObfuscatedSourceCode(declModuleInfo, logger, projectConfig, {}); } catch (e) { } const expectedDeclResult = `export declare function add(d: number, e: number): number; export declare function findElement(b: a[], c: (item: a) => boolean): a | undefined; `; const declResult = fs.readFileSync(declModuleInfo.buildFilePath, 'utf-8'); expect(declResult === expectedDeclResult).to.be.true; }); });