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 { expect } from 'chai';
17import mocha from 'mocha';
18import sinon from 'sinon';
19import fs from 'fs';
20import cluster from 'cluster';
21import path from 'path';
22
23import RollUpPluginMock from '../mock/rollup_mock/rollup_plugin_mock';
24import { BundleMode } from '../../../lib/fast_build/ark_compiler/bundle/bundle_mode';
25import { changeFileExtension } from '../../../lib/fast_build/ark_compiler/utils'
26import {
27  DEBUG,
28  RELEASE,
29  TEMPORARY
30} from '../../../lib/fast_build/ark_compiler/common/ark_define'
31import { toUnixPath } from '../../../lib/utils'
32
33mocha.describe('test bundle_mode file api', function () {
34  mocha.before(function () {
35    this.rollup = new RollUpPluginMock();
36  });
37
38  mocha.after(() => {
39    delete this.rollup;
40  });
41
42  mocha.it('1-1: test error message of executeArkCompiler', function () {
43    this.rollup.build();
44    const rollupBundleFileSet: Object = {
45      'test.js': {
46        'type': 'asset',
47        'source': 'test'
48      }
49    };
50    const bundleMode =  new BundleMode(this.rollup, rollupBundleFileSet);
51    bundleMode.projectConfig.pandaMode = 'invalid value';
52    const stub = sinon.stub(bundleMode, 'throwArkTsCompilerError');
53    bundleMode.executeArkCompiler();
54    expect(stub.calledWith('ArkTS:INTERNAL ERROR: Invalid compilation mode.')).to.be.true;
55    stub.restore();
56  });
57
58  mocha.it('2-1: test the error message of executeEs2AbcCmd handler error', async function () {
59    this.rollup.build();
60    const rollupBundleFileSet: Object = {
61      'test.js': {
62        'type': 'asset',
63        'source': 'test'
64      }
65    };
66    const bundleMode =  new BundleMode(this.rollup, rollupBundleFileSet);
67    const triggerAsyncStub = sinon.stub(bundleMode, 'triggerAsync').throws(new Error('Execution failed'));
68    const stub = sinon.stub(bundleMode, 'throwArkTsCompilerError');
69    try {
70      bundleMode.executeEs2AbcCmd();
71    } catch (e) {
72    }
73    expect(stub.calledWithMatch('ArkTS:ERROR failed to execute es2abc with async handler: ')).to.be.true;
74    triggerAsyncStub.restore();
75    stub.restore();
76  });
77
78  mocha.it('3-1: test the error message of collectBundleFileList(file.type is invalid value)', function () {
79    this.rollup.build();
80    const rollupBundleFileSet: Object = {
81      'test.js': {
82        'type': ''
83      }
84    };
85    const stub = sinon.stub(this.rollup.share, 'throwArkTsCompilerError');
86    new BundleMode(this.rollup, rollupBundleFileSet);
87    expect(stub.calledWith('ArkTS:INTERNAL ERROR: Failed to retrieve source code ' +
88      'for test.js from rollup file set.')).to.be.true;
89    stub.restore();
90  });
91
92  mocha.it('3-2: test the error message of collectBundleFileList', function () {
93    this.rollup.build();
94    const rollupBundleFileSet: Object = {
95      'test.js': {
96          'type': 'asset',
97          'source': 'test'
98      }
99    };
100    const existsSyncStub = sinon.stub(fs, 'existsSync').callsFake((path) => {
101      const pattern = /test\.temp\.js$/;
102      if (pattern.test(path)) {
103        return false;
104      }
105      return true;
106    });
107    const stub = sinon.stub(this.rollup.share, 'throwArkTsCompilerError');
108    try {
109      new BundleMode(this.rollup, rollupBundleFileSet);
110    } catch (e) {
111    }
112    expect(stub.calledWith('ArkTS:INTERNAL ERROR: Failed to generate cached source file: test.js')).to.be.true;
113    existsSyncStub.restore();
114    stub.restore();
115  });
116
117  mocha.it('4-1: test the error message of filterBundleFileListWithHashJson', function () {
118    this.rollup.build();
119    const rollupBundleFileSet: Object = {
120      'test.js': {
121        'type': 'asset',
122        'source': 'test'
123      }
124    };
125    const bundleMode =  new BundleMode(this.rollup, rollupBundleFileSet);
126    const jsonData = JSON.stringify(rollupBundleFileSet, null, 2);
127    const stub = sinon.stub(bundleMode, 'throwArkTsCompilerError');
128    for (const value of bundleMode.intermediateJsBundle.values()) {
129      fs.unlinkSync(value.cacheFilePath);
130    }
131    fs.writeFileSync(bundleMode.hashJsonFilePath, jsonData)
132    bundleMode.filterBundleFileListWithHashJson();
133    expect(stub.calledWithMatch('ArkTS:INTERNAL ERROR: Failed to get bundle cached abc from ')).to.be.true;
134    stub.restore();
135  });
136
137  mocha.it('5-1: test the error message of invokeTs2AbcWorkersToGenAbc(worker error)', function () {
138    this.rollup.build();
139    const rollupBundleFileSet: Object = {
140      'test.js': {
141        'type': 'asset',
142        'source': 'test'
143      }
144    };
145    const bundleMode =  new BundleMode(this.rollup, rollupBundleFileSet);
146    const stub = sinon.stub(bundleMode, 'throwArkTsCompilerError');
147    const clusterStub = sinon.stub(cluster, 'fork');
148    const fakeWorker = {
149      on: sinon.stub()
150    };
151    clusterStub.returns(fakeWorker);
152    const splittedBundles = bundleMode.getSplittedBundles()
153    try {
154      fakeWorker.on.withArgs('message').callsFake((event, callback) => {
155        callback({ data: 'error' });
156      });
157      bundleMode.invokeTs2AbcWorkersToGenAbc(splittedBundles)
158    } catch (e) {
159    }
160    expect(stub.calledWith('ArkTS:ERROR Failed to execute ts2abc')).to.be.true;
161    clusterStub.restore();
162    stub.restore();
163  });
164
165  mocha.it('6-1: test the error message of writeHashJson', function () {
166    this.rollup.build();
167    const rollupBundleFileSet: Object = {
168      'test.js': {
169        'type': 'asset',
170        'source': 'test'
171      }
172    };
173    const bundleMode =  new BundleMode(this.rollup, rollupBundleFileSet);
174    const stub = sinon.stub(bundleMode, 'throwArkTsCompilerError');
175    try {
176      bundleMode.writeHashJson();
177    } catch (e) {
178    }
179    expect(stub.calledWithMatch('ArkTS:INTERNAL ERROR: During hash JSON file generation, ')).to.be.true;
180    stub.restore();
181  });
182
183  mocha.it('7-1: test the error message of copyFileFromCachePathToOutputPath', function () {
184    this.rollup.build();
185    const rollupBundleFileSet: Object = {
186      'test.js': {
187        'type': 'asset',
188        'source': 'test'
189      }
190    };
191    const bundleMode =  new BundleMode(this.rollup, rollupBundleFileSet);
192    const stub = sinon.stub(bundleMode, 'throwArkTsCompilerError');
193    try {
194      bundleMode.copyFileFromCachePathToOutputPath();
195    } catch (e) {
196    }
197    expect(stub.calledWithMatch('not found during incremental build. ' +
198      'Please try to rebuild the project')).to.be.true;
199    stub.restore();
200  });
201
202  mocha.it('8-1: test sourceFile field of bundle mode in release', function () {
203    this.rollup.build();
204    this.rollup.share.projectConfig.buildMode = RELEASE
205    const rollupBundleFileSet: Object = {
206      'test.js': {
207        'type': 'asset',
208        'source': 'test'
209      }
210    };
211    const bundleMode =  new BundleMode(this.rollup, rollupBundleFileSet);
212    const filesinfo: string[] = fs.readFileSync(bundleMode.generateFileInfoOfBundle()).toString().split(";");
213    // sourceFile is the 4th field in filesInfo
214    const sourceFile: string = filesinfo[3];
215    const relativeCachePath: string = toUnixPath(bundleMode.projectConfig.cachePath.replace(
216      bundleMode.projectConfig.projectRootPath + path.sep, ''));
217    const buildDirArr: string[] = bundleMode.projectConfig.aceModuleBuild.split(path.sep);
218    const abilityDir: string = buildDirArr[buildDirArr.length - 1];
219
220    expect(sourceFile === path.join(relativeCachePath, TEMPORARY, abilityDir, 'test.temp.js')).to.be.true;
221  });
222
223  mocha.it('9-1: test sourceFile field of bundle mode in debug', function () {
224    this.rollup.build();
225    this.rollup.share.projectConfig.buildMode = DEBUG
226    const rollupBundleFileSet: Object = {
227      'test.js': {
228        'type': 'asset',
229        'source': 'test'
230      }
231    };
232    const bundleMode =  new BundleMode(this.rollup, rollupBundleFileSet);
233    const filesinfo: string[] = fs.readFileSync(bundleMode.generateFileInfoOfBundle()).toString().split(";");
234    // sourceFile is the 4th field in filesInfo
235    const sourceFile: string = filesinfo[3];
236    const relativePath: string = toUnixPath(bundleMode.projectConfig.aceModuleBuild.replace(
237      bundleMode.projectConfig.projectRootPath + path.sep, ''));
238  
239    expect(sourceFile === path.join(relativePath, 'test.js')).to.be.true;
240  });
241});