Catch2
Catch2 copied to clipboard
Implement process isolation support for tests
Major features that could be implemented afterwards:
- Catch should not go crazy if the test case uses
forkor similar system call - Catch should allow running tests in isolated processes
- Catch should allow testing for abort -- there should be a macro along the lines of
REQUIRE_ABORT(expr), that launches separate process to checkexprand succeeds if the launched process aborts - Catch should allow limited time for test suite execution
Really, really needed feature!
:+1:
@philsquared Is this still under consideration? Would really like to see this feature! :-)
#pragma once
#include <functional>
#include <iostream>
#include <sstream>
#include <thread>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>
struct catchpp_stdstream { int fd[3], target; std::stringstream ss; };
static inline bool
catchpp_fork_and_run(std::function<void(void)> fun) {
struct catchpp_stdstream stream[] = {
{ { -1, -1, -1 }, STDOUT_FILENO, std::stringstream() },
{ { -1, -1, -1 }, STDERR_FILENO, std::stringstream() },
};
for (size_t i = 0; i < sizeof(stream) / sizeof(stream[0]); ++i) {
pipe(stream[i].fd);
fcntl(stream[i].fd[0], F_SETFL, O_NONBLOCK);
fcntl(stream[i].fd[1], F_SETFL, O_NONBLOCK);
}
pid_t pid;
if ((pid = fork()) == 0) {
for (size_t i = 0; i < sizeof(stream) / sizeof(stream[0]); ++i) {
dup2(stream[i].fd[1], stream[i].target);
close(stream[i].fd[1]);
close(stream[i].fd[0]);
}
fun();
_exit(0);
}
int code;
waitpid(pid, &code, 0);
char buf[1024];
for (size_t i = 0; i < sizeof(stream) / sizeof(stream[0]); ++i) {
close(stream[i].fd[1]);
for (ssize_t r = 0; (r = read(stream[i].fd[0], buf, sizeof(buf))) > 0;) stream[i].ss.write(buf, r);
close(stream[i].fd[0]);
}
std::cout << stream[0].ss.str();
std::cerr << stream[1].ss.str();
return WIFEXITED(code);
}
#define REQUIRE_ABORT(x) REQUIRE_FALSE(catchpp_fork_and_run(x))
static inline void
catchpp_catch_stdstreams(std::function<void(void)> fun, std::function<void(const std::stringstream &sout, const std::stringstream &serr)> then) {
struct catchpp_stdstream stream[] = {
{ { -1, -1, -1 }, STDOUT_FILENO, std::stringstream() },
{ { -1, -1, -1 }, STDERR_FILENO, std::stringstream() },
};
for (size_t i = 0; i < sizeof(stream) / sizeof(stream[0]); ++i) {
pipe(stream[i].fd);
fcntl(stream[i].fd[0], F_SETFL, O_NONBLOCK);
fcntl(stream[i].fd[1], F_SETFL, O_NONBLOCK);
stream[i].fd[2] = dup(stream[i].target);
dup2(stream[i].fd[1], stream[i].target);
}
fun();
char buf[1024];
for (size_t i = 0; i < sizeof(stream) / sizeof(stream[0]); ++i) {
fsync(stream[i].target);
for (ssize_t r = 0; (r = read(stream[i].fd[0], buf, sizeof(buf))) > 0;) stream[i].ss.write(buf, r);
close(stream[i].fd[0]);
close(stream[i].fd[1]);
dup2(stream[i].fd[2], stream[i].target);
close(stream[i].fd[2]);
}
std::cout << stream[0].ss.str();
std::cerr << stream[1].ss.str();
then(stream[0].ss, stream[1].ss);
}
#define CATCH_STDSTREAM(x, y) catchpp_catch_stdstreams(x, y)
Something I've used. Fork is not really safe for various things though. Isolating tests to own processes would be the right way.
microsoft/GSL#831
I think I've mentioned this in person but ASSERT_DEATH is the only thing keeping using GTest both at work and at home. It's vital to testing contract tests.
I think I've mentioned this in person but
ASSERT_DEATHis the only think keeping using GTest both at work and at home. It's vital to testing contract tests.
+1, we might have to swap over to gtest..
+1 from my side too
Here is an idea for a design:
I imagine that we extend the existing runner Catch::Session::run() with an undocumented command-line switch --managed
- Request list all available tests with tags
- Run test
The test-executable running Catch::session::run() in managed mode will be able to respond with
- List of all available tests with tags
- Test competion with metadata - duration and test result
catch2-runner will execute the first command to get a list of all tests and after that it will issue Run test
This would give us the following benefits
- A crashing test will still give a full (junit) test report xml - only the failing test(s) will be flagged as crashing instead of what happens now which is leave an empty or missing xml report
- We can reliably capture stderr and stdout (I’d like them to be joined) and include that in the junit xml report for much improved usability in CI. stderr+stdout can be thrown away for all tests that don’t fail to avoid generating huge xml files
- We have a path for running multiple instances of test-executable in parallel. For large suites of tests this would be a significant win. All it would take is for catch2-runner to be able to launch multiple copies of test-executable and run different subset of tests in each.
- Ability to timeout individual tests.
Doing this would require some platform specific code and/or some dependencies, ie for creating pipes, starting and managing processes and perhaps a JSON library for the wire protocol.
I have some code that calls std::terminate() if a requested/required allocation would be too large (I have exceptions disabled so throwing an exception is not an option). Would love this feature!
Any word on when this is being implemented? I have an assert in my code to make sure passed values match as an optimization to avoid a strlen call. Notifies others of proper usage, and gets optimized out on release builds. An exception is not exactly ideal here.
Any word on this? The lack of death tests is forcing me to switch to google test and I'd rather not.
Same same! This feature would be highly appreciated :)
Same same! This feature would be highly appreciated :)
I had one rather "happy" night by looking at gtest and catch2 (I want one proper lib for clang-tidy integration):
Now, gtest thinks clang-tidy is giving false positive: https://github.com/google/googletest/issues/2442
and catch2 currently has no EXPECT_DEATH
Both seems will keep as is for a while 0.0 😆