diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e089898..6631403 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,10 +27,14 @@ jobs: steps: - name: Check out uses: actions/checkout@v2 - - name: Build and test + - name: Build + shell: bash run: | mkdir build cd build cmake .. -G "${{matrix.platform.generator}}" cmake --build . + - name: Test + shell: bash + run: | CTEST_OUTPUT_ON_FAILURE=1 ctest . diff --git a/test/clar.c b/test/clar.c index 00204aa..907b7a9 100644 --- a/test/clar.c +++ b/test/clar.c @@ -6,7 +6,12 @@ #include #include #include -#include +#if defined(__MINGW__) || defined(__MSYS__) || defined(__MINGW32__) +# define WIN32_LEAN_AND_MEAN +# include +#else +# include +#endif #include "clar.h" @@ -59,6 +64,90 @@ static char *read_file(const char *path) static void run(const char *expected_output_file, int expected_error_code, ...) { +#ifdef __MINGW32__ + SECURITY_ATTRIBUTES sec_attributes = { 0 }; + PROCESS_INFORMATION process_info = { 0 }; + STARTUPINFO startup_info = { 0 }; + char cmdline[4096] = { 0 }; + char *expected_output = NULL; + char *output = NULL; + size_t output_size = 0; + HANDLE pipe_write; + HANDLE pipe_read; + DWORD exit_code; + va_list ap; + + /* + * Assemble command line arguments. In theory we'd have to properly + * quote them. In practice none of our tests actually care. + */ + va_start(ap, expected_error_code); + snprintf(cmdline, sizeof(cmdline), "%s", SELFTEST_BINARY_PATH); + while (1) { + size_t cmdline_len = strlen(cmdline); + const char *arg; + + arg = va_arg(ap, const char *); + if (!arg) + break; + + cl_assert(cmdline_len + strlen(arg) < sizeof(cmdline)); + snprintf(cmdline + cmdline_len, sizeof(cmdline) - cmdline_len, + " %s", arg); + } + va_end(ap); + + /* + * Create a pipe that we will use to read data from the child process. + * The writing side needs to be inheritable such that the child can use + * it as stdout and stderr. The reading side should only be used by the + * parent. + */ + sec_attributes.nLength = sizeof(sec_attributes); + sec_attributes.bInheritHandle = TRUE; + cl_assert_equal_b(1, CreatePipe(&pipe_read, &pipe_write, &sec_attributes, 0)); + cl_assert_equal_b(1, SetHandleInformation(pipe_read, HANDLE_FLAG_INHERIT, 0)); + + /* + * Create the child process with our pipe. + */ + startup_info.cb = sizeof(startup_info); + startup_info.hStdError = pipe_write; + startup_info.hStdOutput = pipe_write; + startup_info.dwFlags |= STARTF_USESTDHANDLES; + cl_assert_equal_b(1, CreateProcess("selftest", cmdline, NULL, NULL, TRUE, 0, + NULL, NULL, &startup_info, &process_info)); + cl_assert_equal_b(1, CloseHandle(&pipe_write)); + + while (1) { + CHAR buf[4096]; + DWORD bytes_read; + + cl_assert_equal_b(1, ReadFile(pipe_read, buf, sizeof(buf), + &bytes_read, NULL)); + if (!bytes_read) + break; + + output = realloc(output, output_size + bytes_read); + cl_assert(output); + memcpy(output + output_size, buf, bytes_read); + output_size += bytes_read; + } + + output = realloc(output, output_size + 1); + cl_assert(output); + output[output_size] = '\0'; + + cl_assert_equal_b(1, CloseHandle(&pipe_read)); + cl_assert_equal_b(1, GetExitCodeProcess(process_info.hProcess, &exit_code)); + + expected_output = read_file(cl_fixture(expected_output_file)); + cl_assert_equal_s(output, expected_output); + cl_assert_equal_i(exit_code, expected_error_code); + + free(expected_output); + free(output); +#else const char *argv[16]; int pipe_fds[2]; va_list ap; @@ -111,6 +200,7 @@ static void run(const char *expected_output_file, int expected_error_code, ...) } else { cl_fail("Fork failed."); } +#endif } void test_clar__help(void)