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