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