• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 { ClassObj, StructObj, FuncInfo, FuncObj, 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  // 1.遍历所有class
212  testContent = genClassTestcase(rootInfo, testContent);
213
214  // 1.遍历所有struct
215  testContent = genStructTestcase(rootInfo, testContent);
216
217  // 2.遍历所有函数
218  testContent = genFunctionTestcase(rootInfo, testContent);
219
220  let fileContent = replaceAll(testFirstGenTemplate.content,
221    '[testAbilityFunctions]', testContent);
222  // 将文件写入out文件夹
223  if (!fs.existsSync(out)) {
224    fs.mkdirSync(out, { recursive: true });
225  }
226  let lowerFileName = rootInfo.fileName.toLocaleLowerCase();
227  let fileName = testFirstGenTemplate.name.replace('[fileName]',
228    lowerFileName);
229  fileContent = replaceAll(fileContent, '[fileName]', lowerFileName);
230  let filePath = path.join(out, fileName);
231  fs.writeFileSync(filePath, fileContent);
232}
233
234export function genFunctionTestcase(rootInfo: GenInfo, testContent: string) {
235  if (rootInfo.parseObj && rootInfo.parseObj.funcs) {
236    rootInfo.parseObj.funcs.forEach(funcInfo => {
237      let { funcTestReplace, funcInfoParams } = genMethodTestcase(funcInfo, false, '');
238      let rawFileName = path.basename(rootInfo.rawFilePath);
239      // 替换test_case_name
240      let funcTestContent = replaceAll(testAbilityFuncTemplate,
241        '[func_direct_testCase]', funcTestReplace);
242      funcTestContent = replaceAll(funcTestContent, '[test_case_name]',
243        funcInfo.name);
244      funcTestContent = replaceAll(funcTestContent, '[file_introduce_replace]',
245        rawFileName);
246      funcTestContent = replaceAll(funcTestContent,
247        '[input_introduce_replace]', funcInfoParams === '' ? 'void' : funcInfoParams);
248      funcTestContent = replaceAll(funcTestContent, '[func_return_replace]',
249        funcInfo.returns);
250      testContent += funcTestContent;
251    });
252  }
253  return testContent;
254}
255
256export function genStructTestcase(rootInfo: GenInfo, testContent: string) {
257  if (rootInfo.parseObj && rootInfo.parseObj.structs) {
258    // 只测试成员方法,不测试成员变量的get/set函数??? 还是得测试吧?
259    rootInfo.parseObj.structs.forEach(structItem => {
260      let structTestContent = util.format('let %sObj = new testNapi.%s();\n    ', structItem.name.toLocaleLowerCase(), structItem.name);
261      // 1.1.遍历所有成员变量 测试set/get函数
262      structItem.members.forEach(variable => {
263        structTestContent = genClassGetSetTestcase(variable, structTestContent, structItem);
264      });
265
266      // 1.2.遍历所有成员函数
267      structItem.functions.forEach(funcInfo => {
268        // 无法判断函数是否为静态函数/是否为constructor/析构函数,所以先全部测试一遍
269        let { funcTestReplace, funcInfoParams } = genMethodTestcase(funcInfo, true, structItem.name);
270        structTestContent += funcTestReplace;
271      });
272      let clsTestContent = replaceAll(testAbilityFuncTemplate, '[func_direct_testCase]', structTestContent);
273      clsTestContent = replaceAll(clsTestContent, '[test_case_name]', structItem.name);
274      clsTestContent = replaceAll(clsTestContent, '[file_introduce_replace]', path.basename(rootInfo.rawFilePath));
275      clsTestContent = replaceAll(clsTestContent, '[input_introduce_replace]', 'this is a test case for class.');
276      clsTestContent = replaceAll(clsTestContent, '[func_return_replace]', 'this is a test case for class.');
277
278      testContent += clsTestContent;
279    });
280  }
281  return testContent;
282}
283
284export function genClassTestcase(rootInfo: GenInfo, testContent: string) {
285  if (rootInfo.parseObj && rootInfo.parseObj.classes) {
286    rootInfo.parseObj.classes.forEach(classItem => {
287      let classTestContent = util.format('let %sObj = new testNapi.%s();\n    ', classItem.name.toLocaleLowerCase(), classItem.name);
288      // 1.1.遍历所有成员变量 测试set/get函数
289      classItem.variableList.forEach(variable => {
290        classTestContent = genClassGetSetTestcase(variable, classTestContent, classItem);
291      });
292
293      // 1.2.遍历所有成员函数
294      classItem.functionList.forEach(funcInfo => {
295        // 无法判断函数是否为静态函数/是否为constructor/析构函数,所以先全部测试一遍
296        let { funcTestReplace, funcInfoParams } = genMethodTestcase(funcInfo, true, classItem.name);
297        classTestContent += funcTestReplace;
298      });
299      let clsTestContent = replaceAll(testAbilityFuncTemplate, '[func_direct_testCase]', classTestContent);
300      clsTestContent = replaceAll(clsTestContent, '[test_case_name]', classItem.name);
301      clsTestContent = replaceAll(clsTestContent, '[file_introduce_replace]', path.basename(rootInfo.rawFilePath));
302      clsTestContent = replaceAll(clsTestContent, '[input_introduce_replace]', 'this is a test case for class.');
303      clsTestContent = replaceAll(clsTestContent, '[func_return_replace]', 'this is a test case for class.');
304
305      testContent += clsTestContent;
306    });
307  }
308  return testContent;
309}
310
311export function genClassGetSetTestcase(variable: ParamObj, classTestContent: string, classItem: ClassObj | StructObj) {
312  let variableType = transTskey2Ckey(variable.type);
313  let testValue = 'undefined; // Please give an any value.';
314  dts2TestValue.forEach(item => {
315    if (item.key === variableType) {
316      testValue = item.value;
317    }
318  });
319  // 成员变量的set
320  classTestContent += util.format('%sObj.%s = %s;\n    ', classItem.name.toLocaleLowerCase(), variable.name, testValue);
321  // 成员变量的get  //即打印log
322  classTestContent += util.format('console.log("%sObj.%s = ", %sObj.%s);\n    ', classItem.name.toLocaleLowerCase(),
323    variable.name, classItem.name.toLocaleLowerCase(), variable.name);
324  return classTestContent;
325}
326
327export function genMethodTestcase(funcInfo: FuncObj, isClassMethod: boolean, className: string = '') {
328  let callFunc = ''; // 调用函数内容
329  let hilogContent = ''; // hilog内容
330  let funcParamDefine = ''; // 函数参数定义并初始化
331  let funcParamUse = ''; // 函数参数使用
332  let funcInfoParams = ''; // 注释
333  let funcInfoParamTemp = '[paramName]: [paramType]; ';
334  // 遍历方法参数,给参数赋初始值,生成注释和参数使用内容
335  // 1. 先将所有type转换为ts的type
336  for (let i = 0; i < funcInfo.parameters.length; ++i) {
337    // 注释
338    let funcInfoParamReplace = replaceAll(funcInfoParamTemp, '[paramName]',
339      funcInfo.parameters[i].name);
340    funcInfoParamReplace = replaceAll(funcInfoParamReplace, '[paramType]',
341      funcInfo.parameters[i].type);
342    funcInfoParams += funcInfoParamReplace;
343    // 参数定义并初始化
344    const param = funcInfo.parameters[i];
345    let paramType = transTskey2Ckey(param.type);
346    let testValue = 'undefined; // Please give an any value.'; // any类型咋赋值?
347    dts2TestValue.forEach(item => {
348      if (item.key === paramType) {
349        testValue = item.value;
350      }
351    });
352    funcParamDefine += util.format('let %s: %s = %s;\n    ',
353      funcInfo.parameters[i].name, paramType, testValue);
354    funcParamUse += funcInfo.parameters[i].name + ', ';
355    // 如果是最后一个参数,去掉最后的逗号和空格
356    if (funcInfo.parameters.length === i + 1) {
357      funcParamUse = funcParamUse.slice(0, -2); // 去掉最后一个逗号和空格
358    }
359  }
360  // 返回值,如果本来就是ts类型就不用替换了:如Promise<unknown>,就不用替换了
361  let tsPromiseReg = /Promise<([^>]+)>/g;
362  let returnType = tsPromiseReg.exec(funcInfo.returns) ? funcInfo.returns :
363    transTskey2Ckey(funcInfo.returns);
364  if (returnType === 'void') {
365    callFunc = util.format('%s%s(%s)\n    ', isClassMethod ? className.toLocaleLowerCase() + 'Obj.' : 'testNapi.',
366      funcInfo.name, funcParamUse);
367  } else {
368    callFunc = util.format('let result: %s = %s%s(%s)\n    ', returnType,
369      isClassMethod ? className.toLocaleLowerCase() + 'Obj.' : 'testNapi.', funcInfo.name, funcParamUse);
370    hilogContent = util.format(
371      'hilog.info(0x0000, "testTag", "Test NAPI %s: ", JSON.stringify(result));\n    ', funcInfo.name);
372  }
373  let funcTestReplace = funcParamDefine + callFunc + hilogContent;
374  return { funcTestReplace, funcInfoParams };
375}