support xml report and generate tool
Signed-off-by: zhangchao53 <zhangchao53@xiaomi.com>
This commit is contained in:
parent
a46a661b34
commit
60778f6f8c
@ -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, "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
|
||||
+ fprintf(fp, "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<testsuites>\n");
|
||||
if (!file_opened) {
|
||||
xml_printed = 1;
|
||||
}
|
||||
+ } else {
|
||||
+ fseek(fp, strlen("</testsuites>\n") * -1, SEEK_END);
|
||||
+ ftruncate(fileno(fp), ftell(fp));
|
||||
}
|
||||
|
||||
- fprintf(fp, "<testsuites>\n");
|
||||
fprintf(fp, " <testsuite name=\"%s\" time=\"%.3f\" "
|
||||
"tests=\"%u\" failures=\"%u\" errors=\"%u\" skipped=\"%u\" >\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 <setjmp.h>
|
||||
+#include <stdarg.h>
|
||||
+#include <stddef.h>
|
||||
+#include <stdint.h>
|
||||
+#include <stdio.h>
|
||||
+#include <cmocka.h>
|
||||
+
|
||||
+/****************************************************************************
|
||||
+ * 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 <syslog.h>
|
||||
+#include <stdio.h>
|
||||
+#include <stdlib.h>
|
||||
+#include <string.h>
|
||||
+#include <unistd.h>
|
||||
+#include <cmocka.h>
|
||||
+
|
||||
+
|
||||
+/****************************************************************************
|
||||
+ * 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()
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user