diff --git a/testing/cmocka/0004-cmocka-xml-report-and-generate-case-and-suite-tool.patch b/testing/cmocka/0004-cmocka-xml-report-and-generate-case-and-suite-tool.patch new file mode 100644 index 000000000..5a91130ff --- /dev/null +++ b/testing/cmocka/0004-cmocka-xml-report-and-generate-case-and-suite-tool.patch @@ -0,0 +1,249 @@ +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() diff --git a/testing/cmocka/Makefile b/testing/cmocka/Makefile index d887ef180..f31627831 100644 --- a/testing/cmocka/Makefile +++ b/testing/cmocka/Makefile @@ -42,6 +42,7 @@ cmocka.zip: $(Q) patch -p0 < 0001-cmocka.c-Reduce-the-call-stack-consumption-of-printf.patch $(Q) patch -p0 < 0002-cmocka-feature-to-forwarding-cmocka-log-message-to-c.patch $(Q) patch -p0 < 0003-cmocka-update-method-for-strmatch-to-regex-and-add-list-all-testcases-function.patch + $(Q) patch -p0 < 0004-cmocka-xml-report-and-generate-case-and-suite-tool.patch context:: cmocka.zip diff --git a/testing/cmocka/cmocka_main.c b/testing/cmocka/cmocka_main.c index b13dbbd84..14251a478 100644 --- a/testing/cmocka/cmocka_main.c +++ b/testing/cmocka/cmocka_main.c @@ -57,6 +57,8 @@ static void cm_usage(void) "name matches B pattern\n" " --suite C only run suites where PROGNAME " "matches C pattern\n" + " --output-path use xml report instead of standard " + "output\n" "Example: cmocka --suite mm|sched " "--test Test* --skip TestNuttxMm0[123]\n\n"; printf("%s", mesg); @@ -92,6 +94,7 @@ int main(int argc, FAR char *argv[]) FAR char *bypass[argc + 1]; FAR char *suite = NULL; FAR char *skip = NULL; + FAR char *xml_path = NULL; int num_bypass = 1; int ret; int i; @@ -116,6 +119,10 @@ int main(int argc, FAR char *argv[]) { list_tests = 1; } + else if (strcmp("--output-path", argv[i]) == 0) + { + xml_path = argv[++i]; + } else if (strcmp("--test", argv[i]) == 0) { testcase = argv[++i]; @@ -136,10 +143,17 @@ int main(int argc, FAR char *argv[]) cmocka_set_test_filter(NULL); cmocka_set_skip_filter(NULL); + cmocka_set_message_output(CM_OUTPUT_STDOUT); cmocka_set_list_test(list_tests); if (list_tests == 0) { + if (xml_path != NULL) + { + setenv("CMOCKA_XML_FILE", xml_path, 1); + cmocka_set_message_output(CM_OUTPUT_XML); + } + cmocka_set_test_filter(testcase); cmocka_set_skip_filter(skip); }