1/* 2 * Copyright (c) 2025 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 { ServiceAttrIF, ApiIF } from '../../interface'; 17import { NestFilter } from '../config/Filter'; 18import { Core } from '../../core'; 19import { StaticSpec } from './StaticSpec'; 20import { StaticSuite } from './StaticSuite'; 21import { TAG } from '../../Constant'; 22import { 23 processFunc, 24 processAsyncFunc, 25 processFuncWithArgOne, 26 processAsyncFuncWithArgOne, 27 processFuncWithArgTwo, 28 processAsyncFuncWithArgTwo, 29 getFunctionArgumentsCount, 30 checkIsAsyncFunction 31} from '../../util'; 32import { SuiteService } from './SuiteService'; 33import { ConfigService } from '../config/configService'; 34import DataDriver from '../config/DataDriver'; 35import { ItfnType, AnyType } from '../types/common'; 36class SpecService { 37 public id: string; 38 public totalTest: int; 39 public hasError: boolean; 40 public skipSpecNum: int; 41 public isSkipSpec: boolean; 42 public specSkipReason: string; 43 public coreContext: Core | null; 44 public currentRunningSpec: StaticSpec | null; 45 constructor(attr: ServiceAttrIF) { 46 this.id = attr.id; 47 this.totalTest = 0; 48 this.hasError = false; 49 this.skipSpecNum = 0; 50 this.isSkipSpec = false; 51 this.specSkipReason = ''; 52 this.coreContext = null; 53 this.currentRunningSpec = new StaticSpec({ description: '', fi: 0, fn: (): void => {} }); 54 } 55 56 init(coreContext: Core) { 57 this.coreContext = coreContext; 58 } 59 60 setCurrentRunningSpec(spec: StaticSpec | null) { 61 this.currentRunningSpec = spec; 62 } 63 64 setStatus(hasError: boolean) { 65 this.hasError = hasError; 66 } 67 68 getStatus(): boolean { 69 return this.hasError; 70 } 71 72 getTestTotal() { 73 return this.totalTest; 74 } 75 76 getCurrentRunningSpec() { 77 return this.currentRunningSpec; 78 } 79 getSkipSpecNum() { 80 return this.skipSpecNum; 81 } 82 83 initSpecService() { 84 this.isSkipSpec = false; 85 this.specSkipReason = ''; 86 } 87 88 it(desc: string, filter: int, func: ItfnType): void { 89 const coreContext = this.coreContext; 90 if (coreContext !== null) { 91 const suiteService = coreContext.getDefaultService('suite'); 92 const configService = coreContext.getDefaultService('config'); 93 94 if (suiteService !== null && configService !== null) { 95 const sService = suiteService as SuiteService; 96 const cService = configService as ConfigService; 97 let isFilter = new NestFilter().filterNestName( 98 sService.targetSuiteArray, 99 sService.targetSpecArray, 100 sService.suitesStack, 101 desc 102 ); 103 104 if (cService.filterWithNest(desc, filter)) { 105 this.initSpecService(); 106 return; 107 } 108 const curSuite = sService.getCurrentRunningSuite() as StaticSuite; 109 if (cService.filterDesc(curSuite.description, desc, filter) && isFilter && !sService.fullRun) { 110 this.initSpecService(); 111 } else { 112 const funcStr = Type.of(func as object).getLiteral(); 113 const argsCount = getFunctionArgumentsCount(funcStr); 114 115 const isAsync = checkIsAsyncFunction(funcStr); 116 let processedFunc: (() => Promise<void>) | ((paramItem: AnyType) => Promise<void>) = (): Promise<void> => { 117 return Promise.resolve(); 118 }; 119 if (argsCount === 2) { 120 if (isAsync) { 121 const fn = func as (done: () => void, param: AnyType) => Promise<void>; 122 processedFunc = processAsyncFuncWithArgTwo(coreContext, fn); 123 } else { 124 const fn = func as (done: () => void, param: AnyType) => void; 125 processedFunc = processFuncWithArgTwo(coreContext, fn); 126 } 127 } else if (argsCount === 1) { 128 if (isAsync) { 129 const fn = func as (done: () => void) => Promise<void>; 130 processedFunc = processAsyncFuncWithArgOne(coreContext, fn); 131 } else { 132 const fn = func as (done: () => void) => void; 133 processedFunc = processFuncWithArgOne(coreContext, fn); 134 } 135 } else { 136 if (isAsync) { 137 const fn = func as () => Promise<void>; 138 processedFunc = processAsyncFunc(coreContext, fn); 139 } else { 140 const fn = func as () => void; 141 processedFunc = processFunc(coreContext, fn); 142 } 143 } 144 const spec = new StaticSpec({ description: desc, fi: filter, asyncFn: processedFunc }); 145 if (this.isSkipSpec) { 146 spec.isSkip = true; 147 spec.skipReason = this.specSkipReason; 148 } 149 this.initSpecService(); 150 if (cService.runSkipped === 'skipped' && !spec.isSkip) { 151 console.info(`${TAG} runSkipped is skipped , just run xit, don't run it: ${spec.description}`); 152 return; 153 } 154 155 if (curSuite.isSkip && !spec.isSkip) { 156 cService.filterXdescribe.push(curSuite.description); 157 } 158 if (cService.dryRun !== 'true') { 159 const dataDriverMap = coreContext.getServices('dataDriver'); 160 if (dataDriverMap) { 161 const dataDriver = dataDriverMap.get('dataDriver') as DataDriver; 162 const specStress = dataDriver.getSpecStress(desc); 163 for (let i = 1; i < specStress; i++) { 164 this.totalTest++; 165 curSuite.pushSpec(spec); 166 } 167 } 168 let stress = cService.getStress(); // 命令配置压力测试 169 for (let i = 1; i < stress; i++) { 170 this.totalTest++; 171 curSuite.pushSpec(spec); 172 } 173 } 174 175 this.totalTest++; 176 curSuite.pushSpec(spec); 177 } 178 } 179 } 180 } 181 182 xit(desc: string, filter: int, func: ItfnType, reason: string) { 183 const coreContext = this.coreContext; 184 if (coreContext) { 185 const suiteService = coreContext.getDefaultService('suite'); 186 const configService = coreContext.getDefaultService('config'); 187 if (suiteService !== null && configService !== null) { 188 const sService = suiteService as SuiteService; 189 const cService = configService as ConfigService; 190 if (!cService.skipMessage && cService.runSkipped !== 'all') { 191 if (cService.runSkipped != null && cService.runSkipped !== '') { 192 let finalDesc = ''; 193 sService.suitesStack.map((suite: StaticSuite) => { 194 finalDesc = finalDesc + '.' + suite.description; 195 }); 196 finalDesc = (finalDesc + '#' + desc).substring(2); 197 if (cService.checkIfSpecInSkipRun(finalDesc)) { 198 console.info(`${TAG} runSkipped spec: ${desc}`); 199 } else { 200 console.info( 201 reason == null ? `${TAG} skip spec: ${desc}` : `${TAG} skip spec: ${desc}, and the reason is ${reason}` 202 ); 203 return; 204 } 205 } else { 206 console.info( 207 reason == null ? `${TAG} skip spec: ${desc}` : `${TAG} skip spec: ${desc}, and the reason is ${reason}` 208 ); 209 return; 210 } 211 } 212 this.skipSpecNum++; 213 this.isSkipSpec = true; 214 this.specSkipReason = reason; 215 this.it(desc, filter, func); 216 } 217 } 218 } 219 220 apis(): ApiIF { 221 const _this = this; 222 return { 223 name: 'SpecService', 224 it: (desc: string, filter: int, func: ItfnType) => { 225 return _this.it(desc, filter, func); 226 }, 227 xit: (desc: string, filter: int, func: ItfnType, reason: string) => { 228 return _this.xit(desc, filter, func, reason); 229 }, 230 }; 231 } 232} 233 234export { SpecService }; 235