|
7 | 7 |
|
8 | 8 | #include "testcontainers/Container.hpp" |
9 | 9 | #include "testcontainers/Error.hpp" |
| 10 | +#include "testcontainers/ExecResult.hpp" |
10 | 11 | #include "testcontainers/GenericImage.hpp" |
11 | 12 | #include "testcontainers/Lifecycle.hpp" |
12 | 13 | #include "testcontainers/WaitFor.hpp" |
|
20 | 21 | // Lifecycle.HooksFireInOrder - created/starting/started hooks fire once, in that order, each seeing the live container id. |
21 | 22 | // Lifecycle.StoppingHookFiresOnStop - a stopping hook fires when the container is explicitly stopped. |
22 | 23 | // Lifecycle.StartupRetriesOnFailure - with_startup_attempts(2) retries the whole create→start→wait on failure, creating a fresh container each attempt. |
| 24 | +// Lifecycle.StopStartRoundTrip - stop(0) (explicit zero grace) stops the container, start() brings the same container back (a plain daemon start; exec works again), and a second start() is accepted as already-running. |
23 | 25 | // Lifecycle.KeepLeavesContainerRunning - keep() releases removal ownership: after the handle drops, the container is still running (verified and cleaned up via an adopted RemoveOnDrop handle). |
24 | 26 | // Lifecycle.StaticInspectById - the static Container::inspect(id) reads a running container's state without a handle and throws NotFoundError for an id that does not exist. |
25 | 27 | // WindowsLifecycle.HooksFireInOrder - the same hook ordering against a Windows daemon (the hooks are client-side, but each leg drives real Windows-engine create/start calls). |
@@ -104,6 +106,27 @@ TEST_F(Lifecycle, StartupRetriesOnFailure) { |
104 | 106 | EXPECT_EQ(created_count, 2); |
105 | 107 | } |
106 | 108 |
|
| 109 | +TEST_F(Lifecycle, StopStartRoundTrip) { |
| 110 | + Container c = GenericImage::from_reference(kImage).with_cmd({"sleep", "60"}).start(); |
| 111 | + |
| 112 | + // Explicit zero grace: the container's sleep (PID 1, no SIGTERM handler) |
| 113 | + // would burn the full grace period before the daemon's SIGKILL — zero |
| 114 | + // grace just skips that wait. |
| 115 | + c.stop(0); |
| 116 | + EXPECT_FALSE(c.is_running()); |
| 117 | + |
| 118 | + // A plain daemon start on the SAME container: no waits or hooks re-run. |
| 119 | + c.start(); |
| 120 | + EXPECT_TRUE(c.is_running()); |
| 121 | + // exec proves the restarted container is live, not merely flagged running. |
| 122 | + const ExecResult res = c.exec({"echo", "back"}); |
| 123 | + EXPECT_EQ(res.exit_code, 0); |
| 124 | + EXPECT_EQ(res.stdout_data, "back\n"); |
| 125 | + |
| 126 | + c.start(); // already running: the daemon's 304 is accepted, not an error |
| 127 | + EXPECT_TRUE(c.is_running()); |
| 128 | +} |
| 129 | + |
107 | 130 | TEST_F(Lifecycle, KeepLeavesContainerRunning) { |
108 | 131 | // keep() releases removal ownership: the handle's drop leaves the container |
109 | 132 | // running, exactly like a with_reuse handle would. |
|
0 commit comments