changeset 4325:b96aedfa8cc1

unit tests now running in WebAssembly
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 24 Nov 2020 16:21:29 +0100
parents 433e94d08e36
children 83c5bd439fcc
files OrthancFramework/Resources/CMake/OrthancFrameworkConfiguration.cmake OrthancFramework/Resources/CMake/UuidConfiguration.cmake OrthancFramework/Resources/Patches/e2fsprogs-1.44.5-apple.patch OrthancFramework/Resources/Patches/e2fsprogs-1.44.5.patch OrthancFramework/Sources/Images/PamReader.cpp OrthancFramework/Sources/Images/PamWriter.cpp OrthancFramework/UnitTestsSources/DicomMapTests.cpp OrthancFramework/UnitTestsSources/FrameworkTests.cpp OrthancFramework/UnitTestsSources/FromDcmtkTests.cpp OrthancFramework/UnitTestsSources/ImageTests.cpp OrthancFramework/UnitTestsSources/LoggingTests.cpp OrthancFramework/UnitTestsSources/RestApiTests.cpp OrthancFramework/UnitTestsSources/StreamTests.cpp
diffstat 13 files changed, 275 insertions(+), 127 deletions(-) [+]
line wrap: on
line diff
--- a/OrthancFramework/Resources/CMake/OrthancFrameworkConfiguration.cmake	Tue Nov 24 12:37:52 2020 +0100
+++ b/OrthancFramework/Resources/CMake/OrthancFrameworkConfiguration.cmake	Tue Nov 24 16:21:29 2020 +0100
@@ -153,11 +153,15 @@
   ${CMAKE_CURRENT_LIST_DIR}/../../Sources/EnumerationDictionary.h
   ${CMAKE_CURRENT_LIST_DIR}/../../Sources/Enumerations.cpp
   ${CMAKE_CURRENT_LIST_DIR}/../../Sources/FileStorage/MemoryStorageArea.cpp
+  ${CMAKE_CURRENT_LIST_DIR}/../../Sources/HttpServer/HttpContentNegociation.cpp
+  ${CMAKE_CURRENT_LIST_DIR}/../../Sources/HttpServer/HttpToolbox.cpp
   ${CMAKE_CURRENT_LIST_DIR}/../../Sources/HttpServer/MultipartStreamReader.cpp
   ${CMAKE_CURRENT_LIST_DIR}/../../Sources/HttpServer/StringMatcher.cpp
   ${CMAKE_CURRENT_LIST_DIR}/../../Sources/Logging.cpp
   ${CMAKE_CURRENT_LIST_DIR}/../../Sources/OrthancException.cpp
   ${CMAKE_CURRENT_LIST_DIR}/../../Sources/OrthancFramework.cpp
+  ${CMAKE_CURRENT_LIST_DIR}/../../Sources/RestApi/RestApiHierarchy.cpp
+  ${CMAKE_CURRENT_LIST_DIR}/../../Sources/RestApi/RestApiPath.cpp
   ${CMAKE_CURRENT_LIST_DIR}/../../Sources/SerializationToolbox.cpp
   ${CMAKE_CURRENT_LIST_DIR}/../../Sources/Toolbox.cpp
   ${CMAKE_CURRENT_LIST_DIR}/../../Sources/WebServiceParameters.cpp
@@ -299,19 +303,15 @@
     ${CMAKE_CURRENT_LIST_DIR}/../../Sources/HttpServer/BufferHttpSender.cpp
     ${CMAKE_CURRENT_LIST_DIR}/../../Sources/HttpServer/FilesystemHttpHandler.cpp
     ${CMAKE_CURRENT_LIST_DIR}/../../Sources/HttpServer/FilesystemHttpSender.cpp
-    ${CMAKE_CURRENT_LIST_DIR}/../../Sources/HttpServer/HttpContentNegociation.cpp
     ${CMAKE_CURRENT_LIST_DIR}/../../Sources/HttpServer/HttpFileSender.cpp
     ${CMAKE_CURRENT_LIST_DIR}/../../Sources/HttpServer/HttpOutput.cpp
     ${CMAKE_CURRENT_LIST_DIR}/../../Sources/HttpServer/HttpServer.cpp
     ${CMAKE_CURRENT_LIST_DIR}/../../Sources/HttpServer/HttpStreamTranscoder.cpp
-    ${CMAKE_CURRENT_LIST_DIR}/../../Sources/HttpServer/HttpToolbox.cpp
     ${CMAKE_CURRENT_LIST_DIR}/../../Sources/HttpServer/StringHttpOutput.cpp
     ${CMAKE_CURRENT_LIST_DIR}/../../Sources/RestApi/RestApi.cpp
     ${CMAKE_CURRENT_LIST_DIR}/../../Sources/RestApi/RestApiCall.cpp
     ${CMAKE_CURRENT_LIST_DIR}/../../Sources/RestApi/RestApiGetCall.cpp
-    ${CMAKE_CURRENT_LIST_DIR}/../../Sources/RestApi/RestApiHierarchy.cpp
     ${CMAKE_CURRENT_LIST_DIR}/../../Sources/RestApi/RestApiOutput.cpp
-    ${CMAKE_CURRENT_LIST_DIR}/../../Sources/RestApi/RestApiPath.cpp
     )
 
   if (ENABLE_PUGIXML)
