Mercurial > hg > orthanc-tests
annotate Tests/CheckScuTranscoding.py @ 660:2f6686a3cd16
conditional test
author | Alain Mazy <am@orthanc.team> |
---|---|
date | Mon, 24 Jun 2024 18:32:13 +0200 |
parents | 5d7b6e43ab7d |
children |
rev | line source |
---|---|
610
ec657d1a62a6
fix compatibility with python3
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
511
diff
changeset
|
1 #!/usr/bin/env python3 |
371 | 2 |
3 # Orthanc - A Lightweight, RESTful DICOM Store | |
4 # Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics | |
5 # Department, University Hospital of Liege, Belgium | |
649
5d7b6e43ab7d
updated copyright, as Orthanc Team now replaces Osimis
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
640
diff
changeset
|
6 # Copyright (C) 2017-2023 Osimis S.A., Belgium |
5d7b6e43ab7d
updated copyright, as Orthanc Team now replaces Osimis
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
640
diff
changeset
|
7 # Copyright (C) 2024-2024 Orthanc Team SRL, Belgium |
640
9f8276ac1cdd
update year to 2024
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
610
diff
changeset
|
8 # Copyright (C) 2021-2024 Sebastien Jodogne, ICTEAM UCLouvain, Belgium |
371 | 9 # |
10 # This program is free software: you can redistribute it and/or | |
11 # modify it under the terms of the GNU General Public License as | |
12 # published by the Free Software Foundation, either version 3 of the | |
13 # License, or (at your option) any later version. | |
14 # | |
15 # This program is distributed in the hope that it will be useful, but | |
16 # WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
18 # General Public License for more details. | |
19 # | |
20 # You should have received a copy of the GNU General Public License | |
21 # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
22 | |
23 | |
24 import json | |
25 import os | |
26 import subprocess | |
27 import sys | |
28 import time | |
29 import Toolbox | |
30 | |
31 | |
32 if len(sys.argv) < 2: | |
33 print('Must provide a path to Orthanc binaries') | |
34 exit(-1) | |
35 | |
36 | |
37 TMP = '/tmp/OrthancTest' | |
38 TMP_ORTHANC = os.path.join(TMP, 'orthanc') | |
39 TMP_STORESCP = os.path.join(TMP, 'storescp') | |
40 | |
41 CONFIG = os.path.join(TMP, 'orthanc', 'Configuration.json') | |
42 ORTHANC = Toolbox.DefineOrthanc() | |
43 | |
44 if os.path.exists(TMP): | |
45 print('Temporary path already exists: %s' % TMP) | |
46 exit(-1) | |
47 | |
48 os.mkdir(TMP) | |
49 os.mkdir(TMP_ORTHANC) | |
50 os.mkdir(TMP_STORESCP) | |
51 | |
52 | |
53 def DropOrthanc(): | |
54 while True: | |
55 try: | |
56 instances = Toolbox.DoGet(ORTHANC, '/instances') | |
57 if len(instances) == 0: | |
58 break | |
59 else: | |
60 for i in instances: | |
61 Toolbox.DoDelete(ORTHANC, '/instances/%s' % i) | |
62 except: | |
63 # Orthanc is still in its startup process, wait for it to | |
64 # become available | |
65 time.sleep(0.05) | |
66 | |
67 | |
68 def CreateStorescpConfiguration(acceptedSyntaxes): | |
69 with open(os.path.join(TMP_STORESCP, 'config'), 'w') as f: | |
70 f.write('[[TransferSyntaxes]]\n') | |
71 | |
72 f.write('[Accepted]\n') | |
73 for i in range(len(acceptedSyntaxes)): | |
74 f.write('TransferSyntax%d = %s\n' % (i + 1, acceptedSyntaxes[i])) | |
75 | |
76 f.write('[[PresentationContexts]]\n') | |
77 f.write('[StorageSCP]\n') | |
78 | |
79 # These strings correspond to the SOP class UIDs of the DICOM | |
80 # instances in folder "../Database/TransferSyntaxes/" | |
81 SOP_CLASS_UIDS = [ | |
82 '1.2.840.10008.5.1.4.1.1.2', | |
83 '1.2.840.10008.5.1.4.1.1.4', | |
84 '1.2.840.10008.5.1.4.1.1.6', | |
85 '1.2.840.10008.5.1.4.1.1.6.1', | |
86 '1.2.840.10008.5.1.4.1.1.7', | |
87 ] | |
88 | |
89 for i in range(len(SOP_CLASS_UIDS)): | |
90 f.write('PresentationContext%d = %s\\Accepted\n' % (i + 1, SOP_CLASS_UIDS[i])) | |
91 | |
92 f.write('[[Profiles]]\n') | |
93 f.write('[Default]\n') | |
94 f.write('PresentationContexts = StorageSCP\n') | |
95 | |
96 | |
97 def TestStore(config, storescpArgs, tests): | |
98 config['DicomModalities'] = { | |
99 'storescp' : [ 'STORESCP', 'localhost', 2000 ] | |
100 } | |
101 | |
102 with open(CONFIG, 'w') as f: | |
103 f.write(json.dumps(config)) | |
104 | |
105 FNULL = open(os.devnull, 'w') # Emulates "subprocess.DEVNULL" on Python 2.7 | |
106 process1 = subprocess.Popen( | |
107 sys.argv[1:] + [ CONFIG, '--no-jobs' ], #, '--trace-dicom' ], | |
108 cwd = TMP_ORTHANC, | |
109 #stdout=FNULL, | |
110 stderr=FNULL, | |
111 #shell=True | |
112 ) | |
113 | |
114 process2 = subprocess.Popen( | |
115 [ 'storescp', '-p', '2000' ] + storescpArgs, | |
116 cwd = TMP_STORESCP, | |
117 #stdout=FNULL, | |
118 #stderr=FNULL, | |
119 #shell=True | |
120 ) | |
121 | |
122 success = True | |
123 | |
124 try: | |
125 for test in tests: | |
126 DropOrthanc() | |
127 for f in os.listdir(TMP_STORESCP): | |
128 os.remove(os.path.join(TMP_STORESCP, f)) | |
129 | |
130 i = Toolbox.UploadInstance(ORTHANC, test[0]) ['ID'] | |
131 | |
132 try: | |
133 Toolbox.DoPost(ORTHANC, '/modalities/storescp/store', { | |
134 'Resources' : [ i ], | |
135 'Synchronous' : True, | |
136 }) | |
137 except: | |
138 if test[1] != None: | |
139 print('INTERNAL ERROR on: %s' % test[0]) | |
140 success = False | |
141 continue | |
142 | |
143 f = os.listdir(TMP_STORESCP) | |
144 if len(f) > 1: | |
145 print('INTERNAL ERROR') | |
146 success = False | |
147 elif len(f) == 0: | |
148 if test[1] != None: | |
149 print('No file was received by storescp! %s' % test[0]) | |
150 success = False | |
151 else: | |
152 if test[1] == None: | |
153 print('No file should have been received by storescp! %s' % test[0]) | |
154 success = False | |
155 else: | |
156 with open(os.path.join(TMP_STORESCP, f[0]), 'rb') as f: | |
157 ts = Toolbox.GetTransferSyntax(f.read()) | |
158 | |
159 if ts != test[1]: | |
160 print('TRANSFER SYNTAX MISMATCH: observed %s vs. expected %s' % (ts, test[1])) | |
161 success = False | |
162 | |
163 except Exception as e: | |
164 print('EXCEPTION: %s' % e) | |
165 success = False | |
166 | |
167 process1.terminate() | |
168 process2.terminate() | |
169 | |
170 process1.wait() | |
171 process2.wait() | |
172 | |
173 return success | |
174 | |
175 | |
176 def Assert(b): | |
177 if not b: | |
178 raise Exception('Bad result') | |
179 | |
180 | |
181 | |
182 ## | |
183 ## Each test specifies: The input DICOM instance, and the expected | |
184 ## transfer syntax as received by storescp | |
185 ## | |
186 | |
187 | |
188 print('==== TEST 1 ====') | |
189 Assert(TestStore( | |
190 { | |
191 'DicomScuPreferredTransferSyntax' : '1.2.840.10008.1.2.1', # Little Endian Explicit | |
192 }, | |
193 [ '+xa' ], # storescp accepts any transfer syntax | |
194 # (DicomScuPreferredTransferSyntax has no effect) | |
195 [ | |
196 ('TransferSyntaxes/1.2.840.10008.1.2.dcm', '1.2.840.10008.1.2'), | |
197 ('TransferSyntaxes/1.2.840.10008.1.2.1.dcm', '1.2.840.10008.1.2.1'), | |
198 ('TransferSyntaxes/1.2.840.10008.1.2.2.dcm', '1.2.840.10008.1.2.2'), | |
199 ('TransferSyntaxes/1.2.840.10008.1.2.4.51.dcm', '1.2.840.10008.1.2.4.51'), | |
200 ('TransferSyntaxes/1.2.840.10008.1.2.4.70.dcm', '1.2.840.10008.1.2.4.70'), | |
201 ])) | |
202 | |
203 | |
204 print('==== TEST 2 ====') | |
205 Assert(TestStore( | |
206 { | |
207 'DicomScuPreferredTransferSyntax' : '1.2.840.10008.1.2.2', # Big Endian | |
208 }, | |
209 [ '+xa' ], # storescp accepts any transfer syntax | |
210 # (DicomScuPreferredTransferSyntax has no effect) | |
211 [ | |
212 ('TransferSyntaxes/1.2.840.10008.1.2.dcm', '1.2.840.10008.1.2'), | |
213 ('TransferSyntaxes/1.2.840.10008.1.2.1.dcm', '1.2.840.10008.1.2.1'), | |
214 ('TransferSyntaxes/1.2.840.10008.1.2.2.dcm', '1.2.840.10008.1.2.2'), | |
215 ('TransferSyntaxes/1.2.840.10008.1.2.4.51.dcm', '1.2.840.10008.1.2.4.51'), | |
216 ('TransferSyntaxes/1.2.840.10008.1.2.4.70.dcm', '1.2.840.10008.1.2.4.70'), | |
217 ])) | |
218 | |
219 | |
220 print('==== TEST 3 ====') | |
221 Assert(TestStore( | |
222 { | |
223 'DicomScuPreferredTransferSyntax' : '1.2.840.10008.1.2.4.70', # JPEG baseline 12bpp | |
224 }, | |
225 [ '+xa' ], # storescp accepts any transfer syntax | |
226 # (DicomScuPreferredTransferSyntax has no effect) | |
227 [ | |
228 ('TransferSyntaxes/1.2.840.10008.1.2.dcm', '1.2.840.10008.1.2'), | |
229 ('TransferSyntaxes/1.2.840.10008.1.2.1.dcm', '1.2.840.10008.1.2.1'), | |
230 ('TransferSyntaxes/1.2.840.10008.1.2.2.dcm', '1.2.840.10008.1.2.2'), | |
231 ('TransferSyntaxes/1.2.840.10008.1.2.4.51.dcm', '1.2.840.10008.1.2.4.51'), | |
232 ('TransferSyntaxes/1.2.840.10008.1.2.4.70.dcm', '1.2.840.10008.1.2.4.70'), | |
233 ])) | |
234 | |
235 | |
236 print('==== TEST 4 ====') | |
237 Assert(TestStore( | |
238 { | |
239 'DicomScuPreferredTransferSyntax' : '1.2.840.10008.1.2.1', # Little Endian Explicit | |
240 }, | |
241 [ ], # storescp only accepts uncompressed transfer syntaxes | |
242 [ | |
243 ('TransferSyntaxes/1.2.840.10008.1.2.dcm', '1.2.840.10008.1.2'), | |
244 ('TransferSyntaxes/1.2.840.10008.1.2.1.dcm', '1.2.840.10008.1.2.1'), | |
245 ('TransferSyntaxes/1.2.840.10008.1.2.2.dcm', '1.2.840.10008.1.2.2'), | |
246 ('TransferSyntaxes/1.2.840.10008.1.2.4.51.dcm', '1.2.840.10008.1.2.1'), | |
247 ('TransferSyntaxes/1.2.840.10008.1.2.4.70.dcm', '1.2.840.10008.1.2.1'), | |
248 ])) | |
249 | |
250 | |
251 print('==== TEST 5 ====') | |
252 Assert(TestStore( | |
253 { | |
254 # Defaults to "1.2.840.10008.1.2.1", Little Endian Explicit | |
255 # (was Little Endian Implicit in Orthanc between 1.7.0 and 1.8.2) | |
256 }, | |
257 [ ], # storescp only accepts uncompressed transfer syntaxes | |
258 [ | |
259 ('TransferSyntaxes/1.2.840.10008.1.2.dcm', '1.2.840.10008.1.2'), | |
260 ('TransferSyntaxes/1.2.840.10008.1.2.1.dcm', '1.2.840.10008.1.2.1'), | |
261 ('TransferSyntaxes/1.2.840.10008.1.2.2.dcm', '1.2.840.10008.1.2.2'), | |
262 ('TransferSyntaxes/1.2.840.10008.1.2.4.51.dcm', '1.2.840.10008.1.2.1'), | |
263 ('TransferSyntaxes/1.2.840.10008.1.2.4.70.dcm', '1.2.840.10008.1.2.1'), | |
264 ])) | |
265 | |
266 | |
267 print('==== TEST 6 ====') | |
268 Assert(TestStore( | |
269 { | |
270 'DicomScuPreferredTransferSyntax' : '1.2.840.10008.1.2', # Little Endian Implicit | |
271 }, | |
272 [ ], # storescp only accepts uncompressed transfer syntaxes | |
273 [ | |
274 ('TransferSyntaxes/1.2.840.10008.1.2.dcm', '1.2.840.10008.1.2'), | |
275 ('TransferSyntaxes/1.2.840.10008.1.2.1.dcm', '1.2.840.10008.1.2.1'), | |
276 ('TransferSyntaxes/1.2.840.10008.1.2.2.dcm', '1.2.840.10008.1.2.2'), | |
277 ('TransferSyntaxes/1.2.840.10008.1.2.4.51.dcm', '1.2.840.10008.1.2'), | |
278 ('TransferSyntaxes/1.2.840.10008.1.2.4.70.dcm', '1.2.840.10008.1.2'), | |
279 ])) | |
280 | |
281 | |
282 print('==== TEST 7 ====') | |
283 Assert(TestStore( | |
284 { | |
285 'DicomScuPreferredTransferSyntax' : '1.2.840.10008.1.2.2', # Big Endian | |
286 }, | |
287 [ ], # storescp only accepts uncompressed transfer syntaxes | |
288 [ | |
289 ('TransferSyntaxes/1.2.840.10008.1.2.dcm', '1.2.840.10008.1.2'), | |
290 ('TransferSyntaxes/1.2.840.10008.1.2.1.dcm', '1.2.840.10008.1.2.1'), | |
291 ('TransferSyntaxes/1.2.840.10008.1.2.2.dcm', '1.2.840.10008.1.2.2'), | |
292 ('TransferSyntaxes/1.2.840.10008.1.2.4.51.dcm', '1.2.840.10008.1.2.2'), | |
293 ('TransferSyntaxes/1.2.840.10008.1.2.4.70.dcm', '1.2.840.10008.1.2.2'), | |
294 ])) | |
295 | |
296 | |
297 print('==== TEST 8 ====') | |
298 Assert(TestStore( | |
299 { | |
300 'DicomScuPreferredTransferSyntax' : '1.2.840.10008.1.2.4.70' | |
301 }, | |
302 [ ], # storescp only accepts uncompressed transfer syntaxes, | |
303 # Little Endian Explicit will be chosed by Orthanc (was | |
304 # Little Endian Implicit in Orthanc between 1.7.0 and 1.8.2) | |
305 [ | |
306 ('TransferSyntaxes/1.2.840.10008.1.2.dcm', '1.2.840.10008.1.2'), | |
307 ('TransferSyntaxes/1.2.840.10008.1.2.1.dcm', '1.2.840.10008.1.2.1'), | |
308 ('TransferSyntaxes/1.2.840.10008.1.2.2.dcm', '1.2.840.10008.1.2.2'), | |
309 ('TransferSyntaxes/1.2.840.10008.1.2.4.51.dcm', '1.2.840.10008.1.2.1'), | |
310 ('TransferSyntaxes/1.2.840.10008.1.2.4.70.dcm', '1.2.840.10008.1.2.1'), | |
311 ])) | |
312 | |
313 | |
314 print('==== TEST 9 ====') | |
315 Assert(TestStore( | |
316 { | |
317 'DicomScuPreferredTransferSyntax' : '1.2.840.10008.1.2.4.70' | |
318 }, | |
319 [ '+xi' ], # storescp only accepts Little Endian Implicit (1.2.840.10008.1.2) | |
320 [ | |
321 ('TransferSyntaxes/1.2.840.10008.1.2.dcm', '1.2.840.10008.1.2'), | |
322 ('TransferSyntaxes/1.2.840.10008.1.2.1.dcm', '1.2.840.10008.1.2'), | |
323 ('TransferSyntaxes/1.2.840.10008.1.2.2.dcm', '1.2.840.10008.1.2'), | |
324 ('TransferSyntaxes/1.2.840.10008.1.2.4.51.dcm', '1.2.840.10008.1.2'), | |
325 ('TransferSyntaxes/1.2.840.10008.1.2.4.70.dcm', '1.2.840.10008.1.2'), | |
326 ])) | |
327 | |
328 | |
329 print('==== TEST 10 ====') | |
330 CreateStorescpConfiguration([ | |
331 '1.2.840.10008.1.2.4.70', | |
332 ]) | |
333 Assert(TestStore( | |
334 { | |
335 'DicomScuPreferredTransferSyntax' : '1.2.840.10008.1.2.4.70' | |
336 }, | |
337 [ '-xf', 'config', 'Default' ], # storescp only accepts "1.2.840.10008.1.2.4.70" | |
338 [ | |
339 ('TransferSyntaxes/1.2.840.10008.1.2.dcm', '1.2.840.10008.1.2.4.70'), | |
340 ('TransferSyntaxes/1.2.840.10008.1.2.1.dcm', '1.2.840.10008.1.2.4.70'), | |
341 ('TransferSyntaxes/1.2.840.10008.1.2.2.dcm', '1.2.840.10008.1.2.4.70'), | |
342 ('TransferSyntaxes/1.2.840.10008.1.2.4.51.dcm', '1.2.840.10008.1.2.4.70'), | |
343 ('TransferSyntaxes/1.2.840.10008.1.2.4.70.dcm', '1.2.840.10008.1.2.4.70'), | |
344 ])) | |
345 | |
346 | |
347 print('==== TEST 11 ====') | |
348 CreateStorescpConfiguration([ | |
349 '1.2.840.10008.1.2.4.57', | |
350 ]) | |
351 Assert(TestStore( | |
352 { | |
353 'DicomScuPreferredTransferSyntax' : '1.2.840.10008.1.2.4.70' | |
354 }, | |
355 [ '-xf', 'config', 'Default' ], | |
356 [ | |
357 ('TransferSyntaxes/1.2.840.10008.1.2.4.57.dcm', '1.2.840.10008.1.2.4.57'), | |
358 | |
359 # All the transfers below will be rejected by storescp | |
360 ('TransferSyntaxes/1.2.840.10008.1.2.dcm', None), | |
361 ('TransferSyntaxes/1.2.840.10008.1.2.1.dcm', None), | |
362 ('TransferSyntaxes/1.2.840.10008.1.2.2.dcm', None), | |
363 ('TransferSyntaxes/1.2.840.10008.1.2.4.51.dcm', None), | |
364 ('TransferSyntaxes/1.2.840.10008.1.2.4.70.dcm', None), | |
365 ])) | |
366 | |
367 | |
368 print('==== TEST 12 ====') | |
369 CreateStorescpConfiguration([ | |
370 '1.2.840.10008.1.2.4.70', | |
371 '1.2.840.10008.1.2.1', | |
372 ]) | |
373 Assert(TestStore( | |
374 { | |
375 'DicomScuPreferredTransferSyntax' : '1.2.840.10008.1.2.4.70' | |
376 }, | |
377 [ '-xf', 'config', 'Default' ], | |
378 [ | |
379 ('TransferSyntaxes/1.2.840.10008.1.2.dcm', '1.2.840.10008.1.2.4.70'), | |
380 ('TransferSyntaxes/1.2.840.10008.1.2.1.dcm', '1.2.840.10008.1.2.1'), | |
381 ('TransferSyntaxes/1.2.840.10008.1.2.2.dcm', '1.2.840.10008.1.2.4.70'), | |
382 ('TransferSyntaxes/1.2.840.10008.1.2.4.51.dcm', '1.2.840.10008.1.2.4.70'), | |
383 ('TransferSyntaxes/1.2.840.10008.1.2.4.70.dcm', '1.2.840.10008.1.2.4.70'), | |
384 ])) | |
385 | |
386 | |
387 print('==== TEST 13 ====') | |
388 CreateStorescpConfiguration([ | |
389 '1.2.840.10008.1.2.4.90', | |
390 '1.2.840.10008.1.2.1', | |
391 ]) | |
392 Assert(TestStore( | |
393 { | |
394 # The built-in DCMTK transcoder of Orthanc cannot transcode to | |
395 # JPEG2k, so the fallback "1.2.840.10008.1.2.1" transfer | |
396 # syntax will be used if transcoding is needed | |
397 'DicomScuPreferredTransferSyntax' : '1.2.840.10008.1.2.4.90' | |
398 }, | |
399 [ '-xf', 'config', 'Default' ], | |
400 [ | |
401 ('TransferSyntaxes/1.2.840.10008.1.2.dcm', '1.2.840.10008.1.2.1'), | |
402 ('TransferSyntaxes/1.2.840.10008.1.2.1.dcm', '1.2.840.10008.1.2.1'), | |
403 ('TransferSyntaxes/1.2.840.10008.1.2.2.dcm', '1.2.840.10008.1.2.1'), | |
404 ('TransferSyntaxes/1.2.840.10008.1.2.4.51.dcm', '1.2.840.10008.1.2.1'), | |
405 ('TransferSyntaxes/1.2.840.10008.1.2.4.90.dcm', '1.2.840.10008.1.2.4.90'), | |
406 ])) | |
407 | |
408 | |
409 print('Success!') |