• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2021-2023 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 SysTestKit from "./module/kit/SysTestKit";
17import {TAG} from './Constant';
18import LogExpectError from './module/report/LogExpectError'
19
20class AssertException extends Error {
21    constructor(message) {
22        super();
23        this.name = "AssertException";
24        this.message = message;
25    }
26}
27
28function getFuncWithArgsZero(func, timeout, isStressTest) {
29    return new Promise(async (resolve, reject) => {
30        let timer = null;
31        if (!isStressTest) {
32            timer = setTimeout(() => {
33                reject(new Error('execute timeout ' + timeout + 'ms'));
34            }, timeout);
35        }
36        try {
37            await func();
38        } catch (err) {
39            reject(err);
40        }
41        timer !== null ? clearTimeout(timer) : null;
42        resolve();
43    });
44}
45
46function getFuncWithArgsOne(func, timeout, isStressTest) {
47    return new Promise(async (resolve, reject) => {
48        let timer = null;
49        if (!isStressTest) {
50            timer = setTimeout(() => {
51                reject(new Error('execute timeout ' + timeout + 'ms'));
52            }, timeout);;
53        }
54
55        function done() {
56            timer !== null ? clearTimeout(timer) : null;
57            resolve();
58        }
59
60        try {
61            await func(done);
62        } catch (err) {
63            timer !== null ? clearTimeout(timer) : null;
64            reject(err);
65        }
66    });
67}
68
69function getFuncWithArgsTwo(func, timeout, paramItem, isStressTest) {
70    return new Promise(async (resolve, reject) => {
71        let timer = null;
72        if (!isStressTest) {
73            timer = setTimeout(() => {
74                reject(new Error('execute timeout ' + timeout + 'ms'));
75            }, timeout);
76        }
77
78        function done() {
79            timer !== null ? clearTimeout(timer) : null;
80            resolve();
81        }
82
83        try {
84            await func(done, paramItem);
85        } catch (err) {
86            timer !== null ? clearTimeout(timer) : null;
87            reject(err);
88        }
89    });
90}
91
92function processFunc(coreContext, func) {
93    let argNames = ((func || '').toString()
94        .replace(/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg, '')
95        .match(/^(function)?\s*[^\(]*\(\s*([^\)]*)\)/m) || ['', '', ''])[2]
96        .split(',') // split parameters
97        .map(item => item.replace(/^\s*(_?)(.+?)\1\s*$/, name => name.split('=')[0].trim()))
98        .filter(String);
99    let funcLen = func.length;
100    let processedFunc;
101    const config = coreContext.getDefaultService('config');
102    config.setSupportAsync(true);
103    const timeout = + (config.timeout === undefined ? 5000 : config.timeout);
104    const isStressTest = (coreContext.getServices('dataDriver') !== undefined || config.getStress() > 1);
105    switch (funcLen) {
106        case 0: {
107            processedFunc = function () {
108                return getFuncWithArgsZero(func, timeout, isStressTest);
109            };
110            break;
111        }
112        case 1: {
113            if (argNames[0] === 'data') {
114                processedFunc = function (paramItem) {
115                    func(paramItem);
116                };
117            } else {
118                processedFunc = function () {
119                    return getFuncWithArgsOne(func, timeout, isStressTest);
120                };
121            }
122            break;
123        }
124        default: {
125            processedFunc = function (paramItem) {
126                return getFuncWithArgsTwo(func, timeout, paramItem, isStressTest);
127            };
128            break;
129        }
130    }
131    return processedFunc;
132}
133
134function secureRandomNumber() {
135    return crypto.randomBytes(8).readUInt32LE() / 0xffffffff;
136}
137
138class SuiteService {
139    constructor(attr) {
140        this.id = attr.id;
141        this.rootSuite = new SuiteService.Suite({});
142        this.currentRunningSuite = this.rootSuite;
143        this.suitesStack = [this.rootSuite];
144    }
145
146    describe(desc, func) {
147        const configService = this.coreContext.getDefaultService('config');
148        if (configService.filterSuite(desc)) {
149            console.info(`${TAG}filter suite : ${desc}`);
150            return;
151        }
152        const suite = new SuiteService.Suite({description: desc});
153        if (typeof this.coreContext.getServices('dataDriver') !== 'undefined' && configService['dryRun'] !== 'true') {
154            let suiteStress = this.coreContext.getServices('dataDriver').dataDriver.getSuiteStress(desc);
155            for (let i = 1; i < suiteStress; i++) {
156                this.currentRunningSuite.childSuites.push(suite);
157            }
158        }
159        this.currentRunningSuite.childSuites.push(suite);
160        this.currentRunningSuite = suite;
161        this.suitesStack.push(suite);
162        func.call();
163        let childSuite = this.suitesStack.pop();
164        if (this.suitesStack.length === 0) {
165            this.currentRunningSuite = childSuite;
166            this.suitesStack.push(childSuite);
167        }
168        if (this.suitesStack.length > 1) {
169            this.currentRunningSuite = this.suitesStack.pop();
170        } else {
171            this.currentRunningSuite = this.suitesStack.pop();
172            this.suitesStack.push(this.currentRunningSuite);
173        }
174    }
175
176    beforeAll(func) {
177        this.currentRunningSuite.beforeAll.push(processFunc(this.coreContext, func));
178    }
179
180    beforeEach(func) {
181        this.currentRunningSuite.beforeEach.push(processFunc(this.coreContext, func));
182    }
183
184    beforeItSpecified(itDescs, func) {
185        this.currentRunningSuite.beforeItSpecified.set(itDescs, processFunc(this.coreContext, func));
186    }
187
188    afterItSpecified(itDescs, func) {
189        this.currentRunningSuite.afterItSpecified.set(itDescs, processFunc(this.coreContext, func));
190    }
191
192    afterAll(func) {
193        this.currentRunningSuite.afterAll.push(processFunc(this.coreContext, func));
194    }
195
196    afterEach(func) {
197        this.currentRunningSuite.afterEach.push(processFunc(this.coreContext, func));
198    }
199
200    getCurrentRunningSuite() {
201        return this.currentRunningSuite;
202    }
203
204    setCurrentRunningSuite(suite) {
205        this.currentRunningSuite = suite;
206    }
207
208    traversalResults(suite, obj, breakOnError) {
209        if (suite.childSuites.length === 0 && suite.specs.length === 0) {
210            return obj;
211        }
212        if (suite.specs.length > 0) {
213            for (const itItem of suite.specs) {
214                obj.total++;
215                if (breakOnError && (obj.error > 0 || obj.failure > 0)) { // breakOnError模式
216                    continue;
217                }
218                if (itItem.error) {
219                    obj.error++;
220                } else if (itItem.fail) {
221                    obj.failure++;
222                } else if (itItem.pass === true) {
223                    obj.pass++;
224                }
225            }
226        }
227
228        obj.duration += suite.duration;
229
230        if (suite.childSuites.length > 0) {
231            for (const suiteItem of suite.childSuites) {
232                this.traversalResults(suiteItem, obj, breakOnError);
233            }
234        }
235    }
236
237    async setSuiteResults(suite, error, coreContext) {
238        if (suite.childSuites.length === 0 && suite.specs.length === 0) {
239            return obj;
240        }
241        if (suite.specs.length > 0) {
242            const specService = coreContext.getDefaultService('spec');
243            for (const specItem of suite.specs) {
244                specService.setCurrentRunningSpec(specItem);
245                if (error instanceof AssertException) {
246                    specItem.fail = error;
247                } else {
248                    specItem.error = error;
249                }
250                await coreContext.fireEvents('spec', 'specStart', specItem);
251                await coreContext.fireEvents('spec', 'specDone', specItem);
252            }
253        }
254        if (suite.childSuites.length > 0) {
255            for (const suiteItem of suite.childSuites) {
256                await this.setSuiteResults(suiteItem, error, coreContext);
257            }
258        }
259    }
260
261    getSummary() {
262        let suiteService = this.coreContext.getDefaultService('suite');
263        let rootSuite = suiteService.rootSuite;
264        const specService = this.coreContext.getDefaultService('spec');
265        const configService = this.coreContext.getDefaultService('config');
266        let breakOnError = configService.isBreakOnError();
267        let isError = specService.getStatus();
268        let isBreaKOnError = breakOnError && isError;
269        let obj = {total: 0, failure: 0, error: 0, pass: 0, ignore: 0, duration: 0};
270        for (const suiteItem of rootSuite.childSuites) {
271            this.traversalResults(suiteItem, obj, isBreaKOnError);
272        }
273        obj.ignore = obj.total - obj.pass - obj.failure - obj.error;
274        return obj;
275    }
276
277    init(coreContext) {
278        this.coreContext = coreContext;
279    }
280
281    traversalSuites(suite, obj, configService) {
282        if (suite.childSuites.length === 0 && suite.specs.length === 0) {
283            return [];
284        }
285        if (suite.specs.length > 0) {
286            let itArray = [];
287            for (const itItem of suite['specs']) {
288                if (!configService.filterDesc(suite.description, itItem.description, itItem.fi, null)) {
289                    itArray.push({'itName': itItem.description});
290                }
291            }
292            obj[suite.description] = itArray;
293        }
294
295        if (suite.childSuites.length > 0) {
296            let suiteArray = [];
297            for (const suiteItem of suite.childSuites) {
298                let suiteObj = {};
299                this.traversalSuites(suiteItem, suiteObj, configService);
300                if (!configService.filterSuite(suiteItem.description)) {
301                    suiteArray.push(suiteObj);
302                }
303            }
304            obj.suites = suiteArray;
305        }
306    }
307
308    async dryRun(abilityDelegator) {
309        const configService = this.coreContext.getDefaultService('config');
310        let testSuitesObj = {};
311        let suitesArray = [];
312        for (const suiteItem of this.rootSuite.childSuites) {
313            let obj = {};
314            this.traversalSuites(suiteItem, obj, configService);
315            if (!configService.filterSuite(suiteItem.description)) {
316                suitesArray.push(obj);
317            }
318        }
319        testSuitesObj['suites'] = suitesArray;
320
321        let strJson = JSON.stringify(testSuitesObj);
322        let strLen = strJson.length;
323        let maxLen = 500;
324        let maxCount = Math.floor(strLen / maxLen);
325
326        for (let count = 0; count <= maxCount; count++) {
327            await SysTestKit.print(strJson.substring(count * maxLen, (count + 1) * maxLen));
328        }
329        console.info(`${TAG}dryRun print success`);
330        abilityDelegator.finishTest('dry run finished!!!', 0, () => { });
331    }
332
333    execute() {
334        const configService = this.coreContext.getDefaultService('config');
335        if (configService.filterValid.length !== 0) {
336            this.coreContext.fireEvents('task', 'incorrectFormat');
337            return;
338        }
339
340        if (configService.isRandom() && this.rootSuite.childSuites.length > 0) {
341            this.rootSuite.childSuites.sort(function () {
342                return Math.random().toFixed(1) > 0.5 ? -1 : 1;
343            });
344            this.currentRunningSuite = this.rootSuite.childSuites[0];
345        }
346
347        if (configService.isSupportAsync()) {
348            let asyncExecute = async () => {
349                await this.coreContext.fireEvents('task', 'taskStart');
350                await this.rootSuite.asyncRun(this.coreContext);
351            };
352            asyncExecute().then(async () => {
353                await this.coreContext.fireEvents('task', 'taskDone');
354            });
355        } else {
356            this.coreContext.fireEvents('task', 'taskStart');
357            this.rootSuite.run(this.coreContext);
358            this.coreContext.fireEvents('task', 'taskDone');
359        }
360    }
361
362    apis() {
363        const _this = this;
364        return {
365            describe: function (desc, func) {
366                return _this.describe(desc, func);
367            },
368            beforeItSpecified: function (itDescs, func) {
369                return _this.beforeItSpecified(itDescs, func);
370            },
371            afterItSpecified: function (itDescs, func) {
372                return _this.afterItSpecified(itDescs, func);
373            },
374            beforeAll: function (func) {
375                return _this.beforeAll(func);
376            },
377            beforeEach: function (func) {
378                return _this.beforeEach(func);
379            },
380            afterAll: function (func) {
381                return _this.afterAll(func);
382            },
383            afterEach: function (func) {
384                return _this.afterEach(func);
385            }
386        };
387    }
388}
389
390SuiteService.Suite = class {
391    constructor(attrs) {
392        this.description = attrs.description || '';
393        this.childSuites = [];
394        this.specs = [];
395        this.beforeAll = [];
396        this.afterAll = [];
397        this.beforeItSpecified = new Map();
398        this.afterItSpecified = new Map();
399        this.beforeEach = [];
400        this.afterEach = [];
401        this.duration = 0;
402        this.hookError = null;
403    }
404
405    pushSpec(spec) {
406        this.specs.push(spec);
407    }
408
409    removeSpec(desc) {
410        this.specs = this.specs.filter((item, index) => {
411            return item.description !== desc;
412        });
413    }
414
415    getSpecsNum() {
416        return this.specs.length;
417    }
418
419    isRun(coreContext) {
420        const configService = coreContext.getDefaultService('config');
421        const suiteService = coreContext.getDefaultService('suite');
422        const specService = coreContext.getDefaultService('spec');
423        let breakOnError = configService.isBreakOnError();
424        let isError = specService.getStatus();
425        return breakOnError && isError;
426    }
427
428    run(coreContext) {
429        const suiteService = coreContext.getDefaultService('suite');
430        suiteService.setCurrentRunningSuite(this);
431        if (this.description !== '') {
432            coreContext.fireEvents('suite', 'suiteStart', this);
433        }
434        this.runHookFunc('beforeAll');
435        if (this.specs.length > 0) {
436            const configService = coreContext.getDefaultService('config');
437            if (configService.isRandom()) {
438                this.specs.sort(function () {
439                    return Math.random().toFixed(1) > 0.5 ? -1 : 1;
440                });
441            }
442            for (let spec in this.specs) {
443                let isBreakOnError = this.isRun(coreContext);
444                if (isBreakOnError) {
445                    break;
446                }
447                this.runHookFunc('beforeEach');
448                spec.run(coreContext);
449                this.runHookFunc('afterEach');
450            }
451        }
452        if (this.childSuites.length > 0) {
453            for (let suite in this.childSuites) {
454                let isBreakOnError = this.isRun(coreContext);
455                if (isBreakOnError) {
456                    break;
457                }
458                suite.run(coreContext);
459                suiteService.setCurrentRunningSuite(suite);
460            }
461        }
462        this.runHookFunc('afterAll');
463        if (this.description !== '') {
464            coreContext.fireEvents('suite', 'suiteDone');
465        }
466    }
467
468    async asyncRunSpecs(coreContext) {
469        const configService = coreContext.getDefaultService('config');
470        if (configService.isRandom()) {
471            this.specs.sort(function () {
472                return Math.random().toFixed(1) > 0.5 ? -1 : 1;
473            });
474        }
475        const specService = coreContext.getDefaultService('spec');
476        for (let specItem of this.specs) {
477            specService.setCurrentRunningSpec(specItem);
478            // 遇错即停模式,发现用例有问题,直接返回,不在执行后面的it
479            let isBreakOnError = this.isRun(coreContext);
480            if (isBreakOnError) {
481                console.log("break description :" + this.description);
482                break;
483            }
484            await coreContext.fireEvents('spec', 'specStart', specItem);
485            try {
486                for (const [itNames, hookFunc] of this.beforeItSpecified) {
487                    if ((Object.prototype.toString.call(itNames) === '[object Array]' && itNames.includes(specItem.description)) ||
488                        (Object.prototype.toString.call(itNames) === '[object String]' && itNames === specItem.description)) {
489                        await Reflect.apply(hookFunc, null, []);
490                    }
491                    break;
492                }
493                await this.runAsyncHookFunc('beforeEach');
494                await specItem.asyncRun(coreContext);
495                for (const [itNames, hookFunc] of this.afterItSpecified) {
496                    if ((Object.prototype.toString.call(itNames) === '[object Array]' && itNames.includes(specItem.description)) ||
497                        (Object.prototype.toString.call(itNames) === '[object String]' && itNames === specItem.description)) {
498                        await Reflect.apply(hookFunc, null, []);
499                    }
500                    break;
501                }
502                await this.runAsyncHookFunc('afterEach');
503            } catch (e) {
504                console.error(`${TAG}stack:${e?.stack}`);
505                console.error(`${TAG}stack end`);
506                if (e instanceof AssertException) {
507                    specItem.fail = e;
508                } else {
509                    specItem.error = e;
510                }
511                specService.setStatus(true);
512            }
513            specItem.setResult();
514            await coreContext.fireEvents('spec', 'specDone', specItem);
515            specService.setCurrentRunningSpec(null);
516        }
517    }
518
519    async asyncRunChildSuites(coreContext) {
520        for (let i = 0; i < this.childSuites.length; i++) {
521            // 遇错即停模式, 发现用例有问题,直接返回,不在执行后面的description
522            let isBreakOnError = this.isRun(coreContext);
523            if (isBreakOnError) {
524                console.log(`${TAG}break description : ${this.description}`);
525                break;
526            }
527            await this.childSuites[i].asyncRun(coreContext);
528        }
529    }
530
531    async asyncRun(coreContext) {
532        const suiteService = coreContext.getDefaultService('suite');
533        const specService = coreContext.getDefaultService('spec');
534
535        suiteService.setCurrentRunningSuite(this);
536        suiteService.suitesStack.push(this);
537        if (this.description !== '') {
538            await coreContext.fireEvents('suite', 'suiteStart', this);
539        }
540
541        try {
542            await this.runAsyncHookFunc('beforeAll');
543        } catch (error) {
544            console.error(`${TAG}${error?.stack}`);
545            this.hookError = error;
546        }
547
548        if (this.hookError !== null) {
549            specService.setStatus(true);
550            await suiteService.setSuiteResults(this, this.hookError, coreContext);
551        }
552
553        if (this.specs.length > 0 && this.hookError === null) {
554            await this.asyncRunSpecs(coreContext);
555        }
556
557        if (this.childSuites.length > 0 && this.hookError === null) {
558            await this.asyncRunChildSuites(coreContext);
559        }
560
561        try {
562            await this.runAsyncHookFunc('afterAll');
563        } catch (error) {
564            console.error(`${TAG}${error?.stack}`);
565            this.hookError = error;
566            specService.setStatus(true);
567        }
568
569        if (this.description !== '') {
570            await coreContext.fireEvents('suite', 'suiteDone');
571            let childSuite = suiteService.suitesStack.pop();
572            if (suiteService.suitesStack.length === 0) {
573                suiteService.suitesStack.push(childSuite);
574            }
575            if (suiteService.suitesStack.length > 1) {
576                suiteService.setCurrentRunningSuite(suiteService.suitesStack.pop());
577            } else {
578                let currentRunningSuite = suiteService.suitesStack.pop();
579                suiteService.setCurrentRunningSuite(currentRunningSuite);
580                suiteService.suitesStack.push(currentRunningSuite);
581            }
582        }
583    }
584
585    runHookFunc(hookName) {
586        if (this[hookName] && this[hookName].length > 0) {
587            this[hookName].forEach(func => {
588                try {
589                    func();
590                } catch (e) {
591                    console.error(`${TAG}${e.stack}`);
592                }
593            });
594        }
595    }
596
597    async runAsyncHookFunc(hookName) {
598        for (const hookItem of this[hookName]) {
599            try {
600                await hookItem();
601            } catch (error) {
602                error['message'] += `, error in ${hookName} function`;
603                throw error;
604            }
605
606        }
607    }
608};
609
610class SpecService {
611    constructor(attr) {
612        this.id = attr.id;
613        this.totalTest = 0;
614        this.hasError = false;
615    }
616
617    init(coreContext) {
618        this.coreContext = coreContext;
619    }
620
621    setCurrentRunningSpec(spec) {
622        this.currentRunningSpec = spec;
623    }
624
625    setStatus(obj) {
626        this.hasError = obj;
627    }
628
629    getStatus() {
630        return this.hasError;
631    }
632
633    getTestTotal() {
634        return this.totalTest;
635    }
636
637    getCurrentRunningSpec() {
638        return this.currentRunningSpec;
639    }
640
641    it(desc, filter, func) {
642        const configService = this.coreContext.getDefaultService('config');
643        const currentSuiteName = this.coreContext.getDefaultService('suite').getCurrentRunningSuite().description;
644        if (configService.filterDesc(currentSuiteName, desc, filter, this.coreContext)) {
645            console.info(`${TAG}filter it :${desc}`);
646        } else {
647            let processedFunc = processFunc(this.coreContext, func);
648            const spec = new SpecService.Spec({description: desc, fi: filter, fn: processedFunc});
649            const suiteService = this.coreContext.getDefaultService('suite');
650            if (typeof this.coreContext.getServices('dataDriver') !== 'undefined' && configService['dryRun'] !== 'true') {
651                let specStress = this.coreContext.getServices('dataDriver').dataDriver.getSpecStress(desc);
652                for (let i = 1; i < specStress; i++) {
653                    this.totalTest++;
654                    suiteService.getCurrentRunningSuite().pushSpec(spec);
655                }
656            }
657
658            // dryRun 状态下不统计压力测试重复数据
659            if (configService['dryRun'] !== 'true') {
660                let stress = configService.getStress(); // 命令配置压力测试
661                console.info(`${TAG}stress length : ${stress}`);
662                for (let i = 1; i < stress; i++) {
663                    this.totalTest++;
664                    suiteService.getCurrentRunningSuite().pushSpec(spec);
665                }
666            }
667            this.totalTest++;
668            suiteService.getCurrentRunningSuite().pushSpec(spec);
669        }
670    }
671
672    apis() {
673        const _this = this;
674        return {
675            it: function (desc, filter, func) {
676                return _this.it(desc, filter, func);
677            }
678        };
679    }
680}
681
682SpecService.Spec = class {
683    constructor(attrs) {
684        this.description = attrs.description || '';
685        this.fi = attrs.fi;
686        this.fn = attrs.fn || function () {
687        };
688        this.fail = undefined;
689        this.error = undefined;
690        this.duration = 0;
691        this.startTime = 0;
692        this.isExecuted = false; // 当前用例是否执行
693    }
694
695    setResult() {
696        if (this.fail) {
697            this.pass = false;
698        } else {
699            this.pass = true;
700        }
701    }
702
703    run(coreContext) {
704        const specService = coreContext.getDefaultService('spec');
705        specService.setCurrentRunningSpec(this);
706        coreContext.fireEvents('spec', 'specStart', this);
707        this.isExecuted = true;
708        try {
709            let dataDriver = coreContext.getServices('dataDriver');
710            if (typeof dataDriver === 'undefined') {
711                this.fn();
712            } else {
713                let suiteParams = dataDriver.dataDriver.getSuiteParams();
714                let specParams = dataDriver.dataDriver.getSpecParams();
715                console.info(`${TAG}[suite params] ${JSON.stringify(suiteParams)}`);
716                console.info(`${TAG}[spec params] ${JSON.stringify(specParams)}`);
717                if (this.fn.length === 0) {
718                    this.fn();
719                } else if (specParams.length === 0) {
720                    this.fn(suiteParams);
721                } else {
722                    specParams.forEach(paramItem => this.fn(Object.assign({}, paramItem, suiteParams)));
723                }
724            }
725            this.setResult();
726        } catch (e) {
727            this.error = e;
728            specService.setStatus(true);
729        }
730        coreContext.fireEvents('spec', 'specDone', this);
731    }
732
733    async asyncRun(coreContext) {
734        const dataDriver = coreContext.getServices('dataDriver');
735        if (typeof dataDriver === 'undefined') {
736            await this.fn();
737        } else {
738            const suiteParams = dataDriver.dataDriver.getSuiteParams();
739            const specParams = dataDriver.dataDriver.getSpecParams();
740            console.info(`[suite params] ${JSON.stringify(suiteParams)}`);
741            console.info(`[spec params] ${JSON.stringify(specParams)}`);
742            if (this.fn.length === 0) {
743                await this.fn();
744            } else if (specParams.length === 0) {
745                await this.fn(suiteParams);
746            } else {
747                for (const paramItem of specParams) {
748                    await this.fn(Object.assign({}, paramItem, suiteParams));
749                }
750            }
751        }
752
753        this.isExecuted = true;
754    }
755
756    filterCheck(coreContext) {
757        const specService = coreContext.getDefaultService('spec');
758        specService.setCurrentRunningSpec(this);
759        return true;
760    }
761};
762
763class ExpectService {
764    constructor(attr) {
765        this.id = attr.id;
766        this.matchers = {};
767    }
768
769    expect(actualValue) {
770        return this.wrapMatchers(actualValue);
771    }
772
773    init(coreContext) {
774        this.coreContext = coreContext;
775        this.addMatchers(this.basicMatchers());
776    }
777
778    addMatchers(matchers) {
779        for (const matcherName in matchers) {
780            if (Object.prototype.hasOwnProperty.call(matchers, matcherName)) {
781                this.matchers[matcherName] = matchers[matcherName];
782            }
783        }
784    }
785
786    basicMatchers() {
787        return {
788            assertTrue: function (actualValue) {
789                return {
790                    pass: (actualValue) === true,
791                    message: 'expect true, actualValue is ' + actualValue
792                };
793            },
794            assertEqual: function (actualValue, args) {
795                let msg = 'expect ' + actualValue + ' equals ' + args[0];
796                if(actualValue == args[0]) { // 数值相同,提示数据类型
797                    const aClassName = Object.prototype.toString.call(actualValue);
798                    const bClassName = Object.prototype.toString.call(args[0]);
799                    msg = 'expect ' + actualValue + aClassName + ' equals ' + args[0] + bClassName + "strict mode inspect type";
800                }
801                return {
802                    pass: (actualValue) === args[0],
803                    expectValue: args[0],
804                    message: msg
805                };
806            },
807            assertThrow: function (actual, args) {
808                const result = {
809                    pass: false
810                };
811                if (typeof actual !== 'function') {
812                    result.message = 'toThrow\'s Actual should be a Function';
813                } else {
814                    let hasThrow = false;
815                    let throwError;
816                    try {
817                        actual();
818                    } catch (e) {
819                        hasThrow = true;
820                        throwError = e;
821                    }
822                    if (!hasThrow) {
823                        result.message = 'function did not throw an exception';
824                    } else if (throwError && throwError.message === args[0]) {
825                        result.pass = true;
826                    } else {
827                        result.message = `expect to throw ${args[0]} , actual throw ${throwError.message}`;
828                    }
829                }
830                return result;
831            }
832        };
833    }
834
835    wrapMatchers(actualValue) {
836        const _this = this;
837        const wrappedMatchers = {
838            // 翻转标识
839            isNot: false,
840
841            // 翻转方法
842            not: function () {
843                this.isNot = true;
844                return this;
845            }
846        };
847        const specService = _this.coreContext.getDefaultService('spec');
848        const currentRunningSpec = specService.getCurrentRunningSpec();
849        const currentRunningSuite = _this.coreContext.getDefaultService('suite').getCurrentRunningSuite();
850        for (const matcherName in this.matchers) {
851            let result = Object.prototype.hasOwnProperty.call(this.matchers, matcherName);
852            if (!result) {
853                continue;
854            }
855            if (matcherName.search('assertPromise') == 0) {
856                wrappedMatchers[matcherName] = async function () {
857                    await _this.matchers[matcherName](actualValue, arguments).then(function (result) {
858                        if (wrappedMatchers.isNot) {
859                            result.pass = !result.pass;
860                        }
861                        result.actualValue = actualValue;
862                        result.checkFunc = matcherName;
863                        if (!result.pass) {
864                            const assertError = new AssertException(result.message);
865                            currentRunningSpec ? currentRunningSpec.fail = assertError : currentRunningSuite.hookError = assertError;
866                            throw assertError;
867                        }
868                    });
869                };
870            } else {
871                wrappedMatchers[matcherName] = function () {
872                    const result = _this.matchers[matcherName](actualValue, arguments);
873                    if (wrappedMatchers.isNot) {
874                        result.pass = !result.pass;
875                        result.message = LogExpectError.getErrorMsg(matcherName, actualValue, arguments[0], result.message);
876                    }
877                    result.actualValue = actualValue;
878                    result.checkFunc = matcherName;
879                    if (!result.pass) {
880                        const assertError = new AssertException(result.message);
881                        currentRunningSpec ? currentRunningSpec.fail = assertError : currentRunningSuite.hookError = assertError;
882                        throw assertError;
883                    }
884                };
885            }
886        }
887        return wrappedMatchers;
888    }
889
890    apis() {
891        const _this = this;
892        return {
893            expect: function (actualValue) {
894                return _this.expect(actualValue);
895            }
896        };
897    }
898}
899
900class ReportService {
901    constructor(attr) {
902        this.id = attr.id;
903    }
904
905    init(coreContext) {
906        this.coreContext = coreContext;
907        this.specService = this.coreContext.getDefaultService('spec');
908        this.suiteService = this.coreContext.getDefaultService('suite');
909        this.duration = 0;
910    }
911
912    taskStart() {
913        console.info(`${TAG}[start] start run suites`);
914    }
915
916    async suiteStart() {
917        console.info(`${TAG}[suite start]${this.suiteService.getCurrentRunningSuite().description}`);
918    }
919
920    async specStart() {
921        console.info(`${TAG}start running case '${this.specService.currentRunningSpec.description}'`);
922        this.index = this.index + 1;
923        let spec = this.specService.currentRunningSpec;
924        spec.startTime = await SysTestKit.getRealTime();
925    }
926
927    async specDone() {
928        let msg = '';
929        let spec = this.specService.currentRunningSpec;
930        let suite = this.suiteService.currentRunningSuite;
931        spec.duration = await SysTestKit.getRealTime() - spec.startTime;
932        suite.duration += spec.duration;
933        if (spec.error) {
934            this.formatPrint('error', spec.description + ' ; consuming ' + spec.duration + 'ms');
935            this.formatPrint('errorDetail', spec.error);
936        } else if (spec.fail) {
937            this.formatPrint('fail', spec.description + ' ; consuming ' + spec.duration + 'ms');
938            this.formatPrint('failDetail', spec.fail?.message);
939        } else {
940            this.formatPrint('pass', spec.description + ' ; consuming ' + spec.duration + 'ms');
941        }
942        this.formatPrint(this.specService.currentRunningSpec.error, msg);
943    }
944
945    suiteDone() {
946        let suite = this.suiteService.currentRunningSuite;
947        let message = suite.hookError ? `, ${suite.hookError?.message}` : '';
948        console.info(`[suite end] ${suite.description} consuming ${suite.duration} ms${message}`);
949    }
950
951    taskDone() {
952        let msg = '';
953        let summary = this.suiteService.getSummary();
954        msg = 'total cases:' + summary.total + ';failure ' + summary.failure + ',' + 'error ' + summary.error;
955        msg += ',pass ' + summary.pass + '; consuming ' + summary.duration + 'ms';
956        console.info(`${TAG}${msg}`);
957        console.info(`${TAG}[end] run suites end`);
958    }
959
960    incorrectFormat() {
961        if (this.coreContext.getDefaultService('config').filterValid.length !== 0) {
962            this.coreContext.getDefaultService('config').filterValid.forEach(function (item) {
963                console.info(`${TAG}this param ${item} is invalid`);
964            });
965        }
966    }
967
968    formatPrint(type, msg) {
969        switch (type) {
970            case 'pass':
971                console.info(`${TAG}[pass]${msg}`);
972                break;
973            case 'fail':
974                console.info(`${TAG}[fail]${msg}`);
975                break;
976            case 'failDetail':
977                console.info(`${TAG}[failDetail]${msg}`);
978                break;
979            case 'error':
980                console.info(`${TAG}[error]${msg}`);
981                break;
982            case 'errorDetail':
983                console.info(`${TAG}[errorDetail]${msg}`);
984                break;
985        }
986    }
987
988    sleep(numberMillis) {
989        var now = new Date();
990        var exitTime = now.getTime() + numberMillis;
991        while (true) {
992            now = new Date();
993            if (now.getTime() > exitTime) {
994                return;
995            }
996        }
997    }
998}
999
1000export {
1001    SuiteService,
1002    SpecService,
1003    ExpectService,
1004    ReportService
1005};
1006