diff --git a/src/cmocka.c b/src/cmocka.c index ede5b22..ec47f4e 100644 --- a/src/cmocka.c +++ cmocka/src/cmocka.c @@ -2532,6 +2532,7 @@ static void cmprintf_group_finish_xml(const char *group_name, if (fp == NULL) { fp = fopen(buf, "w"); if (fp != NULL) { + xml_printed = 0; file_append = 1; file_opened = 1; } else { @@ -2554,13 +2555,15 @@ static void cmprintf_group_finish_xml(const char *group_name, } if (!xml_printed || (file_opened && !file_append)) { - fprintf(fp, "\n"); + fprintf(fp, "\n\n"); if (!file_opened) { xml_printed = 1; } + } else { + fseek(fp, strlen("\n") * -1, SEEK_END); + ftruncate(fileno(fp), ftell(fp)); } - fprintf(fp, "\n"); fprintf(fp, " \n", group_name, diff --git a/tool/cmocka_implement.py b/tool/cmocka_implement.py new file mode 100644 index 0000000..11d2842 --- /dev/null +++ cmocka/tool/cmocka_implement.py @@ -0,0 +1,213 @@ +# -*- coding: utf-8 -*- +import os +import re +import typer +import copy +import traceback + +TESTSUITE_TEMPLATE = """ +/* + * Copyright (C) 2023 Xiaomi Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include + +/**************************************************************************** + * Name: cmocka_{suite_file}_main + ****************************************************************************/ + +int main(int argc, char* argv[]) +{ + + /* Add Test Cases */ + const struct CMUnitTest {suite_name}[] = { + cmocka_unit_test_setup_teardown(write case name here, NULL, NULL), + }; + + /* Run Test cases */ + cmocka_run_group_tests({suite_name}, NULL, NULL); + + printf("hello cmocka auto-tests\\n"); + + return 0; +} +""" + +TESTCASE_TEMPLATE = """ +/**************************************************************************** + * Included Files + ****************************************************************************/ +#include +#include +#include +#include +#include +#include + + +/**************************************************************************** + * Name: {case_file} + * Description: Testing for scene "describe scene here". + * The detail test steps are as following: + * 1. describe step 1 here + * 2. describe step 2 here + * 3. describe step 3 here + ****************************************************************************/ + +void {case_name}(FAR void **state) +{ + printf("case: {case_name}\\n"); + assert(true); +} +""" + + +class CmockaGen: + + def __init__(self, path): + self.path = path + self.suite_path = None + self.suite_file = None + self.suite_name = None + self.case_path = None + self.case_file = None + self.case_name = None + + def check_path(self): + if not self.path: + print("request correct path option") + return 1 + if not os.path.exists(self.path): + os.makedirs(self.path) + return 0 + + def check_suite_option(self, suite_option): + if not suite_option: + return 1 + opts = suite_option.split("::") + if len(opts) != 2: + print("suite option must like aaa/bbb/ccc.c::VelaAutoTestSuite") + return 1 + else: + self.suite_path = opts[0] + self.suite_name = opts[1] + paths = self.suite_path.split("/") + if not paths[-1].endswith(".c"): + print("suite option must like aaa/bbb/ccc.c::VelaAutoTestSuite") + return 1 + else: + self.suite_file = paths[-1] + return 0 + + def generate_suite(self): + content = copy.deepcopy(TESTSUITE_TEMPLATE) + file_without_ext = self.suite_file.replace(".c", "") + content = content.replace("{suite_file}", file_without_ext) + content = content.replace("{suite_name}", self.suite_name) + full_path = os.path.join(self.path, self.suite_path) + dir_path = os.path.dirname(full_path) + if not os.path.exists(dir_path): + os.makedirs(dir_path) + with open(full_path, "w") as fl: + fl.write(content) + + def check_case_option(self, case_option): + if not case_option: + return 1 + opts = case_option.split("::") + if len(opts) != 2: + print("case option must like aaa/bbb/ccc.c::test_playback_uv_01") + return 1 + else: + self.case_path = opts[0] + self.case_name = opts[1] + if not self.case_name.startswith("test"): + print("case function name start with 'test'") + return 1 + paths = self.case_path.split("/") + if not paths[-1].endswith(".c"): + print("case option must like aaa/bbb/ccc.c::VelaAutoTestcase") + return 1 + file_parts = paths[-1].split("_") + pattern = r"[0-9]{2,3}\.c$" + if file_parts[0] != "test": + print("case file name must start with 'test'") + return 1 + elif not re.search(pattern, file_parts[-1]): + print("case file name must end with '00-99' or '000-999'") + return 1 + else: + self.case_file = paths[-1] + return 0 + + def generate_case(self): + content = copy.deepcopy(TESTCASE_TEMPLATE) + content = content.replace("{case_file}", self.case_file) + content = content.replace("{case_name}", self.case_name) + full_path = os.path.join(self.path, self.case_path) + dir_path = os.path.dirname(full_path) + if not os.path.exists(dir_path): + os.makedirs(dir_path) + with open(full_path, "w") as fl: + fl.write(content) + + def main(self, suite_option, case_option): + try: + if self.check_path(): + return + if not self.check_suite_option(suite_option): + self.generate_suite() + print("generate suite success") + if not self.check_case_option(case_option): + self.generate_case() + print("generate case success") + except Exception as e: + traceback.print_exc() + + +app = typer.Typer() + +@app.command() +def main( + path: str = typer.Option("", help="where to gnerate suite/case file"), + suite: str = typer.Option( + default="", + help="suite file name and suite function name, path/suite::name, eg aaa/bbb/ccc.c::VelaAutoTestcase" + ), + case: str = typer.Option( + default="", + help="case file name and case function name, path/case::function, eg ddd/eee/fff.c::test_playback_uv_01" + ), +): + """ + :param path: where to gnerate suite/case file + :param suite: suite file name and suite function name + :param case: case file name and case function name + :return: + """ + gen = CmockaGen(path) + gen.main(suite, case) + +if __name__ == '__main__': + app()