1/* 2* Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development 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 util = require('util'); 17import { replaceAll } from "../common/tool"; 18import { FuncInfo, GenInfo, InterfaceList, ParamObj, TypeList } from "./datatype"; 19import { getInterfaceBody, getTypeBody, transTskey2Ckey } from './gendts'; 20import { testAbilityFuncTemplate } from "../template/func_template"; 21import { Logger } from '../common/log'; 22import { cpp2DtsKey, dts2TestValue } from '../template/dtscpp/dts2cpp_key'; 23import * as path from 'path'; 24import * as fs from 'fs'; 25import { testFirstGenTemplate } from '../template/dtscpp/dtscpp_testfirstgen_template'; 26const INTVALUE = 5; 27const FLOATVALUE = 2.5; 28 29export function generateFuncTestCase(funcInfo: FuncInfo, rawFileName: string, typeList: TypeList[], interfaceList: InterfaceList[]) { 30 let { funcParamUse, funcParamDefine, funcInfoParams } = genInitTestfunc(funcInfo, typeList, interfaceList); 31 // 去除调用参数的最后一个',' 32 let index = funcParamUse.lastIndexOf(', '); 33 funcParamUse = funcParamUse.substring(0, index); 34 let callFunc = ''; 35 let hilogContent = ''; 36 // 调用函数 37 Logger.getInstance().info("test funcInfo:" + JSON.stringify(funcInfo)); 38 if (getJsType(funcInfo.retType) !== 'void') { 39 callFunc = util.format('let result: %s = testNapi.%s(%s)\n ', getJsType(funcInfo.retType), funcInfo.name, funcParamUse); 40 // 加 hilog 打印 41 hilogContent = util.format('hilog.info(0x0000, "testTag", "Test NAPI %s: ", JSON.stringify(result));\n ', funcInfo.name); 42 hilogContent += util.format('Logger.getInstance().info("testTag", "Test NAPI %s: ", JSON.stringify(result));\n ', funcInfo.name); 43 } else { 44 callFunc = util.format('testNapi.%s(%s)\n ', funcInfo.name, funcParamUse); 45 } 46 let funcTestReplace = funcParamDefine + callFunc + hilogContent; 47 // 替换test_case_name 48 let funcTestContent = replaceAll(testAbilityFuncTemplate, '[func_direct_testCase]', funcTestReplace); 49 funcTestContent = replaceAll(funcTestContent, '[test_case_name]', funcInfo.name); 50 funcTestContent = replaceAll(funcTestContent, '[file_introduce_replace]', rawFileName); 51 funcTestContent = replaceAll(funcTestContent, '[func_introduce_replace]', funcInfo.name); 52 funcTestContent = replaceAll(funcTestContent, '[input_introduce_replace]', funcInfoParams === '' ? 'void' : funcInfoParams); 53 funcTestContent = replaceAll(funcTestContent, '[func_return_replace]', funcInfo.retType); 54 55 return funcTestContent; 56} 57 58export function genInitTestfunc(funcInfo: FuncInfo, typeList: TypeList[], interfaceList: InterfaceList[]) { 59 let funcParamDefine = ''; 60 let funcParamUse = ''; 61 let funcInfoParams = ''; 62 let funcInfoParamTemp = '[paramName]: [paramType]; '; 63 // 判断函数有几个参数,依次给参数赋值 64 for (let i = 0; i < funcInfo.params.length; i++) { 65 let funcInfoParamReplace = replaceAll(funcInfoParamTemp, '[paramName]', funcInfo.params[i].name); 66 funcInfoParamReplace = replaceAll(funcInfoParamReplace, '[paramType]', funcInfo.params[i].type); 67 funcInfoParams += funcInfoParamReplace; 68 let testType = getTestType(funcInfo.params[i].type); 69 if (testType === 'int') { 70 funcParamDefine += util.format('let %s = %s\n ', funcInfo.params[i].name, INTVALUE); 71 funcParamUse += funcInfo.params[i].name + ', '; 72 } else if (testType === 'float') { 73 funcParamDefine += util.format('let %s = %s\n ', funcInfo.params[i].name, FLOATVALUE); 74 funcParamUse += funcInfo.params[i].name + ', '; 75 } else if (testType === 'bool') { 76 funcParamDefine += util.format('let %s = %s\n ', funcInfo.params[i].name, true); 77 funcParamUse += funcInfo.params[i].name + ', '; 78 } else if (testType === 'string') { 79 funcParamDefine += util.format('let %s = "%s"\n ', funcInfo.params[i].name, 'hello'); 80 funcParamUse += funcInfo.params[i].name + ', '; 81 } else if (getTypeBody(testType, typeList)) { 82 let typeDefineRes = getTypeDefine(testType, funcParamDefine, funcInfo, i, funcParamUse, typeList); 83 funcParamDefine = typeDefineRes[0]; 84 funcParamUse = typeDefineRes[1]; 85 } else if (getInterfaceBody(testType, interfaceList)) { 86 let interfaceDefineRes = getInterfaceDefine(testType, funcParamDefine, funcInfo, i, funcParamUse, interfaceList); 87 funcParamDefine = interfaceDefineRes[0]; 88 funcParamUse = interfaceDefineRes[1]; 89 } 90 } 91 return { funcParamUse, funcParamDefine, funcInfoParams }; 92} 93 94export function getTypeDefine(testType: string, funcParamDefine: string, funcInfo: FuncInfo, i: number, funcParamUse: string, typeList: TypeList[]) { 95 let cTypeDefine = getTypeBody(testType, typeList); 96 let typeDefType = transTskey2Ckey(cTypeDefine as string); 97 // genType 98 if (typeDefType === 'number') { 99 funcParamDefine += util.format('let %s = %s\n ', funcInfo.params[i].name, INTVALUE); 100 funcParamUse += funcInfo.params[i].name + ', '; 101 } else if (typeDefType === 'string') { 102 funcParamDefine += util.format('let %s = "%s"\n ', funcInfo.params[i].name, 'hello'); 103 funcParamUse += funcInfo.params[i].name + ', '; 104 } else if (typeDefType === 'boolean') { 105 funcParamDefine += util.format('let %s = %s\n ', funcInfo.params[i].name, false); 106 funcParamUse += funcInfo.params[i].name + ', '; 107 } 108 return [funcParamDefine, funcParamUse]; 109} 110 111export function genInterFuncParamStr(param: ParamObj[]) { 112 let paramsStr = ''; 113 for(let i = 0; i < param.length; i++) { 114 let rawType = transTskey2Ckey(param[i].type); 115 paramsStr += param[i].name + ': ' + rawType; 116 if (i !== param.length - 1) { 117 paramsStr += ', '; 118 } 119 } 120 return paramsStr; 121} 122 123export function getInterfaceDefine(testType: string, funcParamDefine: string, funcInfo: FuncInfo, i: number, funcParamUse: string, interfaceList: InterfaceList[]) { 124 let objValue = getInterfaceBody(testType, interfaceList); 125 let objTestData = 'let %s:testNapi.%s = { '; 126 let interParams = objValue!.params; 127 let interFuncs = objValue!.funcs; 128 // 成员变量赋值 129 for (let j = 0; j < interParams.length; j++) { 130 let paramType = transTskey2Ckey(interParams[j].type); 131 if (paramType === 'number') { 132 objTestData += util.format('%s: %s, ', interParams[j].name, INTVALUE); 133 } else if (paramType === 'string') { 134 objTestData += util.format('%s: "%s", ', interParams[j].name, 'hello'); 135 } else if (paramType === 'boolean') { 136 objTestData += util.format('%s: %s, ', interParams[j].name, true); 137 } else if (getInterfaceBody(paramType, interfaceList)) { 138 objTestData += util.format('%s: null, ', interParams[j].name); 139 } 140 } 141 142 // 成员函数 143 for (let j = 0; j < interFuncs.length; j++) { 144 let paramStr = genInterFuncParamStr(interFuncs[j].parameters); 145 let initInterFunc = util.format('%s:(%s) => ',interFuncs[j].name, paramStr); 146 let interFuncRetType = transTskey2Ckey(interFuncs[j].returns); 147 if (interFuncRetType === 'void') { 148 let interfaceFuncRetDefine = initInterFunc + '{}'; 149 objTestData += util.format('%s, ', interfaceFuncRetDefine); 150 } else if (interFuncRetType === 'string') { 151 let interfaceFuncRetDefine = initInterFunc + '{return ""}'; 152 objTestData += util.format('%s, ', interfaceFuncRetDefine); 153 } else if (interFuncRetType === 'boolean') { 154 let interfaceFuncRetDefine = initInterFunc + '{ return true }'; 155 objTestData += util.format('%s, ', interfaceFuncRetDefine); 156 } else if (interFuncRetType === 'number') { 157 let interfaceFuncRetDefine = initInterFunc + '{ return 0 }'; 158 objTestData += util.format('%s, ', interfaceFuncRetDefine); 159 } 160 } 161 162 // 去除调用参数的最后一个',' 163 let index = objTestData.lastIndexOf(', '); 164 if (index !== -1) { 165 objTestData = objTestData.substring(0, index) + ' }\n '; 166 } else { 167 objTestData = 'let %s:testNapi.%s = null;\n '; 168 } 169 funcParamDefine += util.format(objTestData, funcInfo.params[i].name, testType); 170 funcParamUse += funcInfo.params[i].name + ', '; 171 return [funcParamDefine, funcParamUse]; 172} 173 174export function getTestType(type: string) { 175 // 去掉const 和 * 176 type = replaceAll(type,'const', ''); 177 type = replaceAll(type, '*', '').trim(); 178 if (type === 'uint32_t' || type === 'int32_t' || type === 'int16_t' || 179 type === 'int64_t' || type === 'int' || type === 'size_t') { 180 return 'int'; 181 } else if (type === 'double_t' || type === 'double' || type === 'float') { 182 return 'float'; 183 } else if (type === 'bool') { 184 return 'bool'; 185 } else if (type === 'std::string' || type.indexOf('char') >= 0) { 186 return 'string'; 187 } 188 return type; 189} 190 191export function getJsType(type: string) { 192 type = replaceAll(type,'const', ''); 193 type = replaceAll(type, '*', '').trim(); 194 for(const keyItem of cpp2DtsKey) { 195 for(const str of keyItem.keys) { 196 if (type.includes(str)) { 197 return transTskey2Ckey(type); 198 } 199 } 200 } 201 return 'testNapi.' + type.replace('*', '').trim(); 202} 203 204// ----------------------- gentest ------------------------ 205// 用H文件为源文件生成Ability.test.ets文件 206export function genAbilitytestFile(rootInfo: GenInfo, out: string) { 207 if (out === undefined || out === null || out.trim() === '') { 208 out = path.dirname(rootInfo.rawFilePath); 209 } 210 let testContent = '' 211 if (rootInfo.parseObj && rootInfo.parseObj.funcs) { 212 rootInfo.parseObj.funcs.forEach(funcInfo => { 213 let callFunc = ''; // 调用函数内容 214 let hilogContent = ''; // hilog内容 215 let funcParamDefine = ''; // 函数参数定义并初始化 216 let funcParamUse = ''; // 函数参数使用 217 let funcInfoParams = ''; // 注释 218 let funcInfoParamTemp = '[paramName]: [paramType]; '; 219 // 遍历方法参数,给参数赋初始值,生成注释和参数使用内容 220 // 1. 先将所有type转换为ts的type 221 for (let i = 0; i < funcInfo.parameters.length; ++i) { 222 // 注释 223 let funcInfoParamReplace = replaceAll(funcInfoParamTemp, '[paramName]', 224 funcInfo.parameters[i].name); 225 funcInfoParamReplace = replaceAll(funcInfoParamReplace, '[paramType]', 226 funcInfo.parameters[i].type); 227 funcInfoParams += funcInfoParamReplace; 228 // 参数定义并初始化 229 const param = funcInfo.parameters[i]; 230 let paramType = transTskey2Ckey(param.type); 231 let testValue = 'undefined; // Please give an any value.'; // any类型咋赋值? 232 dts2TestValue.forEach(item => { 233 if (item.key === paramType) { 234 testValue = item.value; 235 } 236 }) 237 funcParamDefine += util.format('let %s: %s = %s;\n ', 238 funcInfo.parameters[i].name, paramType, testValue); 239 funcParamUse += funcInfo.parameters[i].name + ', '; 240 // 如果是最后一个参数,去掉最后的逗号和空格 241 if (funcInfo.parameters.length === i + 1) { 242 funcParamUse = funcParamUse.slice(0, -2); // 去掉最后一个逗号和空格 243 } 244 } 245 // 返回值,如果本来就是ts类型就不用替换了:如Promise<unknown>,就不用替换了 246 let tsPromiseReg = /Promise<([^>]+)>/g; 247 let returnType = tsPromiseReg.exec(funcInfo.returns)? funcInfo.returns: 248 transTskey2Ckey(funcInfo.returns); 249 if (returnType === 'void') { 250 callFunc = util.format('testNapi.%s(%s)\n ', funcInfo.name, 251 funcParamUse); 252 } else { 253 callFunc = util.format('let result: %s = testNapi.%s(%s)\n ', 254 returnType, funcInfo.name, funcParamUse); 255 hilogContent = util.format( 256 'hilog.info(0x0000, "testTag", "Test NAPI %s: ", JSON.stringify(result));\n ',funcInfo.name); 257 } 258 let funcTestReplace = funcParamDefine + callFunc + hilogContent; 259 let rawFileName = path.basename(rootInfo.rawFilePath); 260 // 替换test_case_name 261 let funcTestContent = replaceAll(testAbilityFuncTemplate, 262 '[func_direct_testCase]', funcTestReplace); 263 funcTestContent = replaceAll(funcTestContent, '[test_case_name]', 264 funcInfo.name); 265 funcTestContent = replaceAll(funcTestContent, '[file_introduce_replace]', 266 rawFileName); 267 funcTestContent = replaceAll(funcTestContent, '[func_introduce_replace]', 268 funcInfo.name); 269 funcTestContent = replaceAll(funcTestContent, 270 '[input_introduce_replace]', funcInfoParams === '' ? 'void' : funcInfoParams); 271 funcTestContent = replaceAll(funcTestContent, '[func_return_replace]', 272 funcInfo.returns); 273 testContent += funcTestContent; 274 }); 275 276 let fileContent = replaceAll(testFirstGenTemplate.content, 277 '[testAbilityFunctions]', testContent); 278 // 将文件写入out文件夹 279 if (!fs.existsSync(out)) { 280 fs.mkdirSync(out, { recursive: true }); 281 } 282 let lowerFileName = rootInfo.fileName.toLocaleLowerCase(); 283 let fileName = testFirstGenTemplate.name.replace('[fileName]', 284 lowerFileName); 285 let filePath = path.join(out, fileName); 286 fs.writeFileSync(filePath, fileContent); 287 } 288}