@@ -8,12 +8,7 @@ use testcontainers::runners::AsyncRunner;
88use testcontainers:: { ContainerAsync , GenericImage } ;
99use tokio:: io:: { AsyncBufReadExt , BufReader , Lines } ;
1010use tokio:: process:: { Child , ChildStdout , Command } ;
11- use tokio:: time:: { timeout_at, Instant } ;
1211
13- const STARTUP_TIMEOUT : Duration = Duration :: from_secs ( 30 ) ;
14- const STARTED_MESSAGE : & str = "Started DICOMweb server" ;
15-
16- /// Spawns a container running the latest version of Orthanc.
1712pub async fn spawn_orthanc ( ) -> anyhow:: Result < ContainerAsync < GenericImage > > {
1813 GenericImage :: new ( "jodogne/orthanc" , "latest" )
1914 . with_exposed_port ( 4242 . tcp ( ) )
@@ -24,17 +19,17 @@ pub async fn spawn_orthanc() -> anyhow::Result<ContainerAsync<GenericImage>> {
2419 . context ( "failed to start Orthanc container" )
2520}
2621
27- /// Spawns the DICOM-RST binary with the given config and waits until it is ready.
2822pub async fn spawn_dicomrst ( config : & str ) -> anyhow:: Result < ServerProcess > {
2923 let mut server = ServerProcess :: spawn ( config) ?;
30- server. wait_until_started ( ) . await ?;
24+ server. http_port = server . wait_until_started ( ) . await ?;
3125 Ok ( server)
3226}
3327
3428pub struct ServerProcess {
3529 child : Child ,
3630 stdout : Lines < BufReader < ChildStdout > > ,
3731 workdir : PathBuf ,
32+ http_port : u16 ,
3833}
3934
4035impl ServerProcess {
@@ -46,46 +41,52 @@ impl ServerProcess {
4641
4742 let mut child = Command :: new ( env ! ( "CARGO_BIN_EXE_dicom-rst" ) )
4843 . stdout ( Stdio :: piped ( ) )
49- . stderr ( Stdio :: piped ( ) )
44+ . stderr ( Stdio :: null ( ) )
45+ . env ( "NO_COLOR" , "true" ) // disables colored ANSI output
5046 . current_dir ( & workdir)
5147 . spawn ( )
5248 . context ( "failed to spawn DICOM-RST server binary" ) ?;
5349
5450 let stdout = BufReader :: new ( child. stdout . take ( ) . unwrap ( ) ) . lines ( ) ;
55- let mut stderr = BufReader :: new ( child. stderr . take ( ) . unwrap ( ) ) . lines ( ) ;
56-
57- tokio:: spawn ( async move {
58- while let Ok ( Some ( line) ) = stderr. next_line ( ) . await {
59- eprintln ! ( "[dicom-rst] {line}" ) ;
60- }
61- } ) ;
6251
6352 Ok ( Self {
6453 child,
6554 stdout,
6655 workdir,
56+ http_port : 0 ,
6757 } )
6858 }
6959
70- async fn wait_until_started ( & mut self ) -> anyhow:: Result < ( ) > {
71- let deadline = Instant :: now ( ) + STARTUP_TIMEOUT ;
72-
73- loop {
74- match timeout_at ( deadline, self . stdout . next_line ( ) ) . await {
75- Ok ( Ok ( Some ( line) ) ) => {
76- eprintln ! ( "[dicom-rst] {line}" ) ;
77- if line. contains ( STARTED_MESSAGE ) {
78- return Ok ( ( ) ) ;
79- }
80- }
81- Ok ( Ok ( None ) ) => {
82- let status = self . child . wait ( ) . await ?;
83- bail ! ( "DICOM-RST exited before becoming ready: {status}" ) ;
60+ async fn wait_until_started ( & mut self ) -> anyhow:: Result < u16 > {
61+ tokio:: time:: timeout ( Duration :: from_secs ( 15 ) , async {
62+ while let Some ( line) = self
63+ . stdout
64+ . next_line ( )
65+ . await
66+ . context ( "Failed to read DICOM-RST stdout" ) ?
67+ {
68+ if !line. contains ( "Started DICOMweb server" ) {
69+ continue ;
8470 }
85- Ok ( Err ( e) ) => return Err ( e) . context ( "failed to read DICOM-RST stdout" ) ,
86- Err ( _) => bail ! ( "timed out waiting for `{STARTED_MESSAGE}`" ) ,
71+
72+ let port = line
73+ . split_whitespace ( )
74+ . find_map ( |part| part. strip_prefix ( "server.port=" ) )
75+ . ok_or_else ( || {
76+ anyhow:: Error :: msg (
77+ "DICOM-RST started, but stdout did not contain server.port=" ,
78+ )
79+ } ) ?
80+ . parse :: < u16 > ( )
81+ . context ( "Failed to parse DICOM-RST server.port as u16" ) ?;
82+
83+ return Ok ( port) ;
8784 }
88- }
85+
86+ bail ! ( "DICOM-RST exited before becoming ready" ) ;
87+ } )
88+ . await
89+ . context ( "Timed out waiting for DICOM-RST to start" ) ?
8990 }
9091}
9192
@@ -96,7 +97,7 @@ impl Drop for ServerProcess {
9697 }
9798}
9899
99- pub async fn with_test_deployment (
100+ pub async fn with_test_environment (
100101 config : & str ,
101102 test : impl AsyncFnOnce ( DicomWebClient ) -> anyhow:: Result < ( ) > ,
102103) -> anyhow:: Result < ( ) > {
@@ -107,9 +108,12 @@ pub async fn with_test_deployment(
107108 . context ( "failed to get mapped Orthanc DIMSE port" ) ?;
108109
109110 let config = config. replace ( "${ORTHANC_PORT}" , & orthanc_port. to_string ( ) ) ;
110- let _server = spawn_dicomrst ( & config) . await ?;
111+ let server = spawn_dicomrst ( & config) . await ?;
111112
112- let client = DicomWebClient :: with_single_url ( & format ! ( "http://localhost:8080/aets/ORTHANC" ) ) ;
113+ let client = DicomWebClient :: with_single_url ( & format ! (
114+ "http://localhost:{}/aets/ORTHANC" ,
115+ server. http_port
116+ ) ) ;
113117 test ( client) . await ?;
114118
115119 Ok ( ( ) )
0 commit comments