diff UnitTests/UnitTestsMain.cpp @ 0:95226b754d9e

initial release
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 17 Sep 2018 11:34:55 +0200
parents
children 7e207ade2f1a
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/UnitTests/UnitTestsMain.cpp	Mon Sep 17 11:34:55 2018 +0200
@@ -0,0 +1,445 @@
+/**
+ * Transfers accelerator plugin for Orthanc
+ * Copyright (C) 2018 Osimis, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero 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
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "../Framework/DownloadArea.h"
+
+#include <Core/Compression/GzipCompressor.h>
+#include <Core/Logging.h>
+#include <Core/OrthancException.h>
+#include <gtest/gtest.h>
+
+
+TEST(Toolbox, Enumerations)
+{
+  using namespace OrthancPlugins;
+  ASSERT_EQ(BucketCompression_None, StringToBucketCompression(EnumerationToString(BucketCompression_None)));
+  ASSERT_EQ(BucketCompression_Gzip, StringToBucketCompression(EnumerationToString(BucketCompression_Gzip)));
+  ASSERT_THROW(StringToBucketCompression("None"), Orthanc::OrthancException);
+}
+
+
+TEST(Toolbox, Conversions)
+{
+  ASSERT_EQ(2u, OrthancPlugins::ConvertToKilobytes(2048));
+  ASSERT_EQ(1u, OrthancPlugins::ConvertToKilobytes(1000));
+  ASSERT_EQ(0u, OrthancPlugins::ConvertToKilobytes(500));
+
+  ASSERT_EQ(2u, OrthancPlugins::ConvertToMegabytes(2048 * 1024));
+  ASSERT_EQ(1u, OrthancPlugins::ConvertToMegabytes(1000 * 1024));
+  ASSERT_EQ(0u, OrthancPlugins::ConvertToMegabytes(500 * 1024));
+}
+
+
+TEST(TransferBucket, Basic)
+{  
+  using namespace OrthancPlugins;
+
+  DicomInstanceInfo d1("d1", 10, "");
+  DicomInstanceInfo d2("d2", 20, "");
+  DicomInstanceInfo d3("d3", 30, "");
+  DicomInstanceInfo d4("d4", 40, "");
+
+  {
+    TransferBucket b;
+    ASSERT_EQ(0u, b.GetTotalSize());
+    ASSERT_EQ(0u, b.GetChunksCount());
+
+    b.AddChunk(d1, 0, 10);
+    b.AddChunk(d2, 0, 20);
+    ASSERT_THROW(b.AddChunk(d3, 0, 31), Orthanc::OrthancException);
+    ASSERT_THROW(b.AddChunk(d3, 1, 30), Orthanc::OrthancException);
+    b.AddChunk(d3, 0, 30);
+    
+    ASSERT_EQ(60u, b.GetTotalSize());
+    ASSERT_EQ(3u, b.GetChunksCount());
+
+    ASSERT_EQ("d1", b.GetChunkInstanceId(0));
+    ASSERT_EQ(0u, b.GetChunkOffset(0));
+    ASSERT_EQ(10u, b.GetChunkSize(0));
+    ASSERT_EQ("d2", b.GetChunkInstanceId(1));
+    ASSERT_EQ(0u, b.GetChunkOffset(1));
+    ASSERT_EQ(20u, b.GetChunkSize(1));
+    ASSERT_EQ("d3", b.GetChunkInstanceId(2));
+    ASSERT_EQ(0u, b.GetChunkOffset(2));
+    ASSERT_EQ(30u, b.GetChunkSize(2));
+
+    std::string uri;
+    b.ComputePullUri(uri, BucketCompression_None);
+    ASSERT_EQ("/transfers/chunks/d1.d2.d3?offset=0&size=60&compression=none", uri);
+    b.ComputePullUri(uri, BucketCompression_Gzip);
+    ASSERT_EQ("/transfers/chunks/d1.d2.d3?offset=0&size=60&compression=gzip", uri);
+      
+    b.Clear();
+    ASSERT_EQ(0u, b.GetTotalSize());
+    ASSERT_EQ(0u, b.GetChunksCount());
+
+    ASSERT_THROW(b.ComputePullUri(uri, BucketCompression_None), Orthanc::OrthancException);  // Empty
+  }
+
+  {
+    TransferBucket b;
+    b.AddChunk(d1, 5, 5);
+    ASSERT_THROW(b.AddChunk(d2, 1, 7), Orthanc::OrthancException);  // Can only skip bytes in 1st chunk
+    b.AddChunk(d2, 0, 20);
+    b.AddChunk(d3, 0, 7);
+    ASSERT_THROW(b.AddChunk(d4, 0, 10), Orthanc::OrthancException); // d2 was not complete
+
+    ASSERT_EQ(32u, b.GetTotalSize());
+    ASSERT_EQ(3u, b.GetChunksCount());
+
+    ASSERT_EQ("d1", b.GetChunkInstanceId(0));
+    ASSERT_EQ(5u, b.GetChunkOffset(0));
+    ASSERT_EQ(5u, b.GetChunkSize(0));
+    ASSERT_EQ("d2", b.GetChunkInstanceId(1));
+    ASSERT_EQ(0u, b.GetChunkOffset(1));
+    ASSERT_EQ(20u, b.GetChunkSize(1));
+    ASSERT_EQ("d3", b.GetChunkInstanceId(2));
+    ASSERT_EQ(0u, b.GetChunkOffset(2));
+    ASSERT_EQ(7u, b.GetChunkSize(2));
+    
+    std::string uri;
+    b.ComputePullUri(uri, BucketCompression_None);
+    ASSERT_EQ("/transfers/chunks/d1.d2.d3?offset=5&size=32&compression=none", uri);
+    b.ComputePullUri(uri, BucketCompression_Gzip);
+    ASSERT_EQ("/transfers/chunks/d1.d2.d3?offset=5&size=32&compression=gzip", uri);
+
+    b.Clear();
+    ASSERT_EQ(0u, b.GetTotalSize());
+    ASSERT_EQ(0u, b.GetChunksCount());
+
+    b.AddChunk(d2, 1, 7);
+    ASSERT_EQ(7u, b.GetTotalSize());
+    ASSERT_EQ(1u, b.GetChunksCount());
+  }
+}
+
+
+TEST(TransferBucket, Serialization)
+{  
+  using namespace OrthancPlugins;
+
+  Json::Value s;
+  
+  {
+    DicomInstanceInfo d1("d1", 10, "");
+    DicomInstanceInfo d2("d2", 20, "");
+    DicomInstanceInfo d3("d3", 30, "");
+
+    TransferBucket b;
+    b.AddChunk(d1, 5, 5);
+    b.AddChunk(d2, 0, 20);
+    b.AddChunk(d3, 0, 7);
+    b.Serialize(s);
+  }
+
+  {
+    TransferBucket b(s);
+
+    std::string uri;
+    b.ComputePullUri(uri, BucketCompression_None);
+    ASSERT_EQ("/transfers/chunks/d1.d2.d3?offset=5&size=32&compression=none", uri);
+  }
+}
+
+
+TEST(TransferScheduler, Empty)
+{  
+  using namespace OrthancPlugins;
+
+  TransferScheduler s;
+  ASSERT_EQ(0u, s.GetInstancesCount());
+  ASSERT_EQ(0u, s.GetTotalSize());
+
+  std::vector<DicomInstanceInfo> i;
+  s.ListInstances(i);
+  ASSERT_TRUE(i.empty());
+
+  std::vector<TransferBucket> b;
+  s.ComputePullBuckets(b, 10, 1000, "http://localhost/", BucketCompression_None);
+  ASSERT_TRUE(b.empty());
+
+  Json::Value v;
+  s.FormatPushTransaction(v, b, 10, 1000, BucketCompression_None);
+  ASSERT_TRUE(b.empty());
+  ASSERT_EQ(Json::objectValue, v.type());
+  ASSERT_TRUE(v.isMember("Buckets"));
+  ASSERT_TRUE(v.isMember("Compression"));
+  ASSERT_TRUE(v.isMember("Instances"));
+  ASSERT_EQ(Json::arrayValue, v["Buckets"].type());
+  ASSERT_EQ(Json::stringValue, v["Compression"].type());
+  ASSERT_EQ(Json::arrayValue, v["Instances"].type());
+  ASSERT_EQ(0u, v["Buckets"].size());
+  ASSERT_EQ("none", v["Compression"].asString());
+  ASSERT_EQ(0u, v["Instances"].size());
+}
+
+
+TEST(TransferScheduler, Basic)
+{  
+  using namespace OrthancPlugins;
+
+  DicomInstanceInfo d1("d1", 10, "md1");
+  DicomInstanceInfo d2("d2", 10, "md2");
+  DicomInstanceInfo d3("d3", 10, "md3");
+
+  TransferScheduler s;
+  s.AddInstance(d1);
+  s.AddInstance(d2);
+  s.AddInstance(d3);
+
+  std::vector<DicomInstanceInfo> i;
+  s.ListInstances(i);
+  ASSERT_EQ(3u, i.size());
+
+  std::vector<TransferBucket> b;
+  s.ComputePullBuckets(b, 10, 1000, "http://localhost/", BucketCompression_None);
+  ASSERT_EQ(3u, b.size());
+  ASSERT_EQ(1u, b[0].GetChunksCount());
+  ASSERT_EQ("d1", b[0].GetChunkInstanceId(0));
+  ASSERT_EQ(0u, b[0].GetChunkOffset(0));
+  ASSERT_EQ(10u, b[0].GetChunkSize(0));
+  ASSERT_EQ(1u, b[1].GetChunksCount());
+  ASSERT_EQ("d2", b[1].GetChunkInstanceId(0));
+  ASSERT_EQ(0u, b[1].GetChunkOffset(0));
+  ASSERT_EQ(10u, b[1].GetChunkSize(0));
+  ASSERT_EQ(1u, b[2].GetChunksCount());
+  ASSERT_EQ("d3", b[2].GetChunkInstanceId(0));
+  ASSERT_EQ(0u, b[2].GetChunkOffset(0));
+  ASSERT_EQ(10u, b[2].GetChunkSize(0));
+
+  Json::Value v;
+  s.FormatPushTransaction(v, b, 10, 1000, BucketCompression_Gzip);
+  ASSERT_EQ(3u, b.size());
+  ASSERT_EQ(3u, v["Buckets"].size());
+  ASSERT_EQ("gzip", v["Compression"].asString());
+  ASSERT_EQ(3u, v["Instances"].size());
+
+  for (Json::Value::ArrayIndex i = 0; i < 3; i++)
+  {
+    TransferBucket b(v["Buckets"][i]);
+    ASSERT_EQ(1u, b.GetChunksCount());
+    if (i == 0)
+      ASSERT_EQ("d1", b.GetChunkInstanceId(0));
+    else if (i == 1)
+      ASSERT_EQ("d2", b.GetChunkInstanceId(0));
+    else
+      ASSERT_EQ("d3", b.GetChunkInstanceId(0));
+        
+    ASSERT_EQ(0u, b.GetChunkOffset(0));
+    ASSERT_EQ(10u, b.GetChunkSize(0));
+  }
+    
+  for (Json::Value::ArrayIndex i = 0; i < 3; i++)
+  {
+    DicomInstanceInfo d(v["Instances"][i]);
+    if (i == 0)
+    {
+      ASSERT_EQ("d1", d.GetId());
+      ASSERT_EQ("md1", d.GetMD5());
+    }
+    else if (i == 1)
+    {
+      ASSERT_EQ("d2", d.GetId());
+      ASSERT_EQ("md2", d.GetMD5());
+    }
+    else
+    {
+      ASSERT_EQ("d3", d.GetId());
+      ASSERT_EQ("md3", d.GetMD5());
+    }
+        
+    ASSERT_EQ(10u, d.GetSize());
+  }
+}
+
+
+
+TEST(TransferScheduler, Grouping)
+{  
+  using namespace OrthancPlugins;
+
+  DicomInstanceInfo d1("d1", 10, "md1");
+  DicomInstanceInfo d2("d2", 10, "md2");
+  DicomInstanceInfo d3("d3", 10, "md3");
+
+  TransferScheduler s;
+  s.AddInstance(d1);
+  s.AddInstance(d2);
+  s.AddInstance(d3);
+
+  {
+    std::vector<TransferBucket> b;
+    s.ComputePullBuckets(b, 20, 1000, "http://localhost/", BucketCompression_None);
+    ASSERT_EQ(2u, b.size());
+    ASSERT_EQ(2u, b[0].GetChunksCount());
+    ASSERT_EQ("d1", b[0].GetChunkInstanceId(0));
+    ASSERT_EQ("d2", b[0].GetChunkInstanceId(1));
+    ASSERT_EQ(1u, b[1].GetChunksCount());    
+    ASSERT_EQ("d3", b[1].GetChunkInstanceId(0));
+  }
+
+  {
+    std::vector<TransferBucket> b;
+    s.ComputePullBuckets(b, 21, 1000, "http://localhost/", BucketCompression_None);
+    ASSERT_EQ(1u, b.size());
+    ASSERT_EQ(3u, b[0].GetChunksCount());
+    ASSERT_EQ("d1", b[0].GetChunkInstanceId(0));
+    ASSERT_EQ("d2", b[0].GetChunkInstanceId(1));
+    ASSERT_EQ("d3", b[0].GetChunkInstanceId(2));
+  }
+
+  {
+    std::string longBase(2048, '_');
+    std::vector<TransferBucket> b;
+    s.ComputePullBuckets(b, 21, 1000, longBase, BucketCompression_None);
+    ASSERT_EQ(3u, b.size());
+    ASSERT_EQ(1u, b[0].GetChunksCount());
+    ASSERT_EQ("d1", b[0].GetChunkInstanceId(0));
+    ASSERT_EQ(1u, b[1].GetChunksCount());
+    ASSERT_EQ("d2", b[1].GetChunkInstanceId(0));
+    ASSERT_EQ(1u, b[2].GetChunksCount());
+    ASSERT_EQ("d3", b[2].GetChunkInstanceId(0));
+  }
+}
+
+
+TEST(TransferScheduler, Splitting)
+{  
+  using namespace OrthancPlugins;
+
+  for (size_t i = 1; i < 20; i++)
+  {
+    DicomInstanceInfo dicom("dicom", i, "");
+
+    TransferScheduler s;
+    s.AddInstance(dicom);
+
+    {
+      std::vector<TransferBucket> b;
+      s.ComputePullBuckets(b, 1, 1000, "http://localhost/", BucketCompression_None);
+      ASSERT_EQ(1u, b.size());
+      ASSERT_EQ(1u, b[0].GetChunksCount());
+      ASSERT_EQ("dicom", b[0].GetChunkInstanceId(0));
+      ASSERT_EQ(0u, b[0].GetChunkOffset(0));
+      ASSERT_EQ(i, b[0].GetChunkSize(0));
+    }
+
+    for (size_t split = 1; split < 20; split++)
+    {
+      size_t count;
+      if (dicom.GetSize() % split != 0)
+        count = dicom.GetSize() / split + 1;
+      else
+        count = dicom.GetSize() / split;
+    
+      std::vector<TransferBucket> b;
+      s.ComputePullBuckets(b, 1, split, "http://localhost/", BucketCompression_None);
+      ASSERT_EQ(count, b.size());
+
+      size_t size = dicom.GetSize() / count;
+      size_t offset = 0;
+      for (size_t j = 0; j < count; j++)
+      {
+        ASSERT_EQ(1u, b[j].GetChunksCount());
+        ASSERT_EQ("dicom", b[j].GetChunkInstanceId(0));
+        ASSERT_EQ(offset, b[j].GetChunkOffset(0));
+        if (j + 1 != count)
+          ASSERT_EQ(size, b[j].GetChunkSize(0));
+        else
+          ASSERT_EQ(dicom.GetSize() - (count - 1) * size, b[j].GetChunkSize(0));
+        offset += b[j].GetChunkSize(0);
+      }
+    }
+  }
+}
+
+
+TEST(DownloadArea, Basic)
+{
+  using namespace OrthancPlugins;
+  
+  std::string s1 = "Hello";
+  std::string s2 = "Hello, World!";
+
+  std::string md1, md2;
+  Orthanc::Toolbox::ComputeMD5(md1, s1);
+  Orthanc::Toolbox::ComputeMD5(md2, s2);
+  
+  std::vector<DicomInstanceInfo> instances;
+  instances.push_back(DicomInstanceInfo("d1", s1.size(), md1));
+  instances.push_back(DicomInstanceInfo("d2", s2.size(), md2));
+
+  {
+    DownloadArea area(instances);
+    ASSERT_EQ(s1.size() + s2.size(), area.GetTotalSize());
+    ASSERT_THROW(area.CheckMD5(), Orthanc::OrthancException);
+
+    area.WriteInstance("d1", s1.c_str(), s1.size());
+    area.WriteInstance("d2", s2.c_str(), s2.size());
+  
+    area.CheckMD5();
+  }
+
+  {
+    DownloadArea area(instances);
+    ASSERT_THROW(area.CheckMD5(), Orthanc::OrthancException);
+
+    {
+      TransferBucket b;
+      b.AddChunk(instances[0] /*d1*/, 0, 2);
+      area.WriteBucket(b, s1.c_str(), 2, BucketCompression_None);
+    }
+
+    {
+      TransferBucket b;
+      b.AddChunk(instances[0] /*d1*/, 2, 3);
+      b.AddChunk(instances[1] /*d2*/, 0, 4);
+      std::string s = s1.substr(2, 3) + s2.substr(0, 4);
+      area.WriteBucket(b, s.c_str(), s.size(), BucketCompression_None);
+    }
+
+    {
+      TransferBucket b;
+      b.AddChunk(instances[1] /*d2*/, 4, 9);
+      std::string s = s2.substr(4);
+      std::string t;
+      Orthanc::GzipCompressor compressor;
+      compressor.Compress(t, s.c_str(), s.size());
+      area.WriteBucket(b, t.c_str(), t.size(), BucketCompression_Gzip);
+    }
+
+    area.CheckMD5();
+  }
+}
+
+
+
+int main(int argc, char **argv)
+{
+  ::testing::InitGoogleTest(&argc, argv);
+  Orthanc::Logging::Initialize();
+  Orthanc::Logging::EnableInfoLevel(true);
+  Orthanc::Logging::EnableTraceLevel(true);
+
+  int result = RUN_ALL_TESTS();
+
+  Orthanc::Logging::Finalize();
+
+  return result;
+}