diff --git a/include/criterion/assert.h b/include/criterion/assert.h
index b3434a5..f88842b 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
new file mode 100644
index 0000000..e037691
--- /dev/null
+++ b/include/criterion/redirect.h
@@ -0,0 +1,184 @@
+/*
+ * 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"
+# include "assert.h"
+
+# ifdef __cplusplus
+# include
+# include
+# include
+
+# ifdef __GNUC__
+# include
+# endif
+# else
+# include
+# endif
+
+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 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__))
+
+# 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)
+ {
+# ifdef __GNUC__
+ std::ios::rdbuf(&*fbuf);
+# endif
+ }
+
+ 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)
+ {
+# ifdef __GNUC__
+ std::ios::rdbuf(&*fbuf);
+# endif
+ }
+
+ 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/po/fr.po b/po/fr.po
index 0ca50cf..84b22a6 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-11 16:47+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"
@@ -142,3 +142,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"
+"\"."
diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt
index 00b2e54..9967e2e 100644
--- a/samples/CMakeLists.txt
+++ b/samples/CMakeLists.txt
@@ -16,6 +16,7 @@ set(SAMPLES
simple.c
theories.c
timeout.c
+ redirect.c
signal.cc
report.cc
@@ -26,6 +27,7 @@ set(SAMPLES
description.cc
simple.cc
theories.cc
+ redirect.cc
)
set(SCRIPTS
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 @@
+[[0;34m====[0m] [0;1mSynthesis: Tested: [0;34m2[0;1m | Passing: [0;32m2[0;1m | Failing: [0;31m0[0;1m | Crashing: [0;31m0[0;1m [0m
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 @@
+[[0;34m====[0m] [0;1mSynthesis: Tested: [0;34m2[0;1m | Passing: [0;32m2[0;1m | Failing: [0;31m0[0;1m | Crashing: [0;31m0[0;1m [0m
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
diff --git a/samples/redirect.c b/samples/redirect.c
new file mode 100644
index 0000000..895eb56
--- /dev/null
+++ b/samples/redirect.c
@@ -0,0 +1,57 @@
+#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) {
+ FILE* f_stdout = cr_get_redirected_stdout();
+
+ fprintf(stdout, "foo");
+ fflush(stdout);
+
+ cr_assert_file_contents_match_str(f_stdout, "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");
+}
diff --git a/samples/redirect.cc b/samples/redirect.cc
new file mode 100644
index 0000000..29ee036
--- /dev/null
+++ b/samples/redirect.cc
@@ -0,0 +1,56 @@
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#ifdef __GNUC__
+# include
+#endif
+
+// Testing stdout/stderr
+
+void redirect_all_std(void) {
+ cr_redirect_stdout();
+ cr_redirect_stderr();
+}
+
+Test(redirect, test_outputs, .init = redirect_all_std) {
+ 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();
+ 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) {
+ auto& f_cin = criterion::get_redirected_cin();
+
+ 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");
+}
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 2808916..21c2c33 100644
--- a/src/posix-compat.c
+++ b/src/posix-compat.c
@@ -24,6 +24,8 @@
#include
#include "posix-compat.h"
#include "process.h"
+#include "criterion/assert.h"
+#include "criterion/redirect.h"
#ifdef VANILLA_WIN32
# define VC_EXTRALEAN
@@ -259,15 +261,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)
@@ -277,15 +281,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)
@@ -295,8 +301,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 = {
@@ -304,16 +309,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() {
@@ -402,3 +413,194 @@ 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 = 0,
+ CR_STDOUT = 1,
+ CR_STDERR = 2,
+};
+
+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];
+}
+
+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 id, int noblock) {
+#ifdef VANILLA_WIN32
+ HANDLE fhs[2];
+ SECURITY_ATTRIBUTES attr = {
+ .nLength = sizeof (SECURITY_ATTRIBUTES),
+ .bInheritHandle = TRUE
+ };
+ char pipe_name[256] = {0};
+ snprintf(pipe_name, sizeof (pipe_name),
+ "\\\\.\\pipe\\criterion_%lu_%d", GetCurrentProcessId(), id);
+ fhs[0] = CreateNamedPipe(pipe_name,
+ PIPE_ACCESS_INBOUND,
+ PIPE_TYPE_BYTE | PIPE_READMODE_BYTE
+ | (noblock ? PIPE_NOWAIT : PIPE_WAIT),
+ 1,
+ 4096 * 4,
+ 4096 * 4,
+ 0,
+ &attr);
+
+ if (fhs[0] == INVALID_HANDLE_VALUE)
+ return 0;
+
+ fhs[1] = CreateFile(pipe_name,
+ GENERIC_WRITE,
+ 0,
+ &attr,
+ OPEN_EXISTING,
+ 0,
+ NULL);
+
+ 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;
+
+ 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, fd_kind, noblock))
+ cr_assert_fail("Could not redirect standard file descriptor.");
+
+ cr_std_fd fd = get_std_fd(fd_kind);
+#ifdef VANILLA_WIN32
+ 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]);
+
+ _dup2(stdfd, fd_kind);
+ _close(stdfd);
+
+ setvbuf(get_std_file(fd_kind), NULL, _IONBF, 0);
+#else
+ close(fd);
+ 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, 1);
+}
+
+void cr_redirect_stderr(void) {
+ cr_redirect(CR_STDERR, &stderr_redir, PIPE_WRITE, 1);
+}
+
+void cr_redirect_stdin(void) {
+ cr_redirect(CR_STDIN, &stdin_redir, PIPE_READ, 0);
+}
+
+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;
+}
+
+int cr_file_match_str(FILE* f, const char *str) {
+ size_t len = strlen(str);
+
+ 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/posix-compat.h b/src/posix-compat.h
index c51ab1b..e6b3005 100644
--- a/src/posix-compat.h
+++ b/src/posix-compat.h
@@ -70,8 +70,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..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,8 +62,8 @@ struct event *worker_read_event(struct process *proc) {
}
void run_worker(struct worker_context *ctx) {
- fclose(stdin);
- g_event_pipe = pipe_out(ctx->pipe);
+ cr_redirect_stdin();
+ g_event_pipe = pipe_out(ctx->pipe, 1);
ctx->func(ctx->test, ctx->suite);
fclose(g_event_pipe);
@@ -102,7 +103,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;