diff --git a/test/test.c b/test/test.c index 4766e74..c72d12a 100644 --- a/test/test.c +++ b/test/test.c @@ -134,3 +134,75 @@ ASSERT_GT(128, utest_fixture->bar); utest_fixture->foo = 13; } + + +#ifdef __linux__ +#include +/** Check that the output from --list-tests matches our internal view of the + * listed tests. This means calling ourselves recursively and filtering the + * output, so it's hacky. To do this, we need to get the full path to the + * current program, there is a different way to get this information on nearly + * every OS, but the thing we're testing is platform independent, so we should + * be able to get away with just Linux here +*/ +static int testname_cmp(const void *x_, const void *y_) { + const char *x = *(const char *const*)x_; + const char *y = *(const char *const*)y_; + return strcmp(x, y); +} + +/* Some bright spark decided -Werror and -Weverything should be used together, + * which breaks idiomatic code... */ + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wcomma" +#endif +UTEST(utest_cmdline, filter_with_list) { + // 64k should be enough for anyone + char exe_buf[64 * 1024] = {0}, cmd_buf[64 * 1024] = {0}; + ssize_t path_len = readlink("/proc/self/exe", exe_buf, sizeof exe_buf); + int count, ret; + FILE *ps; + const char **test_names; + size_t n = 0, i = 0; + char *lineptr; + ASSERT_NE(path_len, UTEST_CAST(ssize_t, -1)); + count = snprintf(cmd_buf, sizeof cmd_buf, "%s --list-tests", exe_buf); + ASSERT_EQ(errno, 0); + ASSERT_LT(UTEST_CAST(size_t, count), sizeof cmd_buf); + + ps = popen(cmd_buf, "r"); + ASSERT_TRUE(ps); + + test_names = + malloc(utest_state.tests_length * sizeof test_names[0]); + ASSERT_TRUE(test_names); + + for (i = 0; i < utest_state.tests_length; ++i) { + test_names[i] = utest_state.tests[i].name; + } + qsort(test_names, utest_state.tests_length, sizeof test_names[0], + testname_cmp); + + lineptr = malloc(1); + ASSERT_TRUE(lineptr); + for (i = 0, n = 0; getline(&lineptr, &n, ps) != (ssize_t)-1; n = 0, ++i) { + /* remove the terminating newline */ + const char **name; + char *nl = strchr(lineptr, '\n'); + EXPECT_TRUE(nl); + *nl = '\0'; + + name = bsearch(&lineptr, test_names, utest_state.tests_length, + sizeof test_names[0], testname_cmp); + EXPECT_TRUE(name); + EXPECT_STREQ(*name, lineptr); + } + EXPECT_EQ(i, utest_state.tests_length); + free(lineptr); + free(test_names); + ret = pclose(ps); + ASSERT_EQ(ret, 0); +} +#endif diff --git a/test/test.c b/test/test.c index 4766e74..c72d12a 100644 --- a/test/test.c +++ b/test/test.c @@ -134,3 +134,75 @@ ASSERT_GT(128, utest_fixture->bar); utest_fixture->foo = 13; } + + +#ifdef __linux__ +#include +/** Check that the output from --list-tests matches our internal view of the + * listed tests. This means calling ourselves recursively and filtering the + * output, so it's hacky. To do this, we need to get the full path to the + * current program, there is a different way to get this information on nearly + * every OS, but the thing we're testing is platform independent, so we should + * be able to get away with just Linux here +*/ +static int testname_cmp(const void *x_, const void *y_) { + const char *x = *(const char *const*)x_; + const char *y = *(const char *const*)y_; + return strcmp(x, y); +} + +/* Some bright spark decided -Werror and -Weverything should be used together, + * which breaks idiomatic code... */ + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wcomma" +#endif +UTEST(utest_cmdline, filter_with_list) { + // 64k should be enough for anyone + char exe_buf[64 * 1024] = {0}, cmd_buf[64 * 1024] = {0}; + ssize_t path_len = readlink("/proc/self/exe", exe_buf, sizeof exe_buf); + int count, ret; + FILE *ps; + const char **test_names; + size_t n = 0, i = 0; + char *lineptr; + ASSERT_NE(path_len, UTEST_CAST(ssize_t, -1)); + count = snprintf(cmd_buf, sizeof cmd_buf, "%s --list-tests", exe_buf); + ASSERT_EQ(errno, 0); + ASSERT_LT(UTEST_CAST(size_t, count), sizeof cmd_buf); + + ps = popen(cmd_buf, "r"); + ASSERT_TRUE(ps); + + test_names = + malloc(utest_state.tests_length * sizeof test_names[0]); + ASSERT_TRUE(test_names); + + for (i = 0; i < utest_state.tests_length; ++i) { + test_names[i] = utest_state.tests[i].name; + } + qsort(test_names, utest_state.tests_length, sizeof test_names[0], + testname_cmp); + + lineptr = malloc(1); + ASSERT_TRUE(lineptr); + for (i = 0, n = 0; getline(&lineptr, &n, ps) != (ssize_t)-1; n = 0, ++i) { + /* remove the terminating newline */ + const char **name; + char *nl = strchr(lineptr, '\n'); + EXPECT_TRUE(nl); + *nl = '\0'; + + name = bsearch(&lineptr, test_names, utest_state.tests_length, + sizeof test_names[0], testname_cmp); + EXPECT_TRUE(name); + EXPECT_STREQ(*name, lineptr); + } + EXPECT_EQ(i, utest_state.tests_length); + free(lineptr); + free(test_names); + ret = pclose(ps); + ASSERT_EQ(ret, 0); +} +#endif diff --git a/utest.h b/utest.h index bff4a0d..8fed407 100644 --- a/utest.h +++ b/utest.h @@ -716,7 +716,10 @@ } /* loop through all arguments looking for our options */ for (index = 1; index < UTEST_CAST(size_t, argc); index++) { + /* Informational switches */ const char help_str[] = "--help"; + const char list_str[] = "--list-tests"; + /* Test config switches */ const char filter_str[] = "--filter="; const char output_str[] = "--output="; @@ -726,6 +729,8 @@ " --help Show this message and exit.\n" " --filter= Filter the test cases to run (EG. MyTest*.a " "would run MyTestCase.a but not MyTestCase.b).\n" + " --list-tests List testnames, one per line. Output names " + "can be passed to --filter.\n" " --output= Output an xunit XML file to the file " "specified in .\n"); goto cleanup; @@ -736,6 +741,12 @@ } else if (0 == utest_strncmp(argv[index], output_str, strlen(output_str))) { utest_state.output = utest_fopen(argv[index] + strlen(output_str), "w+"); + } else if (0 == utest_strncmp(argv[index], list_str, strlen(list_str))) { + for (index = 0; index < utest_state.tests_length; index++) { + UTEST_PRINTF("%s\n", utest_state.tests[index].name); + } + /* When printing the test list, don't actually run the tests */ + return 0; } }