PACS System 0.1.0
PACS DICOM system library
Loading...
Searching...
No Matches
test_dcmtk_tool.cpp
Go to the documentation of this file.
1
12#include "dcmtk_tool.h"
13
14#include <catch2/catch_test_macros.hpp>
15
16#include <chrono>
17#include <thread>
18
19using namespace kcenon::pacs::integration_test;
20
21// =============================================================================
22// DCMTK Availability Tests
23// =============================================================================
24
25TEST_CASE("DCMTK availability check", "[dcmtk][utility]") {
26 bool available = dcmtk_tool::is_available();
27
28 if (!available) {
29 WARN("DCMTK not installed - some tests will be skipped");
30 WARN("Install with: brew install dcmtk (macOS) or apt install dcmtk (Linux)");
31 }
32
33 // This test always passes - it just checks the detection mechanism
34 SUCCEED("DCMTK availability check completed");
35}
36
37TEST_CASE("DCMTK version detection", "[dcmtk][utility]") {
39 SKIP("DCMTK not installed");
40 }
41
42 auto version = dcmtk_tool::version();
43 REQUIRE(version.has_value());
44 INFO("DCMTK version: " << *version);
45
46 // Version string should not be empty
47 REQUIRE_FALSE(version->empty());
48}
49
50// =============================================================================
51// dcmtk_result Tests
52// =============================================================================
53
54TEST_CASE("dcmtk_result success check", "[dcmtk][utility]") {
55 dcmtk_result result;
56
57 SECTION("Default result is not success") {
58 REQUIRE_FALSE(result.success());
59 }
60
61 SECTION("Zero exit code is success") {
62 result.exit_code = 0;
63 REQUIRE(result.success());
64 }
65
66 SECTION("Non-zero exit code is failure") {
67 result.exit_code = 1;
68 REQUIRE_FALSE(result.success());
69 }
70}
71
72TEST_CASE("dcmtk_result error check", "[dcmtk][utility]") {
73 dcmtk_result result;
74
75 SECTION("Empty stderr has no error") {
76 REQUIRE_FALSE(result.has_error());
77 }
78
79 SECTION("Non-empty stderr has error") {
80 result.stderr_output = "error message";
81 REQUIRE(result.has_error());
82 }
83}
84
85// =============================================================================
86// DCMTK Server Lifecycle Tests
87// =============================================================================
88
89TEST_CASE("DCMTK storescp lifecycle", "[dcmtk][utility][server]") {
91 SKIP("DCMTK not installed");
92 }
93
94 auto port = find_available_port();
95 test_directory output_dir;
96
97 SECTION("Server starts and stops correctly") {
98 {
99 auto server = dcmtk_tool::storescp(port, "TEST_SCP", output_dir.path());
100
101 // Check if server started successfully
102 if (server.pid() == process_launcher::invalid_pid) {
103 SKIP("Failed to start storescp - may not be installed correctly");
104 }
105
106 REQUIRE(server.is_running());
107
108 // Server should be listening on the port
110 }
111
112 // After guard destruction, give the process time to fully terminate
113 std::this_thread::sleep_for(std::chrono::milliseconds{200});
114
115 // Server should be stopped after guard destruction
116 REQUIRE_FALSE(process_launcher::is_port_listening(port));
117 }
118}
119
120TEST_CASE("DCMTK echoscp lifecycle", "[dcmtk][utility][server]") {
122 SKIP("DCMTK not installed");
123 }
124
125 auto port = find_available_port();
126
127 SECTION("Server starts and stops correctly") {
128 {
129 auto server = dcmtk_tool::echoscp(port, "TEST_ECHO_SCP");
130
131 if (server.pid() == process_launcher::invalid_pid) {
132 SKIP("Failed to start echoscp - may not be installed correctly");
133 }
134
135 REQUIRE(server.is_running());
137 }
138
139 std::this_thread::sleep_for(std::chrono::milliseconds{200});
140 REQUIRE_FALSE(process_launcher::is_port_listening(port));
141 }
142}
143
144// =============================================================================
145// DCMTK Server Guard Tests
146// =============================================================================
147
148TEST_CASE("dcmtk_server_guard wait_for_ready", "[dcmtk][utility][server]") {
150 SKIP("DCMTK not installed");
151 }
152
153 auto port = find_available_port();
154
155 dcmtk_server_guard server("echoscp", port, {
156 "-aet", "TEST_SCP",
157 std::to_string(port)
158 });
159
160 if (!server.is_running()) {
161 SKIP("Failed to start echoscp");
162 }
163
164 SECTION("wait_for_ready returns true when server is listening") {
165 REQUIRE(server.wait_for_ready(std::chrono::seconds{10}));
166 }
167
168 SECTION("Port matches") {
169 REQUIRE(server.port() == port);
170 }
171}
172
173TEST_CASE("dcmtk_server_guard move semantics", "[dcmtk][utility][server]") {
175 SKIP("DCMTK not installed");
176 }
177
178 auto port = find_available_port();
179
180 SECTION("Move construction transfers ownership") {
181 dcmtk_server_guard server1("echoscp", port, {
182 "-aet", "TEST_SCP",
183 std::to_string(port)
184 });
185
186 if (!server1.is_running()) {
187 SKIP("Failed to start echoscp");
188 }
189
190 auto pid = server1.pid();
191 dcmtk_server_guard server2(std::move(server1));
192
193 REQUIRE(server2.pid() == pid);
194 REQUIRE(server2.is_running());
195 }
196}
197
198// =============================================================================
199// Background Process Guard Integration Tests
200// =============================================================================
201
202TEST_CASE("background_process_guard with DCMTK", "[dcmtk][utility]") {
204 SKIP("DCMTK not installed");
205 }
206
207 auto port = find_available_port();
208
209 SECTION("Guard manages process lifecycle") {
211
212 {
213 auto temp_guard = dcmtk_tool::echoscp(port, "TEST_SCP");
214 if (temp_guard.pid() == process_launcher::invalid_pid) {
215 SKIP("Failed to start echoscp");
216 }
217
218 guard = std::move(temp_guard);
219 }
220
221 // Guard should still manage the process
222 REQUIRE(guard.is_running());
223
224 guard.stop();
225 std::this_thread::sleep_for(std::chrono::milliseconds{200});
226 REQUIRE_FALSE(guard.is_running());
227 }
228}
229
230// =============================================================================
231// Port Availability Tests
232// =============================================================================
233
234TEST_CASE("find_available_port returns valid port", "[dcmtk][utility]") {
235 auto port1 = find_available_port();
236 auto port2 = find_available_port();
237
238 REQUIRE(port1 > 0);
239 REQUIRE(port2 > 0);
240 REQUIRE(port1 != port2); // Should return different ports
241}
242
243TEST_CASE("is_port_listening detection", "[dcmtk][utility]") {
244 auto port = find_available_port();
245
246 SECTION("Unused port is not listening") {
247 REQUIRE_FALSE(process_launcher::is_port_listening(port));
248 }
249}
bool is_running() const
Check if process is running.
RAII guard for DCMTK server processes.
Definition dcmtk_tool.h:415
process_launcher::pid_type pid() const noexcept
Definition dcmtk_tool.h:487
static bool is_available()
Check if DCMTK is available on the system.
Definition dcmtk_tool.h:70
static std::optional< std::string > version()
Get DCMTK version string.
Definition dcmtk_tool.h:79
static background_process_guard storescp(uint16_t port, const std::string &ae_title, const std::filesystem::path &output_dir, std::chrono::seconds startup_timeout=default_scp_startup_timeout())
Start C-STORE SCP (storescp) server.
Definition dcmtk_tool.h:274
static background_process_guard echoscp(uint16_t port, const std::string &ae_title, std::chrono::seconds startup_timeout=default_scp_startup_timeout())
Start C-ECHO SCP (echoscp) server.
Definition dcmtk_tool.h:311
static constexpr pid_type invalid_pid
Invalid PID constant (0 is reserved on both platforms)
static bool is_port_listening(uint16_t port, const std::string &host="127.0.0.1")
Check if a port is currently listening.
RAII wrapper for temporary test directory.
const std::filesystem::path & path() const noexcept
C++ wrapper for DCMTK command-line tools.
uint16_t find_available_port(uint16_t start=default_test_port, int max_attempts=200)
Find an available port for testing.
TEST_CASE("test_data_generator::ct generates valid CT dataset", "[data_generator][ct]")
Result of a DCMTK tool execution.
Definition dcmtk_tool.h:36
std::string stderr_output
Standard error.
Definition dcmtk_tool.h:39