1/* 2 * Copyright (c) 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 16 17const ts = require('typescript'); 18const path = require('path'); 19const fs = require('fs') 20 21 22const ignoreCaseFilePath= path.join(__dirname, "ignorecase.json") 23const compResults = {"detail":{}, 'failNum':0, "passedNum":0} 24let consoleDetail = false; 25let ignoreList = []; 26let failTestCaseList = []; 27let genResultFile = false; 28let arktsVersion = '1.1'; 29// Traverse the directory to find all test cases 30function getAllETSFiles(filePath) { 31 let allFilePaths = []; 32 if (fs.existsSync(filePath)) { 33 const files = fs.readdirSync(filePath); 34 for (let i = 0; i < files.length; i++) { 35 let file = files[i]; // File name (excluding file path) 36 let currentFilePath = filePath + '/' + file; 37 let stats = fs.lstatSync(currentFilePath); 38 if(ignoreList.includes(currentFilePath)){ 39 continue 40 } 41 if (stats.isDirectory()) { 42 allFilePaths = allFilePaths.concat(getAllETSFiles(currentFilePath)); 43 } else { 44 var index= currentFilePath.lastIndexOf("."); 45 var ext = currentFilePath.substring(index+1); 46 if (ext === 'ets' || ext === 'ts') { 47 allFilePaths.push(currentFilePath); 48 runComp(currentFilePath, file) 49 } 50 } 51 } 52 } else { 53 console.warn(`The specified directory ${filePath} Non-existent!`); 54 } 55 return allFilePaths; 56} 57 58function runComp(currentFilePath, file){ 59 const result = runLinter(currentFilePath) 60 let jsonFile = currentFilePath.replace('.ets', '.json'); 61 jsonFile = jsonFile.replace('.ts', '.json'); 62 const checkfile = fs.existsSync(jsonFile); 63 if(checkfile){ 64 loadPares(jsonFile, result, currentFilePath, file) 65 }else{ 66 if(!currentFilePath.includes("-dependencie.ets")){ 67 console.log(`Test cases ${currentFilePath} expected results are not added`) 68 } 69 } 70} 71 72function forceUpdateExpected(expect, reality, jsonFile) { 73 let updateArray = []; 74 for (let i = 0; i < reality.length; i++) { 75 const realErrorItem = reality[i]; 76 const { line, character } = realErrorItem.file.getLineAndCharacterOfPosition(realErrorItem.start); 77 const realLine = { 'line': line + 1, 'character': character + 1 }; 78 const realMessageText = typeof (realErrorItem.messageText) === 'string' ? realErrorItem.messageText : realErrorItem.messageText.messageText; 79 let data = { 80 messageText: realMessageText, 81 expectLineAndCharacter: realLine 82 }; 83 updateArray.push(data); 84 } 85 if (arktsVersion === '1.0') { 86 expect.arktsVersion_1_0 = updateArray; 87 } else { 88 expect.arktsVersion_1_1 = updateArray; 89 } 90 let s = JSON.stringify(expect, null, 2); 91 fs.writeFileSync(jsonFile, s); 92} 93 94// Compare the results with expectations and count the success and failure situations 95function loadPares(jsonFile, result, currentFilePath, file){ 96 const dirName = path.dirname(currentFilePath) 97 let rules = "" 98 if (dirName.includes("\\")){ 99 rules = currentFilePath.split("\\")[currentFilePath.split("\\").length - 2] 100 }else{ 101 rules = currentFilePath.split("/")[currentFilePath.split("/").length - 2] 102 } 103 const testCaseFileName = file 104 dataStr = fs.readFileSync(jsonFile, "utf-8") 105 const expect = JSON.parse(dataStr) 106 // if need update expected files, insert forceUpdateExpected(expect, result, jsonFile) here. 107 let expectByVersion = arktsVersion === '1.0' ? expect.arktsVersion_1_0 : expect.arktsVersion_1_1; 108 if (expectByVersion === undefined) { 109 expectByVersion = []; 110 } 111 112 const compResult = compareResult(expectByVersion, result); 113 compResult["testCaseName"] = testCaseFileName 114 if (Object.prototype.hasOwnProperty.call(compResults.detail, rules)) { 115 compResults["detail"][rules]["detail"].push(compResult) 116 compResults["detail"][rules]["testCaseNum"] += 1 117 }else{ 118 compResults["detail"][rules] = {"detail":[compResult], "testCaseNum": 1, "failNum": 0, "passedNum": 0} 119 } 120 if(compResult.status){ 121 compResults["passedNum"] += 1 122 compResults["detail"][rules]["passedNum"] += 1 123 }else{ 124 failTestCaseList.push(currentFilePath) 125 if(consoleDetail){ 126 console.log(`Test cases ${currentFilePath} Failed!`) 127 for(let compDetail of compResult.detail){ 128 if(!compDetail.compResult){ 129 console.log(`==> Expect the error in Line ${compDetail.expectLineAndCharacter.line} The ${compDetail.expectLineAndCharacter.character} character. Expect exception rules:${compDetail.expectMessageText} Actual error line ${compDetail.realLineAndCharacter.line} The ${compDetail.realLineAndCharacter.character} character. Actual exception rules:${compDetail.realMessageText} Comparison Result:Fail!`) 130 } 131 } 132 } 133 compResults["failNum"] += 1 134 compResults['detail'][rules]["failNum"] += 1 135 } 136} 137 138 139// initial configuration 140options = ts.readConfigFile('tsconfig.json', ts.sys.readFile).config.compilerOptions; 141const allPath = ['*']; 142Object.assign(options, { 143 'emitNodeModulesFiles': true, 144 'importsNotUsedAsValues': ts.ImportsNotUsedAsValues.Preserve, 145 'module': ts.ModuleKind.ES2020, 146 'moduleResolution': ts.ModuleResolutionKind.NodeJs, 147 'noEmit': true, 148 'target': ts.ScriptTarget.ES2021, 149 'baseUrl': "/", 150 'paths': { 151 '*': allPath 152 }, 153 'lib': [ 154 'lib.es2021.d.ts' 155 ], 156 'types': [], 157 'etsLoaderPath': 'null_sdkPath', 158}); 159 160// Calling the runlinter interface 161function runLinter(rootName) { 162 nonStrictCheckParam = { 163 allowJS: true, 164 checkJs: false 165 }; 166 Object.assign(options, nonStrictCheckParam); 167 builderProgram = ts.createIncrementalProgramForArkTs({ 168 rootNames: [path.join(process.cwd(), rootName)], 169 options: options, 170 }); 171 172 let result = arktsVersion === '1.0' ? ts.ArkTSLinter_1_0.runArkTSLinter(builderProgram) : ts.ArkTSLinter_1_1.runArkTSLinter(builderProgram); 173 return result; 174} 175 176// Compare the difference between the expected value and the actual return value of the runlinter to determine if the test has passed 177function compareResult(expect, reality){ 178 let isPass = true 179 const itemPassList = new Array() 180 if(reality.length == 0){ 181 if(expect.length == 0){ 182 // pass 183 isPass = true 184 }else{ 185 isPass = false 186 for(let expectInfo of expect){ 187 const compInfo = { 188 'compResult':false, 189 'realLineAndCharacter':{"line": null,"character": null}, 190 'realMessageText':null, 191 'expectLineAndCharacter':{"line": expectInfo.expectLineAndCharacter.line,"character": expectInfo.expectLineAndCharacter.character}, 192 'expectMessageText':expectInfo.messageText, 193 } 194 itemPassList.push(compInfo) 195 } 196 } 197 }else{ 198 if(expect.length == 0){ 199 isPass = false 200 for(let realityInfo of reality){ 201 const { line, character } = realityInfo.file.getLineAndCharacterOfPosition(realityInfo.start) 202 const compInfo = { 203 'compResult':false, 204 'realLineAndCharacter':{"line": line + 1,"character": character + 1}, 205 'realMessageText':realityInfo.messageText, 206 'expectLineAndCharacter':{"line": null,"character": null}, 207 'expectMessageText':null, 208 } 209 itemPassList.push(compInfo) 210 } 211 }else{ 212 if( reality.length > expect.length){ 213 isPass = false 214 for(let i=0; i<reality.length; i++){ 215 const realErrorItem = reality[i] 216 const { line, character } = realErrorItem.file.getLineAndCharacterOfPosition(realErrorItem.start) 217 const realLine = {"line": line + 1,"character": character + 1} 218 const realMessageText = typeof (realErrorItem.messageText) === 'string' ? realErrorItem.messageText : realErrorItem.messageText.messageText; 219 let expectMessageText = null 220 let compResult = false 221 let expectLineAndCharacter = {"line": null,"character": null} 222 if( expect.length < i+1){ 223 compResult = false 224 }else{ 225 expectErrorItem = expect[i] 226 expectLineAndCharacter = {"line": expectErrorItem.expectLineAndCharacter.line,"character": expectErrorItem.expectLineAndCharacter.character} 227 expectMessageText = expectErrorItem.messageText 228 if ((expectErrorItem.expectLineAndCharacter.line === realLine.line && expectErrorItem.expectLineAndCharacter.character === realLine.character) && 229 realMessageText === expectMessageText) { 230 compResult = true 231 } 232 } 233 const compInfo = { 234 'compResult':compResult, 235 'realLineAndCharacter':realLine, 236 'realMessageText':realMessageText, 237 'expectLineAndCharacter':expectLineAndCharacter, 238 'expectMessageText':expectMessageText, 239 } 240 itemPassList.push(compInfo) 241 } 242 }else if(reality.length < expect.length){ 243 isPass = false 244 for(let i=0; i<expect.length; i++){ 245 const expectErrorItem = expect[i] 246 const expectMessageText = expectErrorItem.messageText 247 let expectLineAndCharacter = {"line": expectErrorItem.expectLineAndCharacter.line,"character": expectErrorItem.expectLineAndCharacter.character} 248 let realLine = {"line": null,"character": null} 249 let realMessageText = null 250 let compResult = false 251 if( reality.length < i+1){ 252 compResult = false 253 }else{ 254 const realErrorItem = reality[i] 255 const { line, character } = realErrorItem.file.getLineAndCharacterOfPosition(realErrorItem.start) 256 realLine = {"line": line + 1,"character": character + 1} 257 realMessageText = typeof (realErrorItem.messageText) === 'string' ? realErrorItem.messageText : realErrorItem.messageText.messageText; 258 if ((expectErrorItem.expectLineAndCharacter.line === realLine.line && expectErrorItem.expectLineAndCharacter.character === realLine.character) && 259 realMessageText === expectMessageText) { 260 compResult = true 261 } 262 } 263 const compInfo = { 264 'compResult':compResult, 265 'realLineAndCharacter':realLine, 266 'realMessageText':realMessageText, 267 'expectLineAndCharacter':expectLineAndCharacter, 268 'expectMessageText':expectMessageText, 269 } 270 itemPassList.push(compInfo) 271 } 272 }else{ 273 for(let i =0;i<reality.length;i++){ 274 const realErrorItem = reality[i] 275 const expectErrorItem = expect[i] 276 const expectMessageText = expectErrorItem.messageText 277 let expectLineAndCharacter = {"line": expectErrorItem.expectLineAndCharacter.line,"character": expectErrorItem.expectLineAndCharacter.character} 278 const { line, character } = realErrorItem.file.getLineAndCharacterOfPosition(realErrorItem.start) 279 const realLine = {"line": line + 1,"character": character + 1} 280 const realMessageText = typeof (realErrorItem.messageText) === 'string' ? realErrorItem.messageText : realErrorItem.messageText.messageText; 281 let compInfo = null; compResult = false 282 if ((expectErrorItem.expectLineAndCharacter.line === realLine.line && expectErrorItem.expectLineAndCharacter.character === realLine.character) && 283 realMessageText === expectMessageText) { 284 compResult = true 285 }else{ 286 isPass = false 287 } 288 compInfo = { 289 'compResult':compResult, 290 'realLineAndCharacter':realLine, 291 'realMessageText':realMessageText, 292 'expectLineAndCharacter':expectLineAndCharacter, 293 'expectMessageText':expectMessageText, 294 } 295 itemPassList.push(compInfo) 296 } 297 } 298 } 299 } 300 return {"status":isPass, "detail":itemPassList} 301} 302 303// output result file 304function writeResult(result){ 305 const dir = path.join(__dirname, "test_results") 306 if (!fs.existsSync(dir)) { 307 fs.mkdirSync(dir) 308 } 309 fs.writeFileSync(path.join(dir, "test_result.json"), JSON.stringify(result, null, 4)) 310} 311 312 313function run(){ 314 let interval = 0, startTime = process.uptime()*1000, endTime = startTime; 315 const pathParam = getParam() 316 let filePath = 'testcase' 317 if(pathParam){ 318 filePath = pathParam 319 } 320 let ignoreCaseConfigList = [] 321 if(fs.existsSync(ignoreCaseFilePath)){ 322 ignoreCaseConfigList = JSON.parse(fs.readFileSync(ignoreCaseFilePath)).ignoreCase 323 } 324 325 ignoreList = ignoreList.concat(ignoreCaseConfigList) 326 let filePathStats = fs.lstatSync(filePath) 327 if(!filePathStats.isDirectory()){ 328 runComp(filePath, path.basename(filePath)) 329 }else{ 330 getAllETSFiles(filePath) 331 } 332 endTime = process.uptime()*1000 333 interval = (endTime - startTime); 334 const compReportDetail = {"compDetail": compResults, "compTime": interval, "failNum": compResults.failNum, "passedNum": compResults.passedNum} 335 const testCaseSum = compReportDetail.failNum + compReportDetail.passedNum 336 compReportDetail["testCaseSum"] = testCaseSum 337 console.log(`Total number of test cases:${testCaseSum} Number of use cases passed:${compResults.passedNum} The number of use cases that failed:${compResults.failNum} Total running time:${JSON.stringify(interval).split(".")[0]}ms`) 338 if(genResultFile){ 339 writeResult(compReportDetail) 340 } 341 if (failTestCaseList.length > 0){ 342 console.log("Failed test cases:") 343 for(let testCase of failTestCaseList){ 344 console.log(testCase) 345 } 346 } 347 if(ignoreList.length>0){ 348 console.log("Ignored test cases:") 349 for(let ignoreCase of ignoreList){ 350 console.log(ignoreCase) 351 } 352 } 353 if(compReportDetail.failNum){ 354 process.exit(1) 355 } 356} 357 358// get parameters 359function getParam(){ 360 let pathArg = null 361 for(let key of process.argv){ 362 if(key.includes("-P:")){ 363 pathArg = key.replace("-P:", "") 364 } 365 if(key === "--detail" || key === "-D"){ 366 consoleDetail = true 367 } 368 if(key === "-e"){ 369 genResultFile = true 370 } 371 if(key.includes("--ignore-list:")){ 372 let ignoreStr = key.replace("--ignore-list:", "") 373 ignoreList = ignoreStr.split(",") 374 } 375 if (key === '-v1.0') { 376 arktsVersion = '1.0'; 377 } 378 if (key === '-v1.1') { 379 arktsVersion = '1.1'; 380 } 381 } 382 return pathArg 383} 384 385// execute 386run() 387