@@ -508,6 +508,7 @@
   endif()
 
   set(ORTHANC_DICOM_SOURCES_INTERNAL
+    ${CMAKE_CURRENT_LIST_DIR}/../../Sources/DicomNetworking/DicomFindAnswers.cpp
     ${CMAKE_CURRENT_LIST_DIR}/../../Sources/DicomParsing/DicomModification.cpp
     ${CMAKE_CURRENT_LIST_DIR}/../../Sources/DicomParsing/DicomWebJsonVisitor.cpp
     ${CMAKE_CURRENT_LIST_DIR}/../../Sources/DicomParsing/FromDcmtkBridge.cpp
@@ -531,7 +532,6 @@
       ${CMAKE_CURRENT_LIST_DIR}/../../Sources/DicomNetworking/DicomAssociation.cpp
       ${CMAKE_CURRENT_LIST_DIR}/../../Sources/DicomNetworking/DicomAssociationParameters.cpp
       ${CMAKE_CURRENT_LIST_DIR}/../../Sources/DicomNetworking/DicomControlUserConnection.cpp
-      ${CMAKE_CURRENT_LIST_DIR}/../../Sources/DicomNetworking/DicomFindAnswers.cpp
       ${CMAKE_CURRENT_LIST_DIR}/../../Sources/DicomNetworking/DicomServer.cpp
       ${CMAKE_CURRENT_LIST_DIR}/../../Sources/DicomNetworking/DicomStoreUserConnection.cpp
       ${CMAKE_CURRENT_LIST_DIR}/../../Sources/DicomNetworking/Internals/CommandDispatcher.cpp
--- a/OrthancFramework/Resources/CMake/UuidConfiguration.cmake	Tue Nov 24 12:37:52 2020 +0100
+++ b/OrthancFramework/Resources/CMake/UuidConfiguration.cmake	Tue Nov 24 16:21:29 2020 +0100
@@ -35,12 +35,13 @@
 
     
     ##
-    ## Patch for OS X, in order to be compatible with Cocoa (used in Stone)
+    ## Patch for OS X, in order to be compatible with Cocoa, and for
+    ## WebAssembly (used in Stone)
     ## 
 
     execute_process(
       COMMAND ${PATCH_EXECUTABLE} -p0 -N -i
-      ${CMAKE_CURRENT_LIST_DIR}/../Patches/e2fsprogs-1.44.5-apple.patch
+      ${CMAKE_CURRENT_LIST_DIR}/../Patches/e2fsprogs-1.44.5.patch
       WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
       RESULT_VARIABLE Failure
       )
--- a/OrthancFramework/Resources/Patches/e2fsprogs-1.44.5-apple.patch	Tue Nov 24 12:37:52 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,24 +0,0 @@
-diff -urEb e2fsprogs-1.44.5.orig/lib/uuid/uuid.h.in e2fsprogs-1.44.5/lib/uuid/uuid.h.in
---- e2fsprogs-1.44.5.orig/lib/uuid/uuid.h.in	2019-02-21 20:17:23.461402522 +0100
-+++ e2fsprogs-1.44.5/lib/uuid/uuid.h.in	2019-02-21 20:25:05.664540445 +0100
-@@ -35,6 +35,20 @@
- #ifndef _UUID_UUID_H
- #define _UUID_UUID_H
- 
-+
-+#if defined(__APPLE__)
-+// This patch defines the "uuid_string_t" type on OS X, which is
-+// required if linking against Cocoa (this occurs in Stone of Orthanc)
-+#include <sys/_types.h>
-+#include <sys/_types/_uuid_t.h>
-+
-+#ifndef _UUID_STRING_T
-+#define _UUID_STRING_T
-+typedef __darwin_uuid_string_t  uuid_string_t;
-+#endif /* _UUID_STRING_T */
-+#endif
-+
-+
- #include <sys/types.h>
- #ifndef _WIN32
- #include <sys/time.h>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancFramework/Resources/Patches/e2fsprogs-1.44.5.patch	Tue Nov 24 16:21:29 2020 +0100
@@ -0,0 +1,42 @@
+diff -urEb e2fsprogs-1.44.5.orig/lib/uuid/gen_uuid.c e2fsprogs-1.44.5/lib/uuid/gen_uuid.c
+--- e2fsprogs-1.44.5.orig/lib/uuid/gen_uuid.c	2020-11-24 15:47:40.950897761 +0100
++++ e2fsprogs-1.44.5/lib/uuid/gen_uuid.c	2020-11-24 15:48:51.234732050 +0100
+@@ -147,12 +147,14 @@
+ 		fd = open("/dev/urandom", O_RDONLY);
+ 		if (fd == -1)
+ 			fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
++#if !defined(__EMSCRIPTEN__)  // By SJO for Stone
+ 		if (fd >= 0) {
+ 			i = fcntl(fd, F_GETFD);
+ 			if (i >= 0)
+ 				fcntl(fd, F_SETFD, i | FD_CLOEXEC);
+ 		}
+ #endif
++#endif
+ 		srand(((unsigned)getpid() << 16) ^ getuid() ^ tv.tv_sec ^ tv.tv_usec);
+ #ifdef DO_JRAND_MIX
+ 		jrand_seed[0] = getpid() ^ (tv.tv_sec & 0xFFFF);
+diff -urEb e2fsprogs-1.44.5.orig/lib/uuid/uuid.h.in e2fsprogs-1.44.5/lib/uuid/uuid.h.in
+--- e2fsprogs-1.44.5.orig/lib/uuid/uuid.h.in	2020-11-24 15:47:40.950897761 +0100
++++ e2fsprogs-1.44.5/lib/uuid/uuid.h.in	2020-11-24 15:48:00.946849227 +0100
+@@ -35,6 +35,20 @@
+ #ifndef _UUID_UUID_H
+ #define _UUID_UUID_H
+ 
++
++#if defined(__APPLE__)
++// This patch defines the "uuid_string_t" type on OS X, which is
++// required if linking against Cocoa (this occurs in Stone of Orthanc)
++#include <sys/_types.h>
++#include <sys/_types/_uuid_t.h>
++
++#ifndef _UUID_STRING_T
++#define _UUID_STRING_T
++typedef __darwin_uuid_string_t  uuid_string_t;
++#endif /* _UUID_STRING_T */
++#endif
++
++
+ #include <sys/types.h>
+ #ifndef _WIN32
+ #include <sys/time.h>
--- a/OrthancFramework/Sources/Images/PamReader.cpp	Tue Nov 24 12:37:52 2020 +0100
+++ b/OrthancFramework/Sources/Images/PamReader.cpp	Tue Nov 24 16:21:29 2020 +0100
@@ -232,35 +232,31 @@
         
         for (unsigned int w = 0; w < width; ++w, ++pixel)
         {
-#if defined(__EMSCRIPTEN__)  // For WebAssembly
-          /* 
-          
-          crash (2019-08-05):
-
-          Uncaught abort(alignment fault) at Error
-            at jsStackTrace
-            at stackTrace
-            at abort
-            at alignfault
-            at SAFE_HEAP_LOAD_i32_2_2 (wasm-function[251132]:39)
-            at __ZN7Orthanc9PamReader12ParseContentEv (wasm-function[11457]:8088)
-
-          Web Assembly IS LITTLE ENDIAN!
-
-          Perhaps in htobe16 ?
-          */
+          /**
+           * This is Little-Endian computer, and PAM uses
+           * Big-Endian. Need to do a 16-bit swap. We DON'T use
+           * "htobe16()", as the latter only works if the "pixel"
+           * pointer is 16-bit aligned (which is not the case if
+           * "offset" is an odd number), and the trick that was used
+           * in Orthanc <= 1.8.0 (i.e. make a "memcpy()" to a local
+           * uint16_t variable) doesn't seem work for WebAssembly. We
+           * thus use a plain old C implementation. Check out issue
+           * #99: https://bugs.orthanc-server.com/show_bug.cgi?id=99
+           *
+           * Here is the crash log on WebAssembly (2019-08-05):
+           * 
+           * Uncaught abort(alignment fault) at Error
+           * at jsStackTrace
+           * at stackTrace
+           * at abort
+           * at alignfault
+           * at SAFE_HEAP_LOAD_i32_2_2 (wasm-function[251132]:39)
+           * at __ZN7Orthanc9PamReader12ParseContentEv (wasm-function[11457]:8088)
+           **/
           uint8_t* srcdst = reinterpret_cast<uint8_t*>(pixel);
           uint8_t tmp = srcdst[0];
           srcdst[0] = srcdst[1];
           srcdst[1] = tmp;
-#else
-          // memcpy() is necessary to avoid segmentation fault if the
-          // "pixel" pointer is not 16-bit aligned (which is the case
-          // if "offset" is an odd number). Check out issue #99:
-          // https://bitbucket.org/sjodogne/orthanc/issues/99
-          uint16_t v = htobe16(*pixel);
-          memcpy(pixel, &v, sizeof(v));
-#endif
         }
       }
     }
