Process tests: check timer status in various tests.
authorDavin McCall <davmac@davmac.org>
Thu, 25 Jan 2018 23:14:17 +0000 (23:14 +0000)
committerDavin McCall <davmac@davmac.org>
Thu, 25 Jan 2018 23:14:17 +0000 (23:14 +0000)
Make sure that timers have been stopped appropriately after state
transitions.

src/tests/proctests.cc
src/tests/test-includes/dinit.h

index d1eae39eb43f8c4151bc5dc91ad53d1814c14073..45b56de7e85682d2c1cf460114d9d1ca0dc2b671 100644 (file)
@@ -12,6 +12,8 @@
 // These tests work mostly by completely mocking out the base_process_service class. The mock
 // implementations can be found in test-baseproc.cc.
 
+extern eventloop_t event_loop;
+
 // Friend interface to access base_process_service private/protected members.
 class base_process_service_test
 {
@@ -35,12 +37,11 @@ namespace bp_sys {
     extern pid_t last_forked_pid;
 }
 
-static void init_service_defaults(process_service &ps)
+static void init_service_defaults(base_process_service &ps)
 {
     ps.set_restart_interval(time_val(10,0), 3);
     ps.set_restart_delay(time_val(0, 200000000)); // 200 milliseconds
     ps.set_stop_timeout(time_val(10,0));
-    ps.set_start_timeout(time_val(10,0));
     ps.set_start_interruptible(false);
 }
 
@@ -68,6 +69,7 @@ void test_proc_service_start()
     sset.process_queues();
 
     assert(p.get_state() == service_state_t::STARTED);
+    assert(event_loop.active_timers.size() == 0);
 }
 
 // Unexpected termination
@@ -95,6 +97,7 @@ void test_proc_unexpected_term()
     sset.process_queues();
 
     assert(p.get_state() == service_state_t::STOPPED);
+    assert(event_loop.active_timers.size() == 0);
 }
 
 // Termination via stop request
@@ -119,16 +122,19 @@ void test_term_via_stop()
     sset.process_queues();
 
     assert(p.get_state() == service_state_t::STARTED);
+    assert(event_loop.active_timers.size() == 0);
 
     p.stop(true);
     sset.process_queues();
 
     assert(p.get_state() == service_state_t::STOPPING);
+    assert(event_loop.active_timers.size() == 1);
 
     base_process_service_test::handle_exit(&p, 0);
     sset.process_queues();
 
     assert(p.get_state() == service_state_t::STOPPED);
+    assert(event_loop.active_timers.size() == 0);
 }
 
 // Time-out during start
@@ -160,6 +166,7 @@ void test_proc_start_timeout()
     sset.process_queues();
 
     assert(p.get_state() == service_state_t::STOPPED);
+    assert(event_loop.active_timers.size() == 0);
 }
 
 // Test stop timeout
@@ -204,6 +211,9 @@ void test_proc_stop_timeout()
     sset.process_queues();
 
     assert(p.get_state() == service_state_t::STOPPED);
+    // Note that timer is still active as we faked its expiry above
+    //assert(event_loop.active_timers.size() == 0);
+    event_loop.active_timers.clear();
 }
 
 // Smooth recovery
@@ -245,6 +255,7 @@ void test_proc_smooth_recovery1()
     // Now a new process should've been launched:
     assert(first_instance + 1 == bp_sys::last_forked_pid);
     assert(p.get_state() == service_state_t::STARTED);
+    event_loop.active_timers.clear();
 }
 
 // Smooth recovery without restart delay
@@ -273,6 +284,7 @@ void test_proc_smooth_recovery2()
     pid_t first_instance = bp_sys::last_forked_pid;
 
     assert(p.get_state() == service_state_t::STARTED);
+    assert(event_loop.active_timers.size() == 0);
 
     base_process_service_test::handle_exit(&p, 0);
     sset.process_queues();
@@ -280,8 +292,64 @@ void test_proc_smooth_recovery2()
     // no restart delay, process should restart immediately:
     assert(first_instance + 1 == bp_sys::last_forked_pid);
     assert(p.get_state() == service_state_t::STARTED);
+    assert(event_loop.active_timers.size() == 0);
 }
 
+// Test stop timeout
+void test_scripted_stop_timeout()
+{
+    using namespace std;
+
+    service_set sset;
+
+    string command = "test-command";
+    string stopcommand = "stop-command";
+    list<pair<unsigned,unsigned>> command_offsets;
+    command_offsets.emplace_back(0, command.length());
+    std::list<prelim_dep> depends;
+
+    scripted_service p = scripted_service(&sset, "testscripted", std::move(command), command_offsets, depends);
+    init_service_defaults(p);
+    p.set_stop_command(stopcommand, command_offsets);
+
+    p.start(true);
+    sset.process_queues();
+
+    assert(p.get_state() == service_state_t::STARTING);
+
+    base_process_service_test::exec_succeeded(&p);
+    sset.process_queues();
+    base_process_service_test::handle_exit(&p, 0);
+    sset.process_queues();
+
+    assert(p.get_state() == service_state_t::STARTED);
+
+    p.stop(true);
+    sset.process_queues();
+
+    assert(p.get_state() == service_state_t::STOPPING);
+
+    base_process_service_test::exec_succeeded(&p);
+    sset.process_queues();
+
+    // should still be stopping:
+    assert(p.get_state() == service_state_t::STOPPING);
+
+    p.timer_expired();
+    sset.process_queues();
+
+    // kill signal (SIGKILL) should have been sent; process not dead until it's dead, however
+    assert(p.get_state() == service_state_t::STOPPING);
+    assert(bp_sys::last_sig_sent == SIGKILL);
+
+    base_process_service_test::handle_exit(&p, SIGKILL);
+    sset.process_queues();
+
+    assert(p.get_state() == service_state_t::STOPPED);
+    event_loop.active_timers.clear();
+}
+
+
 #define RUN_TEST(name, spacing) \
     std::cout << #name "..." spacing; \
     name(); \
@@ -296,4 +364,5 @@ int main(int argc, char **argv)
     RUN_TEST(test_proc_stop_timeout, "    ");
     RUN_TEST(test_proc_smooth_recovery1, "");
     RUN_TEST(test_proc_smooth_recovery2, "");
+    RUN_TEST(test_scripted_stop_timeout, "");
 }
index ef945cae1f59692f83fc52a8117536fb4d7625a9..f43eab498cd5c62f81d7448071d01962ee07aee9 100644 (file)
@@ -3,6 +3,8 @@
 
 // dummy dinit.h
 
+#include <unordered_set>
+
 #include "dasynq.h"
 
 using clock_type = dasynq::clock_type;
@@ -92,12 +94,12 @@ class eventloop_t
 
         void arm_timer_rel(eventloop_t &loop, time_val timeout) noexcept
         {
-
+            loop.active_timers.insert(this);
         }
 
         void stop_timer(eventloop_t &loop) noexcept
         {
-
+            loop.active_timers.erase(this);
         }
     };
 
@@ -105,6 +107,8 @@ class eventloop_t
     {
 
     };
+
+    std::unordered_set<timer *> active_timers;
 };
 
 inline void open_control_socket(bool report_ro_failure = true) noexcept