# HG changeset patch # User Alain Mazy # Date 1589286989 -7200 # Node ID b55b959647edcac689e7b7be36cb8cf5c336c801 # Parent 946b2199f481352ca865ca31e79805ca3a4151ef# Parent 192665e6113f6f20dc72ad2ea957c3436a80e202 merge default -> c-get diff -r 946b2199f481 -r b55b959647ed .hgtags --- a/.hgtags Tue May 12 13:21:38 2020 +0200 +++ b/.hgtags Tue May 12 14:36:29 2020 +0200 @@ -17,3 +17,5 @@ 99acf0c17348ba6de9a1e2e222a9051ec3af3a23 Orthanc-1.6.0 a49055a3399b9015335e54f5a622b4432d303f61 Orthanc-1.6.0 b55a7cb5ee0229f48b7d90e9a05dc15efe2206ec Orthanc-1.6.1 +b55a7cb5ee0229f48b7d90e9a05dc15efe2206ec Orthanc-1.6.1 +2dc6ab42f40133e6e3480a5c03e4b33c9ff34845 Orthanc-1.6.1 diff -r 946b2199f481 -r b55b959647ed Database/Issue169.dcm.bz2 Binary file Database/Issue169.dcm.bz2 has changed diff -r 946b2199f481 -r b55b959647ed README --- a/README Tue May 12 13:21:38 2020 +0200 +++ b/README Tue May 12 14:36:29 2020 +0200 @@ -135,6 +135,21 @@ Note that you will have to grant root access for Docker. +(Option 2c) On Windows: + +Easiest way to run the integration test under windows is actually +run the remote Orthanc and the python script under WSL and run the +orthanc under test on Windows (such that you can debug it). +Note that if Orthanc on Windows is not using the standard port, you +may specify them when starting the test as shown below + +# bash +# python Tests/Run.py --force --dicom 8567 --rest 8568 + +Note that you will have to install Linux prerequisites under WSL +and the orthanc package as well. + + Licensing ========= diff -r 946b2199f481 -r b55b959647ed Tests/Run.py --- a/Tests/Run.py Tue May 12 13:21:38 2020 +0200 +++ b/Tests/Run.py Tue May 12 14:36:29 2020 +0200 @@ -105,11 +105,18 @@ config = re.sub(r'("DicomModalities"\s*:)\s*{', r'\1 { "orthanc" : [ "%s", "%s", %s ]' % (args.aet, args.server, args.dicom), config) +# New to test transcoding over DICOM (1.7.0) +config = re.sub(r'("RleTransferSyntaxAccepted"\s*:)\s*true', r'\1 false', config) + + with open(CONFIG, 'wt') as f: f.write(config) localOrthanc = ExternalCommandThread([ - 'Orthanc', CONFIG, #'--verbose', + 'Orthanc', + CONFIG, + #'--verbose', + #'--no-jobs' #'/home/jodogne/Subversion/Orthanc/i/Orthanc', CONFIG, '--verbose' ]) diff -r 946b2199f481 -r b55b959647ed Tests/Tests.py --- a/Tests/Tests.py Tue May 12 13:21:38 2020 +0200 +++ b/Tests/Tests.py Tue May 12 14:36:29 2020 +0200 @@ -22,6 +22,7 @@ import base64 +import bz2 import copy import pprint import tempfile @@ -139,7 +140,6 @@ - class Orthanc(unittest.TestCase): def setUp(self): if (sys.version_info >= (3, 0)): @@ -5451,6 +5451,189 @@ self.assertEqual(1, len(DoGet(_LOCAL, '/instances'))) self.assertEqual(0, len(DoGet(_REMOTE, '/instances'))) + + def test_storescu_transcoding(self): # New in Orthanc 1.7.0 + # Add a RLE-encoded DICOM file + i = UploadInstance(_REMOTE, 'TransferSyntaxes/1.2.840.10008.1.2.5.dcm')['ID'] + self.assertEqual(0, len(DoGet(_LOCAL, '/instances'))) + self.assertEqual(1, len(DoGet(_REMOTE, '/instances'))) + rleSize = len(DoGet(_REMOTE, '/instances/%s/file' % i)) + + # Export the instance, with transcoding: "_REMOTE" is the + # Orthanc server being tested + try: + DoDelete(_REMOTE, '/modalities/toto') + except: + pass + + params = DoGet(_REMOTE, '/modalities?expand') ['orthanctest'] + DoPut(_REMOTE, '/modalities/toto', params) + DoPost(_REMOTE, '/modalities/toto/store', str(i), 'text/plain') + j = DoGet(_LOCAL, '/instances') + self.assertEqual(1, len(j)) + uncompressedSize = len(DoGet(_LOCAL, '/instances/%s/file' % j[0])) + self.assertTrue(uncompressedSize > rleSize / 2) + + # Export, with transcoding disabled => this fails + params['AllowTranscoding'] = False + DoPut(_REMOTE, '/modalities/toto', params) + self.assertRaises(Exception, lambda: DoPost(_REMOTE, '/modalities/toto/store', str(i), 'text/plain')) + + + def test_bitbucket_issue_169(self): + with open(GetDatabasePath('Issue169.dcm.bz2'), 'rb') as f: + dicom = bz2.decompress(f.read()) + + self.assertEqual('1.2.840.10008.1.2.1', GetTransferSyntax(dicom)) + + self.assertEqual(44350560, len(dicom)) + i = DoPost(_REMOTE, '/instances', dicom, 'application/dicom') ['ID'] + + tags = DoGet(_REMOTE, '/instances/%s/tags' % i) + self.assertEqual('NORMAL', tags['1337,1001']['Value']) + + self.assertEqual(0, len(DoGet(_LOCAL, '/instances'))) + DoPost(_REMOTE, '/modalities/orthanctest/store', str(i), 'text/plain') + j = DoGet(_LOCAL, '/instances') + self.assertEqual(1, len(j)) + + # In Orthanc <= 1.6.1, transfer syntax changed from "Explicit + # VR Little Endian" (1.2.840.10008.1.2.1) to "Implicit VR + # Little Endian" (1.2.840.10008.1.2) + self.assertEqual('1.2.840.10008.1.2.1', GetTransferSyntax( + DoGet(_LOCAL, '/instances/%s/file' % j[0]))) + + # In Orthanc <= 1.6.1, the value of the private tags was lost + # because of this transcoding + tags = DoGet(_LOCAL, '/instances/%s/tags' % j[0]) + self.assertEqual('NORMAL', tags['1337,1001']['Value']) + + + def test_modify_transcode(self): + i = UploadInstance(_REMOTE, 'KarstenHilbertRF.dcm')['ID'] + self.assertEqual('1.2.840.10008.1.2.1', GetTransferSyntax( + DoGet(_REMOTE, '/instances/%s/file' % i))) + + for syntax in [ + '1.2.840.10008.1.2', + '1.2.840.10008.1.2.1', + #'1.2.840.10008.1.2.1.99', # Deflated Explicit VR Little Endian + '1.2.840.10008.1.2.2', + '1.2.840.10008.1.2.4.50', + '1.2.840.10008.1.2.4.51', + '1.2.840.10008.1.2.4.57', + '1.2.840.10008.1.2.4.70', + #'1.2.840.10008.1.2.4.80', # This makes DCMTK 3.6.2 crash + #'1.2.840.10008.1.2.4.81', # This makes DCMTK 3.6.2 crash + ]: + transcoded = DoPost(_REMOTE, '/instances/%s/modify' % i, { + 'Transcode' : syntax, + }) + + self.assertEqual(syntax, GetTransferSyntax(transcoded)) + + + def test_archive_transcode(self): + info = UploadInstance(_REMOTE, 'KarstenHilbertRF.dcm') + + # GET on "/media" + z = GetArchive(_REMOTE, '/patients/%s/media' % info['ParentPatient']) + self.assertEqual(2, len(z.namelist())) + self.assertEqual('1.2.840.10008.1.2.1', GetTransferSyntax(z.read('IMAGES/IM0'))) + + self.assertRaises(Exception, lambda: DoGet(_REMOTE, '/patients/%s/media?transcode=nope' % info['ParentPatient'])) + + z = GetArchive(_REMOTE, '/patients/%s/media?transcode=1.2.840.10008.1.2.4.50' % info['ParentPatient']) + self.assertEqual('1.2.840.10008.1.2.4.50', GetTransferSyntax(z.read('IMAGES/IM0'))) + + z = GetArchive(_REMOTE, '/studies/%s/media?transcode=1.2.840.10008.1.2.4.51' % info['ParentStudy']) + self.assertEqual('1.2.840.10008.1.2.4.51', GetTransferSyntax(z.read('IMAGES/IM0'))) + + z = GetArchive(_REMOTE, '/series/%s/media?transcode=1.2.840.10008.1.2.4.57' % info['ParentSeries']) + self.assertEqual('1.2.840.10008.1.2.4.57', GetTransferSyntax(z.read('IMAGES/IM0'))) + + + # POST on "/media" + self.assertRaises(Exception, lambda: PostArchive( + _REMOTE, '/patients/%s/media' % info['ParentPatient'], { 'Transcode' : 'nope' })) + + z = PostArchive(_REMOTE, '/patients/%s/media' % info['ParentPatient'], { + 'Transcode' : '1.2.840.10008.1.2.4.50', + }) + self.assertEqual('1.2.840.10008.1.2.4.50', GetTransferSyntax(z.read('IMAGES/IM0'))) + + z = PostArchive(_REMOTE, '/studies/%s/media' % info['ParentStudy'], { + 'Transcode' : '1.2.840.10008.1.2.4.51', + }) + self.assertEqual('1.2.840.10008.1.2.4.51', GetTransferSyntax(z.read('IMAGES/IM0'))) + + z = PostArchive(_REMOTE, '/series/%s/media' % info['ParentSeries'], { + 'Transcode' : '1.2.840.10008.1.2.4.57', + }) + self.assertEqual('1.2.840.10008.1.2.4.57', GetTransferSyntax(z.read('IMAGES/IM0'))) + + + # GET on "/archive" + z = GetArchive(_REMOTE, '/patients/%s/archive' % info['ParentPatient']) + self.assertEqual(1, len(z.namelist())) + self.assertEqual('1.2.840.10008.1.2.1', GetTransferSyntax(z.read(z.namelist()[0]))) + + self.assertRaises(Exception, lambda: DoGet(_REMOTE, '/patients/%s/archive?transcode=nope' % info['ParentPatient'])) + + z = GetArchive(_REMOTE, '/patients/%s/archive?transcode=1.2.840.10008.1.2' % info['ParentPatient']) + self.assertEqual('1.2.840.10008.1.2', GetTransferSyntax(z.read(z.namelist()[0]))) + + z = GetArchive(_REMOTE, '/studies/%s/archive?transcode=1.2.840.10008.1.2.2' % info['ParentStudy']) + self.assertEqual('1.2.840.10008.1.2.2', GetTransferSyntax(z.read(z.namelist()[0]))) + + z = GetArchive(_REMOTE, '/series/%s/archive?transcode=1.2.840.10008.1.2.4.70' % info['ParentSeries']) + self.assertEqual('1.2.840.10008.1.2.4.70', GetTransferSyntax(z.read(z.namelist()[0]))) + + + # POST on "/archive" + self.assertRaises(Exception, lambda: PostArchive( + _REMOTE, '/patients/%s/archive' % info['ParentPatient'], { 'Transcode' : 'nope' })) + + z = PostArchive(_REMOTE, '/patients/%s/archive' % info['ParentPatient'], { + 'Transcode' : '1.2.840.10008.1.2.4.50', + }) + self.assertEqual('1.2.840.10008.1.2.4.50', GetTransferSyntax(z.read(z.namelist()[0]))) + + z = PostArchive(_REMOTE, '/studies/%s/archive' % info['ParentStudy'], { + 'Transcode' : '1.2.840.10008.1.2.4.51', + }) + self.assertEqual('1.2.840.10008.1.2.4.51', GetTransferSyntax(z.read(z.namelist()[0]))) + + z = PostArchive(_REMOTE, '/series/%s/archive' % info['ParentSeries'], { + 'Transcode' : '1.2.840.10008.1.2.4.57', + }) + self.assertEqual('1.2.840.10008.1.2.4.57', GetTransferSyntax(z.read(z.namelist()[0]))) + + + # "/tools/create-*" + z = PostArchive(_REMOTE, '/tools/create-archive', { + 'Resources' : [ info['ParentStudy'] ], + 'Transcode' : '1.2.840.10008.1.2.4.50', + }) + self.assertEqual(1, len(z.namelist())) + self.assertEqual('1.2.840.10008.1.2.4.50', GetTransferSyntax(z.read(z.namelist()[0]))) + + z = PostArchive(_REMOTE, '/tools/create-media', { + 'Resources' : [ info['ParentStudy'] ], + 'Transcode' : '1.2.840.10008.1.2.4.51', + }) + self.assertEqual(2, len(z.namelist())) + self.assertEqual('1.2.840.10008.1.2.4.51', GetTransferSyntax(z.read('IMAGES/IM0'))) + + z = PostArchive(_REMOTE, '/tools/create-media-extended', { + 'Resources' : [ info['ParentStudy'] ], + 'Transcode' : '1.2.840.10008.1.2.4.57', + }) + self.assertEqual(2, len(z.namelist())) + self.assertEqual('1.2.840.10008.1.2.4.57', GetTransferSyntax(z.read('IMAGES/IM0'))) + + + def test_getscu(self): # no transcoding required diff -r 946b2199f481 -r b55b959647ed Tests/Toolbox.py --- a/Tests/Toolbox.py Tue May 12 13:21:38 2020 +0200 +++ b/Tests/Toolbox.py Tue May 12 14:36:29 2020 +0200 @@ -26,6 +26,7 @@ import re import signal import subprocess +import tempfile import threading import sys import time @@ -379,3 +380,12 @@ else: self.assertAlmostEqual(a, b, places = places) + + +def GetTransferSyntax(dicom): + with tempfile.NamedTemporaryFile(delete = True) as f: + f.write(dicom) + f.flush() + data = subprocess.check_output([ FindExecutable('dcm2xml'), f.name ]) + + return re.search('