--- a/OrthancFramework/Sources/Images/PamWriter.cpp	Tue Nov 24 12:37:52 2020 +0100
+++ b/OrthancFramework/Sources/Images/PamWriter.cpp	Tue Nov 24 16:21:29 2020 +0100
@@ -117,16 +117,25 @@
           (reinterpret_cast<const uint8_t*>(buffer) + h * sourcePitch);
         uint16_t* q = reinterpret_cast<uint16_t*>
           (reinterpret_cast<uint8_t*>(&target[offset]) + h * targetPitch);
-        
+
         for (unsigned int w = 0; w < width * channelCount; ++w)
         {
-          // memcpy() is necessary to avoid segmentation fault if the
-          // "pixel" pointer is not 16-bit aligned (which is the case
-          // if "offset" is an odd number). Check out issue #99:
-          // https://bitbucket.org/sjodogne/orthanc/issues/99
-          uint16_t v = htobe16(*p);
-          memcpy(q, &v, sizeof(uint16_t));
-
+          /**
+           * This is Little-Endian computer, and PAM uses
+           * Big-Endian. Need to do a 16-bit swap. We DON'T use
+           * "htobe16()", as the latter only works if the "pixel"
+           * pointer is 16-bit aligned (which is not the case if
+           * "offset" is an odd number), and the trick that was used
+           * in Orthanc <= 1.8.0 (i.e. make a "memcpy()" to a local
+           * uint16_t variable) doesn't seem work for WebAssembly. We
+           * thus use a plain old C implementation. Check out issue
+           * #99: https://bugs.orthanc-server.com/show_bug.cgi?id=99
+           **/
+          const uint8_t* a = reinterpret_cast<const uint8_t*>(p);
+          uint8_t* b = reinterpret_cast<uint8_t*>(q);
+          b[0] = a[1];
+          b[1] = a[0];
+          
           p++;
           q++;
         }
--- a/OrthancFramework/UnitTestsSources/DicomMapTests.cpp	Tue Nov 24 12:37:52 2020 +0100
+++ b/OrthancFramework/UnitTestsSources/DicomMapTests.cpp	Tue Nov 24 16:21:29 2020 +0100
@@ -757,6 +757,8 @@
 
 
 
+#if ORTHANC_SANDBOXED != 1
+
 #include "../Sources/SystemToolbox.h"
 
 TEST(DicomMap, DISABLED_ParseDicomMetaInformation)
@@ -1014,3 +1016,5 @@
 
   printf("\n== SUCCESSES: %u ==\n\n", success);
 }
+
+#endif
--- a/OrthancFramework/UnitTestsSources/FrameworkTests.cpp	Tue Nov 24 12:37:52 2020 +0100
+++ b/OrthancFramework/UnitTestsSources/FrameworkTests.cpp	Tue Nov 24 16:21:29 2020 +0100
@@ -34,15 +34,18 @@
 #include <gtest/gtest.h>
 
 #include "../Sources/DicomFormat/DicomTag.h"
-#include "../Sources/FileBuffer.h"
 #include "../Sources/HttpServer/HttpToolbox.h"
 #include "../Sources/Logging.h"
