From 53c9edc23eaedcab3b9b730b452c99809c945aac Mon Sep 17 00:00:00 2001 From: Snaipe Date: Thu, 10 Sep 2015 05:33:35 +0200 Subject: [PATCH 01/17] Added redirection functions --- include/criterion/redirect.h | 41 +++++++++++ src/posix-compat.c | 132 +++++++++++++++++++++++++++++++---- src/posix-compat.h | 4 +- src/process.c | 4 +- 4 files changed, 163 insertions(+), 18 deletions(-) create mode 100644 include/criterion/redirect.h diff --git a/include/criterion/redirect.h b/include/criterion/redirect.h new file mode 100644 index 0000000..bb5bc6c --- /dev/null +++ b/include/criterion/redirect.h @@ -0,0 +1,41 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2015 Franklin "Snaipe" Mathieu + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef CRITERION_REDIRECT_H_ +# define CRITERION_REDIRECT_H_ + +# include "common.h" + +CR_BEGIN_C_API + +CR_API void cr_redirect_stdout(void); +CR_API void cr_redirect_stderr(void); +CR_API void cr_redirect_stdin(void); + +CR_API FILE* cr_get_redirected_stdout(void); +CR_API FILE* cr_get_redirected_stderr(void); +CR_API FILE* cr_get_redirected_stdin(void); + +CR_END_C_API + +#endif /* !CRITERION_REDIRECT_H_ */ diff --git a/src/posix-compat.c b/src/posix-compat.c index 291a727..d7894b7 100644 --- a/src/posix-compat.c +++ b/src/posix-compat.c @@ -24,6 +24,7 @@ #include #include "posix-compat.h" #include "process.h" +#include "criterion/assert.h" #ifdef VANILLA_WIN32 # define VC_EXTRALEAN @@ -262,15 +263,17 @@ void wait_process(s_proc_handle *handle, int *status) { #endif } -FILE *pipe_in(s_pipe_handle *p) { +FILE *pipe_in(s_pipe_handle *p, int do_close) { #ifdef VANILLA_WIN32 - CloseHandle(p->fhs[1]); + if (do_close) + CloseHandle(p->fhs[1]); int fd = _open_osfhandle((intptr_t) p->fhs[0], _O_RDONLY); if (fd == -1) return NULL; FILE *in = _fdopen(fd, "r"); #else - close(p->fds[1]); + if (do_close) + close(p->fds[1]); FILE *in = fdopen(p->fds[0], "r"); #endif if (!in) @@ -280,15 +283,17 @@ FILE *pipe_in(s_pipe_handle *p) { return in; } -FILE *pipe_out(s_pipe_handle *p) { +FILE *pipe_out(s_pipe_handle *p, int do_close) { #ifdef VANILLA_WIN32 - CloseHandle(p->fhs[0]); + if (do_close) + CloseHandle(p->fhs[0]); int fd = _open_osfhandle((intptr_t) p->fhs[1], _O_WRONLY); if (fd == -1) return NULL; FILE *out = _fdopen(fd, "w"); #else - close(p->fds[0]); + if (do_close) + close(p->fds[0]); FILE *out = fdopen(p->fds[1], "w"); #endif if (!out) @@ -298,8 +303,7 @@ FILE *pipe_out(s_pipe_handle *p) { return out; } -s_pipe_handle *stdpipe() { - s_pipe_handle *handle = smalloc(sizeof (s_pipe_handle)); +int stdpipe_stack(s_pipe_handle *out) { #ifdef VANILLA_WIN32 HANDLE fhs[2]; SECURITY_ATTRIBUTES attr = { @@ -307,16 +311,22 @@ s_pipe_handle *stdpipe() { .bInheritHandle = TRUE }; if (!CreatePipe(fhs, fhs + 1, &attr, 0)) - return NULL; - *handle = (s_pipe_handle) {{ fhs[0], fhs[1] }}; - return handle; + return -1; + *out = (s_pipe_handle) {{ fhs[0], fhs[1] }}; #else int fds[2] = { -1, -1 }; if (pipe(fds) == -1) - return NULL; - *handle = (s_pipe_handle) {{ fds[0], fds[1] }}; - return handle; + return -1; + *out = (s_pipe_handle) {{ fds[0], fds[1] }}; #endif + return 0; +} + +s_pipe_handle *stdpipe() { + s_pipe_handle *handle = smalloc(sizeof (s_pipe_handle)); + if (stdpipe_stack(handle) < 0) + return NULL; + return handle; } s_proc_handle *get_current_process() { @@ -405,3 +415,97 @@ const char *basename_compat(const char *str) { start = c + 1; return start; } + +#ifdef VANILLA_WIN32 +typedef DWORD cr_std_fd; +#else +typedef int cr_std_fd; +#endif + +static s_pipe_handle stdout_redir; +static s_pipe_handle stderr_redir; +static s_pipe_handle stdin_redir; + +enum criterion_std_fd { + CR_STDIN, + CR_STDOUT, + CR_STDERR +}; + +enum criterion_pipe_end { + PIPE_READ = 0, + PIPE_WRITE = 1, +}; + +cr_std_fd get_std_fd(int fd_kind) { + static int kinds[] = { +#ifdef VANILLA_WIN32 + [CR_STDIN] = STD_INPUT_HANDLE, + [CR_STDOUT] = STD_OUTPUT_HANDLE, + [CR_STDERR] = STD_ERROR_HANDLE, +#else + [CR_STDIN] = STDIN_FILENO, + [CR_STDOUT] = STDOUT_FILENO, + [CR_STDERR] = STDERR_FILENO, +#endif + }; + + return kinds[fd_kind]; +} + +void cr_redirect(int fd_kind, s_pipe_handle *pipe, int fd_index) { + if (stdpipe_stack(pipe) < 0) + cr_assert_fail("Could not redirect standard file descriptor."); + + cr_std_fd fd = get_std_fd(fd_kind); +#ifdef VANILLA_WIN32 + CloseHandle(GetStdHandle(fd)); + SetStdHandle(fd, pipe->fds[fd_index]); +#else + close(fd); + dup2(fd, pipe->fds[fd_index]); + close(pipe->fds[fd_index]); +#endif +} + +void cr_redirect_stdout(void) { + cr_redirect(CR_STDOUT, &stdout_redir, PIPE_WRITE); +} + +void cr_redirect_stderr(void) { + cr_redirect(CR_STDERR, &stderr_redir, PIPE_WRITE); +} + +void cr_redirect_stdin(void) { + cr_redirect(CR_STDIN, &stdin_redir, PIPE_READ); +} + +FILE* cr_get_redirected_stdout(void) { + static FILE *f; + if (!f) { + f = pipe_in(&stdout_redir, 0); + if (!f) + cr_assert_fail("Could not get redirected stdout read end."); + } + return f; +} + +FILE* cr_get_redirected_stderr(void) { + static FILE *f; + if (!f) { + f = pipe_in(&stderr_redir, 0); + if (!f) + cr_assert_fail("Could not get redirected stderr read end."); + } + return f; +} + +FILE* cr_get_redirected_stdin(void) { + static FILE *f; + if (!f) { + f = pipe_out(&stdin_redir, 0); + if (!f) + cr_assert_fail("Could not get redirected stdin write end."); + } + return f; +} diff --git a/src/posix-compat.h b/src/posix-compat.h index 9f938db..8ad38f3 100644 --- a/src/posix-compat.h +++ b/src/posix-compat.h @@ -67,8 +67,8 @@ extern struct worker_context g_worker_context; int resume_child(void); s_pipe_handle *stdpipe(); -FILE *pipe_in(s_pipe_handle *p); -FILE *pipe_out(s_pipe_handle *p); +FILE *pipe_in(s_pipe_handle *p, int do_close); +FILE *pipe_out(s_pipe_handle *p, int do_close); s_proc_handle *fork_process(); void wait_process(s_proc_handle *handle, int *status); diff --git a/src/process.c b/src/process.c index 036b790..dd0a0c7 100644 --- a/src/process.c +++ b/src/process.c @@ -62,7 +62,7 @@ struct event *worker_read_event(struct process *proc) { void run_worker(struct worker_context *ctx) { fclose(stdin); - g_event_pipe = pipe_out(ctx->pipe); + g_event_pipe = pipe_out(ctx->pipe, 1); ctx->func(ctx->test, ctx->suite); fclose(g_event_pipe); @@ -102,7 +102,7 @@ struct process *spawn_test_worker(struct criterion_test *test, .kind = UNIQUE, .dtor = close_process); - *ptr = (struct process) { .proc = proc, .in = pipe_in(pipe) }; + *ptr = (struct process) { .proc = proc, .in = pipe_in(pipe, 1) }; cleanup: sfree(pipe); return ptr; From 3744d67b0c460c78d3b37c6ba2c59ecd1af7983d Mon Sep 17 00:00:00 2001 From: Snaipe Date: Thu, 10 Sep 2015 05:36:03 +0200 Subject: [PATCH 02/17] Fixed typo in cr_redirect in windows-specific code --- src/posix-compat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/posix-compat.c b/src/posix-compat.c index d7894b7..55a506f 100644 --- a/src/posix-compat.c +++ b/src/posix-compat.c @@ -460,7 +460,7 @@ void cr_redirect(int fd_kind, s_pipe_handle *pipe, int fd_index) { cr_std_fd fd = get_std_fd(fd_kind); #ifdef VANILLA_WIN32 CloseHandle(GetStdHandle(fd)); - SetStdHandle(fd, pipe->fds[fd_index]); + SetStdHandle(fd, pipe->fhs[fd_index]); #else close(fd); dup2(fd, pipe->fds[fd_index]); From 3dc9d47c0b57ab3317dbf9cb129b008739ae7c34 Mon Sep 17 00:00:00 2001 From: Snaipe Date: Fri, 11 Sep 2015 00:21:31 +0200 Subject: [PATCH 03/17] Added file assertions for redirection --- include/criterion/assert.h | 3 +- include/criterion/preprocess.h | 1 + include/criterion/redirect.h | 41 +++++++++++++- samples/CMakeLists.txt | 1 + src/i18n.c | 6 ++ src/posix-compat.c | 100 +++++++++++++++++++++++++++++++-- src/process.c | 3 +- 7 files changed, 144 insertions(+), 11 deletions(-) diff --git a/include/criterion/assert.h b/include/criterion/assert.h index 62c578c..6d50a1c 100644 --- a/include/criterion/assert.h +++ b/include/criterion/assert.h @@ -61,6 +61,7 @@ enum criterion_assert_messages { CRITERION_ASSERT_MSG_IS_NOT_NULL, CRITERION_ASSERT_MSG_IS_EMPTY, CRITERION_ASSERT_MSG_IS_NOT_EMPTY, + CRITERION_ASSERT_MSG_FILE_STR_MATCH, }; CR_BEGIN_C_API @@ -80,7 +81,7 @@ CR_END_C_API # endif # define CR_TRANSLATE_DEF_MSG__(Arg) \ - CR_EXPAND Arg + CR_IDENTITY Arg # define CR_TRANSLATE_DEF_MSG_(...) \ CR_EXPAND(translate_assert_msg( \ diff --git a/include/criterion/preprocess.h b/include/criterion/preprocess.h index 822e0e3..aee370a 100644 --- a/include/criterion/preprocess.h +++ b/include/criterion/preprocess.h @@ -33,6 +33,7 @@ # endif # define CR_EXPAND(x) x +# define CR_IDENTITY(...) __VA_ARGS__ # define CR_STR(x) CR_EXPAND(CR_STR_(x)) # define CR_STR_(x) #x diff --git a/include/criterion/redirect.h b/include/criterion/redirect.h index bb5bc6c..cf1efcf 100644 --- a/include/criterion/redirect.h +++ b/include/criterion/redirect.h @@ -25,6 +25,13 @@ # define CRITERION_REDIRECT_H_ # include "common.h" +# include "assert.h" + +# ifdef __cplusplus +# include +# else +# include +# endif CR_BEGIN_C_API @@ -32,10 +39,38 @@ CR_API void cr_redirect_stdout(void); CR_API void cr_redirect_stderr(void); CR_API void cr_redirect_stdin(void); -CR_API FILE* cr_get_redirected_stdout(void); -CR_API FILE* cr_get_redirected_stderr(void); -CR_API FILE* cr_get_redirected_stdin(void); +CR_API CR_STDN FILE* cr_get_redirected_stdout(void); +CR_API CR_STDN FILE* cr_get_redirected_stderr(void); +CR_API CR_STDN FILE* cr_get_redirected_stdin(void); + +CR_API int cr_file_match_str(CR_STDN FILE* f, const char *str); CR_END_C_API +# define cr_assert_redir_op_(Fail, Fun, Op, File, Str, ...) \ + CR_EXPAND(cr_assert_impl( \ + Fail, \ + !(Fun((File), (Str)) Op 0), \ + dummy, \ + CRITERION_ASSERT_MSG_FILE_STR_MATCH, \ + (CR_STR(File), Str), \ + __VA_ARGS__ \ + )) + +# define cr_assert_redir_op_va_(Fail, Fun, Op, ...) \ + CR_EXPAND(cr_assert_redir_op_( \ + Fail, \ + Fun, \ + Op, \ + CR_VA_HEAD(__VA_ARGS__), \ + CR_VA_HEAD(CR_VA_TAIL(__VA_ARGS__)), \ + CR_VA_TAIL(CR_VA_TAIL(__VA_ARGS__)) \ + )) + +# define cr_assert_file_contents_match_str(...) CR_EXPAND(cr_assert_redir_op_va_(CR_FAIL_ABORT_, cr_file_match_str, ==, __VA_ARGS__)) +# define cr_expect_file_contents_match_str(...) CR_EXPAND(cr_assert_redir_op_va_(CR_FAIL_CONTINUES_, cr_file_match_str, ==, __VA_ARGS__)) + +# define cr_assert_file_contents_not_match_str(...) CR_EXPAND(cr_assert_redir_op_va_(CR_FAIL_ABORT_, cr_file_match_str, !=, __VA_ARGS__)) +# define cr_expect_file_contents_not_match_str(...) CR_EXPAND(cr_assert_redir_op_va_(CR_FAIL_CONTINUES_, cr_file_match_str, !=, __VA_ARGS__)) + #endif /* !CRITERION_REDIRECT_H_ */ diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index 038f8c2..f792c1d 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -15,6 +15,7 @@ set(SAMPLES description.c simple.c theories.c + redirect.c signal.cc report.cc diff --git a/src/i18n.c b/src/i18n.c index b600399..fb6e7d7 100644 --- a/src/i18n.c +++ b/src/i18n.c @@ -17,6 +17,12 @@ char *translate_assert_msg(int msg_index, ...) { [CRITERION_ASSERT_MSG_IS_NOT_NULL] = N_("%s is not null."), [CRITERION_ASSERT_MSG_IS_EMPTY] = N_("%s is empty."), [CRITERION_ASSERT_MSG_IS_NOT_EMPTY] = N_("%s is not empty."), + +#ifdef ENABLE_NLS + [CRITERION_ASSERT_MSG_FILE_STR_MATCH] = N_("The file contents of %1$s does not match the string \"%2$s\"."), +#else + [CRITERION_ASSERT_MSG_FILE_STR_MATCH] = "The file contents of %s does not match the string \"%s\".", +#endif }; va_list vl; diff --git a/src/posix-compat.c b/src/posix-compat.c index 55a506f..45cf897 100644 --- a/src/posix-compat.c +++ b/src/posix-compat.c @@ -453,8 +453,72 @@ cr_std_fd get_std_fd(int fd_kind) { return kinds[fd_kind]; } -void cr_redirect(int fd_kind, s_pipe_handle *pipe, int fd_index) { - if (stdpipe_stack(pipe) < 0) +FILE* get_std_file(int fd_kind) { + switch (fd_kind) { + case CR_STDIN: return stdin; + case CR_STDOUT: return stdout; + case CR_STDERR: return stderr; + } + return NULL; +} + +int make_redirect_pipe(s_pipe_handle *handle, int noblock) { +#ifdef VANILLA_WIN32 + static char pipe_name[256] = {0}; + + HANDLE fhs[2]; + SECURITY_ATTRIBUTES attr = { + .nLength = sizeof (SECURITY_ATTRIBUTES), + .bInheritHandle = TRUE + }; + if (!pipe_name[0]) { + snprintf(pipe_name, sizeof (pipe_name), + "\\\\.\\pipe\\criterion_%d", GetCurrentProcessId()); + } + fhs[0] = CreateNamedPipe(pipe_name, + PIPE_ACCESS_DUPLEX, + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE + | (noblock ? PIPE_NOWAIT : PIPE_WAIT), + PIPE_UNLIMITED_INSTANCES + 4096 * 4, + 4096 * 4, + 0, + attr); + + if (fds[0] == INVALID_HANDLE_VALUE) + return 0; + + fhs[1] = CreateFile(pipe_name, + GENERIC_READ | GENERIC_WRITE, + 0, + attr, + OPEN_EXISTING, + 0, + NULL); + + if (fds[1] == INVALID_HANDLE_VALUE) { + CloseHandle(fds[0]); + return 0; + } + + *handle = (s_pipe_handle) {{ fhs[0], fhs[1] }}; +#else + int fds[2] = { -1, -1 }; + if (pipe(fds) == -1) + return 0; + + if (noblock) + for (int i = 0; i < 2; ++i) + fcntl(fds[i], F_SETFL, fcntl(fds[i], F_GETFL) | O_NONBLOCK); + + *handle = (s_pipe_handle) {{ fds[0], fds[1] }}; +#endif + return 1; +} + +void cr_redirect(int fd_kind, s_pipe_handle *pipe, int fd_index, int noblock) { + fflush(get_std_file(fd_kind)); + if (make_redirect_pipe(pipe, noblock) < 0) cr_assert_fail("Could not redirect standard file descriptor."); cr_std_fd fd = get_std_fd(fd_kind); @@ -463,21 +527,21 @@ void cr_redirect(int fd_kind, s_pipe_handle *pipe, int fd_index) { SetStdHandle(fd, pipe->fhs[fd_index]); #else close(fd); - dup2(fd, pipe->fds[fd_index]); + dup2(pipe->fds[fd_index], fd); close(pipe->fds[fd_index]); #endif } void cr_redirect_stdout(void) { - cr_redirect(CR_STDOUT, &stdout_redir, PIPE_WRITE); + cr_redirect(CR_STDOUT, &stdout_redir, PIPE_WRITE, 1); } void cr_redirect_stderr(void) { - cr_redirect(CR_STDERR, &stderr_redir, PIPE_WRITE); + cr_redirect(CR_STDERR, &stderr_redir, PIPE_WRITE, 1); } void cr_redirect_stdin(void) { - cr_redirect(CR_STDIN, &stdin_redir, PIPE_READ); + cr_redirect(CR_STDIN, &stdin_redir, PIPE_READ, 0); } FILE* cr_get_redirected_stdout(void) { @@ -509,3 +573,27 @@ FILE* cr_get_redirected_stdin(void) { } return f; } + +int cr_file_match_str(FILE* f, const char *str) { + size_t len = strlen(str); + + fputs(str, stderr); + char buf[512]; + size_t read; + int matches = 0; + while ((read = fread(buf, 1, sizeof (buf), f)) > 0) { + matches = !strncmp(buf, str, read); + if (!matches || read > len) { + matches = 0; + break; + } + + len -= read; + str += read; + } + + // consume the rest of what's available + while (fread(buf, 1, sizeof (buf), f) > 0); + + return matches; +} diff --git a/src/process.c b/src/process.c index dd0a0c7..561665c 100644 --- a/src/process.c +++ b/src/process.c @@ -27,6 +27,7 @@ #include "criterion/types.h" #include "criterion/options.h" +#include "criterion/redirect.h" #include "process.h" #include "event.h" #include "posix-compat.h" @@ -61,7 +62,7 @@ struct event *worker_read_event(struct process *proc) { } void run_worker(struct worker_context *ctx) { - fclose(stdin); + cr_redirect_stdin(); g_event_pipe = pipe_out(ctx->pipe, 1); ctx->func(ctx->test, ctx->suite); From ec1095c96628bd932be9e4e27ac92251503b6ea5 Mon Sep 17 00:00:00 2001 From: Snaipe Date: Fri, 11 Sep 2015 02:16:54 +0200 Subject: [PATCH 04/17] Added redirect.c sample --- samples/redirect.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 samples/redirect.c diff --git a/samples/redirect.c b/samples/redirect.c new file mode 100644 index 0000000..fdeeb78 --- /dev/null +++ b/samples/redirect.c @@ -0,0 +1,15 @@ +#include +#include + +#include + +Test(redirect, stdout) { + cr_redirect_stdout(); + + fprintf(stdout, "foo"); + fflush(stdout); + + FILE* stdout_in = cr_get_redirected_stdout(); + + cr_assert_file_contents_match_str(stdout_in, "foo"); +} From 9444d4f0a13cc1126854819c3654175a2e53fae5 Mon Sep 17 00:00:00 2001 From: Snaipe Date: Fri, 11 Sep 2015 02:24:10 +0200 Subject: [PATCH 05/17] Fixed windows redirect pipe code --- src/posix-compat.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/posix-compat.c b/src/posix-compat.c index 45cf897..f809a1d 100644 --- a/src/posix-compat.c +++ b/src/posix-compat.c @@ -462,7 +462,7 @@ FILE* get_std_file(int fd_kind) { return NULL; } -int make_redirect_pipe(s_pipe_handle *handle, int noblock) { +int make_redirect_pipe(s_pipe_handle *handle, int id, int noblock) { #ifdef VANILLA_WIN32 static char pipe_name[256] = {0}; @@ -473,36 +473,38 @@ int make_redirect_pipe(s_pipe_handle *handle, int noblock) { }; if (!pipe_name[0]) { snprintf(pipe_name, sizeof (pipe_name), - "\\\\.\\pipe\\criterion_%d", GetCurrentProcessId()); + "\\\\.\\pipe\\criterion_%lu_%d", GetCurrentProcessId(), id); } fhs[0] = CreateNamedPipe(pipe_name, PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | (noblock ? PIPE_NOWAIT : PIPE_WAIT), - PIPE_UNLIMITED_INSTANCES + PIPE_UNLIMITED_INSTANCES, 4096 * 4, 4096 * 4, 0, - attr); + &attr); - if (fds[0] == INVALID_HANDLE_VALUE) + if (fhs[0] == INVALID_HANDLE_VALUE) return 0; fhs[1] = CreateFile(pipe_name, GENERIC_READ | GENERIC_WRITE, 0, - attr, + &attr, OPEN_EXISTING, 0, NULL); - if (fds[1] == INVALID_HANDLE_VALUE) { - CloseHandle(fds[0]); + if (fhs[1] == INVALID_HANDLE_VALUE) { + CloseHandle(fhs[0]); return 0; } *handle = (s_pipe_handle) {{ fhs[0], fhs[1] }}; #else + (void) id; + int fds[2] = { -1, -1 }; if (pipe(fds) == -1) return 0; @@ -518,7 +520,7 @@ int make_redirect_pipe(s_pipe_handle *handle, int noblock) { void cr_redirect(int fd_kind, s_pipe_handle *pipe, int fd_index, int noblock) { fflush(get_std_file(fd_kind)); - if (make_redirect_pipe(pipe, noblock) < 0) + if (make_redirect_pipe(pipe, fd_kind, noblock) < 0) cr_assert_fail("Could not redirect standard file descriptor."); cr_std_fd fd = get_std_fd(fd_kind); From 73255382a385032554ea5fb51d7551e9b98c0bd5 Mon Sep 17 00:00:00 2001 From: Snaipe Date: Fri, 11 Sep 2015 02:34:25 +0200 Subject: [PATCH 06/17] Fixed stdout being macro-substitued in test name --- samples/redirect.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/redirect.c b/samples/redirect.c index fdeeb78..f32c0c8 100644 --- a/samples/redirect.c +++ b/samples/redirect.c @@ -3,7 +3,7 @@ #include -Test(redirect, stdout) { +Test(redirect, test_stdout) { cr_redirect_stdout(); fprintf(stdout, "foo"); From 2642c420f4f491888f7341f955959e21921f1ea1 Mon Sep 17 00:00:00 2001 From: Snaipe Date: Fri, 11 Sep 2015 02:38:10 +0200 Subject: [PATCH 07/17] Added missing header to posix-compat.c --- src/posix-compat.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/posix-compat.c b/src/posix-compat.c index f809a1d..85ff1ce 100644 --- a/src/posix-compat.c +++ b/src/posix-compat.c @@ -25,6 +25,7 @@ #include "posix-compat.h" #include "process.h" #include "criterion/assert.h" +#include "criterion/redirect.h" #ifdef VANILLA_WIN32 # define VC_EXTRALEAN From d1861db6d0fb41c0604d65500e15cc297ced71e7 Mon Sep 17 00:00:00 2001 From: Snaipe Date: Fri, 11 Sep 2015 02:50:17 +0200 Subject: [PATCH 08/17] Removed debug code --- src/posix-compat.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/posix-compat.c b/src/posix-compat.c index 85ff1ce..b8a88da 100644 --- a/src/posix-compat.c +++ b/src/posix-compat.c @@ -580,7 +580,6 @@ FILE* cr_get_redirected_stdin(void) { int cr_file_match_str(FILE* f, const char *str) { size_t len = strlen(str); - fputs(str, stderr); char buf[512]; size_t read; int matches = 0; From 1d06689aa044dd91239279e0f8726f7d156a49b0 Mon Sep 17 00:00:00 2001 From: Snaipe Date: Fri, 11 Sep 2015 03:16:50 +0200 Subject: [PATCH 09/17] Enhanced redirect sample --- samples/redirect.c | 48 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/samples/redirect.c b/samples/redirect.c index f32c0c8..895eb56 100644 --- a/samples/redirect.c +++ b/samples/redirect.c @@ -2,14 +2,56 @@ #include #include +#include -Test(redirect, test_stdout) { +// Testing stdout/stderr + +void redirect_all_std(void) { cr_redirect_stdout(); + cr_redirect_stderr(); +} + +Test(redirect, test_outputs, .init = redirect_all_std) { + FILE* f_stdout = cr_get_redirected_stdout(); fprintf(stdout, "foo"); fflush(stdout); - FILE* stdout_in = cr_get_redirected_stdout(); + cr_assert_file_contents_match_str(f_stdout, "foo"); - cr_assert_file_contents_match_str(stdout_in, "foo"); + FILE* f_stderr = cr_get_redirected_stderr(); + + fprintf(stderr, "bar"); + fflush(stderr); + + cr_assert_file_contents_match_str(f_stderr, "bar"); +} + +// Testing general I/O with sample command-line rot13 + +char rot13_char(char c) { + return isalpha(c) ? (c - 'a' + 13) % 26 + 'a' : c; +} + +void rot13_io(void) { + char buf[512]; + + size_t read; + while ((read = fread(buf, 1, sizeof (buf), stdin)) > 0) { + for (size_t i = 0; i < read; ++i) + buf[i] = rot13_char(buf[i]); + fwrite(buf, 1, read, stdout); + } + fflush(stdout); +} + +Test(redirect, rot13, .init = cr_redirect_stdout) { + FILE* f_stdin = cr_get_redirected_stdin(); + fprintf(f_stdin, "the quick brown fox jumps over the lazy dog"); + fclose(f_stdin); + + rot13_io(); + + FILE* f_stdout = cr_get_redirected_stdout(); + cr_assert_file_contents_match_str(f_stdout, "gur dhvpx oebja sbk whzcf bire gur ynml qbt"); } From 30e71b42eb822e5ffb4778b219e12d502875605c Mon Sep 17 00:00:00 2001 From: Snaipe Date: Sat, 12 Sep 2015 08:52:03 -0700 Subject: [PATCH 10/17] Corrected the windows redirect code for pipe initialization --- src/posix-compat.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/posix-compat.c b/src/posix-compat.c index b8a88da..eb2ba3d 100644 --- a/src/posix-compat.c +++ b/src/posix-compat.c @@ -465,22 +465,19 @@ FILE* get_std_file(int fd_kind) { int make_redirect_pipe(s_pipe_handle *handle, int id, int noblock) { #ifdef VANILLA_WIN32 - static char pipe_name[256] = {0}; - HANDLE fhs[2]; SECURITY_ATTRIBUTES attr = { .nLength = sizeof (SECURITY_ATTRIBUTES), .bInheritHandle = TRUE }; - if (!pipe_name[0]) { - snprintf(pipe_name, sizeof (pipe_name), - "\\\\.\\pipe\\criterion_%lu_%d", GetCurrentProcessId(), id); - } + char pipe_name[256] = {0}; + snprintf(pipe_name, sizeof (pipe_name), + "\\\\.\\pipe\\criterion_%lu_%d", GetCurrentProcessId(), id); fhs[0] = CreateNamedPipe(pipe_name, - PIPE_ACCESS_DUPLEX, - PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE - | (noblock ? PIPE_NOWAIT : PIPE_WAIT), - PIPE_UNLIMITED_INSTANCES, + PIPE_ACCESS_INBOUND, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE + | (noblock ? PIPE_NOWAIT : PIPE_WAIT), + 1, 4096 * 4, 4096 * 4, 0, @@ -490,7 +487,7 @@ int make_redirect_pipe(s_pipe_handle *handle, int id, int noblock) { return 0; fhs[1] = CreateFile(pipe_name, - GENERIC_READ | GENERIC_WRITE, + GENERIC_WRITE, 0, &attr, OPEN_EXISTING, @@ -521,7 +518,7 @@ int make_redirect_pipe(s_pipe_handle *handle, int id, int noblock) { void cr_redirect(int fd_kind, s_pipe_handle *pipe, int fd_index, int noblock) { fflush(get_std_file(fd_kind)); - if (make_redirect_pipe(pipe, fd_kind, noblock) < 0) + if (!make_redirect_pipe(pipe, fd_kind, noblock)) cr_assert_fail("Could not redirect standard file descriptor."); cr_std_fd fd = get_std_fd(fd_kind); From 6c35c644568eaabd57a7d7b950ed1ff72bd16a26 Mon Sep 17 00:00:00 2001 From: Snaipe Date: Sat, 12 Sep 2015 09:33:55 -0700 Subject: [PATCH 11/17] Fixed windows redirection not replacing standard file pointers --- src/posix-compat.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/posix-compat.c b/src/posix-compat.c index eb2ba3d..2b0c6a7 100644 --- a/src/posix-compat.c +++ b/src/posix-compat.c @@ -524,7 +524,14 @@ void cr_redirect(int fd_kind, s_pipe_handle *pipe, int fd_index, int noblock) { cr_std_fd fd = get_std_fd(fd_kind); #ifdef VANILLA_WIN32 CloseHandle(GetStdHandle(fd)); + _close(fd_kind); SetStdHandle(fd, pipe->fhs[fd_index]); + + fflush(NULL); + FILE *stdf = fd_kind == 0 ? pipe_in(pipe, 0) : pipe_out(pipe, 0); + if (stdf == NULL) + cr_assert_fail("Could not redirect standard file descriptor."); + _dup2(_fileno(stdf), fd_kind); #else close(fd); dup2(pipe->fds[fd_index], fd); From 17a609ba8898835aae813d35a4c906df24eda84d Mon Sep 17 00:00:00 2001 From: Snaipe Date: Sat, 12 Sep 2015 10:22:14 -0700 Subject: [PATCH 12/17] Fixed windows redirect code not working for stderr --- src/posix-compat.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/posix-compat.c b/src/posix-compat.c index 2b0c6a7..2e46ca6 100644 --- a/src/posix-compat.c +++ b/src/posix-compat.c @@ -428,9 +428,9 @@ static s_pipe_handle stderr_redir; static s_pipe_handle stdin_redir; enum criterion_std_fd { - CR_STDIN, - CR_STDOUT, - CR_STDERR + CR_STDIN = 0, + CR_STDOUT = 1, + CR_STDERR = 2, }; enum criterion_pipe_end { @@ -523,15 +523,19 @@ void cr_redirect(int fd_kind, s_pipe_handle *pipe, int fd_index, int noblock) { cr_std_fd fd = get_std_fd(fd_kind); #ifdef VANILLA_WIN32 - CloseHandle(GetStdHandle(fd)); + int stdfd = _open_osfhandle((intptr_t) pipe->fhs[fd_index], fd_kind == 0 ? _O_RDONLY : _O_WRONLY); + if (stdfd == -1) + cr_assert_fail("Could not redirect standard file descriptor."); + + fflush(get_std_file(fd_kind)); + _close(fd_kind); SetStdHandle(fd, pipe->fhs[fd_index]); - fflush(NULL); - FILE *stdf = fd_kind == 0 ? pipe_in(pipe, 0) : pipe_out(pipe, 0); - if (stdf == NULL) - cr_assert_fail("Could not redirect standard file descriptor."); - _dup2(_fileno(stdf), fd_kind); + _dup2(stdfd, fd_kind); + _close(stdfd); + + setvbuf(get_std_file(fd_kind), NULL, _IONBF, 0); #else close(fd); dup2(pipe->fds[fd_index], fd); From 2be4bfd24151794223efa631211bc9ba9122870a Mon Sep 17 00:00:00 2001 From: Snaipe Date: Sat, 12 Sep 2015 10:24:00 -0700 Subject: [PATCH 13/17] Added C++ redirect sample --- samples/CMakeLists.txt | 1 + samples/redirect.cc | 56 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 samples/redirect.cc diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index f792c1d..b7b13cc 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -26,6 +26,7 @@ set(SAMPLES description.cc simple.cc theories.cc + redirect.cc ) set(SCRIPTS diff --git a/samples/redirect.cc b/samples/redirect.cc new file mode 100644 index 0000000..f4eae63 --- /dev/null +++ b/samples/redirect.cc @@ -0,0 +1,56 @@ +#include +#include + +#include +#include +#include +#include + +// Testing stdout/stderr + +void redirect_all_std(void) { + cr_redirect_stdout(); + cr_redirect_stderr(); +} + +Test(redirect, test_outputs, .init = redirect_all_std) { + std::FILE* f_stdout = cr_get_redirected_stdout(); + + std::cout << "foo" << std::flush; + + cr_assert_file_contents_match_str(f_stdout, "foo"); + + std::FILE* f_stderr = cr_get_redirected_stderr(); + + std::cerr << "bar" << std::flush; + + cr_assert_file_contents_match_str(f_stderr, "bar"); +} + +// Testing general I/O with sample command-line rot13 + +char rot13_char(char c) { + return std::isalpha(c) ? (c - 'a' + 13) % 26 + 'a' : c; +} + +void rot13_io(void) { + std::string s; + + std::cin >> s; + for (size_t i = 0; i < s.length(); ++i) + s[i] = rot13_char(s[i]); + std::cout << s << std::flush; +} + +Test(redirect, rot13, .init = cr_redirect_stdout) { + std::FILE* f_stdin = cr_get_redirected_stdin(); + std::ofstream f_cin(f_stdin); + + f_cin << "the quick brown fox jumps over the lazy dog"; + f_cin.close(); + + rot13_io(); + + std::FILE* f_stdout = cr_get_redirected_stdout(); + cr_assert_file_contents_match_str(f_stdout, "gur dhvpx oebja sbk whzcf bire gur ynml qbt"); +} From ac6415d465ab12c269629c1139f47c0cae7cc80a Mon Sep 17 00:00:00 2001 From: Snaipe Date: Sun, 13 Sep 2015 13:11:11 +0200 Subject: [PATCH 14/17] Added C++ stream interface for redirection functions --- include/criterion/redirect.h | 104 +++++++++++++++++++++++++++++++++++ samples/redirect.cc | 14 ++--- 2 files changed, 111 insertions(+), 7 deletions(-) diff --git a/include/criterion/redirect.h b/include/criterion/redirect.h index cf1efcf..eacd0bb 100644 --- a/include/criterion/redirect.h +++ b/include/criterion/redirect.h @@ -29,6 +29,12 @@ # ifdef __cplusplus # include +# include +# include + +# ifdef __GNUC__ +# include +# endif # else # include # endif @@ -73,4 +79,102 @@ CR_END_C_API # define cr_assert_file_contents_not_match_str(...) CR_EXPAND(cr_assert_redir_op_va_(CR_FAIL_ABORT_, cr_file_match_str, !=, __VA_ARGS__)) # define cr_expect_file_contents_not_match_str(...) CR_EXPAND(cr_assert_redir_op_va_(CR_FAIL_CONTINUES_, cr_file_match_str, !=, __VA_ARGS__)) +# ifdef __cplusplus +namespace criterion { + + template + class basic_ofstream : public std::basic_ofstream { + public: + basic_ofstream(FILE* f) +# ifdef __GNUC__ + : std::ofstream() + , fbuf(new ::__gnu_cxx::stdio_filebuf(f, std::ios::out)) +# else + : std::ofstream(f) +# endif + , file(f) + { + std::ios::rdbuf(&*fbuf); + } + + void close(void) { + std::basic_ofstream::flush(); + std::basic_ofstream::close(); + std::fclose(file); + } + + private: +# ifdef __GNUC__ + std::unique_ptr<::__gnu_cxx::stdio_filebuf> fbuf; +# endif + std::FILE* file; + }; + + template + class basic_ifstream : public std::basic_ifstream { + public: + basic_ifstream(FILE* f) +# ifdef __GNUC__ + : std::ifstream() + , fbuf(new ::__gnu_cxx::stdio_filebuf(f, std::ios::in)) +# else + : std::ifstream(f) +# endif + , file(f) + { + std::ios::rdbuf(&*fbuf); + } + + void close(void) { + std::basic_ifstream::flush(); + std::basic_ifstream::close(); + std::fclose(file); + } + + private: +# ifdef __GNUC__ + std::unique_ptr<::__gnu_cxx::stdio_filebuf> fbuf; +# endif + std::FILE* file; + }; + + template + struct get_redirected_out_stream_ { + static inline basic_ofstream& call(std::FILE* f) { + static std::unique_ptr> stream; + + if (!stream) + stream.reset(new basic_ofstream(f)); + return *stream; + } + + }; + + template + struct get_redirected_in_stream_ { + static inline basic_ifstream& call(std::FILE* f) { + static std::unique_ptr> stream; + if (!stream) + stream.reset(new basic_ifstream(f)); + return *stream; + } + }; + + using ofstream = basic_ofstream; + using ifstream = basic_ifstream; + + static inline ofstream& get_redirected_cin(void) { + return get_redirected_out_stream_::call(cr_get_redirected_stdin()); + } + + static inline ifstream& get_redirected_cout(void) { + return get_redirected_in_stream_::call(cr_get_redirected_stdout()); + } + + static inline ifstream& get_redirected_cerr(void) { + return get_redirected_in_stream_::call(cr_get_redirected_stderr()); + } +} +# endif + #endif /* !CRITERION_REDIRECT_H_ */ diff --git a/samples/redirect.cc b/samples/redirect.cc index f4eae63..29ee036 100644 --- a/samples/redirect.cc +++ b/samples/redirect.cc @@ -6,6 +6,10 @@ #include #include +#ifdef __GNUC__ +# include +#endif + // Testing stdout/stderr void redirect_all_std(void) { @@ -14,16 +18,13 @@ void redirect_all_std(void) { } Test(redirect, test_outputs, .init = redirect_all_std) { - std::FILE* f_stdout = cr_get_redirected_stdout(); - std::cout << "foo" << std::flush; + std::cerr << "bar" << std::flush; + std::FILE* f_stdout = cr_get_redirected_stdout(); cr_assert_file_contents_match_str(f_stdout, "foo"); std::FILE* f_stderr = cr_get_redirected_stderr(); - - std::cerr << "bar" << std::flush; - cr_assert_file_contents_match_str(f_stderr, "bar"); } @@ -43,8 +44,7 @@ void rot13_io(void) { } Test(redirect, rot13, .init = cr_redirect_stdout) { - std::FILE* f_stdin = cr_get_redirected_stdin(); - std::ofstream f_cin(f_stdin); + auto& f_cin = criterion::get_redirected_cin(); f_cin << "the quick brown fox jumps over the lazy dog"; f_cin.close(); From 8232862d599a9e3ebdd4ef27c6afc900432de535 Mon Sep 17 00:00:00 2001 From: Snaipe Date: Sun, 13 Sep 2015 13:11:41 +0200 Subject: [PATCH 15/17] Added french translation message for mismatching file contents --- po/fr.po | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/po/fr.po b/po/fr.po index 79c850d..17d25ae 100644 --- a/po/fr.po +++ b/po/fr.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: criterion 1.0.0\n" "Report-Msgid-Bugs-To: franklinmathieu+criterion@gmail.com\n" -"POT-Creation-Date: 2015-09-10 03:35+0200\n" +"POT-Creation-Date: 2015-09-13 13:10+0200\n" "PO-Revision-Date: 2015-04-03 17:58+0200\n" "Last-Translator: \n" "Language-Team: French\n" @@ -137,3 +137,10 @@ msgstr "%s est vide." #, c-format msgid "%s is not empty." msgstr "%s n'est pas vide." + +#: src/i18n.c:22 +#, c-format +msgid "The file contents of %1$s does not match the string \"%2$s\"." +msgstr "" +"Le contenu du fichier %1$s ne correspond pas à la chaine de caractères \"%2$s" +"\"." From 20c93d86dea0b90129a3ea768c1a17a8c2c525d1 Mon Sep 17 00:00:00 2001 From: Snaipe Date: Sun, 13 Sep 2015 13:45:10 +0200 Subject: [PATCH 16/17] Fixed GCC-specific code being visible to MSVC --- include/criterion/redirect.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/criterion/redirect.h b/include/criterion/redirect.h index eacd0bb..e037691 100644 --- a/include/criterion/redirect.h +++ b/include/criterion/redirect.h @@ -94,7 +94,9 @@ namespace criterion { # endif , file(f) { +# ifdef __GNUC__ std::ios::rdbuf(&*fbuf); +# endif } void close(void) { @@ -122,7 +124,9 @@ namespace criterion { # endif , file(f) { +# ifdef __GNUC__ std::ios::rdbuf(&*fbuf); +# endif } void close(void) { From dd3f5344046790ab9e59aa41e641ca3e28b8d6af Mon Sep 17 00:00:00 2001 From: Snaipe Date: Sun, 13 Sep 2015 13:52:02 +0200 Subject: [PATCH 17/17] Added regression tests for redirect.{c,cc} --- samples/outputs/redirect.c.bin.err.expected | 1 + samples/outputs/redirect.c.bin.out.expected | 0 samples/outputs/redirect.cc.bin.err.expected | 1 + samples/outputs/redirect.cc.bin.out.expected | 0 4 files changed, 2 insertions(+) create mode 100644 samples/outputs/redirect.c.bin.err.expected create mode 100644 samples/outputs/redirect.c.bin.out.expected create mode 100644 samples/outputs/redirect.cc.bin.err.expected create mode 100644 samples/outputs/redirect.cc.bin.out.expected diff --git a/samples/outputs/redirect.c.bin.err.expected b/samples/outputs/redirect.c.bin.err.expected new file mode 100644 index 0000000..09eb50f --- /dev/null +++ b/samples/outputs/redirect.c.bin.err.expected @@ -0,0 +1 @@ +[====] Synthesis: Tested: 2 | Passing: 2 | Failing: 0 | Crashing: 0  diff --git a/samples/outputs/redirect.c.bin.out.expected b/samples/outputs/redirect.c.bin.out.expected new file mode 100644 index 0000000..e69de29 diff --git a/samples/outputs/redirect.cc.bin.err.expected b/samples/outputs/redirect.cc.bin.err.expected new file mode 100644 index 0000000..09eb50f --- /dev/null +++ b/samples/outputs/redirect.cc.bin.err.expected @@ -0,0 +1 @@ +[====] Synthesis: Tested: 2 | Passing: 2 | Failing: 0 | Crashing: 0  diff --git a/samples/outputs/redirect.cc.bin.out.expected b/samples/outputs/redirect.cc.bin.out.expected new file mode 100644 index 0000000..e69de29