PACS System 0.1.0
PACS DICOM system library
Loading...
Searching...
No Matches
test_dcmtk_tool.cpp File Reference

Unit tests for DCMTK tool wrapper utilities. More...

#include "dcmtk_tool.h"
#include <catch2/catch_test_macros.hpp>
#include <chrono>
#include <thread>
Include dependency graph for test_dcmtk_tool.cpp:

Go to the source code of this file.

Functions

 TEST_CASE ("DCMTK availability check", "[dcmtk][utility]")
 
 TEST_CASE ("DCMTK version detection", "[dcmtk][utility]")
 
 TEST_CASE ("dcmtk_result success check", "[dcmtk][utility]")
 
 TEST_CASE ("dcmtk_result error check", "[dcmtk][utility]")
 
 TEST_CASE ("DCMTK storescp lifecycle", "[dcmtk][utility][server]")
 
 TEST_CASE ("DCMTK echoscp lifecycle", "[dcmtk][utility][server]")
 
 TEST_CASE ("dcmtk_server_guard wait_for_ready", "[dcmtk][utility][server]")
 
 TEST_CASE ("dcmtk_server_guard move semantics", "[dcmtk][utility][server]")
 
 TEST_CASE ("background_process_guard with DCMTK", "[dcmtk][utility]")
 
 TEST_CASE ("find_available_port returns valid port", "[dcmtk][utility]")
 
 TEST_CASE ("is_port_listening detection", "[dcmtk][utility]")
 

Detailed Description

Unit tests for DCMTK tool wrapper utilities.

Tests the dcmtk_tool class functionality including availability checks, version detection, and server lifecycle management.

See also
Issue #450 - DCMTK Process Launcher and Test Utilities
Issue #449 - DCMTK Interoperability Test Automation Epic

Definition in file test_dcmtk_tool.cpp.

Function Documentation

◆ TEST_CASE() [1/11]

TEST_CASE ( "background_process_guard with DCMTK" ,
"" [dcmtk][utility] )

Definition at line 202 of file test_dcmtk_tool.cpp.

202 {
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}
bool is_running() const
Check if process is running.
static bool is_available()
Check if DCMTK is available on the system.
Definition dcmtk_tool.h:70
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)
uint16_t find_available_port(uint16_t start=default_test_port, int max_attempts=200)
Find an available port for testing.

References kcenon::pacs::integration_test::dcmtk_tool::echoscp(), kcenon::pacs::integration_test::find_available_port(), kcenon::pacs::integration_test::process_launcher::invalid_pid, kcenon::pacs::integration_test::dcmtk_tool::is_available(), kcenon::pacs::integration_test::background_process_guard::is_running(), and kcenon::pacs::integration_test::background_process_guard::stop().

Here is the call graph for this function:

◆ TEST_CASE() [2/11]

TEST_CASE ( "DCMTK availability check" ,
"" [dcmtk][utility] )

Definition at line 25 of file test_dcmtk_tool.cpp.

25 {
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}

References kcenon::pacs::integration_test::dcmtk_tool::is_available().

Here is the call graph for this function:

◆ TEST_CASE() [3/11]

TEST_CASE ( "DCMTK echoscp lifecycle" ,
"" [dcmtk][utility][server] )

Definition at line 120 of file test_dcmtk_tool.cpp.

120 {
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}
static bool is_port_listening(uint16_t port, const std::string &host="127.0.0.1")
Check if a port is currently listening.

References kcenon::pacs::integration_test::dcmtk_tool::echoscp(), kcenon::pacs::integration_test::find_available_port(), kcenon::pacs::integration_test::process_launcher::invalid_pid, kcenon::pacs::integration_test::dcmtk_tool::is_available(), and kcenon::pacs::integration_test::process_launcher::is_port_listening().

Here is the call graph for this function:

◆ TEST_CASE() [4/11]