-#include "../Sources/MetricsRegistry.h"
 #include "../Sources/OrthancException.h"
-#include "../Sources/SystemToolbox.h"
-#include "../Sources/TemporaryFile.h"
 #include "../Sources/Toolbox.h"
 
+#if ORTHANC_SANDBOXED != 1
+#  include "../Sources/FileBuffer.h"
+#  include "../Sources/MetricsRegistry.h"
+#  include "../Sources/SystemToolbox.h"
+#  include "../Sources/TemporaryFile.h"
+#endif
+
 #include <ctype.h>
 
 
@@ -298,6 +301,8 @@
   ASSERT_TRUE(Toolbox::IsChildUri(c5, c5));
 }
 
+
+#if ORTHANC_SANDBOXED != 1
 TEST(Uri, AutodetectMimeType)
 {
   ASSERT_EQ(MimeType_Binary, SystemToolbox::AutodetectMimeType("../NOTES"));
@@ -351,6 +356,8 @@
   ASSERT_STREQ("text/css", EnumerationToString(SystemToolbox::AutodetectMimeType(".css")));
   ASSERT_STREQ("text/html", EnumerationToString(SystemToolbox::AutodetectMimeType(".html")));
 }
+#endif
+
 
 TEST(Toolbox, ComputeMD5)
 {
@@ -374,11 +381,13 @@
   ASSERT_EQ("da39a3ee-5e6b4b0d-3255bfef-95601890-afd80709", s);
 }
 
+#if ORTHANC_SANDBOXED != 1
 TEST(Toolbox, PathToExecutable)
 {
   printf("[%s]\n", SystemToolbox::GetPathToExecutable().c_str());
   printf("[%s]\n", SystemToolbox::GetDirectoryOfExecutable().c_str());
 }
+#endif
 
 TEST(Toolbox, StripSpaces)
 {
@@ -567,6 +576,7 @@
 #endif
 
 
+#if ORTHANC_SANDBOXED != 1
 TEST(Toolbox, WriteFile)
 {
   std::string path;
@@ -613,8 +623,10 @@
     ASSERT_EQ(s, t);
   }
 }
+#endif
 
 
+#if ORTHANC_SANDBOXED != 1
 TEST(Toolbox, FileBuffer)
 {
   FileBuffer f;
@@ -628,6 +640,7 @@
 
   ASSERT_THROW(f.Append("d", 1), OrthancException);  // File is closed
 }
+#endif
 
 
 TEST(Toolbox, Wildcard)
@@ -820,8 +833,15 @@
   ASSERT_EQ(Endianness_Little, Toolbox::DetectEndianness());
 #  endif
 
+  
+  /**
+   * WebAssembly is always little-endian.
+   **/
+  
+#elif defined(__EMSCRIPTEN__)
+  ASSERT_EQ(Endianness_Little, Toolbox::DetectEndianness());
 #else
-#error Support your platform here
+#  error Support your platform here
 #endif
 }
 
@@ -1007,6 +1027,7 @@
 }
 
 
+#if ORTHANC_SANDBOXED != 1
 TEST(Toolbox, Now)
 {
   LOG(WARNING) << "Local time: " << SystemToolbox::GetNowIsoString(false);
@@ -1019,7 +1040,7 @@
   SystemToolbox::GetNowDicom(date, time, true);
   LOG(WARNING) << "Universal DICOM time: [" << date << "] [" << time << "]";
 }
-
+#endif
 
 
 #if ORTHANC_ENABLE_PUGIXML == 1
@@ -1040,7 +1061,7 @@
 #endif
 
 
-#if !defined(_WIN32)
+#if !defined(_WIN32) && (ORTHANC_SANDBOXED != 1)
 TEST(Toolbox, ExecuteSystemCommand)
 {
   std::vector<std::string> args(2);
@@ -1200,6 +1221,7 @@
 }
 
 
+#if ORTHANC_SANDBOXED != 1
 TEST(Toolbox, SubstituteVariables)
 {
   std::map<std::string, std::string> env;
@@ -1228,8 +1250,10 @@
   ASSERT_EQ("AhelloB",
             Toolbox::SubstituteVariables("A${PATH}B", env));
 }
+#endif
 
 
+#if ORTHANC_SANDBOXED != 1
 TEST(MetricsRegistry, Basic)
 {
   {
@@ -1342,3 +1366,4 @@
     ASSERT_EQ(MetricsType_MinOver10Seconds, m.GetMetricsType("b"));
   }
 }
+#endif
--- a/OrthancFramework/UnitTestsSources/FromDcmtkTests.cpp	Tue Nov 24 12:37:52 2020 +0100
+++ b/OrthancFramework/UnitTestsSources/FromDcmtkTests.cpp	Tue Nov 24 16:21:29 2020 +0100
@@ -49,9 +49,12 @@
 #include "../Sources/Images/PngWriter.h"
 #include "../Sources/Logging.h"
 #include "../Sources/OrthancException.h"
-#include "../Sources/SystemToolbox.h"
 #include "../Resources/CodeGeneration/EncodingTests.h"
 
+#if ORTHANC_SANDBOXED != 1
+#  include "../Sources/SystemToolbox.h"
+#endif
+
 #include <dcmtk/dcmdata/dcdeftag.h>
 #include <dcmtk/dcmdata/dcelem.h>
 #include <dcmtk/dcmdata/dcvrat.h>
@@ -86,6 +89,7 @@
 }
 
 
+#if ORTHANC_SANDBOXED != 1
 TEST(DicomModification, Basic)
 {
   DicomModification m;
@@ -108,6 +112,7 @@
     f->SaveToFile(b);
   }
 }
+#endif
 
 
 TEST(DicomModification, Anonymization)
@@ -182,18 +187,27 @@
 
   ParsedDicomFile o(true);
   o.EmbedContent(s);
