changeset 404:931be0125954

Tests/CheckZipStreams.py
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 03 Jun 2021 17:03:06 +0200
parents e08e15befa0c
children 6bbc89c2c2d1
files Tests/CheckZipStreams.py Tests/Toolbox.py
diffstat 2 files changed, 202 insertions(+), 9 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Tests/CheckZipStreams.py	Thu Jun 03 17:03:06 2021 +0200
@@ -0,0 +1,197 @@
+#!/usr/bin/python2.7
+
+# Orthanc - A Lightweight, RESTful DICOM Store
+# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+# Department, University Hospital of Liege, Belgium
+# Copyright (C) 2017-2021 Osimis S.A., Belgium
+#
+# This program is free software: you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+import Toolbox
+import json
+import os
+import subprocess
+import sys
+import time
+import zipfile
+import multiprocessing
+import threading
+import queue
+
+
+if len(sys.argv) != 3:
+    print('Must provide a path to Orthanc binaries and to a sample ZIP archive with one DICOM study')
+    exit(-1)
+
+if not os.path.isfile(sys.argv[1]):
+    raise Exception('Inexistent path: %s' % sys.argv[1])
+    
+if not os.path.isfile(sys.argv[2]):
+    raise Exception('Inexistent path: %s' % sys.argv[2])
+    
+
+TMP = '/tmp/OrthancTest'
+CONFIG = os.path.join(TMP, 'Configuration.json')
+
+if os.path.exists(TMP):
+    print('Temporary path already exists: %s' % TMP)
+    exit(-1)
+
+os.mkdir(TMP)
+
+
+ORTHANC = Toolbox.DefineOrthanc(username = 'orthanc',
+                                password = 'orthanc')
+
+
+def GetArchive(config, testFunction):
+    with open(CONFIG, 'w') as f:
+        f.write(json.dumps(config))
+    
+    process = subprocess.Popen(
+        [ sys.argv[1], CONFIG, '--no-jobs' ],
+        cwd = TMP,
+        #stdout=subprocess.PIPE, 
+        stderr=subprocess.PIPE, 
+        #shell=True
+        )
+
+    #time.sleep(1)
+
+    while True:
+        try:
+            system = Toolbox.DoGet(ORTHANC, '/system')
+            break
+        except:
+            time.sleep(0.1)
+
+    try:
+        with open(sys.argv[2], 'rb') as f:
+            Toolbox.DoPost(ORTHANC, '/instances', f.read())
+
+        studies = Toolbox.DoGet(ORTHANC, '/studies')
+        if len(studies) != 1:
+            raise Exception('More than one study is available in Orthanc')
+
+        testFunction(ORTHANC, studies[0])
+
+    finally:
+        process.terminate()
+        process.wait()
+
+
+def Assert(b):
+    if not b:
+        raise Exception('Bad result')
+
+
+for streaming in [ False, True, None ]:
+    if streaming == True:
+        suffix = 'with streaming'
+        config = { 'SynchronousZipStream' : True }
+    elif streaming == False:
+        suffix = 'without streaming'
+        config = { 'SynchronousZipStream' : False }
+    else:
+        suffix = 'default streaming'
+        config = { }
+
+
+    print('==== SIMPLE TEST - %s ====' % suffix)
+    def test(ORTHANC, study):
+        instances = Toolbox.DoGet(ORTHANC, '/instances')
+        z = Toolbox.ParseArchive(Toolbox.DoGet(ORTHANC, '/studies/%s/archive' % study))
+        Assert(len(instances) == len(z.namelist()))   
+
+    GetArchive(config, test)
+
+
+    print('==== CANCEL SERVER JOB - %s ====' % suffix)
+    def TestCancelServerJob(ORTHANC, study):
+        def CheckCorruptedArchive(queue):
+            try:
+                z = Toolbox.DoGet(ORTHANC, '/studies/%s/archive' % study)
+                Assert(streaming == True or streaming == None)
+
+                try:
+                    Toolbox.ParseArchive(z)
+                    print('error, got valid archive')
+                    queue.put(False)  # The archive is not corrupted as expected
+                except zipfile.BadZipfile as e:
+                    print('ok, got corrupted archive')
+                    queue.put(True)
+
+            except Exception as e:
+                Assert(streaming == False)
+                Assert(e[0] == 500)  # HTTP status code 500
+                print('ok, got none archive')
+                queue.put(True)
+
+        def cancel():
+            while True:
+                j = Toolbox.DoGet(ORTHANC, '/jobs?expand')
+                Assert(len(j) <= 1)
+                if len(j) == 1:
+                    Assert(j[0]['State'] == 'Running')
+                    Toolbox.DoPost(ORTHANC, '/jobs/%s/cancel' % j[0]['ID'], {})
+                    return
+                time.sleep(.01)
+
+        q = queue.Queue()
+        t1 = threading.Thread(target=cancel)
+        t2 = threading.Thread(target=CheckCorruptedArchive, args=(q,))
+        t1.start()
+        t2.start()
+        t1.join()
+        t2.join()
+
+        Assert(q.get() == True)
+
+    GetArchive(config, TestCancelServerJob)
+
+
+    print('==== CANCEL HTTP CLIENT - %s ====' % suffix)
+    def TestCancelHttpClient(ORTHANC, study):
+        def DownloadArchive(queue):
+            z = Toolbox.DoGet(ORTHANC, '/studies/%s/archive' % study)
+            queue.put('success')
+
+        q = multiprocessing.Queue()
+        p = multiprocessing.Process(target=DownloadArchive, args=(q, ))
+        p.start()
+        time.sleep(0.05)
+        p.terminate()
+        p.join()
+        Assert(q.qsize() == 0)
+
+        while True:
+            j = Toolbox.DoGet(ORTHANC, '/jobs?expand')
+            Assert(len(j) == 1)
+            if j[0]['State'] == 'Running':
+                continue
+            else:
+                if streaming == False:
+                    # The sending of the temporary file is *not* part
+                    # of the job in this case
+                    Assert(j[0]['State'] == 'Success')
+                else:
+                    Assert(j[0]['State'] == 'Failure')
+                    Assert(j[0]['ErrorCode'] == 14)  # Cannot write to file
+                break
+        
+    GetArchive(config, TestCancelHttpClient)
+        
+
+print('Success!')
--- a/Tests/Toolbox.py	Wed Jun 02 17:42:20 2021 +0200
+++ b/Tests/Toolbox.py	Thu Jun 03 17:03:06 2021 +0200
@@ -230,23 +230,19 @@
     # http://www.pythonware.com/library/pil/handbook/introduction.htm
     return UncompressImage(DoGet(orthanc, uri, headers = headers))
 
-def GetArchive(orthanc, uri):
+def ParseArchive(s):
     # http://stackoverflow.com/a/1313868/881731
-    s = DoGet(orthanc, uri)
-
     if (sys.version_info >= (3, 0)):
         return zipfile.ZipFile(BytesIO(s), "r")
     else:
         return zipfile.ZipFile(StringIO(s), "r")
 
+def GetArchive(orthanc, uri):
+    return ParseArchive(DoGet(orthanc, uri))
+
 def PostArchive(orthanc, uri, body):
     # http://stackoverflow.com/a/1313868/881731
-    s = DoPost(orthanc, uri, body)
-
-    if (sys.version_info >= (3, 0)):
-        return zipfile.ZipFile(BytesIO(s), "r")
-    else:
-        return zipfile.ZipFile(StringIO(s), "r")
+    return ParseArchive(DoPost(orthanc, uri, body))
 
 def IsDefinedInLua(orthanc, name):
     s = DoPost(orthanc, '/tools/execute-script', 'print(type(%s))' % name, 'application/lua')