[Issue #33] Merge branch 'features/redirect' into bleeding
This commit is contained in:
commit
98a892d344
15 changed files with 540 additions and 21 deletions
|
@ -61,6 +61,7 @@ enum criterion_assert_messages {
|
||||||
CRITERION_ASSERT_MSG_IS_NOT_NULL,
|
CRITERION_ASSERT_MSG_IS_NOT_NULL,
|
||||||
CRITERION_ASSERT_MSG_IS_EMPTY,
|
CRITERION_ASSERT_MSG_IS_EMPTY,
|
||||||
CRITERION_ASSERT_MSG_IS_NOT_EMPTY,
|
CRITERION_ASSERT_MSG_IS_NOT_EMPTY,
|
||||||
|
CRITERION_ASSERT_MSG_FILE_STR_MATCH,
|
||||||
};
|
};
|
||||||
|
|
||||||
CR_BEGIN_C_API
|
CR_BEGIN_C_API
|
||||||
|
@ -80,7 +81,7 @@ CR_END_C_API
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# define CR_TRANSLATE_DEF_MSG__(Arg) \
|
# define CR_TRANSLATE_DEF_MSG__(Arg) \
|
||||||
CR_EXPAND Arg
|
CR_IDENTITY Arg
|
||||||
|
|
||||||
# define CR_TRANSLATE_DEF_MSG_(...) \
|
# define CR_TRANSLATE_DEF_MSG_(...) \
|
||||||
CR_EXPAND(translate_assert_msg( \
|
CR_EXPAND(translate_assert_msg( \
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# define CR_EXPAND(x) x
|
# define CR_EXPAND(x) x
|
||||||
|
# define CR_IDENTITY(...) __VA_ARGS__
|
||||||
|
|
||||||
# define CR_STR(x) CR_EXPAND(CR_STR_(x))
|
# define CR_STR(x) CR_EXPAND(CR_STR_(x))
|
||||||
# define CR_STR_(x) #x
|
# define CR_STR_(x) #x
|
||||||
|
|
184
include/criterion/redirect.h
Normal file
184
include/criterion/redirect.h
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright © 2015 Franklin "Snaipe" Mathieu <http://snai.pe/>
|
||||||
|
*
|
||||||
|
* 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 <cstdio>
|
||||||
|
# include <memory>
|
||||||
|
# include <fstream>
|
||||||
|
|
||||||
|
# ifdef __GNUC__
|
||||||
|
# include <ext/stdio_filebuf.h>
|
||||||
|
# endif
|
||||||
|
# else
|
||||||
|
# include <stdio.h>
|
||||||
|
# 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 <typename CharT>
|
||||||
|
class basic_ofstream : public std::basic_ofstream<CharT> {
|
||||||
|
public:
|
||||||
|
basic_ofstream(FILE* f)
|
||||||
|
# ifdef __GNUC__
|
||||||
|
: std::ofstream()
|
||||||
|
, fbuf(new ::__gnu_cxx::stdio_filebuf<CharT>(f, std::ios::out))
|
||||||
|
# else
|
||||||
|
: std::ofstream(f)
|
||||||
|
# endif
|
||||||
|
, file(f)
|
||||||
|
{
|
||||||
|
# ifdef __GNUC__
|
||||||
|
std::ios::rdbuf(&*fbuf);
|
||||||
|
# endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void close(void) {
|
||||||
|
std::basic_ofstream<CharT>::flush();
|
||||||
|
std::basic_ofstream<CharT>::close();
|
||||||
|
std::fclose(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
# ifdef __GNUC__
|
||||||
|
std::unique_ptr<::__gnu_cxx::stdio_filebuf<CharT>> fbuf;
|
||||||
|
# endif
|
||||||
|
std::FILE* file;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename CharT>
|
||||||
|
class basic_ifstream : public std::basic_ifstream<CharT> {
|
||||||
|
public:
|
||||||
|
basic_ifstream(FILE* f)
|
||||||
|
# ifdef __GNUC__
|
||||||
|
: std::ifstream()
|
||||||
|
, fbuf(new ::__gnu_cxx::stdio_filebuf<CharT>(f, std::ios::in))
|
||||||
|
# else
|
||||||
|
: std::ifstream(f)
|
||||||
|
# endif
|
||||||
|
, file(f)
|
||||||
|
{
|
||||||
|
# ifdef __GNUC__
|
||||||
|
std::ios::rdbuf(&*fbuf);
|
||||||
|
# endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void close(void) {
|
||||||
|
std::basic_ifstream<CharT>::flush();
|
||||||
|
std::basic_ifstream<CharT>::close();
|
||||||
|
std::fclose(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
# ifdef __GNUC__
|
||||||
|
std::unique_ptr<::__gnu_cxx::stdio_filebuf<CharT>> fbuf;
|
||||||
|
# endif
|
||||||
|
std::FILE* file;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename CharT>
|
||||||
|
struct get_redirected_out_stream_ {
|
||||||
|
static inline basic_ofstream<CharT>& call(std::FILE* f) {
|
||||||
|
static std::unique_ptr<basic_ofstream<CharT>> stream;
|
||||||
|
|
||||||
|
if (!stream)
|
||||||
|
stream.reset(new basic_ofstream<CharT>(f));
|
||||||
|
return *stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename CharT>
|
||||||
|
struct get_redirected_in_stream_ {
|
||||||
|
static inline basic_ifstream<CharT>& call(std::FILE* f) {
|
||||||
|
static std::unique_ptr<basic_ifstream<CharT>> stream;
|
||||||
|
if (!stream)
|
||||||
|
stream.reset(new basic_ifstream<CharT>(f));
|
||||||
|
return *stream;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using ofstream = basic_ofstream<char>;
|
||||||
|
using ifstream = basic_ifstream<char>;
|
||||||
|
|
||||||
|
static inline ofstream& get_redirected_cin(void) {
|
||||||
|
return get_redirected_out_stream_<char>::call(cr_get_redirected_stdin());
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline ifstream& get_redirected_cout(void) {
|
||||||
|
return get_redirected_in_stream_<char>::call(cr_get_redirected_stdout());
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline ifstream& get_redirected_cerr(void) {
|
||||||
|
return get_redirected_in_stream_<char>::call(cr_get_redirected_stderr());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# endif
|
||||||
|
|
||||||
|
#endif /* !CRITERION_REDIRECT_H_ */
|
9
po/fr.po
9
po/fr.po
|
@ -8,7 +8,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: criterion 1.0.0\n"
|
"Project-Id-Version: criterion 1.0.0\n"
|
||||||
"Report-Msgid-Bugs-To: franklinmathieu+criterion@gmail.com\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"
|
"PO-Revision-Date: 2015-04-03 17:58+0200\n"
|
||||||
"Last-Translator: <franklinmathieu@gmail.com>\n"
|
"Last-Translator: <franklinmathieu@gmail.com>\n"
|
||||||
"Language-Team: French\n"
|
"Language-Team: French\n"
|
||||||
|
@ -142,3 +142,10 @@ msgstr "%s est vide."
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%s is not empty."
|
msgid "%s is not empty."
|
||||||
msgstr "%s n'est pas vide."
|
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"
|
||||||
|
"\"."
|
||||||
|
|
|
@ -16,6 +16,7 @@ set(SAMPLES
|
||||||
simple.c
|
simple.c
|
||||||
theories.c
|
theories.c
|
||||||
timeout.c
|
timeout.c
|
||||||
|
redirect.c
|
||||||
|
|
||||||
signal.cc
|
signal.cc
|
||||||
report.cc
|
report.cc
|
||||||
|
@ -26,6 +27,7 @@ set(SAMPLES
|
||||||
description.cc
|
description.cc
|
||||||
simple.cc
|
simple.cc
|
||||||
theories.cc
|
theories.cc
|
||||||
|
redirect.cc
|
||||||
)
|
)
|
||||||
|
|
||||||
set(SCRIPTS
|
set(SCRIPTS
|
||||||
|
|
1
samples/outputs/redirect.c.bin.err.expected
Normal file
1
samples/outputs/redirect.c.bin.err.expected
Normal file
|
@ -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
|
0
samples/outputs/redirect.c.bin.out.expected
Normal file
0
samples/outputs/redirect.c.bin.out.expected
Normal file
1
samples/outputs/redirect.cc.bin.err.expected
Normal file
1
samples/outputs/redirect.cc.bin.err.expected
Normal file
|
@ -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
|
0
samples/outputs/redirect.cc.bin.out.expected
Normal file
0
samples/outputs/redirect.cc.bin.out.expected
Normal file
57
samples/redirect.c
Normal file
57
samples/redirect.c
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
#include <criterion/criterion.h>
|
||||||
|
#include <criterion/redirect.h>
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
// 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");
|
||||||
|
}
|
56
samples/redirect.cc
Normal file
56
samples/redirect.cc
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
#include <criterion/criterion.h>
|
||||||
|
#include <criterion/redirect.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <cctype>
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
# include <ext/stdio_filebuf.h>
|
||||||
|
#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");
|
||||||
|
}
|
|
@ -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_NOT_NULL] = N_("%s is not null."),
|
||||||
[CRITERION_ASSERT_MSG_IS_EMPTY] = N_("%s is empty."),
|
[CRITERION_ASSERT_MSG_IS_EMPTY] = N_("%s is empty."),
|
||||||
[CRITERION_ASSERT_MSG_IS_NOT_EMPTY] = N_("%s is not 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;
|
va_list vl;
|
||||||
|
|
|
@ -24,6 +24,8 @@
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include "posix-compat.h"
|
#include "posix-compat.h"
|
||||||
#include "process.h"
|
#include "process.h"
|
||||||
|
#include "criterion/assert.h"
|
||||||
|
#include "criterion/redirect.h"
|
||||||
|
|
||||||
#ifdef VANILLA_WIN32
|
#ifdef VANILLA_WIN32
|
||||||
# define VC_EXTRALEAN
|
# define VC_EXTRALEAN
|
||||||
|
@ -259,15 +261,17 @@ void wait_process(s_proc_handle *handle, int *status) {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
FILE *pipe_in(s_pipe_handle *p) {
|
FILE *pipe_in(s_pipe_handle *p, int do_close) {
|
||||||
#ifdef VANILLA_WIN32
|
#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);
|
int fd = _open_osfhandle((intptr_t) p->fhs[0], _O_RDONLY);
|
||||||
if (fd == -1)
|
if (fd == -1)
|
||||||
return NULL;
|
return NULL;
|
||||||
FILE *in = _fdopen(fd, "r");
|
FILE *in = _fdopen(fd, "r");
|
||||||
#else
|
#else
|
||||||
close(p->fds[1]);
|
if (do_close)
|
||||||
|
close(p->fds[1]);
|
||||||
FILE *in = fdopen(p->fds[0], "r");
|
FILE *in = fdopen(p->fds[0], "r");
|
||||||
#endif
|
#endif
|
||||||
if (!in)
|
if (!in)
|
||||||
|
@ -277,15 +281,17 @@ FILE *pipe_in(s_pipe_handle *p) {
|
||||||
return in;
|
return in;
|
||||||
}
|
}
|
||||||
|
|
||||||
FILE *pipe_out(s_pipe_handle *p) {
|
FILE *pipe_out(s_pipe_handle *p, int do_close) {
|
||||||
#ifdef VANILLA_WIN32
|
#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);
|
int fd = _open_osfhandle((intptr_t) p->fhs[1], _O_WRONLY);
|
||||||
if (fd == -1)
|
if (fd == -1)
|
||||||
return NULL;
|
return NULL;
|
||||||
FILE *out = _fdopen(fd, "w");
|
FILE *out = _fdopen(fd, "w");
|
||||||
#else
|
#else
|
||||||
close(p->fds[0]);
|
if (do_close)
|
||||||
|
close(p->fds[0]);
|
||||||
FILE *out = fdopen(p->fds[1], "w");
|
FILE *out = fdopen(p->fds[1], "w");
|
||||||
#endif
|
#endif
|
||||||
if (!out)
|
if (!out)
|
||||||
|
@ -295,8 +301,7 @@ FILE *pipe_out(s_pipe_handle *p) {
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
s_pipe_handle *stdpipe() {
|
int stdpipe_stack(s_pipe_handle *out) {
|
||||||
s_pipe_handle *handle = smalloc(sizeof (s_pipe_handle));
|
|
||||||
#ifdef VANILLA_WIN32
|
#ifdef VANILLA_WIN32
|
||||||
HANDLE fhs[2];
|
HANDLE fhs[2];
|
||||||
SECURITY_ATTRIBUTES attr = {
|
SECURITY_ATTRIBUTES attr = {
|
||||||
|
@ -304,16 +309,22 @@ s_pipe_handle *stdpipe() {
|
||||||
.bInheritHandle = TRUE
|
.bInheritHandle = TRUE
|
||||||
};
|
};
|
||||||
if (!CreatePipe(fhs, fhs + 1, &attr, 0))
|
if (!CreatePipe(fhs, fhs + 1, &attr, 0))
|
||||||
return NULL;
|
return -1;
|
||||||
*handle = (s_pipe_handle) {{ fhs[0], fhs[1] }};
|
*out = (s_pipe_handle) {{ fhs[0], fhs[1] }};
|
||||||
return handle;
|
|
||||||
#else
|
#else
|
||||||
int fds[2] = { -1, -1 };
|
int fds[2] = { -1, -1 };
|
||||||
if (pipe(fds) == -1)
|
if (pipe(fds) == -1)
|
||||||
return NULL;
|
return -1;
|
||||||
*handle = (s_pipe_handle) {{ fds[0], fds[1] }};
|
*out = (s_pipe_handle) {{ fds[0], fds[1] }};
|
||||||
return handle;
|
|
||||||
#endif
|
#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() {
|
s_proc_handle *get_current_process() {
|
||||||
|
@ -402,3 +413,194 @@ const char *basename_compat(const char *str) {
|
||||||
start = c + 1;
|
start = c + 1;
|
||||||
return start;
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -70,8 +70,8 @@ extern struct worker_context g_worker_context;
|
||||||
int resume_child(void);
|
int resume_child(void);
|
||||||
|
|
||||||
s_pipe_handle *stdpipe();
|
s_pipe_handle *stdpipe();
|
||||||
FILE *pipe_in(s_pipe_handle *p);
|
FILE *pipe_in(s_pipe_handle *p, int do_close);
|
||||||
FILE *pipe_out(s_pipe_handle *p);
|
FILE *pipe_out(s_pipe_handle *p, int do_close);
|
||||||
|
|
||||||
s_proc_handle *fork_process();
|
s_proc_handle *fork_process();
|
||||||
void wait_process(s_proc_handle *handle, int *status);
|
void wait_process(s_proc_handle *handle, int *status);
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
|
|
||||||
#include "criterion/types.h"
|
#include "criterion/types.h"
|
||||||
#include "criterion/options.h"
|
#include "criterion/options.h"
|
||||||
|
#include "criterion/redirect.h"
|
||||||
#include "process.h"
|
#include "process.h"
|
||||||
#include "event.h"
|
#include "event.h"
|
||||||
#include "posix-compat.h"
|
#include "posix-compat.h"
|
||||||
|
@ -61,8 +62,8 @@ struct event *worker_read_event(struct process *proc) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void run_worker(struct worker_context *ctx) {
|
void run_worker(struct worker_context *ctx) {
|
||||||
fclose(stdin);
|
cr_redirect_stdin();
|
||||||
g_event_pipe = pipe_out(ctx->pipe);
|
g_event_pipe = pipe_out(ctx->pipe, 1);
|
||||||
|
|
||||||
ctx->func(ctx->test, ctx->suite);
|
ctx->func(ctx->test, ctx->suite);
|
||||||
fclose(g_event_pipe);
|
fclose(g_event_pipe);
|
||||||
|
@ -102,7 +103,7 @@ struct process *spawn_test_worker(struct criterion_test *test,
|
||||||
.kind = UNIQUE,
|
.kind = UNIQUE,
|
||||||
.dtor = close_process);
|
.dtor = close_process);
|
||||||
|
|
||||||
*ptr = (struct process) { .proc = proc, .in = pipe_in(pipe) };
|
*ptr = (struct process) { .proc = proc, .in = pipe_in(pipe, 1) };
|
||||||
cleanup:
|
cleanup:
|
||||||
sfree(pipe);
|
sfree(pipe);
|
||||||
return ptr;
|
return ptr;
|
||||||
|
|
Loading…
Add table
Reference in a new issue