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 } from 'chai';
18import { ArkObfuscator, FileUtils, wildcardTransformer } from '../../../src/ArkObfuscator';
19import { ArkObfuscatorForTest } from '../../../src/ArkObfuscatorForTest'
20import path from 'path';
21import { TransformerFactory, Node, SourceFile, createSourceFile, ScriptTarget, Printer, createTextWriter, RawSourceMap } from 'typescript';
22import { IOptions } from '../../../src/configs/IOptions';
23import { getSourceMapGenerator } from '../../../src/utils/SourceMapUtil';
24import fs from 'fs';
25
26describe('Tester Cases for <ArkObfuscatorForTest>.', function () {
27  describe('Tester Cases for <ArkObfuscatorForTest>.', function () {
28    let etsSourceFile: SourceFile;
29    let dEtsSourceFile: SourceFile;
30    let tsSourceFile: SourceFile;
31    let etsSourceFilePath: string = 'demo.ets';
32    let dEtsSourceFilePath: string = 'demo.d.ets';
33    let tsSourceFilePath: string = 'demo.ts';
34
35    before('init sourceFile', function () {
36      const etsfileContent = `//This is a comment
37//This is a comment
38//This is a comment
39//This is a comment
40class Demo{
41  constructor(public  title: string, public  content: string, public  mark: number) {
42      this.title = title
43      this.content = content
44      this.mark = mark
45  }
46}
47`;
48      const dEtsFileContent = `
49      /**
50       * This is a comment
51       */
52      
53      class Demo{
54          constructor(public  title: string, public  content: string, public  mark: number) {
55              this.title = title
56              this.content = content
57              this.mark = mark
58          }
59      }
60      `;
61
62      const tsFileContent = `
63      //This is a comment
64      class Demo{
65          constructor(public  title: string, public  content: string, public  mark: number) {
66              this.title = title
67              this.content = content
68              this.mark = mark
69          }
70      }
71      `;
72
73      etsSourceFile = createSourceFile('demo.ts', etsfileContent, ScriptTarget.ES2015, true);
74      dEtsSourceFile = createSourceFile(dEtsSourceFilePath, dEtsFileContent, ScriptTarget.ES2015, true);
75      tsSourceFile = createSourceFile(tsSourceFilePath, tsFileContent, ScriptTarget.ES2015, true);
76    });
77
78    it('Tester: test case for handleTsHarComments for ets file', function () {
79      let mCustomProfiles: IOptions | undefined = FileUtils.readFileAsJson(path.join(__dirname, "default_config.json"));
80      let arkobfuscator = new ArkObfuscatorForTest();
81      arkobfuscator.init(mCustomProfiles);
82      let originalFilePath = 'demo.ets';
83      ArkObfuscatorForTest.projectInfo = { packageDir: '', projectRootPath: '', localPackageSet: new Set<string>(), useNormalized: false, useTsHar: true };
84      arkobfuscator.handleTsHarComments(etsSourceFile, originalFilePath);
85      let sourceMapGenerator = getSourceMapGenerator(originalFilePath);
86      const textWriter = createTextWriter('\n');
87      arkobfuscator.createObfsPrinter(etsSourceFile.isDeclarationFile).writeFile(etsSourceFile, textWriter, sourceMapGenerator);
88      const actualContent = textWriter.getText();
89      const expectContent = `
90      // @keepTs
91      // @ts-nocheck
92      class Demo {
93          constructor(public title: string, public content: string, public mark: number) {
94              this.title = title;
95              this.content = content;
96              this.mark = mark;
97          }
98      }`;
99      
100      let actualSourceMap: RawSourceMap = sourceMapGenerator.toJSON();
101      actualSourceMap.sourceRoot = "";
102      let expectSourceMap = {
103          "version": 3,
104          "file": "demo.ets",
105          "sourceRoot": "",
106          "sources": [
107            "demo.ts"
108          ],
109          "names": [],
110          "mappings": ";;AAIA,MAAM,IAAI;IACR,YAAY,MAAM,CAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAE,IAAI,EAAE,MAAM;QAC5E,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;QAClB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;QACtB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;IACpB,CAAC;CACF"
111        }
112      console.log(JSON.stringify(actualSourceMap, null, 2))
113      assert.strictEqual(compareStringsIgnoreNewlines(actualContent, expectContent), true);
114      assert.strictEqual(compareStringsIgnoreNewlines(JSON.stringify(actualSourceMap, null, 2), JSON.stringify(expectSourceMap, null, 2)), true);
115    });
116
117    it('Tester: test case for handleTsHarComments for d.ets file', function () {
118      let mCustomProfiles: IOptions | undefined = FileUtils.readFileAsJson(path.join(__dirname, "default_config.json"));
119      let arkobfuscator = new ArkObfuscatorForTest();
120      arkobfuscator.init(mCustomProfiles);
121      ArkObfuscatorForTest.projectInfo = { packageDir: '', projectRootPath: '', localPackageSet: new Set<string>(), useNormalized: false, useTsHar: true };
122      arkobfuscator.handleTsHarComments(dEtsSourceFile, dEtsSourceFilePath);
123      let sourceMapGenerator = getSourceMapGenerator(dEtsSourceFilePath);
124      const textWriter = createTextWriter('\n');
125      arkobfuscator.createObfsPrinter(dEtsSourceFile.isDeclarationFile).writeFile(dEtsSourceFile, textWriter, sourceMapGenerator);
126      const actualContent = textWriter.getText();
127      const expectContent = `
128      /**
129       * This is a comment
130       */
131      class Demo {
132          constructor(public title: string, public content: string, public mark: number) {
133              this.title = title;
134              this.content = content;
135              this.mark = mark;
136          }
137      }`;
138      assert.strictEqual(compareStringsIgnoreNewlines(actualContent, expectContent), true);
139    });
140
141    it('Tester: test case for handleTsHarComments for ts file', function () {
142      let mCustomProfiles: IOptions | undefined = FileUtils.readFileAsJson(path.join(__dirname, "default_config.json"));
143      let arkobfuscator = new ArkObfuscatorForTest();
144      arkobfuscator.init(mCustomProfiles);
145      ArkObfuscatorForTest.projectInfo = { packageDir: '', projectRootPath: '', localPackageSet: new Set<string>(), useNormalized: false, useTsHar: true };
146      arkobfuscator.handleTsHarComments(tsSourceFile, tsSourceFilePath);
147      let sourceMapGenerator = getSourceMapGenerator(tsSourceFilePath);
148      const textWriter = createTextWriter('\n');
149      arkobfuscator.createObfsPrinter(tsSourceFile.isDeclarationFile).writeFile(tsSourceFile, textWriter, sourceMapGenerator);
150      const actualContent = textWriter.getText();
151      const expectContent = `
152        class Demo {
153          constructor(public title: string, public content: string, public mark: number) {
154            this.title = title;
155            this.content = content;
156            this.mark = mark;
157          }
158        }`;
159      console.log(actualContent)
160      assert.strictEqual(compareStringsIgnoreNewlines(actualContent, expectContent), true);
161      console.log(actualContent);
162    });
163  });
164
165  describe('Tester Cases for <ArkObfuscatorForTest>.', function () {
166    it('Tester: test case for ArkObfuscatorForTest.ini', function (){
167      let configPath = "test/ut/arkobfuscator/iniTestObfConfig.json"
168      let obfuscator: ArkObfuscatorForTest = new ArkObfuscatorForTest();
169      const originalConfig: IOptions | undefined = FileUtils.readFileAsJson(configPath);
170      obfuscator.init(originalConfig);
171      let config = obfuscator.customProfiles;
172      let reservedTopelevelNames = config.mNameObfuscation?.mReservedToplevelNames;
173      let reservedProperty = config.mNameObfuscation?.mReservedProperties;
174      let universalReservedToplevelNames = config.mNameObfuscation?.mUniversalReservedToplevelNames as RegExp[];
175      let universalReservedProperties = config.mNameObfuscation?.mUniversalReservedProperties as RegExp[];
176      assert.isTrue(reservedTopelevelNames?.includes("func2"));
177      assert.isTrue(reservedProperty?.includes("prop"));
178      assert.equal(universalReservedToplevelNames[0].toString(), new RegExp(`^${wildcardTransformer("a*")}$`).toString());
179      assert.equal(universalReservedToplevelNames[1].toString(), new RegExp(`^${wildcardTransformer("*shoul?keep*")}$`).toString());
180      assert.equal(universalReservedProperties[0].toString(), new RegExp(`^${wildcardTransformer("prop?")}$`).toString());
181      assert.equal(universalReservedProperties[2].toString(), new RegExp(`^${wildcardTransformer("*pro?")}$`).toString());
182      assert.equal(universalReservedProperties[1].toString(), new RegExp(`^${wildcardTransformer("function*")}$`).toString());
183    });
184  });
185
186  describe('Tester Cases for <ArkObfuscator>.', function () {
187    it('Tester: test case for ArkObfuscator.ini', function (){
188      let configPath = "test/ut/arkobfuscator/iniTestObfConfig.json"
189      let obfuscator: ArkObfuscator = new ArkObfuscator();
190      let config = FileUtils.readFileAsJson(configPath) as IOptions;
191      obfuscator.init(config);
192      let reservedTopelevelNames = config.mNameObfuscation?.mReservedToplevelNames;
193      let reservedProperty = config.mNameObfuscation?.mReservedProperties;
194      let universalReservedToplevelNames = config.mNameObfuscation?.mUniversalReservedToplevelNames;
195      let universalReservedProperties = config.mNameObfuscation?.mUniversalReservedProperties;
196      assert.isTrue(reservedTopelevelNames?.includes("func2"));
197      assert.isTrue(reservedTopelevelNames?.includes("a*"));
198      assert.isTrue(reservedTopelevelNames?.includes("*shoul?keep*"));
199      assert.isTrue(reservedProperty?.includes("prop"));
200      assert.isTrue(reservedProperty?.includes("prop?"));
201      assert.isTrue(reservedProperty?.includes("*pro?"));
202      assert.isTrue(reservedProperty?.includes("function*"));
203      assert.equal(universalReservedToplevelNames, undefined);
204      assert.equal(universalReservedProperties, undefined);
205    });
206  });
207});
208
209function compareStringsIgnoreNewlines(str1: string, str2: string): boolean {
210  const normalize = (str: string) => str.replace(/[\n\r\s]+/g, '');
211  return normalize(str1) === normalize(str2);
212}