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 = path.join(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') || ignoreList.includes(currentFilePath)) { 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 'compatibleSdkVersion': 12, 159 'compatibleSdkVersionStage': 'beta3' 160}); 161 162// Calling the runlinter interface 163function runLinter(rootName) { 164 nonStrictCheckParam = { 165 allowJS: true, 166 checkJs: false 167 }; 168 Object.assign(options, nonStrictCheckParam); 169 builderProgram = ts.createIncrementalProgramForArkTs({ 170 rootNames: [path.join(process.cwd(), rootName)], 171 options: options, 172 }); 173 174 let result = arktsVersion === '1.0' ? ts.ArkTSLinter_1_0.runArkTSLinter(builderProgram) : ts.ArkTSLinter_1_1.runArkTSLinter(builderProgram); 175 return result; 176} 177 178// Compare the difference between the expected value and the actual return value of the runlinter to determine if the test has passed 179function compareResult(expect, reality){ 180 let isPass = true 181 const itemPassList = new Array() 182 if(reality.length == 0){ 183 if(expect.length == 0){ 184 // pass 185 isPass = true 186 }else{ 187 isPass = false 188 for(let expectInfo of expect){ 189 const compInfo = { 190 'compResult':false, 191 'realLineAndCharacter':{"line": null,"character": null}, 192 'realMessageText':null, 193 'expectLineAndCharacter':{"line": expectInfo.expectLineAndCharacter.line,"character": expectInfo.expectLineAndCharacter.character}, 194 'expectMessageText':expectInfo.messageText, 195 } 196 itemPassList.push(compInfo) 197 } 198 } 199 }else{ 200 if(expect.length == 0){ 201 isPass = false 202 for(let realityInfo of reality){ 203 const { line, character } = realityInfo.file.getLineAndCharacterOfPosition(realityInfo.start) 204 const compInfo = { 205 'compResult':false, 206 'realLineAndCharacter':{"line": line + 1,"character": character + 1}, 207 'realMessageText':realityInfo.messageText, 208 'expectLineAndCharacter':{"line": null,"character": null}, 209 'expectMessageText':null, 210 } 211 itemPassList.push(compInfo) 212 } 213 }else{ 214 if( reality.length > expect.length){ 215 isPass = false 216 for(let i=0; i<reality.length; i++){ 217 const realErrorItem = reality[i] 218 const { line, character } = realErrorItem.file.getLineAndCharacterOfPosition(realErrorItem.start) 219 const realLine = {"line": line + 1,"character": character + 1} 220 const realMessageText = typeof (realErrorItem.messageText) === 'string' ? realErrorItem.messageText : realErrorItem.messageText.messageText; 221 let expectMessageText = null 222 let compResult = false 223 let expectLineAndCharacter = {"line": null,"character": null} 224 if( expect.length < i+1){ 225 compResult = false 226 }else{ 227 expectErrorItem = expect[i] 228 expectLineAndCharacter = {"line": expectErrorItem.expectLineAndCharacter.line,"character": expectErrorItem.expectLineAndCharacter.character} 229 expectMessageText = expectErrorItem.messageText 230 if ((expectErrorItem.expectLineAndCharacter.line === realLine.line && expectErrorItem.expectLineAndCharacter.character === realLine.character) && 231 realMessageText === expectMessageText) { 232 compResult = true 233 } 234 } 235 const compInfo = { 236 'compResult':compResult, 237 'realLineAndCharacter':realLine, 238 'realMessageText':realMessageText, 239 'expectLineAndCharacter':expectLineAndCharacter, 240 'expectMessageText':expectMessageText, 241 } 242 itemPassList.push(compInfo) 243 } 244 }else if(reality.length < expect.length){ 245 isPass = false 246 for(let i=0; i<expect.length; i++){ 247 const expectErrorItem = expect[i] 248 const expectMessageText = expectErrorItem.messageText 249 let expectLineAndCharacter = {"line": expectErrorItem.expectLineAndCharacter.line,"character": expectErrorItem.expectLineAndCharacter.character} 250 let realLine = {"line": null,"character": null} 251 let realMessageText = null 252 let compResult = false 253 if( reality.length < i+1){ 254 compResult = false 255 }else{ 256 const realErrorItem = reality[i] 257 const { line, character } = realErrorItem.file.getLineAndCharacterOfPosition(realErrorItem.start) 258 realLine = {"line": line + 1,"character": character + 1} 259 realMessageText = typeof (realErrorItem.messageText) === 'string' ? realErrorItem.messageText : realErrorItem.messageText.messageText; 260 if ((expectErrorItem.expectLineAndCharacter.line === realLine.line && expectErrorItem.expectLineAndCharacter.character === realLine.character) && 261 realMessageText === expectMessageText) { 262 compResult = true 263 } 264 } 265 const compInfo = { 266 'compResult':compResult, 267 'realLineAndCharacter':realLine, 268 'realMessageText':realMessageText, 269 'expectLineAndCharacter':expectLineAndCharacter, 270 'expectMessageText':expectMessageText, 271 } 272 itemPassList.push(compInfo) 273 } 274 }else{ 275 for(let i =0;i<reality.length;i++){ 276 const realErrorItem = reality[i] 277 const expectErrorItem = expect[i] 278 const expectMessageText = expectErrorItem.messageText 279 let expectLineAndCharacter = {"line": expectErrorItem.expectLineAndCharacter.line,"character": expectErrorItem.expectLineAndCharacter.character} 280 const { line, character } = realErrorItem.file.getLineAndCharacterOfPosition(realErrorItem.start) 281 const realLine = {"line": line + 1,"character": character + 1} 282 const realMessageText = typeof (realErrorItem.messageText) === 'string' ? realErrorItem.messageText : realErrorItem.messageText.messageText; 283 let compInfo = null; compResult = false 284 if ((expectErrorItem.expectLineAndCharacter.line === realLine.line && expectErrorItem.expectLineAndCharacter.character === realLine.character) && 285 realMessageText === expectMessageText) { 286 compResult = true 287 }else{ 288 isPass = false 289 } 290 compInfo = { 291 'compResult':compResult, 292 'realLineAndCharacter':realLine, 293 'realMessageText':realMessageText, 294 'expectLineAndCharacter':expectLineAndCharacter, 295 'expectMessageText':expectMessageText, 296 } 297 itemPassList.push(compInfo) 298 } 299 } 300 } 301 } 302 return {"status":isPass, "detail":itemPassList} 303} 304 305// output result file 306function writeResult(result){ 307 const dir = path.join(__dirname, "test_results") 308 if (!fs.existsSync(dir)) { 309 fs.mkdirSync(dir) 310 } 311 fs.writeFileSync(path.join(dir, "test_result.json"), JSON.stringify(result, null, 4)) 312} 313 314 315function run(){ 316 let interval = 0, startTime = process.uptime()*1000, endTime = startTime; 317 const pathParam = getParam() 318 let filePath = 'testcase' 319 if(pathParam){ 320 filePath = pathParam 321 } 322 let ignoreCaseConfigList = [] 323 if(fs.existsSync(ignoreCaseFilePath)){ 324 ignoreCaseConfigList = JSON.parse(fs.readFileSync(ignoreCaseFilePath)).ignoreCase 325 } 326 327 ignoreList = ignoreList.concat(ignoreCaseConfigList).map(x => path.normalize(x)); 328 329 let filePathStats = fs.lstatSync(filePath) 330 if(!filePathStats.isDirectory()){ 331 runComp(filePath, path.basename(filePath)) 332 }else{ 333 getAllETSFiles(filePath) 334 } 335 endTime = process.uptime()*1000 336 interval = (endTime - startTime); 337 const compReportDetail = {"compDetail": compResults, "compTime": interval, "failNum": compResults.failNum, "passedNum": compResults.passedNum} 338 const testCaseSum = compReportDetail.failNum + compReportDetail.passedNum 339 compReportDetail["testCaseSum"] = testCaseSum 340 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. ArkTSVersion: ${arktsVersion}`) 341 if(genResultFile){ 342 writeResult(compReportDetail) 343 } 344 if (failTestCaseList.length > 0){ 345 console.log("Failed test cases:") 346 for(let testCase of failTestCaseList){ 347 console.log(testCase) 348 } 349 } 350 if(ignoreList.length>0){ 351 console.log("Ignored test cases:") 352 for(let ignoreCase of ignoreList){ 353 console.log(ignoreCase) 354 } 355 } 356 if(compReportDetail.failNum){ 357 process.exit(1) 358 } 359} 360 361// get parameters 362function getParam(){ 363 let pathArg = null 364 for(let key of process.argv){ 365 if(key.includes("-P:")){ 366 pathArg = key.replace("-P:", "") 367 } 368 if(key === "--detail" || key === "-D"){ 369 consoleDetail = true 370 } 371 if(key === "-e"){ 372 genResultFile = true 373 } 374 if(key.includes("--ignore-list:")){ 375 let ignoreStr = key.replace("--ignore-list:", "") 376 ignoreList = ignoreStr.split(",") 377 } 378 if (key === '-v1.0') { 379 arktsVersion = '1.0'; 380 } 381 if (key === '-v1.1') { 382 arktsVersion = '1.1'; 383 } 384 } 385 return pathArg 386} 387 388// execute 389run() 390