-
-
Notifications
You must be signed in to change notification settings - Fork 3.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement process isolation support for tests #853
Comments
Really, really needed feature! |
👍 |
@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. |
I think I've mentioned this in person but |
+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 where you can pass two file descriptors used for communication. Then we create an external runner called catch2-runner that you run e.g like this: catch2-runner test-executable . The catch2-runner will then create two pipes and launch test-executable with test-executable --managed fd1 fd2. It will then be able to send instructions to the test-executable via one of the fds and receive responses on the other. I imagine we make a simple protocol for communication - perhaps json based. I imagine the protocol will provide catch2-runner with commands like these:
The test-executable running Catch::session::run() in managed mode will be able to respond with
catch2-runner will execute the first command to get a list of all tests and after that it will issue Run test commands every time it receives a response back about a test completing. This would give us the following benefits
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 |
Any word on when this is being implemented? I have an |
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 :) |
1 similar comment
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: google/googletest#2442 and catch2 currently has no Both seems will keep as is for a while 0.0 😆 |
Major features that could be implemented afterwards:
fork
or similar system callREQUIRE_ABORT(expr)
, that launches separate process to checkexpr
and succeeds if the launched process abortsThe text was updated successfully, but these errors were encountered: