changeset 1541:ae17c8c8838f

standalone compilation of unit tests
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 11 Aug 2020 13:47:24 +0200
parents e20a2381200d
children 6e0da8370270
files OrthancStone/Resources/CMake/OrthancStoneConfiguration.cmake OrthancStone/UnitTestsSources/CMakeLists.txt OrthancStone/UnitTestsSources/Graveyard/TestStructureSet_BGO.cpp OrthancStone/UnitTestsSources/TestStructureSet.cpp OrthancStone/UnitTestsSources/UnitTestsMain.cpp
diffstat 5 files changed, 440 insertions(+), 404 deletions(-) [+]
line wrap: on
line diff
--- a/OrthancStone/Resources/CMake/OrthancStoneConfiguration.cmake	Tue Aug 11 13:28:40 2020 +0200
+++ b/OrthancStone/Resources/CMake/OrthancStoneConfiguration.cmake	Tue Aug 11 13:47:24 2020 +0200
@@ -74,6 +74,7 @@
   endif()
 
   set(ENABLE_THREADS OFF)
+  set(ENABLE_WEB_CLIENT OFF)
   add_definitions(-DORTHANC_ENABLE_WASM=1)
 else()
   if (CMAKE_SYSTEM_NAME STREQUAL "Emscripten" OR
@@ -103,7 +104,7 @@
 ## Configure optional third-party components
 #####################################################################
 
-if (NOT ORTHANC_SANDBOXED)
+if (ENABLE_WEB_CLIENT)
   list(APPEND ORTHANC_STONE_SOURCES
     ${ORTHANC_STONE_ROOT}/Sources/Toolbox/OrthancDatasets/OrthancHttpConnection.cpp
     )
@@ -213,29 +214,20 @@
 ## All the source files required to build Stone of Orthanc
 #####################################################################
 
-if (NOT ORTHANC_SANDBOXED)
-  set(PLATFORM_SOURCES
-    ${ORTHANC_STONE_ROOT}/Sources/Loaders/GenericLoadersContext.cpp
-    ${ORTHANC_STONE_ROOT}/Sources/Loaders/GenericLoadersContext.h
+if (ENABLE_SDL)
+  list(APPEND ORTHANC_STONE_SOURCES
+    ${ORTHANC_STONE_ROOT}/Sources/Viewport/SdlWindow.cpp
+    ${ORTHANC_STONE_ROOT}/Sources/Viewport/SdlWindow.h
     )
 
-  if (ENABLE_SDL)
+  if (ENABLE_OPENGL)
     list(APPEND ORTHANC_STONE_SOURCES
-      ${ORTHANC_STONE_ROOT}/Sources/Viewport/SdlWindow.cpp
-      ${ORTHANC_STONE_ROOT}/Sources/Viewport/SdlWindow.h
+      ${ORTHANC_STONE_ROOT}/Sources/OpenGL/SdlOpenGLContext.cpp
+      ${ORTHANC_STONE_ROOT}/Sources/OpenGL/SdlOpenGLContext.h
+      ${ORTHANC_STONE_ROOT}/Sources/Viewport/SdlViewport.cpp
+      ${ORTHANC_STONE_ROOT}/Sources/Viewport/SdlViewport.h
       )
   endif()
-
-  if (ENABLE_SDL)
-    if (ENABLE_OPENGL)
-      list(APPEND ORTHANC_STONE_SOURCES
-        ${ORTHANC_STONE_ROOT}/Sources/OpenGL/SdlOpenGLContext.cpp
-        ${ORTHANC_STONE_ROOT}/Sources/OpenGL/SdlOpenGLContext.h
-        ${ORTHANC_STONE_ROOT}/Sources/Viewport/SdlViewport.cpp
-        ${ORTHANC_STONE_ROOT}/Sources/Viewport/SdlViewport.h
-        )
-    endif()
-  endif()
 endif()
 
 
@@ -247,10 +239,13 @@
     )
 endif()
 
-if (ENABLE_THREADS)
+
+if (NOT ORTHANC_SANDBOXED AND ENABLE_THREADS AND ENABLE_WEB_CLIENT)
   list(APPEND ORTHANC_STONE_SOURCES
+    ${ORTHANC_STONE_ROOT}/Sources/Loaders/GenericLoadersContext.cpp
+    ${ORTHANC_STONE_ROOT}/Sources/Loaders/GenericLoadersContext.h
+    ${ORTHANC_STONE_ROOT}/Sources/Oracle/GenericOracleRunner.cpp
     ${ORTHANC_STONE_ROOT}/Sources/Oracle/ThreadedOracle.cpp
-    ${ORTHANC_STONE_ROOT}/Sources/Oracle/GenericOracleRunner.cpp
     )
 endif()
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancStone/UnitTestsSources/CMakeLists.txt	Tue Aug 11 13:47:24 2020 +0200
@@ -0,0 +1,41 @@
+cmake_minimum_required(VERSION 2.8.10)
+
+project(UnitTests)
+
+include(${CMAKE_SOURCE_DIR}/../Resources/CMake/OrthancStoneParameters.cmake)
+
+if (ORTHANC_FRAMEWORK_SOURCE STREQUAL "system")
+  set(ORTHANC_BOOST_COMPONENTS program_options)
+
+  set(USE_SYSTEM_GOOGLE_TEST ON CACHE BOOL "Use the system version of Google Test")
+  set(USE_GOOGLE_TEST_DEBIAN_PACKAGE OFF CACHE BOOL "Use the sources of Google Test shipped with libgtest-dev (Debian only)")
+  mark_as_advanced(USE_GOOGLE_TEST_DEBIAN_PACKAGE)
+  include(${ORTHANC_STONE_ROOT}/Resources/Orthanc/CMake/DownloadPackage.cmake)
+  include(${ORTHANC_STONE_ROOT}/Resources/Orthanc/CMake/GoogleTestConfiguration.cmake)
+
+else()
+  set(ENABLE_GOOGLE_TEST ON)
+endif()
+
+set(ENABLE_DCMTK OFF)
+set(ENABLE_OPENGL OFF)
+
+include(${ORTHANC_STONE_ROOT}/Resources/CMake/OrthancStoneConfiguration.cmake)
+
+add_executable(UnitTests
+  ${AUTOGENERATED_SOURCES}
+  ${BOOST_EXTENDED_SOURCES}
+  ${GOOGLE_TEST_SOURCES}
+  ${ORTHANC_STONE_ROOT}/UnitTestsSources/GenericToolboxTests.cpp
+  ${ORTHANC_STONE_ROOT}/UnitTestsSources/ImageToolboxTests.cpp
+  ${ORTHANC_STONE_ROOT}/UnitTestsSources/PixelTestPatternsTests.cpp
+  ${ORTHANC_STONE_ROOT}/UnitTestsSources/SortedFramesTests.cpp
+  ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestCommands.cpp
+  ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestMessageBroker.cpp
+  ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestStrategy.cpp
+  ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestStructureSet.cpp
+  ${ORTHANC_STONE_ROOT}/UnitTestsSources/UnitTestsMain.cpp
+  ${ORTHANC_STONE_SOURCES}
+  )
+
+target_link_libraries(UnitTests ${DCMTK_LIBRARIES})
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancStone/UnitTestsSources/Graveyard/TestStructureSet_BGO.cpp	Tue Aug 11 13:47:24 2020 +0200
@@ -0,0 +1,383 @@
+/*
+these tests are single-threaded... no worries for old buggy compilers
+(I'm talking to YOU, cl.exe v100! And to your ancestors!)
+*/
+static std::string& GetTestJson()
+{
+  static const char* resultRaw = NULL;
+  static std::string result;
+  if (resultRaw == NULL)
+  {
+    std::stringstream sst;
+
+    sst << k_rtStruct_json00
+      << k_rtStruct_json01
+      << k_rtStruct_json02
+      << k_rtStruct_json03
+      << k_rtStruct_json04
+      << k_rtStruct_json05
+      << k_rtStruct_json06
+      << k_rtStruct_json07
+      << k_rtStruct_json08;
+
+    std::string wholeBody = sst.str();
+    result.swap(wholeBody);
+    resultRaw = result.c_str();
+  }
+  return result;
+}
+
+
+namespace
+{
+  void Initialize(const char* orthancApiUrl, OrthancStone::ILoadersContext& loadersContext)
+  {
+    Orthanc::WebServiceParameters p;
+
+    OrthancStone::GenericLoadersContext& typedLoadersContext =
+      dynamic_cast<OrthancStone::GenericLoadersContext&>(loadersContext);
+    // Default is http://localhost:8042
+    // Here's how you may change it
+    p.SetUrl(orthancApiUrl);
+    p.SetCredentials("orthanc", "orthanc");
+    typedLoadersContext.SetOrthancParameters(p);
+
+    typedLoadersContext.StartOracle();
+  }
+
+  void Exitialize(OrthancStone::ILoadersContext& loadersContext)
+  {
+    OrthancStone::GenericLoadersContext& typedLoadersContext =
+      dynamic_cast<OrthancStone::GenericLoadersContext&>(loadersContext);
+
+    typedLoadersContext.StopOracle();
+  }
+
+
+#if 0
+  class TestObserver : public ObserverBase<TestObserver>
+  {
+  public:
+    TestObserver() {};
+
+    virtual void Handle
+
+  };
+#endif
+
+}
+
+TEST(StructureSet, DISABLED_StructureSetLoader_injection_feature_2020_05_10)
+{
+  namespace pt = boost::posix_time;
+
+  std::unique_ptr<OrthancStone::ILoadersContext> loadersContext(new OrthancStone::GenericLoadersContext(1,4,1));
+  Initialize("http://localhost:8042/", *loadersContext);
+
+  boost::shared_ptr<DicomStructureSetLoader> loader = DicomStructureSetLoader::Create(*loadersContext);
+
+  // replace with Orthanc ID of an uploaded RTSTRUCT instance!
+  loader->LoadInstanceFullVisibility("72c773ac-5059f2c4-2e6a9120-4fd4bca1-45701661");
+
+  bool bContinue(true);
+
+  pt::ptime initialTime = pt::second_clock::local_time();
+
+  while (bContinue)
+  {
+    bContinue = !loader->AreStructuresReady();
+    boost::this_thread::sleep_for(boost::chrono::milliseconds(1000));
+
+    {
+      pt::ptime nowTime = pt::second_clock::local_time();
+      pt::time_duration diff = nowTime - initialTime;
+      double seconds = static_cast<double>(diff.total_milliseconds()) * 0.001;
+      std::cout << seconds << " seconds elapsed...\n";
+      if (seconds > 30)
+      {
+        std::cout << "More than 30 seconds elapsed... Aborting test :(\n";
+        //GTEST_FATAL_FAILURE_("More than 30 seconds elapsed... Aborting test :(");
+        //bContinue = false;
+      }
+    }
+  }
+}
+
+class SliceProcessor :
+  public OrthancStone::OrthancSeriesVolumeProgressiveLoader::ISlicePostProcessor,
+  public OrthancStone::DicomStructureSetLoader::IInstanceLookupHandler
+{
+public:
+  SliceProcessor(OrthancStone::DicomStructureSetLoader& structLoader) : structLoader_(structLoader)
+  {
+  }
+
+  virtual void ProcessCTDicomSlice(const Orthanc::DicomMap& instance) ORTHANC_OVERRIDE
+  {
+    std::string sopInstanceUid;
+    if (!instance.LookupStringValue(sopInstanceUid, Orthanc::DICOM_TAG_SOP_INSTANCE_UID, false))
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, "Missing SOPInstanceUID in a DICOM instance");
+    }
+    slicesDicom_[sopInstanceUid] = boost::shared_ptr<DicomMap>(instance.Clone());
+  }
+
+  virtual void RetrieveReferencedSlices(const std::set<std::string>& nonEmptyInstances) ORTHANC_OVERRIDE
+  {
+    for (std::set<std::string>::const_iterator it = nonEmptyInstances.begin(); 
+         it != nonEmptyInstances.end(); 
+         ++it)
+    {
+      const std::string nonEmptyInstance = *it;
+      if (slicesDicom_.find(nonEmptyInstance) == slicesDicom_.end())
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, "Referenced SOPInstanceUID not found in CT");
+      }
+      boost::shared_ptr<Orthanc::DicomMap> instance = slicesDicom_[nonEmptyInstance];
+      structLoader_.AddReferencedSlice(*instance);
+    }
+  }
+
+  OrthancStone::DicomStructureSetLoader& structLoader_;
+  std::map<std::string, boost::shared_ptr<Orthanc::DicomMap> > slicesDicom_;
+};
+
+void LoadCtSeriesBlocking(boost::shared_ptr<OrthancStone::OrthancSeriesVolumeProgressiveLoader> ctLoader, std::string seriesId)
+{
+  namespace pt = boost::posix_time;
+  
+  // Load the CT 
+  ctLoader->LoadSeries(seriesId);
+
+  // Wait for CT to be loaded
+  pt::ptime initialTime = pt::second_clock::local_time();
+  {
+    bool bContinue(true);
+    while (bContinue)
+    {
+      bContinue = !ctLoader->IsVolumeImageReadyInHighQuality();
+      boost::this_thread::sleep_for(boost::chrono::milliseconds(1000));
+
+      {
+        pt::ptime nowTime = pt::second_clock::local_time();
+        pt::time_duration diff = nowTime - initialTime;
+        double seconds = static_cast<double>(diff.total_milliseconds()) * 0.001;
+        std::cout << seconds << " seconds elapsed...\n";
+        if (seconds > 30)
+        {
+          const char* msg = "More than 30 seconds elapsed when waiting for CT... Aborting test :(\n";
+          GTEST_FATAL_FAILURE_(msg);
+          bContinue = false;
+        }
+      }
+    }
+  }
+}
+
+
+/**
+Will fill planes
+*/
+void GetCTPlanes(std::vector<OrthancStone::CoordinateSystem3D>& planes, 
+                 OrthancStone::VolumeProjection projection,
+                 boost::shared_ptr<OrthancStone::OrthancSeriesVolumeProgressiveLoader> ctLoader)
+{
+    planes.clear(); // inefficient : we don't care
+
+    const VolumeImageGeometry& geometry = ctLoader->GetImageGeometry();
+    const unsigned int depth = geometry.GetProjectionDepth(projection);
+
+    planes.resize(depth);
+
+    for (unsigned int z = 0; z < depth; z++)
+    {
+      planes[z] = geometry.GetProjectionSlice(projection, z);
+    }
+}
+
+void LoadRtStructBlocking(boost::shared_ptr<OrthancStone::DicomStructureSetLoader> structLoader, std::string instanceId)
+{
+  namespace pt = boost::posix_time;
+
+  // Load RTSTRUCT
+  structLoader->LoadInstanceFullVisibility(instanceId);
+
+  pt::ptime initialTime = pt::second_clock::local_time();
+
+  // Wait for the loading process to complete
+  {
+    bool bContinue(true);
+    while (bContinue)
+    {
+      bContinue = !structLoader->AreStructuresReady();
+      boost::this_thread::sleep_for(boost::chrono::milliseconds(1000));
+
+      {
+        pt::ptime nowTime = pt::second_clock::local_time();
+        pt::time_duration diff = nowTime - initialTime;
+        double seconds = static_cast<double>(diff.total_milliseconds()) * 0.001;
+        std::cout << seconds << " seconds elapsed...\n";
+        if (seconds > 30)
+        {
+          const char* msg = "More than 30 seconds elapsed when waiting for RTSTRUCT... Aborting test :(\n";
+          GTEST_FATAL_FAILURE_(msg);
+          bContinue = false;
+        }
+      }
+    }
+  }
+}
+
+TEST(StructureSet, DISABLED_Integration_Compound_CT_Struct_Loading)
+{
+  const double TOLERANCE = 0.0000001;
+
+  // create loaders context
+  std::unique_ptr<OrthancStone::ILoadersContext> loadersContext(new OrthancStone::GenericLoadersContext(1,4,1));
+  Initialize("http://localhost:8042/", *loadersContext);
+
+  const char* ctSeriesId = "a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa";
+  const char* rtStructInstanceId = "54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9";
+
+  // we'll compare normal loading and optimized loading with SliceProcessor to store the dicom
+
+  boost::shared_ptr<OrthancStone::DicomStructureSetLoader> normalStructLoader;
+  boost::shared_ptr<OrthancStone::DicomStructureSetLoader> optimizedStructLoader;
+  
+  {
+    // Create the CT volume
+    boost::shared_ptr<OrthancStone::DicomVolumeImage> volume = boost::make_shared<OrthancStone::DicomVolumeImage>();
+
+    // Create CT loader
+    boost::shared_ptr<OrthancStone::OrthancSeriesVolumeProgressiveLoader> ctLoader =
+      OrthancStone::OrthancSeriesVolumeProgressiveLoader::Create(*loadersContext, volume);
+
+    // Create struct loader
+    normalStructLoader = OrthancStone::DicomStructureSetLoader::Create(*loadersContext);
+
+    // Load the CT
+    LoadCtSeriesBlocking(ctLoader, ctSeriesId);
+    
+    const OrthancStone::VolumeImageGeometry& imageGeometry = ctLoader->GetImageGeometry();
+    unsigned int width = imageGeometry.GetWidth();
+    EXPECT_EQ(512u, width);
+    unsigned int height = imageGeometry.GetHeight();
+    EXPECT_EQ(512u, height);
+    unsigned int depth = imageGeometry.GetDepth();
+    EXPECT_EQ(109u, depth);
+
+    // Load the RTStruct
+    LoadRtStructBlocking(normalStructLoader, rtStructInstanceId);
+  }
+
+  std::vector<OrthancStone::CoordinateSystem3D> axialPlanes;
+  std::vector<OrthancStone::CoordinateSystem3D> coronalPlanes;
+  std::vector<OrthancStone::CoordinateSystem3D> sagittalPlanes;
+
+  {
+    // Create the CT volume
+    boost::shared_ptr<OrthancStone::DicomVolumeImage> volume = boost::make_shared<OrthancStone::DicomVolumeImage>();
+
+    // Create CT loader
+    boost::shared_ptr<OrthancStone::OrthancSeriesVolumeProgressiveLoader> ctLoader =
+      OrthancStone::OrthancSeriesVolumeProgressiveLoader::Create(*loadersContext, volume);
+
+    // Create struct loader
+    optimizedStructLoader = OrthancStone::DicomStructureSetLoader::Create(*loadersContext);
+
+    // create the slice processor / instance lookup
+    boost::shared_ptr<SliceProcessor> sliceProcessor(new SliceProcessor(*optimizedStructLoader));
+
+    // Inject it into CT loader
+    ctLoader->SetDicomSlicePostProcessor(sliceProcessor);
+
+    // Inject it into RTSTRUCT loader
+    optimizedStructLoader->SetInstanceLookupHandler(sliceProcessor);
+
+    // Load the CT
+    LoadCtSeriesBlocking(ctLoader, ctSeriesId);
+  
+    // now, the slices are collected. let's do some checks
+    EXPECT_EQ(109u, sliceProcessor->slicesDicom_.size());
+
+    // Load the RTStruct
+    LoadRtStructBlocking(optimizedStructLoader, rtStructInstanceId);
+
+    GetCTPlanes(axialPlanes, VolumeProjection_Axial, ctLoader);
+    GetCTPlanes(coronalPlanes, VolumeProjection_Coronal, ctLoader);
+    GetCTPlanes(sagittalPlanes, VolumeProjection_Sagittal, ctLoader);
+  }
+
+  // DO NOT DELETE THOSE!
+  OrthancStone::DicomStructureSet* normalContent = normalStructLoader->GetContent();
+  OrthancStone::DicomStructureSet* optimizedContent = optimizedStructLoader->GetContent();
+
+  EXPECT_EQ(normalContent->GetStructuresCount(), optimizedContent->GetStructuresCount());
+
+  /*void GetCTPlanes(std::vector<OrthancStone::CoordinateSystem3D>& planes, 
+                 OrthancStone::VolumeProjection projection,
+                 boost::shared_ptr<OrthancStone::OrthancSeriesVolumeProgressiveLoader> ctLoader)*/
+
+
+  std::vector<OrthancStone::CoordinateSystem3D> allPlanes;
+
+  // let's gather all the possible cutting planes in a single struct
+  for (size_t i = 0; i < axialPlanes.size(); ++i)
+    allPlanes.push_back(axialPlanes[i]);
+
+  for (size_t i = 0; i < coronalPlanes.size(); ++i)
+    allPlanes.push_back(coronalPlanes[i]);
+
+  for (size_t i = 0; i < sagittalPlanes.size(); ++i)
+    allPlanes.push_back(sagittalPlanes[i]);
+
+  for (size_t i = 0; i < normalContent->GetStructuresCount(); ++i)
+  {
+    std::cout << "Testing structure (" << i << "/" << normalContent->GetStructuresCount() << ")\n";
+    Vector structureCenter1                     = normalContent->GetStructureCenter(i);
+    const std::string& structureName1           = normalContent->GetStructureName(i);
+    const std::string& structureInterpretation1 = normalContent->GetStructureInterpretation(i);
+    Color structureColor1                       = normalContent->GetStructureColor(i);
+
+    Vector structureCenter2                     = optimizedContent->GetStructureCenter(i);
+    const std::string& structureName2           = optimizedContent->GetStructureName(i);
+    const std::string& structureInterpretation2 = optimizedContent->GetStructureInterpretation(i);
+    Color structureColor2                       = optimizedContent->GetStructureColor(i);
+
+    EXPECT_NEAR(structureCenter1[0], structureCenter2[0], TOLERANCE);
+    EXPECT_NEAR(structureCenter1[1], structureCenter2[1], TOLERANCE);
+    EXPECT_NEAR(structureCenter1[2], structureCenter2[2], TOLERANCE);
+    
+    EXPECT_EQ(structureName1, structureName2);
+    EXPECT_EQ(structureInterpretation1, structureInterpretation2);
+    EXPECT_EQ(structureColor1.GetRed(), structureColor2.GetRed());
+    EXPECT_EQ(structureColor1.GetGreen(), structureColor2.GetGreen());
+    EXPECT_EQ(structureColor1.GetBlue(), structureColor2.GetBlue());
+
+    // "random" walk through the planes. Processing them all takes too long (~ 1 min)
+    for (size_t j = 0; j < allPlanes.size(); j += 37)
+    {
+      const OrthancStone::CoordinateSystem3D& plane = allPlanes[j];
+
+      std::vector< std::pair<Point2D, Point2D> > segments1;
+      std::vector< std::pair<Point2D, Point2D> > segments2;
+      
+      bool ok1 = normalContent->ProjectStructure(segments1, i, plane);
+      bool ok2 = optimizedContent->ProjectStructure(segments2, i, plane);
+
+      // checks here
+      EXPECT_EQ(ok1, ok2);
+      EXPECT_EQ(segments1.size(), segments2.size());
+
+      for (size_t k = 0; k < segments1.size(); ++k)
+      {
+        EXPECT_NEAR(segments1[k].first.x, segments2[k].first.x, TOLERANCE);
+        EXPECT_NEAR(segments1[k].first.y, segments2[k].first.y, TOLERANCE);
+        EXPECT_NEAR(segments1[k].second.x, segments2[k].second.x, TOLERANCE);
+        EXPECT_NEAR(segments1[k].second.y, segments2[k].second.y, TOLERANCE);
+      }
+    }
+  }
+
+  Exitialize(*loadersContext);
+}
--- a/OrthancStone/UnitTestsSources/TestStructureSet.cpp	Tue Aug 11 13:28:40 2020 +0200
+++ b/OrthancStone/UnitTestsSources/TestStructureSet.cpp	Tue Aug 11 13:47:24 2020 +0200
@@ -4268,34 +4268,6 @@
 #endif
 // _MSC_VER
 
-/*
-these tests are single-threaded... no worries for old buggy compilers
-(I'm talking to YOU, cl.exe v100! And to your ancestors!)
-*/
-static std::string& GetTestJson()
-{
-  static const char* resultRaw = NULL;
-  static std::string result;
-  if (resultRaw == NULL)
-  {
-    std::stringstream sst;
-
-    sst << k_rtStruct_json00
-      << k_rtStruct_json01
-      << k_rtStruct_json02
-      << k_rtStruct_json03
-      << k_rtStruct_json04
-      << k_rtStruct_json05
-      << k_rtStruct_json06
-      << k_rtStruct_json07
-      << k_rtStruct_json08;
-
-    std::string wholeBody = sst.str();
-    result.swap(wholeBody);
-    resultRaw = result.c_str();
-  }
-  return result;
-}
 
 #define STONE_ARRAY_SIZE(arr)     (sizeof(arr) / sizeof((arr)[0]))
 
@@ -5414,357 +5386,3 @@
 
 #endif 
 // BGO_ENABLE_DICOMSTRUCTURESETLOADER2
-
-namespace
-{
-  void Initialize(const char* orthancApiUrl, OrthancStone::ILoadersContext& loadersContext)
-  {
-    Orthanc::WebServiceParameters p;
-
-    OrthancStone::GenericLoadersContext& typedLoadersContext =
-      dynamic_cast<OrthancStone::GenericLoadersContext&>(loadersContext);
-    // Default is http://localhost:8042
-    // Here's how you may change it
-    p.SetUrl(orthancApiUrl);
-    p.SetCredentials("orthanc", "orthanc");
-    typedLoadersContext.SetOrthancParameters(p);
-
-    typedLoadersContext.StartOracle();
-  }
-
-  void Exitialize(OrthancStone::ILoadersContext& loadersContext)
-  {
-    OrthancStone::GenericLoadersContext& typedLoadersContext =
-      dynamic_cast<OrthancStone::GenericLoadersContext&>(loadersContext);
-
-    typedLoadersContext.StopOracle();
-  }
-
-
-#if 0
-  class TestObserver : public ObserverBase<TestObserver>
-  {
-  public:
-    TestObserver() {};
-
-    virtual void Handle
-
-  };
-#endif
-
-}
-
-TEST(StructureSet, DISABLED_StructureSetLoader_injection_feature_2020_05_10)
-{
-  namespace pt = boost::posix_time;
-
-  std::unique_ptr<OrthancStone::ILoadersContext> loadersContext(new OrthancStone::GenericLoadersContext(1,4,1));
-  Initialize("http://localhost:8042/", *loadersContext);
-
-  boost::shared_ptr<DicomStructureSetLoader> loader = DicomStructureSetLoader::Create(*loadersContext);
-
-  // replace with Orthanc ID of an uploaded RTSTRUCT instance!
-  loader->LoadInstanceFullVisibility("72c773ac-5059f2c4-2e6a9120-4fd4bca1-45701661");
-
-  bool bContinue(true);
-
-  pt::ptime initialTime = pt::second_clock::local_time();
-
-  while (bContinue)
-  {
-    bContinue = !loader->AreStructuresReady();
-    boost::this_thread::sleep_for(boost::chrono::milliseconds(1000));
-
-    {
-      pt::ptime nowTime = pt::second_clock::local_time();
-      pt::time_duration diff = nowTime - initialTime;
-      double seconds = static_cast<double>(diff.total_milliseconds()) * 0.001;
-      std::cout << seconds << " seconds elapsed...\n";
-      if (seconds > 30)
-      {
-        std::cout << "More than 30 seconds elapsed... Aborting test :(\n";
-        //GTEST_FATAL_FAILURE_("More than 30 seconds elapsed... Aborting test :(");
-        //bContinue = false;
-      }
-    }
-  }
-}
-
-class SliceProcessor :
-  public OrthancStone::OrthancSeriesVolumeProgressiveLoader::ISlicePostProcessor,
-  public OrthancStone::DicomStructureSetLoader::IInstanceLookupHandler
-{
-public:
-  SliceProcessor(OrthancStone::DicomStructureSetLoader& structLoader) : structLoader_(structLoader)
-  {
-  }
-
-  virtual void ProcessCTDicomSlice(const Orthanc::DicomMap& instance) ORTHANC_OVERRIDE
-  {
-    std::string sopInstanceUid;
-    if (!instance.LookupStringValue(sopInstanceUid, Orthanc::DICOM_TAG_SOP_INSTANCE_UID, false))
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, "Missing SOPInstanceUID in a DICOM instance");
-    }
-    slicesDicom_[sopInstanceUid] = boost::shared_ptr<DicomMap>(instance.Clone());
-  }
-
-  virtual void RetrieveReferencedSlices(const std::set<std::string>& nonEmptyInstances) ORTHANC_OVERRIDE
-  {
-    for (std::set<std::string>::const_iterator it = nonEmptyInstances.begin(); 
-         it != nonEmptyInstances.end(); 
-         ++it)
-    {
-      const std::string nonEmptyInstance = *it;
-      if (slicesDicom_.find(nonEmptyInstance) == slicesDicom_.end())
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, "Referenced SOPInstanceUID not found in CT");
-      }
-      boost::shared_ptr<Orthanc::DicomMap> instance = slicesDicom_[nonEmptyInstance];
-      structLoader_.AddReferencedSlice(*instance);
-    }
-  }
-
-  OrthancStone::DicomStructureSetLoader& structLoader_;
-  std::map<std::string, boost::shared_ptr<Orthanc::DicomMap> > slicesDicom_;
-};
-
-void LoadCtSeriesBlocking(boost::shared_ptr<OrthancStone::OrthancSeriesVolumeProgressiveLoader> ctLoader, std::string seriesId)
-{
-  namespace pt = boost::posix_time;
-  
-  // Load the CT 
-  ctLoader->LoadSeries(seriesId);
-
-  // Wait for CT to be loaded
-  pt::ptime initialTime = pt::second_clock::local_time();
-  {
-    bool bContinue(true);
-    while (bContinue)
-    {
-      bContinue = !ctLoader->IsVolumeImageReadyInHighQuality();
-      boost::this_thread::sleep_for(boost::chrono::milliseconds(1000));
-
-      {
-        pt::ptime nowTime = pt::second_clock::local_time();
-        pt::time_duration diff = nowTime - initialTime;
-        double seconds = static_cast<double>(diff.total_milliseconds()) * 0.001;
-        std::cout << seconds << " seconds elapsed...\n";
-        if (seconds > 30)
-        {
-          const char* msg = "More than 30 seconds elapsed when waiting for CT... Aborting test :(\n";
-          GTEST_FATAL_FAILURE_(msg);
-          bContinue = false;
-        }
-      }
-    }
-  }
-}
-
-
-/**
-Will fill planes
-*/
-void GetCTPlanes(std::vector<OrthancStone::CoordinateSystem3D>& planes, 
-                 OrthancStone::VolumeProjection projection,
-                 boost::shared_ptr<OrthancStone::OrthancSeriesVolumeProgressiveLoader> ctLoader)
-{
-    planes.clear(); // inefficient : we don't care
-
-    const VolumeImageGeometry& geometry = ctLoader->GetImageGeometry();
-    const unsigned int depth = geometry.GetProjectionDepth(projection);
-
-    planes.resize(depth);
-
-    for (unsigned int z = 0; z < depth; z++)
-    {
-      planes[z] = geometry.GetProjectionSlice(projection, z);
-    }
-}
-
-void LoadRtStructBlocking(boost::shared_ptr<OrthancStone::DicomStructureSetLoader> structLoader, std::string instanceId)
-{
-  namespace pt = boost::posix_time;
-
-  // Load RTSTRUCT
-  structLoader->LoadInstanceFullVisibility(instanceId);
-
-  pt::ptime initialTime = pt::second_clock::local_time();
-
-  // Wait for the loading process to complete
-  {
-    bool bContinue(true);
-    while (bContinue)
-    {
-      bContinue = !structLoader->AreStructuresReady();
-      boost::this_thread::sleep_for(boost::chrono::milliseconds(1000));
-
-      {
-        pt::ptime nowTime = pt::second_clock::local_time();
-        pt::time_duration diff = nowTime - initialTime;
-        double seconds = static_cast<double>(diff.total_milliseconds()) * 0.001;
-        std::cout << seconds << " seconds elapsed...\n";
-        if (seconds > 30)
-        {
-          const char* msg = "More than 30 seconds elapsed when waiting for RTSTRUCT... Aborting test :(\n";
-          GTEST_FATAL_FAILURE_(msg);
-          bContinue = false;
-        }
-      }
-    }
-  }
-}
-
-TEST(StructureSet, DISABLED_Integration_Compound_CT_Struct_Loading)
-{
-  const double TOLERANCE = 0.0000001;
-
-  // create loaders context
-  std::unique_ptr<OrthancStone::ILoadersContext> loadersContext(new OrthancStone::GenericLoadersContext(1,4,1));
-  Initialize("http://localhost:8042/", *loadersContext);
-
-  const char* ctSeriesId = "a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa";
-  const char* rtStructInstanceId = "54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9";
-
-  // we'll compare normal loading and optimized loading with SliceProcessor to store the dicom
-
-  boost::shared_ptr<OrthancStone::DicomStructureSetLoader> normalStructLoader;
-  boost::shared_ptr<OrthancStone::DicomStructureSetLoader> optimizedStructLoader;
-  
-  {
-    // Create the CT volume
-    boost::shared_ptr<OrthancStone::DicomVolumeImage> volume = boost::make_shared<OrthancStone::DicomVolumeImage>();
-
-    // Create CT loader
-    boost::shared_ptr<OrthancStone::OrthancSeriesVolumeProgressiveLoader> ctLoader =
-      OrthancStone::OrthancSeriesVolumeProgressiveLoader::Create(*loadersContext, volume);
-
-    // Create struct loader
-    normalStructLoader = OrthancStone::DicomStructureSetLoader::Create(*loadersContext);
-
-    // Load the CT
-    LoadCtSeriesBlocking(ctLoader, ctSeriesId);
-    
-    const OrthancStone::VolumeImageGeometry& imageGeometry = ctLoader->GetImageGeometry();
-    unsigned int width = imageGeometry.GetWidth();
-    EXPECT_EQ(512u, width);
-    unsigned int height = imageGeometry.GetHeight();
-    EXPECT_EQ(512u, height);
-    unsigned int depth = imageGeometry.GetDepth();
-    EXPECT_EQ(109u, depth);
-
-    // Load the RTStruct
-    LoadRtStructBlocking(normalStructLoader, rtStructInstanceId);
-  }
-
-  std::vector<OrthancStone::CoordinateSystem3D> axialPlanes;
-  std::vector<OrthancStone::CoordinateSystem3D> coronalPlanes;
-  std::vector<OrthancStone::CoordinateSystem3D> sagittalPlanes;
-
-  {
-    // Create the CT volume
-    boost::shared_ptr<OrthancStone::DicomVolumeImage> volume = boost::make_shared<OrthancStone::DicomVolumeImage>();
-
-    // Create CT loader
-    boost::shared_ptr<OrthancStone::OrthancSeriesVolumeProgressiveLoader> ctLoader =
-      OrthancStone::OrthancSeriesVolumeProgressiveLoader::Create(*loadersContext, volume);
-
-    // Create struct loader
-    optimizedStructLoader = OrthancStone::DicomStructureSetLoader::Create(*loadersContext);
-
-    // create the slice processor / instance lookup
-    boost::shared_ptr<SliceProcessor> sliceProcessor(new SliceProcessor(*optimizedStructLoader));
-
-    // Inject it into CT loader
-    ctLoader->SetDicomSlicePostProcessor(sliceProcessor);
-
-    // Inject it into RTSTRUCT loader
-    optimizedStructLoader->SetInstanceLookupHandler(sliceProcessor);
-
-    // Load the CT
-    LoadCtSeriesBlocking(ctLoader, ctSeriesId);
-  
-    // now, the slices are collected. let's do some checks
-    EXPECT_EQ(109u, sliceProcessor->slicesDicom_.size());
-
-    // Load the RTStruct
-    LoadRtStructBlocking(optimizedStructLoader, rtStructInstanceId);
-
-    GetCTPlanes(axialPlanes, VolumeProjection_Axial, ctLoader);
-    GetCTPlanes(coronalPlanes, VolumeProjection_Coronal, ctLoader);
-    GetCTPlanes(sagittalPlanes, VolumeProjection_Sagittal, ctLoader);
-  }
-
-  // DO NOT DELETE THOSE!
-  OrthancStone::DicomStructureSet* normalContent = normalStructLoader->GetContent();
-  OrthancStone::DicomStructureSet* optimizedContent = optimizedStructLoader->GetContent();
-
-  EXPECT_EQ(normalContent->GetStructuresCount(), optimizedContent->GetStructuresCount());
-
-  /*void GetCTPlanes(std::vector<OrthancStone::CoordinateSystem3D>& planes, 
-                 OrthancStone::VolumeProjection projection,
-                 boost::shared_ptr<OrthancStone::OrthancSeriesVolumeProgressiveLoader> ctLoader)*/
-
-
-  std::vector<OrthancStone::CoordinateSystem3D> allPlanes;
-
-  // let's gather all the possible cutting planes in a single struct
-  for (size_t i = 0; i < axialPlanes.size(); ++i)
-    allPlanes.push_back(axialPlanes[i]);
-
-  for (size_t i = 0; i < coronalPlanes.size(); ++i)
-    allPlanes.push_back(coronalPlanes[i]);
-
-  for (size_t i = 0; i < sagittalPlanes.size(); ++i)
-    allPlanes.push_back(sagittalPlanes[i]);
-
-  for (size_t i = 0; i < normalContent->GetStructuresCount(); ++i)
-  {
-    std::cout << "Testing structure (" << i << "/" << normalContent->GetStructuresCount() << ")\n";
-    Vector structureCenter1                     = normalContent->GetStructureCenter(i);
-    const std::string& structureName1           = normalContent->GetStructureName(i);
-    const std::string& structureInterpretation1 = normalContent->GetStructureInterpretation(i);
-    Color structureColor1                       = normalContent->GetStructureColor(i);
-
-    Vector structureCenter2                     = optimizedContent->GetStructureCenter(i);
-    const std::string& structureName2           = optimizedContent->GetStructureName(i);
-    const std::string& structureInterpretation2 = optimizedContent->GetStructureInterpretation(i);
-    Color structureColor2                       = optimizedContent->GetStructureColor(i);
-
-    EXPECT_NEAR(structureCenter1[0], structureCenter2[0], TOLERANCE);
-    EXPECT_NEAR(structureCenter1[1], structureCenter2[1], TOLERANCE);
-    EXPECT_NEAR(structureCenter1[2], structureCenter2[2], TOLERANCE);
-    
-    EXPECT_EQ(structureName1, structureName2);
-    EXPECT_EQ(structureInterpretation1, structureInterpretation2);
-    EXPECT_EQ(structureColor1.GetRed(), structureColor2.GetRed());
-    EXPECT_EQ(structureColor1.GetGreen(), structureColor2.GetGreen());
-    EXPECT_EQ(structureColor1.GetBlue(), structureColor2.GetBlue());
-
-    // "random" walk through the planes. Processing them all takes too long (~ 1 min)
-    for (size_t j = 0; j < allPlanes.size(); j += 37)
-    {
-      const OrthancStone::CoordinateSystem3D& plane = allPlanes[j];
-
-      std::vector< std::pair<Point2D, Point2D> > segments1;
-      std::vector< std::pair<Point2D, Point2D> > segments2;
-      
-      bool ok1 = normalContent->ProjectStructure(segments1, i, plane);
-      bool ok2 = optimizedContent->ProjectStructure(segments2, i, plane);
-
-      // checks here
-      EXPECT_EQ(ok1, ok2);
-      EXPECT_EQ(segments1.size(), segments2.size());
-
-      for (size_t k = 0; k < segments1.size(); ++k)
-      {
-        EXPECT_NEAR(segments1[k].first.x, segments2[k].first.x, TOLERANCE);
-        EXPECT_NEAR(segments1[k].first.y, segments2[k].first.y, TOLERANCE);
-        EXPECT_NEAR(segments1[k].second.x, segments2[k].second.x, TOLERANCE);
-        EXPECT_NEAR(segments1[k].second.y, segments2[k].second.y, TOLERANCE);
-      }
-    }
-  }
-
-  Exitialize(*loadersContext);
-}
--- a/OrthancStone/UnitTestsSources/UnitTestsMain.cpp	Tue Aug 11 13:28:40 2020 +0200
+++ b/OrthancStone/UnitTestsSources/UnitTestsMain.cpp	Tue Aug 11 13:47:24 2020 +0200
@@ -27,7 +27,6 @@
 #include "../Sources/Volumes/ImageBuffer3D.h"
 #include "../Sources/Loaders/LoaderCache.h"
 
-#include <HttpClient.h>
 #include <Images/ImageProcessing.h>
 #include <Logging.h>
 #include <MultiThreading/SharedMessageQueue.h>