1 /* SPDX-License-Identifier: GPL-2.0 */
5 #include <linux/ctype.h>
6 #include <linux/kernel.h>
7 #include <linux/string.h>
8 #include <linux/zalloc.h>
11 #include <sys/types.h>
13 #include <subcmd/exec-cmd.h>
14 #include <subcmd/parse-options.h>
19 #include "tests-scripts.h"
27 #include "util/rlimit.h"
28 #include "util/util.h"
30 static int shell_tests__dir_fd(void)
33 char path
[PATH_MAX
], path2
[PATH_MAX
], *exec_path
;
34 static const char * const devel_dirs
[] = {
35 "./tools/perf/tests/shell",
37 "./source/tests/shell"
42 for (size_t i
= 0; i
< ARRAY_SIZE(devel_dirs
); ++i
) {
43 fd
= open(devel_dirs
[i
], O_PATH
);
49 /* Use directory of executable */
50 if (readlink("/proc/self/exe", path2
, sizeof path2
) < 0)
52 /* Follow another level of symlink if there */
53 if (lstat(path2
, &st
) == 0 && (st
.st_mode
& S_IFMT
) == S_IFLNK
) {
54 scnprintf(path
, sizeof(path
), path2
);
55 if (readlink(path
, path2
, sizeof path2
) < 0)
59 p
= strrchr(path2
, '/');
62 scnprintf(path
, sizeof(path
), "%s/tests/shell", path2
);
63 fd
= open(path
, O_PATH
);
66 scnprintf(path
, sizeof(path
), "%s/source/tests/shell", path2
);
67 fd
= open(path
, O_PATH
);
71 /* Then installed path. */
72 exec_path
= get_argv_exec_path();
73 scnprintf(path
, sizeof(path
), "%s/tests/shell", exec_path
);
75 return open(path
, O_PATH
);
78 static char *shell_test__description(int dir_fd
, const char *name
)
81 char buf
[128], desc
[256];
84 io__init(&io
, openat(dir_fd
, name
, O_RDONLY
), buf
, sizeof(buf
));
88 /* Skip first line - should be #!/bin/sh Shebang */
89 if (io__get_char(&io
) != '#')
91 if (io__get_char(&io
) != '!')
94 ch
= io__get_char(&io
);
100 ch
= io__get_char(&io
);
103 } while (ch
== '#' || isspace(ch
));
104 while (ch
> 0 && ch
!= '\n') {
106 if (pos
>= (int)sizeof(desc
) - 1)
108 ch
= io__get_char(&io
);
110 while (pos
> 0 && isspace(desc
[--pos
]))
120 /* Is this full file path a shell script */
121 static bool is_shell_script(int dir_fd
, const char *path
)
125 ext
= strrchr(path
, '.');
128 if (!strcmp(ext
, ".sh")) { /* Has .sh extension */
129 if (faccessat(dir_fd
, path
, R_OK
| X_OK
, 0) == 0) /* Is executable */
135 /* Is this file in this dir a shell script (for test purposes) */
136 static bool is_test_script(int dir_fd
, const char *name
)
138 return is_shell_script(dir_fd
, name
);
141 /* Duplicate a string and fall over and die if we run out of memory */
142 static char *strdup_check(const char *str
)
146 newstr
= strdup(str
);
148 pr_err("Out of memory while duplicating test script string\n");
154 static int shell_test__run(struct test_suite
*test
, int subtest __maybe_unused
)
156 const char *file
= test
->priv
;
160 if (asprintf(&cmd
, "%s%s", file
, verbose
? " -v" : "") < 0)
167 return WEXITSTATUS(err
) == 2 ? TEST_SKIP
: TEST_FAIL
;
170 static void append_script(int dir_fd
, const char *name
, char *desc
,
171 struct test_suite
***result
,
174 char filename
[PATH_MAX
], link
[128];
175 struct test_suite
*test_suite
, **result_tmp
;
176 struct test_case
*tests
;
180 snprintf(link
, sizeof(link
), "/proc/%d/fd/%d", getpid(), dir_fd
);
181 len
= readlink(link
, filename
, sizeof(filename
));
183 pr_err("Failed to readlink %s", link
);
186 filename
[len
++] = '/';
187 strcpy(&filename
[len
], name
);
189 tests
= calloc(2, sizeof(*tests
));
191 pr_err("Out of memory while building script test suite list\n");
194 tests
[0].name
= strdup_check(name
);
195 exclusive
= strstr(desc
, " (exclusive)");
196 if (exclusive
!= NULL
) {
197 tests
[0].exclusive
= true;
200 tests
[0].desc
= strdup_check(desc
);
201 tests
[0].run_case
= shell_test__run
;
202 test_suite
= zalloc(sizeof(*test_suite
));
204 pr_err("Out of memory while building script test suite list\n");
208 test_suite
->desc
= desc
;
209 test_suite
->test_cases
= tests
;
210 test_suite
->priv
= strdup_check(filename
);
211 /* Realloc is good enough, though we could realloc by chunks, not that
212 * anyone will ever measure performance here */
213 result_tmp
= realloc(*result
, (*result_sz
+ 1) * sizeof(*result_tmp
));
214 if (result_tmp
== NULL
) {
215 pr_err("Out of memory while building script test suite list\n");
220 /* Add file to end and NULL terminate the struct array */
221 *result
= result_tmp
;
222 (*result
)[*result_sz
] = test_suite
;
226 static void append_scripts_in_dir(int dir_fd
,
227 struct test_suite
***result
,
230 struct dirent
**entlist
;
234 /* List files, sorted by alpha */
235 n_dirs
= scandirat(dir_fd
, ".", &entlist
, NULL
, alphasort
);
238 for (i
= 0; i
< n_dirs
&& (ent
= entlist
[i
]); i
++) {
241 if (ent
->d_name
[0] == '.')
242 continue; /* Skip hidden files */
243 if (is_test_script(dir_fd
, ent
->d_name
)) { /* It's a test */
244 char *desc
= shell_test__description(dir_fd
, ent
->d_name
);
246 if (desc
) /* It has a desc line - valid script */
247 append_script(dir_fd
, ent
->d_name
, desc
, result
, result_sz
);
250 if (ent
->d_type
!= DT_DIR
) {
253 if (ent
->d_type
!= DT_UNKNOWN
)
255 fstatat(dir_fd
, ent
->d_name
, &st
, 0);
256 if (!S_ISDIR(st
.st_mode
))
259 if (strncmp(ent
->d_name
, "base_", 5) == 0)
260 continue; /* Skip scripts that have a separate driver. */
261 fd
= openat(dir_fd
, ent
->d_name
, O_PATH
);
262 append_scripts_in_dir(fd
, result
, result_sz
);
264 for (i
= 0; i
< n_dirs
; i
++) /* Clean up */
269 struct test_suite
**create_script_test_suites(void)
271 struct test_suite
**result
= NULL
, **result_tmp
;
272 size_t result_sz
= 0;
273 int dir_fd
= shell_tests__dir_fd(); /* Walk dir */
276 * Append scripts if fd is good, otherwise return a NULL terminated zero
280 append_scripts_in_dir(dir_fd
, &result
, &result_sz
);
282 result_tmp
= realloc(result
, (result_sz
+ 1) * sizeof(*result_tmp
));
283 if (result_tmp
== NULL
) {
284 pr_err("Out of memory while building script test suite list\n");
287 /* NULL terminate the test suite array. */
289 result
[result_sz
] = NULL
;