• 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 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
16import mocha from 'mocha';
17import fs from 'fs';
18import path from 'path';
19import { expect } from 'chai';
20import * as ts from 'typescript';
21
22import { EXPECT_INDEX_ETS } from './mock/rollup_mock/path_config';
23import RollUpPluginMock from './mock/rollup_mock/rollup_plugin_mock';
24import {
25  addLocalPackageSet,
26  compilerOptions,
27  localPackageSet,
28  needReCheckForChangedDepUsers,
29  resetEtsCheck,
30  serviceChecker,
31  getMaxFlowDepth,
32  MAX_FLOW_DEPTH_DEFAULT_VALUE
33} from '../../lib/ets_checker';
34import { TS_BUILD_INFO_SUFFIX } from '../../lib/pre_define';
35import {
36  globalProgram,
37  projectConfig
38} from '../../main';
39
40mocha.describe('test ets_checker file api', function () {
41    mocha.before(function () {
42        this.rollup = new RollUpPluginMock();
43    });
44
45    mocha.after(() => {
46        delete this.rollup;
47        const cacheFile: string = path.resolve(projectConfig.cachePath, '../.ts_checker_cache');
48        if (fs.existsSync(cacheFile)) {
49            fs.unlinkSync(cacheFile);
50        }
51        const tsBuildInfoFilePath: string = path.resolve(projectConfig.cachePath, '..', TS_BUILD_INFO_SUFFIX);
52        if (fs.existsSync(tsBuildInfoFilePath)) {
53            fs.unlinkSync(tsBuildInfoFilePath);
54        }
55        const tsBuildInfoLinterFilePath: string = tsBuildInfoFilePath + '.linter';
56        if (fs.existsSync(tsBuildInfoLinterFilePath)) {
57            fs.unlinkSync(tsBuildInfoLinterFilePath);
58        }
59    });
60
61    mocha.it('1-1: test addLocalPackageSet for original ohmurl', function () {
62        this.rollup.build();
63        const rollupObject = this.rollup;
64        const rollupFileList = rollupObject.getModuleIds();
65        projectConfig.useNormalizedOHMUrl = false;
66        for (const moduleId of rollupFileList) {
67            if (fs.existsSync(moduleId)) {
68                addLocalPackageSet(moduleId, rollupObject);
69            }
70        }
71        expect(localPackageSet.has('entry')).to.be.true;
72        localPackageSet.clear();
73    });
74
75    mocha.it('1-2: test addLocalPackageSet for normalized ohmurl', function () {
76        this.rollup.build();
77        const rollupObject = this.rollup;
78        const rollupFileList = rollupObject.getModuleIds();
79        projectConfig.useNormalizedOHMUrl = true;
80        for (const moduleId of rollupFileList) {
81            const moduleInfo: Object = rollupObject.getModuleInfo(moduleId);
82            if (moduleInfo) {
83                const metaInfo: Object = moduleInfo.meta;
84                metaInfo.pkgName = 'pkgname';
85            }
86            if (fs.existsSync(moduleId)) {
87                addLocalPackageSet(moduleId, rollupObject);
88            }
89        }
90        expect(localPackageSet.has('pkgname')).to.be.true;
91    });
92
93    mocha.it('1-3: test getOrCreateLanguageService when dependency in oh-package.json change', function () {
94        this.timeout(10000);
95        this.rollup.build();
96        let rollupObject = this.rollup;
97        expect(needReCheckForChangedDepUsers).to.be.false;
98
99        interface MockCacheStore {
100            service: Object | undefined;
101            pkgJsonFileHash: string;
102            targetESVersion: number;
103        }
104        const mockServiceCache = {
105            service: undefined,
106            pkgJsonFileHash: '9f07917d395682c73a90af8f5796a2c6',
107            targetESVersion: 8
108        }
109        let mockCache = new Map<string, MockCacheStore>();
110        mockCache.set('service', mockServiceCache);
111        rollupObject.share.cache = mockCache;
112        rollupObject.share.depInfo = {enableIncre: true};
113        rollupObject.share.projectConfig.pkgJsonFileHash = '26bde0f30dda53b0afcbf39428ec9851';
114        Object.assign(projectConfig, rollupObject.share.projectConfig);
115
116        // The current hash and the hash in cache of the dependency differs, should recheck
117        serviceChecker([EXPECT_INDEX_ETS], null, null, null, rollupObject.share);
118        expect(needReCheckForChangedDepUsers).to.be.true;
119        expect(globalProgram.program != null).to.be.true;
120
121        resetEtsCheck();
122        expect(needReCheckForChangedDepUsers).to.be.false;
123        expect(globalProgram.program == null).to.be.true;
124
125        // The current hash and the hash in cache of the dependency are the same, no need to recheck
126        serviceChecker([EXPECT_INDEX_ETS], null, null, null, rollupObject.share);
127        expect(needReCheckForChangedDepUsers).to.be.false;
128        expect(globalProgram.program != null).to.be.true;
129
130        resetEtsCheck();
131        expect(needReCheckForChangedDepUsers).to.be.false;
132        expect(globalProgram.program == null).to.be.true;
133    });
134
135    mocha.it('1-4: test getOrCreateLanguageService when paths in compilerOptions change', function () {
136        this.timeout(10000);
137        this.rollup.build();
138        let rollupObject = this.rollup;
139        process.env.compileTool = 'rollup';
140
141        Object.assign(projectConfig, rollupObject.share.projectConfig);
142        serviceChecker([EXPECT_INDEX_ETS], null, null, null, rollupObject.share);
143        expect(JSON.stringify(compilerOptions.paths) === '{"*":["*","../../../../*","../*"]}').to.be.true;
144        expect(needReCheckForChangedDepUsers).to.be.false;
145        expect(globalProgram.program != null).to.be.true;
146        expect(compilerOptions.skipPathsInKeyForCompilationSettings).to.be.true;
147
148        resetEtsCheck();
149        expect(needReCheckForChangedDepUsers).to.be.false;
150        expect(globalProgram.program == null).to.be.true;
151        expect(compilerOptions.skipPathsInKeyForCompilationSettings).to.be.true;
152
153        interface MockCacheStore {
154            service: Object | undefined;
155            pkgJsonFileHash: string;
156            targetESVersion: number;
157        }
158        const mockServiceCache = {
159            service: undefined,
160            pkgJsonFileHash: '9f07917d395682c73a90af8f5796a2c6',
161            targetESVersion: 8
162        }
163        let mockCache = new Map<string, MockCacheStore>();
164        mockCache.set('service', mockServiceCache);
165        rollupObject.share.cache = mockCache;
166        rollupObject.share.depInfo = {enableIncre: true};
167        rollupObject.share.projectConfig.pkgJsonFileHash = '26bde0f30dda53b0afcbf39428ec9851';
168
169        // The current hash of the dependency differs, and the paths in compilerOptions will change since resolveModulePaths change
170        const resolveModulePaths = ['../testdata/expect'];
171        serviceChecker([EXPECT_INDEX_ETS], null, resolveModulePaths, null, rollupObject.share);
172        expect(JSON.stringify(compilerOptions.paths) === '{"*":["*","../../../../../../../testdata/expect/*"]}').to.be.true;
173        expect(needReCheckForChangedDepUsers).to.be.true;
174        expect(globalProgram.program != null).to.be.true;
175        expect(compilerOptions.skipPathsInKeyForCompilationSettings).to.be.true;
176
177        resetEtsCheck();
178        expect(needReCheckForChangedDepUsers).to.be.false;
179        expect(globalProgram.program == null).to.be.true;
180        expect(compilerOptions.skipPathsInKeyForCompilationSettings).to.be.true;
181    });
182    mocha.it('1-5: test GetEmitHost of program', function () {
183        const compilerOptions: ts.CompilerOptions = {
184            target: ts.ScriptTarget.ES2021
185        };
186        const fileNames: string[] = ['../testdata/testfiles/testGetEmitHost.ts'];
187        let program: ts.Program = ts.createProgram(fileNames, compilerOptions);
188        expect(program.getEmitHost()).to.not.be.undefined;
189    });
190});
191
192mocha.describe('getMaxFlowDepth', () => {
193    mocha.it('1-1: test should return the default value when maxFlowDepth is undefined', () => {
194        const result = getMaxFlowDepth();
195        expect(result).to.equal(MAX_FLOW_DEPTH_DEFAULT_VALUE);
196    });
197
198    mocha.it('1-2: test should return the default value and log a warning when maxFlowDepth is less than the minimum valid value', () => {
199        const invalidMaxFlowDepth = 1999;
200        projectConfig.projectArkOption = {
201            tscConfig: {
202                maxFlowDepth: invalidMaxFlowDepth
203          }
204        }
205        const result = getMaxFlowDepth();
206        expect(result).to.equal(MAX_FLOW_DEPTH_DEFAULT_VALUE);
207      });
208
209    mocha.it('1-3: test should return the value of maxFlowDepth when it is 2000 within the valid range', () => {
210        const validMaxFlowDepth = 2000;
211        projectConfig.projectArkOption = {
212            tscConfig: {
213                maxFlowDepth: validMaxFlowDepth
214            }
215        }
216        const result = getMaxFlowDepth();
217        expect(result).to.equal(validMaxFlowDepth);
218    });
219
220    mocha.it('1-4: test should return the value of maxFlowDepth when it is 3000 within the valid range', () => {
221        const validMaxFlowDepth = 3000;
222        projectConfig.projectArkOption = {
223            tscConfig: {
224                maxFlowDepth: validMaxFlowDepth
225            }
226        }
227        const result = getMaxFlowDepth();
228        expect(result).to.equal(validMaxFlowDepth);
229    });
230
231    mocha.it('1-5: test should return the value of maxFlowDepth when it is 65535 within the valid range', () => {
232        const validMaxFlowDepth = 65535;
233        projectConfig.projectArkOption = {
234            tscConfig: {
235                maxFlowDepth: validMaxFlowDepth
236            }
237        }
238        const result = getMaxFlowDepth();
239        expect(result).to.equal(validMaxFlowDepth);
240    });
241
242    mocha.it('1-6: test should return the default value and log a warning when maxFlowDepth is greater than the maximum valid value', () => {
243        const invalidMaxFlowDepth = 65536;
244        projectConfig.projectArkOption = {
245            tscConfig: {
246                maxFlowDepth: invalidMaxFlowDepth
247            }
248        }
249        const result = getMaxFlowDepth();
250        expect(result).to.equal(MAX_FLOW_DEPTH_DEFAULT_VALUE);
251    });
252});
253
254mocha.describe('setProgramSourceFiles', () => {
255    mocha.it('1-1: test add new sourcefile to program', () => {
256        const compilerOptions: ts.CompilerOptions = {
257            target: ts.ScriptTarget.ES2021
258        };
259        const fileNames: string[] = ['../testdata/testfiles/testProgramSourceFiles.ts'];
260        let program: ts.Program = ts.createProgram(fileNames, compilerOptions);
261        const fileContent: string = `
262            let a = 1;
263        `;
264        let newSourceFile: ts.SourceFile = ts.createSourceFile('demo.ts', fileContent, ts.ScriptTarget.ES2021, true);
265        expect(program.getSourceFiles().includes(newSourceFile)).to.be.false;
266        expect(program.getSourceFile('demo.ts')).to.be.undefined;
267        const origrinFileSize: number = program.getSourceFiles().length;
268        program.setProgramSourceFiles(newSourceFile);
269        const newFileSize: number = program.getSourceFiles().length;
270        expect(program.getSourceFiles().includes(newSourceFile)).to.be.true;
271        expect(program.getSourceFile('demo.ts')).to.not.be.undefined;
272        expect(origrinFileSize === (newFileSize - 1)).to.be.true;
273    });
274
275    mocha.it('1-2: test should do nothing when adding an existed sourcefile to the program', () => {
276        const compilerOptions: ts.CompilerOptions = {
277            target: ts.ScriptTarget.ES2021
278        };
279        const fileNames: string[] = ['../testdata/testfiles/testProgramSourceFiles.ts'];
280        let program: ts.Program = ts.createProgram(fileNames, compilerOptions);
281        const fileContent1: string = `let a = 1;`;
282        let newSourceFile1: ts.SourceFile = ts.createSourceFile('demo.ts', fileContent1, ts.ScriptTarget.ES2021, true);
283        program.setProgramSourceFiles(newSourceFile1);
284        const origrinFileSize: number = program.getSourceFiles().length;
285        expect(program.getSourceFiles().includes(newSourceFile1)).to.be.true;
286        expect(program.getSourceFile('demo.ts').text).to.be.equal('let a = 1;');
287        const fileContent2: string = `let b = 1;`;
288        let newSourceFile2: ts.SourceFile = ts.createSourceFile('demo.ts', fileContent2, ts.ScriptTarget.ES2021, true);
289        program.setProgramSourceFiles(newSourceFile2);
290        const newFileSize: number = program.getSourceFiles().length;
291        expect(program.getSourceFiles().includes(newSourceFile2)).to.be.false;
292        expect(program.getSourceFile('demo.ts').text).to.be.equal('let a = 1;');
293        expect(origrinFileSize === newFileSize).to.be.true;
294    });
295});
296
297mocha.describe('initProcessingFiles and getProcessingFiles', () => {
298    mocha.it('1-1: test should return undefined when processingDefaultFiles and processingOtherFiles is undefined', () => {
299        const compilerOptions: ts.CompilerOptions = {
300            target: ts.ScriptTarget.ES2021
301        };
302        const fileNames: string[] = ['../testdata/testfiles/testProgramSourceFiles.ts'];
303        let program: ts.Program = ts.createProgram(fileNames, compilerOptions);
304        let processingFiles: ts.SourceFile[] | undefined = program.getProcessingFiles();
305        expect(processingFiles === undefined).to.be.true;
306    });
307
308    mocha.it('1-2: test should return array when processingDefaultFiles and processingOtherFiles is not undefined', () => {
309        const compilerOptions: ts.CompilerOptions = {
310            target: ts.ScriptTarget.ES2021
311        };
312        const fileNames: string[] = ['../testdata/testfiles/testProgramSourceFiles.ts'];
313        let program: ts.Program = ts.createProgram(fileNames, compilerOptions);
314        program.initProcessingFiles();
315        let processingFiles: ts.SourceFile[] | undefined = program.getProcessingFiles();
316        expect(processingFiles === undefined).to.be.false;
317    });
318
319});
320
321mocha.describe('refreshTypeChecker', () => {
322    mocha.it('1-1: test should recreate typeChecker and linterTypeChecker', () => {
323        const compilerOptions: ts.CompilerOptions = {
324            target: ts.ScriptTarget.ES2021
325        };
326        const fileNames: string[] = ['../testdata/testfiles/testProgramSourceFiles.ts'];
327        let program: ts.Program = ts.createProgram(fileNames, compilerOptions);
328        const fileContent: string = `
329            let a = 1;
330            let b = x; // Error!
331        `;
332        let newSourceFile: ts.SourceFile = ts.createSourceFile('demo.ts', fileContent, ts.ScriptTarget.ES2021, true);
333        newSourceFile.originalFileName = newSourceFile.fileName;
334        newSourceFile.resolvedPath = 'demo.ts';
335        newSourceFile.path = 'demo.ts';
336        program.processImportedModules(newSourceFile);
337        program.setProgramSourceFiles(newSourceFile);
338        program.refreshTypeChecker();
339        const diagnostics: readonly ts.Diagnostic[] = program.getSemanticDiagnostics(newSourceFile);
340        expect(diagnostics[0].messageText === `Cannot find name 'x'.`);
341        expect(diagnostics.length === 1).to.be.true;
342    });
343});
344
345mocha.describe('deleteProgramSourceFiles', () => {
346    mocha.it('1-1: test should delete when sourcefiles are exist in program', () => {
347        const compilerOptions: ts.CompilerOptions = {
348            target: ts.ScriptTarget.ES2021
349        };
350        const fileNames: string[] = ['../testdata/testfiles/testProgramSourceFiles.ts'];
351        let program: ts.Program = ts.createProgram(fileNames, compilerOptions);
352        const fileContent1: string = `
353            let a = 1;
354        `;
355        let newSourceFile1: ts.SourceFile = ts.createSourceFile('demo1.ts', fileContent1, ts.ScriptTarget.ES2021, true);
356        program.setProgramSourceFiles(newSourceFile1);
357        expect(program.getSourceFiles().includes(newSourceFile1)).to.be.true;
358        expect(program.getSourceFile('demo1.ts')).to.not.be.undefined;
359        const fileContent2: string = `
360            let b = 1;
361        `;
362        let newSourceFile2: ts.SourceFile = ts.createSourceFile('demo2.ts', fileContent2, ts.ScriptTarget.ES2021, true);
363        program.setProgramSourceFiles(newSourceFile2);
364        expect(program.getSourceFiles().includes(newSourceFile2)).to.be.true;
365        expect(program.getSourceFile('demo2.ts')).to.not.be.undefined;
366        const origrinFileSize: number = program.getSourceFiles().length;
367        const deleteFileNames: string[] = ['demo1.ts', 'demo2.ts'];
368        program.deleteProgramSourceFiles(deleteFileNames);
369        expect(program.getSourceFiles().includes(newSourceFile1)).to.be.false;
370        expect(program.getSourceFile('demo1.ts')).to.be.undefined;
371        expect(program.getSourceFiles().includes(newSourceFile2)).to.be.false;
372        expect(program.getSourceFile('demo2.ts')).to.be.undefined;
373        const newFileSize: number = program.getSourceFiles().length;
374        expect(origrinFileSize === (newFileSize + 2)).to.be.true;
375    });
376
377    mocha.it('1-2: test should do nothing when sourcefiles are not exist in program', () => {
378        const compilerOptions: ts.CompilerOptions = {
379            target: ts.ScriptTarget.ES2021
380        };
381        const fileNames: string[] = ['../testdata/testfiles/testProgramSourceFiles.ts'];
382        let program: ts.Program = ts.createProgram(fileNames, compilerOptions);
383        const fileContent1: string = `
384            let a = 1;
385        `;
386        let newSourceFile1: ts.SourceFile = ts.createSourceFile('demo1.ts', fileContent1, ts.ScriptTarget.ES2021, true);
387        program.setProgramSourceFiles(newSourceFile1);
388        expect(program.getSourceFiles().includes(newSourceFile1)).to.be.true;
389        expect(program.getSourceFile('demo1.ts')).to.not.be.undefined;
390        const fileContent2: string = `
391            let b = 1;
392        `;
393        let newSourceFile2: ts.SourceFile = ts.createSourceFile('demo2.ts', fileContent2, ts.ScriptTarget.ES2021, true);
394        program.setProgramSourceFiles(newSourceFile2);
395        expect(program.getSourceFiles().includes(newSourceFile2)).to.be.true;
396        expect(program.getSourceFile('demo2.ts')).to.not.be.undefined;
397        const origrinFileSize: number = program.getSourceFiles().length;
398        const deleteFileNames: string[] = ['demo3.ts', 'demo4.ts'];
399        program.deleteProgramSourceFiles(deleteFileNames);
400        expect(program.getSourceFiles().includes(newSourceFile1)).to.be.true;
401        expect(program.getSourceFile('demo1.ts')).to.not.be.undefined;
402        expect(program.getSourceFiles().includes(newSourceFile2)).to.be.true;
403        expect(program.getSourceFile('demo2.ts')).to.not.be.undefined;
404        const newFileSize: number = program.getSourceFiles().length;
405        expect(origrinFileSize === newFileSize).to.be.true;
406    });
407});