+
+#if ORTHANC_SANDBOXED != 1
   o.SaveToFile("UnitTestsResults/png1.dcm");
+#endif
 
   // Red dot, without alpha channel
   s = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAIAAAACDbGyAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gUGDTcIn2+8BgAAACJJREFUCNdj/P//PwMjIwME/P/P+J8BBTAxEOL/R9Lx/z8AynoKAXOeiV8AAAAASUVORK5CYII=";
   o.EmbedContent(s);
+
+#if ORTHANC_SANDBOXED != 1
   o.SaveToFile("UnitTestsResults/png2.dcm");
+#endif
 
   // Check box in Graylevel8
   s = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gUGDDcB53FulQAAAElJREFUGNNtj0sSAEEEQ1+U+185s1CtmRkblQ9CZldsKHJDk6DLGLJa6chjh0ooQmpjXMM86zPwydGEj6Ed/UGykkEM8X+p3u8/8LcOJIWLGeMAAAAASUVORK5CYII=";
   o.EmbedContent(s);
   //o.ReplacePlainString(DICOM_TAG_SOP_CLASS_UID, UID_DigitalXRayImageStorageForProcessing);
+
+#if ORTHANC_SANDBOXED != 1
   o.SaveToFile("UnitTestsResults/png3.dcm");
+#endif
 
 
   {
@@ -218,7 +232,10 @@
     }
 
     o.EmbedImage(accessor);
+
+#if ORTHANC_SANDBOXED != 1
     o.SaveToFile("UnitTestsResults/png4.dcm");
+#endif
   }
 }
 
@@ -743,8 +760,6 @@
 
 TEST(TestImages, PatternGrayscale8)
 {
-  static const char* PATH = "UnitTestsResults/PatternGrayscale8.dcm";
-
   Orthanc::Image image(Orthanc::PixelFormat_Grayscale8, 256, 256, false);
 
   for (int y = 0; y < 256; y++)
@@ -764,6 +779,8 @@
   image.GetRegion(r, 160, 32, 64, 192);
   Orthanc::ImageProcessing::Set(r, 255); 
 
+  std::string saved;
+  
   {
     ParsedDicomFile f(true);
     f.ReplacePlainString(DICOM_TAG_SOP_CLASS_UID, "1.2.840.10008.5.1.4.1.1.7");
@@ -774,13 +791,11 @@
     f.ReplacePlainString(DICOM_TAG_SERIES_DESCRIPTION, "Grayscale8");
     f.EmbedImage(image);
 
-    f.SaveToFile(PATH);
+    f.SaveToMemoryBuffer(saved);
   }
 
   {
-    std::string s;
-    Orthanc::SystemToolbox::ReadFile(s, PATH);
-    Orthanc::ParsedDicomFile f(s);
+    Orthanc::ParsedDicomFile f(saved);
     
     std::unique_ptr<Orthanc::ImageAccessor> decoded(f.DecodeFrame(0));
     ASSERT_EQ(256u, decoded->GetWidth());
@@ -799,8 +814,6 @@
 
 TEST(TestImages, PatternRGB)
 {
-  static const char* PATH = "UnitTestsResults/PatternRGB24.dcm";
-
   Orthanc::Image image(Orthanc::PixelFormat_RGB24, 384, 256, false);
 
   for (int y = 0; y < 256; y++)
@@ -826,6 +839,8 @@
     }
   }
 
+  std::string saved;
+
   {
     ParsedDicomFile f(true);
     f.ReplacePlainString(DICOM_TAG_SOP_CLASS_UID, "1.2.840.10008.5.1.4.1.1.7");
@@ -836,13 +851,11 @@
     f.ReplacePlainString(DICOM_TAG_SERIES_DESCRIPTION, "RGB24");
     f.EmbedImage(image);
 
-    f.SaveToFile(PATH);
+    f.SaveToMemoryBuffer(saved);
   }
 
   {
-    std::string s;
-    Orthanc::SystemToolbox::ReadFile(s, PATH);
-    Orthanc::ParsedDicomFile f(s);
+    Orthanc::ParsedDicomFile f(saved);
     
     std::unique_ptr<Orthanc::ImageAccessor> decoded(f.DecodeFrame(0));
     ASSERT_EQ(384u, decoded->GetWidth());
@@ -861,8 +874,6 @@
 
 TEST(TestImages, PatternUint16)
 {
-  static const char* PATH = "UnitTestsResults/PatternGrayscale16.dcm";
-
   Orthanc::Image image(Orthanc::PixelFormat_Grayscale16, 256, 256, false);
 
   uint16_t v = 0;
@@ -883,6 +894,8 @@
   image.GetRegion(r, 160, 32, 64, 192);
   Orthanc::ImageProcessing::Set(r, 65535); 
 
+  std::string saved;
+  
   {
     ParsedDicomFile f(true);
     f.ReplacePlainString(DICOM_TAG_SOP_CLASS_UID, "1.2.840.10008.5.1.4.1.1.7");
@@ -893,13 +906,11 @@
     f.ReplacePlainString(DICOM_TAG_SERIES_DESCRIPTION, "Grayscale16");
     f.EmbedImage(image);
 
-    f.SaveToFile(PATH);
+    f.SaveToMemoryBuffer(saved);
   }
 
   {
-    std::string s;
-    Orthanc::SystemToolbox::ReadFile(s, PATH);
-    Orthanc::ParsedDicomFile f(s);
+    Orthanc::ParsedDicomFile f(saved);
     
     std::unique_ptr<Orthanc::ImageAccessor> decoded(f.DecodeFrame(0));
     ASSERT_EQ(256u, decoded->GetWidth());
@@ -918,8 +929,6 @@
 
 TEST(TestImages, PatternInt16)
 {
-  static const char* PATH = "UnitTestsResults/PatternSignedGrayscale16.dcm";
-
   Orthanc::Image image(Orthanc::PixelFormat_SignedGrayscale16, 256, 256, false);
 
   int16_t v = -32768;
@@ -939,6 +948,8 @@
   image.GetRegion(r, 160, 32, 64, 192);
   Orthanc::ImageProcessing::Set(r, 32767); 
 
+  std::string saved;
+  
   {
     ParsedDicomFile f(true);
     f.ReplacePlainString(DICOM_TAG_SOP_CLASS_UID, "1.2.840.10008.5.1.4.1.1.7");
@@ -949,13 +960,11 @@
     f.ReplacePlainString(DICOM_TAG_SERIES_DESCRIPTION, "SignedGrayscale16");
     f.EmbedImage(image);
 
-    f.SaveToFile(PATH);
+    f.SaveToMemoryBuffer(saved);
   }
 
   {
-    std::string s;
-    Orthanc::SystemToolbox::ReadFile(s, PATH);
-    Orthanc::ParsedDicomFile f(s);
+    Orthanc::ParsedDicomFile f(saved);
     
     std::unique_ptr<Orthanc::ImageAccessor> decoded(f.DecodeFrame(0));
     ASSERT_EQ(256u, decoded->GetWidth());
--- a/OrthancFramework/UnitTestsSources/ImageTests.cpp	Tue Nov 24 12:37:52 2020 +0100
+++ b/OrthancFramework/UnitTestsSources/ImageTests.cpp	Tue Nov 24 16:21:29 2020 +0100
@@ -36,9 +36,12 @@
 #include "../Sources/Images/PngWriter.h"
 #include "../Sources/Images/PamReader.h"
 #include "../Sources/Images/PamWriter.h"
-#include "../Sources/SystemToolbox.h"
 #include "../Sources/Toolbox.h"
-#include "../Sources/TemporaryFile.h"
+
+#if ORTHANC_SANDBOXED != 1
+#  include "../Sources/SystemToolbox.h"
+#  include "../Sources/TemporaryFile.h"
+#endif
 
 #include <stdint.h>
 
@@ -65,14 +68,21 @@
   Orthanc::ImageAccessor accessor;
   accessor.AssignReadOnly(Orthanc::PixelFormat_RGB24, width, height, pitch, &image[0]);
 
-  Orthanc::IImageWriter::WriteToFile(w, "UnitTestsResults/ColorPattern.png", accessor);
+  std::string f;
 
-  std::string f, md5;
+#if ORTHANC_SANDBOXED == 1
+  Orthanc::IImageWriter::WriteToMemory(w, f, accessor);
+#else
+  Orthanc::IImageWriter::WriteToFile(w, "UnitTestsResults/ColorPattern.png", accessor);
   Orthanc::SystemToolbox::ReadFile(f, "UnitTestsResults/ColorPattern.png");
+#endif
+
+  std::string md5;
   Orthanc::Toolbox::ComputeMD5(md5, f);
   ASSERT_EQ("604e785f53c99cae6ea4584870b2c41d", md5);
 }
 
+
 TEST(PngWriter, Gray8Pattern)
 {
   Orthanc::PngWriter w;
@@ -93,10 +103,16 @@
   Orthanc::ImageAccessor accessor;
   accessor.AssignReadOnly(Orthanc::PixelFormat_Grayscale8, width, height, pitch, &image[0]);
 
+  std::string f;
+  
+#if ORTHANC_SANDBOXED == 1
+  Orthanc::IImageWriter::WriteToMemory(w, f, accessor);
+#else
   Orthanc::IImageWriter::WriteToFile(w, "UnitTestsResults/Gray8Pattern.png", accessor);
+  Orthanc::SystemToolbox::ReadFile(f, "UnitTestsResults/Gray8Pattern.png");
+#endif
 
-  std::string f, md5;
-  Orthanc::SystemToolbox::ReadFile(f, "UnitTestsResults/Gray8Pattern.png");
+  std::string md5;
   Orthanc::Toolbox::ComputeMD5(md5, f);
   ASSERT_EQ("5a9b98bea3d0a6d983980cc38bfbcdb3", md5);
 }
@@ -122,10 +138,17 @@
 
   Orthanc::ImageAccessor accessor;
   accessor.AssignReadOnly(Orthanc::PixelFormat_Grayscale16, width, height, pitch, &image[0]);
-  Orthanc::IImageWriter::WriteToFile(w, "UnitTestsResults/Gray16Pattern.png", accessor);
 
-  std::string f, md5;
+  std::string f;
+  
+#if ORTHANC_SANDBOXED == 1
+  Orthanc::IImageWriter::WriteToMemory(w, f, accessor);
+#else
+  Orthanc::IImageWriter::WriteToFile(w, "UnitTestsResults/Gray16Pattern.png", accessor);
   Orthanc::SystemToolbox::ReadFile(f, "UnitTestsResults/Gray16Pattern.png");
+#endif
+
+  std::string md5;
   Orthanc::Toolbox::ComputeMD5(md5, f);
   ASSERT_EQ("0785866a08bf0a02d2eeff87f658571c", md5);
 }
@@ -175,6 +198,7 @@
     }
   }
 
+#if ORTHANC_SANDBOXED != 1
   {
     Orthanc::TemporaryFile tmp;
     tmp.Write(s);
@@ -197,6 +221,7 @@
       }
     }
   }
+#endif
 }
 
 
@@ -218,34 +243,51 @@
     }
 
     Orthanc::JpegWriter w;
-    Orthanc::IImageWriter::WriteToFile(w, "UnitTestsResults/hello.jpg", img);
+    Orthanc::IImageWriter::WriteToMemory(w, s, img);
 
-    Orthanc::IImageWriter::WriteToMemory(w, s, img);
+#if ORTHANC_SANDBOXED != 1
+    Orthanc::IImageWriter::WriteToFile(w, "UnitTestsResults/hello.jpg", img);
     Orthanc::SystemToolbox::WriteFile(s, "UnitTestsResults/hello2.jpg");
 
     std::string t;
     Orthanc::SystemToolbox::ReadFile(t, "UnitTestsResults/hello.jpg");
     ASSERT_EQ(s.size(), t.size());
     ASSERT_EQ(0, memcmp(s.c_str(), t.c_str(), s.size()));
+#endif
   }
 
   {
-    Orthanc::JpegReader r1, r2;
-    r1.ReadFromFile("UnitTestsResults/hello.jpg");
+    Orthanc::JpegReader r1;
+    r1.ReadFromMemory(s);
     ASSERT_EQ(16u, r1.GetWidth());
     ASSERT_EQ(16u, r1.GetHeight());
 
-    r2.ReadFromMemory(s);
+#if ORTHANC_SANDBOXED != 1
+    Orthanc::JpegReader r2;
+    r2.ReadFromFile("UnitTestsResults/hello.jpg");
     ASSERT_EQ(16u, r2.GetWidth());
     ASSERT_EQ(16u, r2.GetHeight());
+#endif
 
+    unsigned int value = 0;
     for (unsigned int y = 0; y < r1.GetHeight(); y++)
     {
       const uint8_t* p1 = reinterpret_cast<const uint8_t*>(r1.GetConstRow(y));
+#if ORTHANC_SANDBOXED != 1
       const uint8_t* p2 = reinterpret_cast<const uint8_t*>(r2.GetConstRow(y));
-      for (unsigned int x = 0; x < r1.GetWidth(); x++)
+#endif
+      for (unsigned int x = 0; x < r1.GetWidth(); x++, value++)
       {
+        ASSERT_TRUE(*p1 == value ||
+                    *p1 == value - 1 ||
+                    *p1 == value + 1);  // Be tolerant to differences of +-1
+
+#if ORTHANC_SANDBOXED != 1
         ASSERT_EQ(*p1, *p2);
+        p2++;
+#endif
+
+        p1++;
       }
     }
   }
@@ -274,10 +316,16 @@
   Orthanc::ImageAccessor accessor;
   accessor.AssignReadOnly(Orthanc::PixelFormat_RGB24, width, height, pitch, &image[0]);
 
-  Orthanc::IImageWriter::WriteToFile(w, "UnitTestsResults/ColorPattern.pam", accessor);
+  std::string f;
 
-  std::string f, md5;
+#if ORTHANC_SANDBOXED == 1
+  Orthanc::IImageWriter::WriteToMemory(w, f, accessor);
+#else
+  Orthanc::IImageWriter::WriteToFile(w, "UnitTestsResults/ColorPattern.pam", accessor);
   Orthanc::SystemToolbox::ReadFile(f, "UnitTestsResults/ColorPattern.pam");
+#endif
+
+  std::string md5;
   Orthanc::Toolbox::ComputeMD5(md5, f);
   ASSERT_EQ("81a3441754e88969ebbe53e69891e841", md5);
 }
@@ -302,10 +350,16 @@
   Orthanc::ImageAccessor accessor;
   accessor.AssignReadOnly(Orthanc::PixelFormat_Grayscale8, width, height, pitch, &image[0]);
 
-  Orthanc::IImageWriter::WriteToFile(w, "UnitTestsResults/Gray8Pattern.pam", accessor);
+  std::string f;
 
-  std::string f, md5;
+#if ORTHANC_SANDBOXED == 1
+  Orthanc::IImageWriter::WriteToMemory(w, f, accessor);
+#else
+  Orthanc::IImageWriter::WriteToFile(w, "UnitTestsResults/Gray8Pattern.pam", accessor);
   Orthanc::SystemToolbox::ReadFile(f, "UnitTestsResults/Gray8Pattern.pam");
+#endif
+  
+  std::string md5;
   Orthanc::Toolbox::ComputeMD5(md5, f);
   ASSERT_EQ("7873c408d26a9d11dd1c1de5e69cc0a3", md5);
 }
@@ -331,10 +385,17 @@
 
   Orthanc::ImageAccessor accessor;
   accessor.AssignReadOnly(Orthanc::PixelFormat_Grayscale16, width, height, pitch, &image[0]);
-  Orthanc::IImageWriter::WriteToFile(w, "UnitTestsResults/Gray16Pattern.pam", accessor);
+
+  std::string f;
 
-  std::string f, md5;
+#if ORTHANC_SANDBOXED == 1
+  Orthanc::IImageWriter::WriteToMemory(w, f, accessor);
+#else
+  Orthanc::IImageWriter::WriteToFile(w, "UnitTestsResults/Gray16Pattern.pam", accessor);
   Orthanc::SystemToolbox::ReadFile(f, "UnitTestsResults/Gray16Pattern.pam");
+#endif
+
+  std::string md5;
   Orthanc::Toolbox::ComputeMD5(md5, f);
   ASSERT_EQ("b268772bf28f3b2b8520ff21c5e3dcb6", md5);
 }
@@ -407,6 +468,7 @@
     }
   }
 
+#if ORTHANC_SANDBOXED != 1
   {
     Orthanc::TemporaryFile tmp;
     tmp.Write(s);
@@ -430,7 +492,9 @@
       }
     }
   }
+#endif
 
+#if ORTHANC_SANDBOXED != 1
   {
     Orthanc::TemporaryFile tmp;
     tmp.Write(s);
@@ -455,5 +519,5 @@
       }
     }
   }
-
+#endif
 }
--- a/OrthancFramework/UnitTestsSources/LoggingTests.cpp	Tue Nov 24 12:37:52 2020 +0100
+++ b/OrthancFramework/UnitTestsSources/LoggingTests.cpp	Tue Nov 24 16:21:29 2020 +0100
@@ -129,6 +129,7 @@
 }
 
 
+#if ORTHANC_ENABLE_LOGGING_STDIO == 0
 TEST(FuncStreamBuf, BasicTest)
 {
   LoggingMementoScope loggingConfiguration;
@@ -199,6 +200,7 @@
 
   Orthanc::Logging::EnableTraceLevel(false);  // Back to normal
 }
+#endif
 
 
 
--- a/OrthancFramework/UnitTestsSources/RestApiTests.cpp	Tue Nov 24 12:37:52 2020 +0100
+++ b/OrthancFramework/UnitTestsSources/RestApiTests.cpp	Tue Nov 24 16:21:29 2020 +0100
@@ -28,15 +28,14 @@
 #include <gtest/gtest.h>
 
 #include "../Sources/ChunkedBuffer.h"
-#include "../Sources/HttpClient.h"
-#include "../Sources/Logging.h"
-#include "../Sources/SystemToolbox.h"
-#include "../Sources/RestApi/RestApi.h"
-#include "../Sources/OrthancException.h"
 #include "../Sources/Compression/ZlibCompressor.h"
-#include "../Sources/RestApi/RestApiHierarchy.h"
 #include "../Sources/HttpServer/HttpContentNegociation.h"
 #include "../Sources/HttpServer/MultipartStreamReader.h"
+#include "../Sources/Logging.h"
+#include "../Sources/OrthancException.h"
+#include "../Sources/RestApi/RestApi.h"
+#include "../Sources/RestApi/RestApiHierarchy.h"
+#include "../Sources/WebServiceParameters.h"
 
 #include <ctype.h>
 #include <boost/lexical_cast.hpp>
@@ -45,7 +44,7 @@
 
 using namespace Orthanc;
 
-#if !defined(UNIT_TESTS_WITH_HTTP_CONNEXIONS)
+#if !defined(UNIT_TESTS_WITH_HTTP_CONNEXIONS) && (ORTHANC_SANDBOXED != 1)
 #  error UNIT_TESTS_WITH_HTTP_CONNEXIONS is not defined
 #endif
 
@@ -53,8 +52,13 @@
 #  error ORTHANC_ENABLE_SSL is not defined
 #endif
 
+#if ORTHANC_SANDBOXED != 1
+#  include "../Sources/HttpClient.h"
+#  include "../Sources/SystemToolbox.h"
+#endif
 
 
+#if ORTHANC_SANDBOXED != 1
 TEST(HttpClient, Basic)
 {
   HttpClient c;
@@ -78,9 +82,10 @@
   ASSERT_TRUE(v.isMember("Description"));
 #endif
 }
+#endif
 
 
-#if UNIT_TESTS_WITH_HTTP_CONNEXIONS == 1 && ORTHANC_ENABLE_SSL == 1
+#if (UNIT_TESTS_WITH_HTTP_CONNEXIONS == 1) && (ORTHANC_ENABLE_SSL == 1) && (ORTHANC_SANDBOXED != 1)
 
 /**
    The HTTPS CA certificates for BitBucket were extracted as follows:
@@ -969,6 +974,8 @@
 
 
 
+#if ORTHANC_SANDBOXED != 1
+
 namespace
 {
   class TotoBody : public HttpClient::IRequestBody
@@ -1059,6 +1066,7 @@
 }
 
 
+
 #include "../Sources/HttpServer/HttpServer.h"
 
 TEST(HttpClient, DISABLED_Issue156_Slow)
@@ -1117,3 +1125,4 @@
 
   server.Stop();
 }
+#endif
--- a/OrthancFramework/UnitTestsSources/StreamTests.cpp	Tue Nov 24 12:37:52 2020 +0100
+++ b/OrthancFramework/UnitTestsSources/StreamTests.cpp	Tue Nov 24 16:21:29 2020 +0100
@@ -27,15 +27,18 @@
 
 #include <gtest/gtest.h>
 
-#include "../Sources/SystemToolbox.h"
 #include "../Sources/Toolbox.h"
 #include "../Sources/OrthancException.h"
 #include "../Sources/HttpServer/BufferHttpSender.h"
-#include "../Sources/HttpServer/FilesystemHttpSender.h"
 #include "../Sources/HttpServer/HttpStreamTranscoder.h"
 #include "../Sources/Compression/ZlibCompressor.h"
 #include "../Sources/Compression/GzipCompressor.h"
 
+#if ORTHANC_SANDBOXED != 1
+#  include "../Sources/HttpServer/FilesystemHttpSender.h"
+#  include "../Sources/SystemToolbox.h"
+#endif
+
 
 using namespace Orthanc;
 
@@ -171,6 +174,7 @@
 }
 
 
+#if ORTHANC_SANDBOXED != 1
 static bool ReadAllStream(std::string& result,
                           IHttpStreamAnswer& stream,
                           bool allowGzip = false,
@@ -195,8 +199,10 @@
 
   return pos == result.size();
 }
+#endif
 
 
+#if ORTHANC_SANDBOXED != 1
 TEST(BufferHttpSender, Basic)
 {
   const std::string s = "Hello world";
@@ -218,8 +224,10 @@
     ASSERT_EQ(s, t);
   }
 }
+#endif
 
 
+#if ORTHANC_SANDBOXED != 1
 TEST(FilesystemHttpSender, Basic)
 {
   const std::string& path = "UnitTestsResults/stream";
@@ -240,8 +248,10 @@
     ASSERT_EQ(0u, t.size());
   }
 }
+#endif
 
 
+#if ORTHANC_SANDBOXED != 1
 TEST(HttpStreamTranscoder, Basic)
 {
   ZlibCompressor compressor;
@@ -322,3 +332,4 @@
     ASSERT_EQ(0u, u.size());
   }
 }
+#endif