24 #include <gmock/gmock.h> 25 #include <gtest/gtest.h> 33 ::testing::AssertionResult is_error(
const std::error_code& ec)
35 return ec ? ::testing::AssertionResult{
true} : ::testing::AssertionResult{
false};
38 struct ForkedSpinningProcess :
public ::testing::Test
66 std::shared_ptr<core::posix::SignalTrap> signal_trap;
67 std::unique_ptr<core::posix::ChildProcess::DeathObserver> death_observer;
72 TEST(PosixProcess, ctor_throws_for_invalid_pid)
74 pid_t invalid_pid{-1};
78 TEST(PosixProcess, this_process_instance_reports_correct_pid)
83 TEST(PosixProcess, this_process_instance_reports_correct_parent)
88 TEST(PosixProcess, throwing_access_to_process_group_id_of_this_process_works)
93 TEST(PosixProcess, non_throwing_access_to_process_group_id_of_this_process_works)
97 EXPECT_FALSE(is_error(se));
98 EXPECT_EQ(getpgrp(), pg.id());
101 TEST(PosixProcess, trying_to_access_process_group_of_invalid_process_throws)
106 TEST(PosixProcess, trying_to_access_process_group_of_invalid_process_reports_error)
110 EXPECT_TRUE(is_error(se));
113 TEST_F(ForkedSpinningProcess, throwing_access_to_process_group_id_of_a_forked_process_works)
115 auto pg = child.process_group_or_throw();
116 EXPECT_EQ(getpgrp(), pg.id());
119 TEST_F(ForkedSpinningProcess, non_throwing_access_to_process_group_id_of_a_forked_process_works)
122 auto pg = child.process_group(se);
125 EXPECT_EQ(getpgrp(), pg.id());
128 TEST(PosixProcess, accessing_streams_of_this_process_works)
131 std::stringstream ss;
134 EXPECT_EQ(ss.str(),
"core::posix::this_process::instance().cout()\n");
139 std::stringstream ss;
142 EXPECT_EQ(ss.str(),
"core::posix::this_process::instance().cerr()\n");
147 TEST(Self, non_mutable_access_to_the_environment_returns_correct_results)
149 static const char* home =
"HOME";
150 static const char* totally_not_existent =
"totally_not_existent_42";
155 TEST(Self, mutable_access_to_the_environment_alters_the_environment)
157 static const char* totally_not_existent =
"totally_not_existent_42";
158 static const char* totally_not_existent_value =
"42";
162 totally_not_existent,
163 totally_not_existent_value));
164 EXPECT_EQ(totally_not_existent_value,
169 totally_not_existent));
174 TEST(Self, getting_env_var_for_empty_key_does_not_throw)
179 TEST(Self, setting_env_var_for_empty_key_throws)
186 TEST(ChildProcess, fork_returns_process_object_with_valid_pid_and_wait_for_returns_correct_result)
191 EXPECT_TRUE(child.
pid() > 0);
197 result.detail.if_exited.status);
202 EXPECT_TRUE(child.
pid() > 0);
208 result.detail.if_exited.status);
211 TEST_F(ForkedSpinningProcess, signalling_a_forked_child_makes_wait_for_return_correct_result)
218 result.detail.if_signaled.signal);
223 EXPECT_TRUE(child.pid() > 0);
230 result.detail.if_signaled.signal);
233 TEST(ChildProcess, stopping_a_forked_child_makes_wait_for_return_correct_result)
247 EXPECT_TRUE(child.
pid() > 0);
249 const std::string echo_value{
"42"};
250 child.
cin() << echo_value << std::endl;
251 std::string line; child.
cout() >> line;
253 EXPECT_EQ(echo_value, line);
260 result.detail.if_stopped.signal);
267 result.detail.if_signaled.signal);
270 TEST(ChildProcess, exec_returns_process_object_with_valid_pid_and_wait_for_returns_correct_result)
272 const std::string program{
"/usr/bin/sleep"};
273 const std::vector<std::string> argv = {
"10"};
274 std::map<std::string, std::string> env;
277 env.insert(std::make_pair(key, value));
284 EXPECT_TRUE(child.pid() > 0);
290 result.detail.if_signaled.signal);
293 TEST(ChildProcess, exec_child_setup)
295 const std::string program{
"/usr/bin/sleep"};
296 const std::vector<std::string> argv = {
"10"};
297 std::map<std::string, std::string> env;
298 std::function<void()> child_setup = []()
308 EXPECT_TRUE(child.
pid() > 0);
310 child.
cout() >> output;
311 EXPECT_EQ(
"hello_there", output);
314 TEST(ChildProcess, signalling_an_execd_child_makes_wait_for_return_correct_result)
316 const std::string program{
"/usr/bin/env"};
317 const std::vector<std::string> argv = {};
318 std::map<std::string, std::string> env;
321 env.insert(std::make_pair(key, value));
330 EXPECT_TRUE(child.pid() > 0);
337 result.detail.if_signaled.signal);
343 EXPECT_TRUE(child.pid() > 0);
350 result.detail.if_signaled.signal);
353 TEST(ChildProcess, stopping_an_execd_child_makes_wait_for_return_correct_result)
355 const std::string program{
"/usr/bin/sleep"};
356 const std::vector<std::string> argv = {
"10"};
357 std::map<std::string, std::string> env;
360 env.insert(std::make_pair(key, value));
367 EXPECT_TRUE(child.pid() > 0);
374 result.detail.if_signaled.signal);
380 result.detail.if_signaled.signal);
385 struct ChildDeathObserverEventCollector
391 TEST_F(ForkedSpinningProcess, observing_child_processes_for_death_works_if_child_is_signalled_with_sigkill)
393 using namespace ::testing;
395 ChildDeathObserverEventCollector event_collector;
397 core::ScopedConnection sc
401 event_collector.on_child_died(cp);
405 EXPECT_TRUE(init.death_observer->add(child));
406 EXPECT_CALL(event_collector, on_child_died(_))
410 init.signal_trap.get(),
413 std::thread worker{[]() { init.signal_trap->run(); }};
417 if (worker.joinable())
421 TEST_F(ForkedSpinningProcess, observing_child_processes_for_death_works_if_child_is_signalled_with_sigterm)
423 using namespace ::testing;
425 ChildDeathObserverEventCollector signal_trap;
427 EXPECT_TRUE(init.death_observer->add(child));
429 core::ScopedConnection sc
433 signal_trap.on_child_died(child);
437 EXPECT_CALL(signal_trap, on_child_died(_))
441 init.signal_trap.get(),
444 std::thread worker{[]() { init.signal_trap->run(); }};
448 if (worker.joinable())
452 TEST(ChildProcess, ensure_that_forked_children_are_cleaned_up)
454 static const unsigned int child_process_count = 100;
455 unsigned int counter = 1;
457 core::ScopedConnection sc
463 if (counter == child_process_count)
465 init.signal_trap->stop();
470 std::thread t([]() {init.signal_trap->run();});
472 for (
unsigned int i = 0; i < child_process_count; i++)
477 init.death_observer->add(child);
480 std::this_thread::sleep_for(std::chrono::milliseconds{5});
486 EXPECT_EQ(child_process_count, counter);
489 TEST(StreamRedirect, redirecting_stdin_stdout_stderr_works)
505 ASSERT_TRUE(child.
pid() > 0);
507 const std::string echo_value{
"42"};
508 child.
cin() << echo_value << std::endl;
510 EXPECT_NO_THROW(child.
cout() >> line);
511 EXPECT_EQ(echo_value, line);
512 EXPECT_NO_THROW(child.
cerr() >> line);
513 EXPECT_EQ(echo_value, line);
518 TEST(Environment, iterating_the_environment_does_not_throw)
521 [](
const std::string& key,
const std::string& value)
523 std::cout << key <<
" -> " << value << std::endl;
527 TEST(Environment, specifying_default_value_for_get_returns_correct_result)
529 const std::string expected_value{
"42"};
530 EXPECT_EQ(expected_value,
534 TEST(Environment, for_each_returns_correct_results)
536 std::array<std::string, 3> env_keys = {
"totally_non_existant_key_in_env_blubb0",
537 "totally_non_existant_key_in_env_blubb1",
538 "totally_non_existant_key_in_env_blubb2"};
539 std::array<std::string, 3> env_vars = {env_keys[0] +
"=" +
"hello",
540 env_keys[1] +
"=" +
"",
541 env_keys[2] +
"=" +
"string=hello"};
542 for(
auto const& env_var : env_vars )
544 ::putenv(const_cast<char*>(env_var.c_str()));
549 if (key == env_keys[0])
551 EXPECT_EQ(
"hello", value);
553 else if (key == env_keys[1])
555 EXPECT_EQ(
"", value);
557 else if (key == env_keys[2])
559 EXPECT_EQ(
"string=hello", value);
static ChildProcess invalid()
Creates an invalid ChildProcess.
static Process invalid()
Returns an invalid instance for testing purposes.
std::istream & cout()
Access this process's stdout.
The process was signalled and stopped.
std::ostream & cin()
Access this process's stdin.
The Process class models a process and possible operations on it.
The process exited normally.
static std::unique_ptr< DeathObserver > create_once_with_signal_trap(std::shared_ptr< SignalTrap > trap)
Creates the unique instance of class DeathObserver.
The process was signalled and terminated.
CORE_POSIX_DLL_PUBLIC std::istream & cin() noexcept(true)
Access this process's stdin.
EXPECT_ANY_THROW(auto death_observer=core::posix::ChildProcess::DeathObserver::create_once_with_signal_trap(trap))
CORE_POSIX_DLL_PUBLIC void for_each(const std::function< void(const std::string &, const std::string &)> &functor) noexcept(true)
for_each invokes a functor for every key-value pair in the environment.
CORE_POSIX_DLL_PUBLIC ChildProcess fork(const std::function< posix::exit::Status()> &main, const StandardStream &flags)
fork forks a new process and executes the provided main function in the newly forked process...
CORE_POSIX_DLL_PUBLIC std::ostream & cerr() noexcept(true)
Access this process's stderr.
CORE_POSIX_DLL_PUBLIC void set_or_throw(const std::string &key, const std::string &value)
set_or_throw will adjust the contents of the variable identified by key to the provided value...
TEST_F(ForkedSpinningProcess, throwing_access_to_process_group_id_of_a_forked_process_works)
CORE_POSIX_DLL_PUBLIC ChildProcess exec(const std::string &fn, const std::vector< std::string > &argv, const std::map< std::string, std::string > &env, const StandardStream &flags)
exec execve's the executable with the provided arguments and environment.
virtual pid_t pid() const
Query the pid of the process.
virtual void stop()=0
Stops execution of the signal trap.
virtual ProcessGroup process_group(std::error_code &se) const noexcept(true)
Queries the id of the process group this process belongs to.
std::istream & cerr()
Access this process's stderr.
CORE_POSIX_DLL_PUBLIC Process instance() noexcept(true)
Returns a Process instance corresponding to this process.
CORE_POSIX_DLL_PUBLIC void unset_or_throw(const std::string &key)
unset_or_throw removes the variable with name key from the environment.
wait::Result wait_for(const wait::Flags &flags)
Wait for the child process to change state.
The Process class models a child process of this process.
CORE_POSIX_DLL_PUBLIC std::shared_ptr< SignalTrap > trap_signals_for_all_subsequent_threads(std::initializer_list< core::posix::Signal > blocked_signals)
Traps the specified signals for the current thread, and inherits the respective signal mask to all ch...
CORE_POSIX_DLL_PUBLIC std::ostream & cout() noexcept(true)
Access this process's stdout.
CORE_POSIX_DLL_PUBLIC std::string get(const std::string &key, const std::string &default_value=std::string()) noexcept(true)
get queries the value of an environment variable.
Also wait for state changes in untraced children.
CORE_POSIX_DLL_PUBLIC Process parent() noexcept(true)
Query the parent of the process.
TEST(PosixProcess, ctor_throws_for_invalid_pid)
virtual void send_signal_or_throw(Signal signal)
Sends a signal to this signalable object.