TEST_CASE ( "DCMTK storescp lifecycle" ,
"" [dcmtk][utility][server] )

Definition at line 89 of file test_dcmtk_tool.cpp.

89 {
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}
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
RAII wrapper for temporary test directory.
const std::filesystem::path & path() const noexcept

References kcenon::pacs::integration_test::find_available_port(), kcenon::pacs::integration_test::process_launcher::invalid_pid, kcenon::pacs::integration_test::dcmtk_tool::is_available(), kcenon::pacs::integration_test::process_launcher::is_port_listening(), kcenon::pacs::integration_test::test_directory::path(), and kcenon::pacs::integration_test::dcmtk_tool::storescp().

Here is the call graph for this function:

◆ TEST_CASE() [5/11]

TEST_CASE ( "DCMTK version detection" ,
"" [dcmtk][utility] )

Definition at line 37 of file test_dcmtk_tool.cpp.

37 {
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}
static std::optional< std::string > version()
Get DCMTK version string.
Definition dcmtk_tool.h:79

References kcenon::pacs::integration_test::dcmtk_tool::is_available(), and kcenon::pacs::integration_test::dcmtk_tool::version().

Here is the call graph for this function:

◆ TEST_CASE() [6/11]

TEST_CASE ( "dcmtk_result error check" ,
"" [dcmtk][utility] )

Definition at line 72 of file test_dcmtk_tool.cpp.

72 {
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}
Result of a DCMTK tool execution.
Definition dcmtk_tool.h:36
std::string stderr_output
Standard error.
Definition dcmtk_tool.h:39

References kcenon::pacs::integration_test::dcmtk_result::has_error(), and kcenon::pacs::integration_test::dcmtk_result::stderr_output.

Here is the call graph for this function:

◆ TEST_CASE() [7/11]

TEST_CASE ( "dcmtk_result success check" ,
"" [dcmtk][utility] )

Definition at line 54 of file test_dcmtk_tool.cpp.

54 {
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}

References kcenon::pacs::integration_test::dcmtk_result::exit_code, and kcenon::pacs::integration_test::dcmtk_result::success().

Here is the call graph for this function:

◆ TEST_CASE() [8/11]

TEST_CASE ( "dcmtk_server_guard move semantics" ,
"" [dcmtk][utility][server] )

Definition at line 173 of file test_dcmtk_tool.cpp.

173 {
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}
RAII guard for DCMTK server processes.
Definition dcmtk_tool.h:415
process_launcher::pid_type pid() const noexcept
Definition dcmtk_tool.h:487

References kcenon::pacs::integration_test::find_available_port(), kcenon::pacs::integration_test::dcmtk_tool::is_available(), kcenon::pacs::integration_test::dcmtk_server_guard::is_running(), and kcenon::pacs::integration_test::dcmtk_server_guard::pid().

Here is the call graph for this function:

◆ TEST_CASE() [9/11]

TEST_CASE ( "dcmtk_server_guard wait_for_ready" ,
"" [dcmtk][utility][server] )

Definition at line 148 of file test_dcmtk_tool.cpp.

148 {
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}

References kcenon::pacs::integration_test::find_available_port(), and kcenon::pacs::integration_test::dcmtk_tool::is_available().

Here is the call graph for this function:

◆ TEST_CASE() [10/11]

TEST_CASE ( "find_available_port returns valid port" ,
"" [dcmtk][utility] )

Definition at line 234 of file test_dcmtk_tool.cpp.

234 {
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}

References kcenon::pacs::integration_test::find_available_port().

Here is the call graph for this function:

◆ TEST_CASE() [11/11]

TEST_CASE ( "is_port_listening detection" ,
"" [dcmtk][utility] )

Definition at line 243 of file test_dcmtk_tool.cpp.

243 {
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}

References kcenon::pacs::integration_test::find_available_port(), and kcenon::pacs::integration_test::process_launcher::is_port_listening().

Here is the call graph for this function: