1 /*
2  * Copyright (c) 2022 Google LLC
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include "cmdline.h" /* native_sim command line options header */
10 #include "soc.h"
11 #include <zephyr/tc_util.h>
12 #include <zephyr/ztest_test.h>
13 #include "nsi_host_trampolines.h"
14 
15 static const char *test_args;
16 static bool list_tests;
17 
add_test_filter_option(void)18 static void add_test_filter_option(void)
19 {
20 	static struct args_struct_t test_filter_s[] = {
21 		/*
22 		 * Fields:
23 		 * manual, mandatory, switch,
24 		 * option_name, var_name ,type,
25 		 * destination, callback,
26 		 * description
27 		 */
28 		{ false, false, true, "list", NULL, 'b', (void *)&list_tests, NULL,
29 		  "List all suite and test cases" },
30 		{ false, false, false, "test", "suite::test", 's', (void *)&test_args, NULL,
31 		  "Name of tests to run. Comma separated list formatted as "
32 		  "\'suiteA::test1,suiteA::test2,suiteB::*\'. An * can be used "
33 		  "as a wildcard to run all tests within a suite." },
34 		ARG_TABLE_ENDMARKER
35 	};
36 
37 	native_add_command_line_opts(test_filter_s);
38 }
39 
40 NATIVE_TASK(add_test_filter_option, PRE_BOOT_1, 10);
41 
42 /**
43  * @brief Try to shorten a filename by removing the current directory
44  *
45  * This helps to reduce the very long filenames in assertion failures. It
46  * removes the current directory from the filename and returns the rest.
47  * This makes assertions a lot more readable, and sometimes they fit on one
48  * line.
49  *
50  * Overrides implementation in ztest.c
51  *
52  * @param file Filename to check
53  * @returns Shortened filename, or @file if it could not be shortened
54  */
ztest_relative_filename(const char * file)55 const char *ztest_relative_filename(const char *file)
56 {
57 	const char *cwd;
58 	char buf[200];
59 
60 	cwd = nsi_host_getcwd(buf, sizeof(buf));
61 	if (cwd && strlen(file) > strlen(cwd) && !strncmp(file, cwd, strlen(cwd))) {
62 		return file + strlen(cwd) + 1; /* move past the trailing '/' */
63 	}
64 	return file;
65 }
66 
67 /**
68  * @brief Helper function to set list_tests
69  *
70  * @param value - Sets list_tests to value
71  */
ztest_set_list_test(bool value)72 void ztest_set_list_test(bool value)
73 {
74 	list_tests = value;
75 }
76 
77 /**
78  * @brief Helper function to get command line argument for listing tests
79  *
80  * @return true
81  * @return false
82  */
z_ztest_get_list_test(void)83 bool z_ztest_get_list_test(void)
84 {
85 	return list_tests;
86 }
87 
88 /**
89  * @brief Helper function to get command line test arguments
90  *
91  * @return const char*
92  */
ztest_get_test_args(void)93 const char *ztest_get_test_args(void)
94 {
95 	return test_args;
96 }
97 
98 
99 
100 /**
101  * @brief Lists registered unit tests in this binary, one per line
102  *
103  * @return int Number of tests in binary
104  */
z_ztest_list_tests(void)105 int z_ztest_list_tests(void)
106 {
107 	struct ztest_suite_node *ptr;
108 	struct ztest_unit_test *test = NULL;
109 	int test_count = 0;
110 	static bool list_once = true;
111 
112 	if (list_once) {
113 		for (ptr = _ztest_suite_node_list_start; ptr < _ztest_suite_node_list_end; ++ptr) {
114 			test = NULL;
115 			while ((test = z_ztest_get_next_test(ptr->name, test)) != NULL) {
116 				TC_PRINT("%s::%s\n", test->test_suite_name, test->name);
117 				test_count++;
118 			}
119 		}
120 		list_once = false;
121 	}
122 
123 	return test_count;
124 }
125 
126 /**
127  * Default entry point for running or listing registered unit tests.
128  *
129  * @param state The current state of the machine as it relates to the test executable.
130  */
z_ztest_run_all(const void * state,bool shuffle,int suite_iter,int case_iter)131 void z_ztest_run_all(const void *state, bool shuffle, int suite_iter, int case_iter)
132 {
133 	if (z_ztest_get_list_test()) {
134 		z_ztest_list_tests();
135 	} else {
136 		ztest_run_test_suites(state, shuffle, suite_iter, case_iter);
137 	}
138 }
139 
140 /**
141  * @brief Checks if the test_args contains the suite/test name.
142  *
143  * @param suite_name
144  * @param test_name
145  * @return true
146  * @return false
147  */
z_ztest_testargs_contains(const char * suite_name,const char * test_name)148 static bool z_ztest_testargs_contains(const char *suite_name, const char *test_name)
149 {
150 	bool found = false;
151 	char *test_args_local = nsi_host_strdup(test_args);
152 	char *suite_test_pair;
153 	char *last_suite_test_pair;
154 	char *suite_arg;
155 	char *test_arg;
156 	char *last_arg;
157 
158 	suite_test_pair = strtok_r(test_args_local, ",", &last_suite_test_pair);
159 
160 	while (suite_test_pair && !found) {
161 		suite_arg = strtok_r(suite_test_pair, ":", &last_arg);
162 		test_arg = strtok_r(NULL, ":", &last_arg);
163 
164 		found = !strcmp(suite_arg, suite_name);
165 		if (test_name) {
166 			found &= !strcmp(test_arg, "*") ||
167 				 !strcmp(test_arg, test_name);
168 		}
169 
170 		suite_test_pair = strtok_r(NULL, ",", &last_suite_test_pair);
171 	}
172 
173 	nsi_host_free(test_args_local);
174 	return found;
175 }
176 
177 /**
178  * @brief Determines if the test case should run based on test cases listed
179  *	  in the command line argument.
180  *
181  * Overrides implementation in ztest.c
182  *
183  * @param suite - name of test suite
184  * @param test  - name of unit test
185  * @return true
186  * @return false
187  */
z_ztest_should_test_run(const char * suite,const char * test)188 bool z_ztest_should_test_run(const char *suite, const char *test)
189 {
190 	bool run_test = false;
191 
192 	run_test = (test_args == NULL ||
193 		    z_ztest_testargs_contains(suite, test));
194 
195 	return run_test;
196 }
197 
198 /**
199  * @brief Determines if the test suite should run based on test cases listed
200  *	  in the command line argument.
201  *
202  * Overrides implementation in ztest.c
203  *
204  * @param state The current state of the machine as it relates to the test
205  *		executable.
206  * @param suite Pointer to ztest_suite_node
207  * @return true
208  * @return false
209  */
z_ztest_should_suite_run(const void * state,struct ztest_suite_node * suite)210 bool z_ztest_should_suite_run(const void *state, struct ztest_suite_node *suite)
211 {
212 	bool run_suite = true;
213 
214 	if (test_args != NULL && !z_ztest_testargs_contains(suite->name, NULL)) {
215 		run_suite = false;
216 		suite->stats->run_count++;
217 	} else if (suite->predicate != NULL) {
218 		run_suite = suite->predicate(state);
219 	}
220 
221 	return run_suite;
222 }
223 
224 ZTEST_DMEM const struct ztest_arch_api ztest_api = {
225 	.run_all = z_ztest_run_all,
226 	.should_suite_run = z_ztest_should_suite_run,
227 	.should_test_run = z_ztest_should_test_run
228 };
229