# HG changeset patch # User Sebastien Jodogne # Date 1382974011 -3600 # Node ID 17815b9d4280670b0292328ef83c0cf3f1d6ccdd # Parent fc6ad5b97219eedb7bd51bea2a447b53e73ae4d4 rename the UnitTests directory to avoid clashes in filenames diff -r fc6ad5b97219 -r 17815b9d4280 CMakeLists.txt --- a/CMakeLists.txt Fri Oct 25 14:52:26 2013 +0200 +++ b/CMakeLists.txt Mon Oct 28 16:26:51 2013 +0100 @@ -242,17 +242,17 @@ include(${CMAKE_SOURCE_DIR}/Resources/CMake/GoogleTestConfiguration.cmake) add_executable(UnitTests ${GTEST_SOURCES} - UnitTests/FileStorage.cpp - UnitTests/MemoryCache.cpp - UnitTests/Png.cpp - UnitTests/RestApi.cpp - UnitTests/SQLite.cpp - UnitTests/SQLiteChromium.cpp - UnitTests/ServerIndex.cpp - UnitTests/Versions.cpp - UnitTests/Zip.cpp - UnitTests/Lua.cpp - UnitTests/main.cpp + UnitTestsSources/FileStorage.cpp + UnitTestsSources/MemoryCache.cpp + UnitTestsSources/Png.cpp + UnitTestsSources/RestApi.cpp + UnitTestsSources/SQLite.cpp + UnitTestsSources/SQLiteChromium.cpp + UnitTestsSources/ServerIndex.cpp + UnitTestsSources/Versions.cpp + UnitTestsSources/Zip.cpp + UnitTestsSources/Lua.cpp + UnitTestsSources/main.cpp ) target_link_libraries(UnitTests ServerLibrary CoreLibrary) diff -r fc6ad5b97219 -r 17815b9d4280 UnitTests/FileStorage.cpp --- a/UnitTests/FileStorage.cpp Fri Oct 25 14:52:26 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,149 +0,0 @@ -#include "gtest/gtest.h" - -#include -#include - -#include "../Core/FileStorage/FileStorage.h" -#include "../OrthancServer/ServerIndex.h" -#include "../Core/Toolbox.h" -#include "../Core/OrthancException.h" -#include "../Core/Uuid.h" -#include "../Core/HttpServer/FilesystemHttpSender.h" -#include "../Core/HttpServer/BufferHttpSender.h" -#include "../Core/FileStorage/FileStorageAccessor.h" -#include "../Core/FileStorage/CompressedFileStorageAccessor.h" - -using namespace Orthanc; - -TEST(FileStorage, Basic) -{ - FileStorage s("FileStorageUnitTests"); - - std::string data = Toolbox::GenerateUuid(); - std::string uid = s.Create(data); - std::string d; - s.ReadFile(d, uid); - ASSERT_EQ(d.size(), data.size()); - ASSERT_FALSE(memcmp(&d[0], &data[0], data.size())); -} - -TEST(FileStorage, EndToEnd) -{ - FileStorage s("FileStorageUnitTests"); - s.Clear(); - - std::list u; - for (unsigned int i = 0; i < 10; i++) - { - u.push_back(s.Create(Toolbox::GenerateUuid())); - } - - std::set ss; - s.ListAllFiles(ss); - ASSERT_EQ(10u, ss.size()); - - unsigned int c = 0; - for (std::list::iterator - i = u.begin(); i != u.end(); i++, c++) - { - ASSERT_TRUE(ss.find(*i) != ss.end()); - if (c < 5) - s.Remove(*i); - } - - s.ListAllFiles(ss); - ASSERT_EQ(5u, ss.size()); - - s.Clear(); - s.ListAllFiles(ss); - ASSERT_EQ(0u, ss.size()); -} - - -TEST(FileStorageAccessor, Simple) -{ - FileStorage s("FileStorageUnitTests"); - FileStorageAccessor accessor(s); - - std::string data = "Hello world"; - FileInfo info = accessor.Write(data, FileContentType_Dicom); - - std::string r; - accessor.Read(r, info.GetUuid()); - - ASSERT_EQ(data, r); - ASSERT_EQ(CompressionType_None, info.GetCompressionType()); - ASSERT_EQ(11u, info.GetUncompressedSize()); - ASSERT_EQ(11u, info.GetCompressedSize()); - ASSERT_EQ(FileContentType_Dicom, info.GetContentType()); -} - - -TEST(FileStorageAccessor, NoCompression) -{ - FileStorage s("FileStorageUnitTests"); - CompressedFileStorageAccessor accessor(s); - - accessor.SetCompressionForNextOperations(CompressionType_None); - std::string data = "Hello world"; - FileInfo info = accessor.Write(data, FileContentType_Dicom); - - std::string r; - accessor.Read(r, info.GetUuid()); - - ASSERT_EQ(data, r); - ASSERT_EQ(CompressionType_None, info.GetCompressionType()); - ASSERT_EQ(11u, info.GetUncompressedSize()); - ASSERT_EQ(11u, info.GetCompressedSize()); - ASSERT_EQ(FileContentType_Dicom, info.GetContentType()); -} - - -TEST(FileStorageAccessor, Compression) -{ - FileStorage s("FileStorageUnitTests"); - CompressedFileStorageAccessor accessor(s); - - accessor.SetCompressionForNextOperations(CompressionType_Zlib); - std::string data = "Hello world"; - FileInfo info = accessor.Write(data, FileContentType_Dicom); - - std::string r; - accessor.Read(r, info.GetUuid()); - - ASSERT_EQ(data, r); - ASSERT_EQ(CompressionType_Zlib, info.GetCompressionType()); - ASSERT_EQ(11u, info.GetUncompressedSize()); - ASSERT_EQ(FileContentType_Dicom, info.GetContentType()); -} - - -TEST(FileStorageAccessor, Mix) -{ - FileStorage s("FileStorageUnitTests"); - CompressedFileStorageAccessor accessor(s); - - std::string r; - std::string compressedData = "Hello"; - std::string uncompressedData = "HelloWorld"; - - accessor.SetCompressionForNextOperations(CompressionType_Zlib); - FileInfo compressedInfo = accessor.Write(compressedData, FileContentType_Dicom); - - accessor.SetCompressionForNextOperations(CompressionType_None); - FileInfo uncompressedInfo = accessor.Write(uncompressedData, FileContentType_Dicom); - - accessor.SetCompressionForNextOperations(CompressionType_Zlib); - accessor.Read(r, compressedInfo.GetUuid()); - ASSERT_EQ(compressedData, r); - - accessor.SetCompressionForNextOperations(CompressionType_None); - accessor.Read(r, compressedInfo.GetUuid()); - ASSERT_NE(compressedData, r); - - /* - // This test is too slow on Windows - accessor.SetCompressionForNextOperations(CompressionType_Zlib); - ASSERT_THROW(accessor.Read(r, uncompressedInfo.GetUuid()), OrthancException); - */ -} diff -r fc6ad5b97219 -r 17815b9d4280 UnitTests/Lua.cpp --- a/UnitTests/Lua.cpp Fri Oct 25 14:52:26 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,72 +0,0 @@ -#include "gtest/gtest.h" - -#include "../Core/Lua/LuaFunctionCall.h" - - -TEST(Lua, Simple) -{ - Orthanc::LuaContext lua; - lua.Execute(Orthanc::EmbeddedResources::LUA_TOOLBOX); - lua.Execute("a={}"); - lua.Execute("a['x'] = 10"); - lua.Execute("a['y'] = {}"); - lua.Execute("a['y'][1] = 20"); - lua.Execute("a['y'][2] = 20"); - lua.Execute("PrintRecursive(a)"); - - lua.Execute("function f(a) print(a.bool) return a.bool,20,30,40,50,60 end"); - - Json::Value v, vv, o; - //v["a"] = "b"; - v.append("hello"); - v.append("world"); - v.append("42"); - vv.append("sub"); - vv.append("set"); - v.append(vv); - o = Json::objectValue; - o["x"] = 10; - o["y"] = 20; - o["z"] = 20.5f; - v.append(o); - - { - Orthanc::LuaFunctionCall f(lua, "PrintRecursive"); - f.PushJSON(v); - f.Execute(); - } - - { - Orthanc::LuaFunctionCall f(lua, "f"); - f.PushJSON(o); - ASSERT_THROW(f.ExecutePredicate(), Orthanc::LuaException); - } - - o["bool"] = false; - - { - Orthanc::LuaFunctionCall f(lua, "f"); - f.PushJSON(o); - ASSERT_FALSE(f.ExecutePredicate()); - } - - o["bool"] = true; - - { - Orthanc::LuaFunctionCall f(lua, "f"); - f.PushJSON(o); - ASSERT_TRUE(f.ExecutePredicate()); - } -} - - -TEST(Lua, Existing) -{ - Orthanc::LuaContext lua; - lua.Execute("a={}"); - lua.Execute("function f() end"); - - ASSERT_TRUE(lua.IsExistingFunction("f")); - ASSERT_FALSE(lua.IsExistingFunction("a")); - ASSERT_FALSE(lua.IsExistingFunction("Dummy")); -} diff -r fc6ad5b97219 -r 17815b9d4280 UnitTests/MemoryCache.cpp --- a/UnitTests/MemoryCache.cpp Fri Oct 25 14:52:26 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,197 +0,0 @@ -#include "gtest/gtest.h" - -#include -#include -#include -#include -#include "../Core/IDynamicObject.h" -#include "../Core/Cache/MemoryCache.h" - - -TEST(LRU, Basic) -{ - Orthanc::LeastRecentlyUsedIndex r; - - r.Add("d"); - r.Add("a"); - r.Add("c"); - r.Add("b"); - - r.MakeMostRecent("a"); - r.MakeMostRecent("d"); - r.MakeMostRecent("b"); - r.MakeMostRecent("c"); - r.MakeMostRecent("d"); - r.MakeMostRecent("c"); - - ASSERT_EQ("a", r.GetOldest()); - ASSERT_EQ("a", r.RemoveOldest()); - ASSERT_EQ("b", r.GetOldest()); - ASSERT_EQ("b", r.RemoveOldest()); - ASSERT_EQ("d", r.GetOldest()); - ASSERT_EQ("d", r.RemoveOldest()); - ASSERT_EQ("c", r.GetOldest()); - ASSERT_EQ("c", r.RemoveOldest()); - - ASSERT_TRUE(r.IsEmpty()); - - ASSERT_THROW(r.GetOldest(), Orthanc::OrthancException); - ASSERT_THROW(r.RemoveOldest(), Orthanc::OrthancException); -} - - -TEST(LRU, Payload) -{ - Orthanc::LeastRecentlyUsedIndex r; - - r.Add("a", 420); - r.Add("b", 421); - r.Add("c", 422); - r.Add("d", 423); - - r.MakeMostRecent("a"); - r.MakeMostRecent("d"); - r.MakeMostRecent("b"); - r.MakeMostRecent("c"); - r.MakeMostRecent("d"); - r.MakeMostRecent("c"); - - ASSERT_TRUE(r.Contains("b")); - ASSERT_EQ(421, r.Invalidate("b")); - ASSERT_FALSE(r.Contains("b")); - - int p; - ASSERT_TRUE(r.Contains("a", p)); ASSERT_EQ(420, p); - ASSERT_TRUE(r.Contains("c", p)); ASSERT_EQ(422, p); - ASSERT_TRUE(r.Contains("d", p)); ASSERT_EQ(423, p); - - ASSERT_EQ("a", r.GetOldest()); - ASSERT_EQ(420, r.GetOldestPayload()); - ASSERT_EQ("a", r.RemoveOldest(p)); ASSERT_EQ(420, p); - - ASSERT_EQ("d", r.GetOldest()); - ASSERT_EQ(423, r.GetOldestPayload()); - ASSERT_EQ("d", r.RemoveOldest(p)); ASSERT_EQ(423, p); - - ASSERT_EQ("c", r.GetOldest()); - ASSERT_EQ(422, r.GetOldestPayload()); - ASSERT_EQ("c", r.RemoveOldest(p)); ASSERT_EQ(422, p); - - ASSERT_TRUE(r.IsEmpty()); -} - - -TEST(LRU, PayloadUpdate) -{ - Orthanc::LeastRecentlyUsedIndex r; - - r.Add("a", 420); - r.Add("b", 421); - r.Add("d", 423); - - r.MakeMostRecent("a", 424); - r.MakeMostRecent("d", 421); - - ASSERT_EQ("b", r.GetOldest()); - ASSERT_EQ(421, r.GetOldestPayload()); - r.RemoveOldest(); - - ASSERT_EQ("a", r.GetOldest()); - ASSERT_EQ(424, r.GetOldestPayload()); - r.RemoveOldest(); - - ASSERT_EQ("d", r.GetOldest()); - ASSERT_EQ(421, r.GetOldestPayload()); - r.RemoveOldest(); - - ASSERT_TRUE(r.IsEmpty()); -} - - - -TEST(LRU, PayloadUpdateBis) -{ - Orthanc::LeastRecentlyUsedIndex r; - - r.AddOrMakeMostRecent("a", 420); - r.AddOrMakeMostRecent("b", 421); - r.AddOrMakeMostRecent("d", 423); - r.AddOrMakeMostRecent("a", 424); - r.AddOrMakeMostRecent("d", 421); - - ASSERT_EQ("b", r.GetOldest()); - ASSERT_EQ(421, r.GetOldestPayload()); - r.RemoveOldest(); - - ASSERT_EQ("a", r.GetOldest()); - ASSERT_EQ(424, r.GetOldestPayload()); - r.RemoveOldest(); - - ASSERT_EQ("d", r.GetOldest()); - ASSERT_EQ(421, r.GetOldestPayload()); - r.RemoveOldest(); - - ASSERT_TRUE(r.IsEmpty()); -} - - - - -namespace -{ - class Integer : public Orthanc::IDynamicObject - { - private: - std::string& log_; - int value_; - - public: - Integer(std::string& log, int v) : log_(log), value_(v) - { - } - - virtual ~Integer() - { - LOG(INFO) << "Removing cache entry for " << value_; - log_ += boost::lexical_cast(value_) + " "; - } - - int GetValue() const - { - return value_; - } - }; - - class IntegerProvider : public Orthanc::ICachePageProvider - { - public: - std::string log_; - - Orthanc::IDynamicObject* Provide(const std::string& s) - { - LOG(INFO) << "Providing " << s; - return new Integer(log_, boost::lexical_cast(s)); - } - }; -} - - -TEST(MemoryCache, Basic) -{ - IntegerProvider provider; - - { - Orthanc::MemoryCache cache(provider, 3); - cache.Access("42"); // 42 -> exit - cache.Access("43"); // 43, 42 -> exit - cache.Access("45"); // 45, 43, 42 -> exit - cache.Access("42"); // 42, 45, 43 -> exit - cache.Access("43"); // 43, 42, 45 -> exit - cache.Access("47"); // 45 is removed; 47, 43, 42 -> exit - cache.Access("44"); // 42 is removed; 44, 47, 43 -> exit - cache.Access("42"); // 43 is removed; 42, 44, 47 -> exit - // Closing the cache: 47, 44, 42 are successively removed - } - - ASSERT_EQ("45 42 43 47 44 42 ", provider.log_); -} diff -r fc6ad5b97219 -r 17815b9d4280 UnitTests/Png.cpp --- a/UnitTests/Png.cpp Fri Oct 25 14:52:26 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,109 +0,0 @@ -#include "gtest/gtest.h" - -#include -#include "../Core/FileFormats/PngReader.h" -#include "../Core/FileFormats/PngWriter.h" - -TEST(PngWriter, ColorPattern) -{ - Orthanc::PngWriter w; - int width = 17; - int height = 61; - int pitch = width * 3; - - std::vector image(height * pitch); - for (int y = 0; y < height; y++) - { - uint8_t *p = &image[0] + y * pitch; - for (int x = 0; x < width; x++, p += 3) - { - p[0] = (y % 3 == 0) ? 255 : 0; - p[1] = (y % 3 == 1) ? 255 : 0; - p[2] = (y % 3 == 2) ? 255 : 0; - } - } - - w.WriteToFile("ColorPattern.png", width, height, pitch, Orthanc::PixelFormat_RGB24, &image[0]); -} - -TEST(PngWriter, Gray8Pattern) -{ - Orthanc::PngWriter w; - int width = 17; - int height = 256; - int pitch = width; - - std::vector image(height * pitch); - for (int y = 0; y < height; y++) - { - uint8_t *p = &image[0] + y * pitch; - for (int x = 0; x < width; x++, p++) - { - *p = y; - } - } - - w.WriteToFile("Gray8Pattern.png", width, height, pitch, Orthanc::PixelFormat_Grayscale8, &image[0]); -} - -TEST(PngWriter, Gray16Pattern) -{ - Orthanc::PngWriter w; - int width = 256; - int height = 256; - int pitch = width * 2 + 16; - - std::vector image(height * pitch); - - int v = 0; - for (int y = 0; y < height; y++) - { - uint16_t *p = reinterpret_cast(&image[0] + y * pitch); - for (int x = 0; x < width; x++, p++, v++) - { - *p = v; - } - } - - w.WriteToFile("Gray16Pattern.png", width, height, pitch, Orthanc::PixelFormat_Grayscale16, &image[0]); -} - -TEST(PngWriter, EndToEnd) -{ - Orthanc::PngWriter w; - int width = 256; - int height = 256; - int pitch = width * 2 + 16; - - std::vector image(height * pitch); - - int v = 0; - for (int y = 0; y < height; y++) - { - uint16_t *p = reinterpret_cast(&image[0] + y * pitch); - for (int x = 0; x < width; x++, p++, v++) - { - *p = v; - } - } - - std::string s; - w.WriteToMemory(s, width, height, pitch, Orthanc::PixelFormat_Grayscale16, &image[0]); - - Orthanc::PngReader r; - r.ReadFromMemory(s); - - ASSERT_EQ(r.GetWidth(), width); - ASSERT_EQ(r.GetHeight(), height); - - v = 0; - for (int y = 0; y < height; y++) - { - uint16_t *p = reinterpret_cast((uint8_t*) r.GetBuffer() + y * r.GetPitch()); - for (int x = 0; x < width; x++, p++, v++) - { - ASSERT_EQ(*p, v); - } - } - -} diff -r fc6ad5b97219 -r 17815b9d4280 UnitTests/RestApi.cpp --- a/UnitTests/RestApi.cpp Fri Oct 25 14:52:26 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,81 +0,0 @@ -#include "gtest/gtest.h" - -#include -#include - -#include "../Core/RestApi/RestApi.h" -#include "../Core/Uuid.h" -#include "../Core/OrthancException.h" -#include "../Core/Compression/ZlibCompressor.h" - -using namespace Orthanc; - -TEST(RestApi, ParseCookies) -{ - HttpHandler::Arguments headers; - HttpHandler::Arguments cookies; - - headers["cookie"] = "a=b;c=d;;;e=f;;g=h;"; - HttpHandler::ParseCookies(cookies, headers); - ASSERT_EQ(4u, cookies.size()); - ASSERT_EQ("b", cookies["a"]); - ASSERT_EQ("d", cookies["c"]); - ASSERT_EQ("f", cookies["e"]); - ASSERT_EQ("h", cookies["g"]); - - headers["cookie"] = " name = value ; name2=value2"; - HttpHandler::ParseCookies(cookies, headers); - ASSERT_EQ(2u, cookies.size()); - ASSERT_EQ("value", cookies["name"]); - ASSERT_EQ("value2", cookies["name2"]); - - headers["cookie"] = " ;;; "; - HttpHandler::ParseCookies(cookies, headers); - ASSERT_EQ(0u, cookies.size()); - - headers["cookie"] = " ; n=v ;; "; - HttpHandler::ParseCookies(cookies, headers); - ASSERT_EQ(1u, cookies.size()); - ASSERT_EQ("v", cookies["n"]); -} - -TEST(RestApi, RestApiPath) -{ - RestApiPath::Components args; - UriComponents trail; - - { - RestApiPath uri("/coucou/{abc}/d/*"); - ASSERT_TRUE(uri.Match(args, trail, "/coucou/moi/d/e/f/g")); - ASSERT_EQ(1u, args.size()); - ASSERT_EQ(3u, trail.size()); - ASSERT_EQ("moi", args["abc"]); - ASSERT_EQ("e", trail[0]); - ASSERT_EQ("f", trail[1]); - ASSERT_EQ("g", trail[2]); - - ASSERT_FALSE(uri.Match(args, trail, "/coucou/moi/f")); - ASSERT_TRUE(uri.Match(args, trail, "/coucou/moi/d/")); - ASSERT_FALSE(uri.Match(args, trail, "/a/moi/d")); - ASSERT_FALSE(uri.Match(args, trail, "/coucou/moi")); - } - - { - RestApiPath uri("/coucou/{abc}/d"); - ASSERT_FALSE(uri.Match(args, trail, "/coucou/moi/d/e/f/g")); - ASSERT_TRUE(uri.Match(args, trail, "/coucou/moi/d")); - ASSERT_EQ(1u, args.size()); - ASSERT_EQ(0u, trail.size()); - ASSERT_EQ("moi", args["abc"]); - } - - { - RestApiPath uri("/*"); - ASSERT_TRUE(uri.Match(args, trail, "/a/b/c")); - ASSERT_EQ(0u, args.size()); - ASSERT_EQ(3u, trail.size()); - ASSERT_EQ("a", trail[0]); - ASSERT_EQ("b", trail[1]); - ASSERT_EQ("c", trail[2]); - } -} diff -r fc6ad5b97219 -r 17815b9d4280 UnitTests/SQLite.cpp --- a/UnitTests/SQLite.cpp Fri Oct 25 14:52:26 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,238 +0,0 @@ -#include "gtest/gtest.h" - -#include "../Core/Toolbox.h" -#include "../Core/SQLite/Connection.h" -#include "../Core/SQLite/Statement.h" -#include "../Core/SQLite/Transaction.h" - -#include - -using namespace Orthanc; - - -TEST(SQLite, Configuration) -{ - ASSERT_EQ(1, sqlite3_threadsafe()); -} - - -TEST(SQLite, Connection) -{ - Toolbox::RemoveFile("coucou"); - SQLite::Connection c; - c.Open("coucou"); - c.Execute("CREATE TABLE c(k INTEGER PRIMARY KEY AUTOINCREMENT, v INTEGER)"); - c.Execute("INSERT INTO c VALUES(NULL, 42);"); -} - - -TEST(SQLite, StatementReferenceBasic) -{ - sqlite3* db; - sqlite3_open(":memory:", &db); - - { - SQLite::StatementReference r(db, "SELECT * FROM sqlite_master"); - ASSERT_EQ(0u, r.GetReferenceCount()); - - { - SQLite::StatementReference r1(r); - ASSERT_EQ(1u, r.GetReferenceCount()); - ASSERT_EQ(0u, r1.GetReferenceCount()); - - { - SQLite::StatementReference r2(r); - ASSERT_EQ(2u, r.GetReferenceCount()); - ASSERT_EQ(0u, r1.GetReferenceCount()); - ASSERT_EQ(0u, r2.GetReferenceCount()); - - SQLite::StatementReference r3(r2); - ASSERT_EQ(3u, r.GetReferenceCount()); - ASSERT_EQ(0u, r1.GetReferenceCount()); - ASSERT_EQ(0u, r2.GetReferenceCount()); - ASSERT_EQ(0u, r3.GetReferenceCount()); - } - - ASSERT_EQ(1u, r.GetReferenceCount()); - ASSERT_EQ(0u, r1.GetReferenceCount()); - - { - SQLite::StatementReference r2(r); - ASSERT_EQ(2u, r.GetReferenceCount()); - ASSERT_EQ(0u, r1.GetReferenceCount()); - ASSERT_EQ(0u, r2.GetReferenceCount()); - } - - ASSERT_EQ(1u, r.GetReferenceCount()); - ASSERT_EQ(0u, r1.GetReferenceCount()); - } - - ASSERT_EQ(0u, r.GetReferenceCount()); - } - - sqlite3_close(db); -} - -TEST(SQLite, StatementBasic) -{ - SQLite::Connection c; - c.OpenInMemory(); - - SQLite::Statement s(c, "SELECT * from sqlite_master"); - s.Run(); - - for (unsigned int i = 0; i < 5; i++) - { - SQLite::Statement cs(c, SQLITE_FROM_HERE, "SELECT * from sqlite_master"); - cs.Step(); - } -} - - -namespace -{ - static bool destroyed; - - class MyFunc : public SQLite::IScalarFunction - { - public: - MyFunc() - { - destroyed = false; - } - - virtual ~MyFunc() - { - destroyed = true; - } - - virtual const char* GetName() const - { - return "MYFUNC"; - } - - virtual unsigned int GetCardinality() const - { - return 2; - } - - virtual void Compute(SQLite::FunctionContext& context) - { - context.SetIntResult(1000 + context.GetIntValue(0) * context.GetIntValue(1)); - } - }; - - class MyDelete : public SQLite::IScalarFunction - { - public: - std::set deleted_; - - virtual const char* GetName() const - { - return "MYDELETE"; - } - - virtual unsigned int GetCardinality() const - { - return 1; - } - - virtual void Compute(SQLite::FunctionContext& context) - { - deleted_.insert(context.GetIntValue(0)); - context.SetNullResult(); - } - }; -} - -TEST(SQLite, ScalarFunction) -{ - { - SQLite::Connection c; - c.OpenInMemory(); - c.Register(new MyFunc()); - c.Execute("CREATE TABLE t(id INTEGER PRIMARY KEY, v1 INTEGER, v2 INTEGER);"); - c.Execute("INSERT INTO t VALUES(NULL, 2, 3);"); - c.Execute("INSERT INTO t VALUES(NULL, 4, 4);"); - c.Execute("INSERT INTO t VALUES(NULL, 6, 5);"); - SQLite::Statement t(c, "SELECT MYFUNC(v1, v2), v1, v2 FROM t"); - int i = 0; - while (t.Step()) - { - ASSERT_EQ(t.ColumnInt(0), 1000 + t.ColumnInt(1) * t.ColumnInt(2)); - i++; - } - ASSERT_EQ(3, i); - ASSERT_FALSE(destroyed); - } - ASSERT_TRUE(destroyed); -} - -TEST(SQLite, CascadedDeleteCallback) -{ - SQLite::Connection c; - c.OpenInMemory(); - MyDelete *func = new MyDelete(); - c.Register(func); - c.Execute("CREATE TABLE parent(id INTEGER PRIMARY KEY, dummy INTEGER);"); - c.Execute("CREATE TABLE child(" - " id INTEGER PRIMARY KEY, " - " parent INTEGER REFERENCES parent(id) ON DELETE CASCADE, " - " value INTEGER);"); - c.Execute("CREATE TRIGGER childRemoved " - "AFTER DELETE ON child " - "FOR EACH ROW BEGIN " - " SELECT MYDELETE(old.value); " - "END;"); - - c.Execute("INSERT INTO parent VALUES(42, 100);"); - c.Execute("INSERT INTO parent VALUES(43, 101);"); - - c.Execute("INSERT INTO child VALUES(NULL, 42, 4200);"); - c.Execute("INSERT INTO child VALUES(NULL, 42, 4201);"); - - c.Execute("INSERT INTO child VALUES(NULL, 43, 4300);"); - c.Execute("INSERT INTO child VALUES(NULL, 43, 4301);"); - - // The following command deletes "parent(43, 101)", then in turns - // "child(NULL, 43, 4300/4301)", then calls the MyDelete on 4300 and - // 4301 - c.Execute("DELETE FROM parent WHERE dummy=101"); - - ASSERT_EQ(2u, func->deleted_.size()); - ASSERT_TRUE(func->deleted_.find(4300) != func->deleted_.end()); - ASSERT_TRUE(func->deleted_.find(4301) != func->deleted_.end()); -} - - -TEST(SQLite, EmptyTransactions) -{ - try - { - SQLite::Connection c; - c.OpenInMemory(); - - c.Execute("CREATE TABLE a(id INTEGER PRIMARY KEY);"); - c.Execute("INSERT INTO a VALUES(NULL)"); - - { - SQLite::Transaction t(c); - t.Begin(); - { - SQLite::Statement s(c, SQLITE_FROM_HERE, "SELECT * FROM a"); - s.Step(); - } - //t.Commit(); - } - - { - SQLite::Statement s(c, SQLITE_FROM_HERE, "SELECT * FROM a"); - s.Step(); - } - } - catch (OrthancException& e) - { - fprintf(stderr, "Exception: [%s]\n", e.What()); - throw e; - } -} diff -r fc6ad5b97219 -r 17815b9d4280 UnitTests/SQLiteChromium.cpp --- a/UnitTests/SQLiteChromium.cpp Fri Oct 25 14:52:26 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,344 +0,0 @@ -#include "gtest/gtest.h" - -#include "../Core/Toolbox.h" -#include "../Core/SQLite/Connection.h" -#include "../Core/SQLite/Statement.h" -#include "../Core/SQLite/Transaction.h" - -#include - - -using namespace Orthanc; -using namespace Orthanc::SQLite; - - -/******************************************************************** - ** Tests from - ** http://src.chromium.org/viewvc/chrome/trunk/src/sql/connection_unittest.cc - ********************************************************************/ - -class SQLConnectionTest : public testing::Test -{ -public: - SQLConnectionTest() - { - } - - virtual ~SQLConnectionTest() - { - } - - virtual void SetUp() - { - db_.OpenInMemory(); - } - - virtual void TearDown() - { - db_.Close(); - } - - Connection& db() - { - return db_; - } - -private: - Connection db_; -}; - - - -TEST_F(SQLConnectionTest, Execute) -{ - // Valid statement should return true. - ASSERT_TRUE(db().Execute("CREATE TABLE foo (a, b)")); - EXPECT_EQ(SQLITE_OK, db().GetErrorCode()); - - // Invalid statement should fail. - ASSERT_EQ(SQLITE_ERROR, - db().ExecuteAndReturnErrorCode("CREATE TAB foo (a, b")); - EXPECT_EQ(SQLITE_ERROR, db().GetErrorCode()); -} - -TEST_F(SQLConnectionTest, ExecuteWithErrorCode) { - ASSERT_EQ(SQLITE_OK, - db().ExecuteAndReturnErrorCode("CREATE TABLE foo (a, b)")); - ASSERT_EQ(SQLITE_ERROR, - db().ExecuteAndReturnErrorCode("CREATE TABLE TABLE")); - ASSERT_EQ(SQLITE_ERROR, - db().ExecuteAndReturnErrorCode( - "INSERT INTO foo(a, b) VALUES (1, 2, 3, 4)")); -} - -TEST_F(SQLConnectionTest, CachedStatement) { - StatementId id1("foo", 12); - ASSERT_TRUE(db().Execute("CREATE TABLE foo (a, b)")); - ASSERT_TRUE(db().Execute("INSERT INTO foo(a, b) VALUES (12, 13)")); - - // Create a new cached statement. - { - Statement s(db(), id1, "SELECT a FROM foo"); - ASSERT_TRUE(s.Step()); - EXPECT_EQ(12, s.ColumnInt(0)); - } - - // The statement should be cached still. - EXPECT_TRUE(db().HasCachedStatement(id1)); - - { - // Get the same statement using different SQL. This should ignore our - // SQL and use the cached one (so it will be valid). - Statement s(db(), id1, "something invalid("); - ASSERT_TRUE(s.Step()); - EXPECT_EQ(12, s.ColumnInt(0)); - } - - // Make sure other statements aren't marked as cached. - EXPECT_FALSE(db().HasCachedStatement(SQLITE_FROM_HERE)); -} - -TEST_F(SQLConnectionTest, IsSQLValidTest) { - ASSERT_TRUE(db().Execute("CREATE TABLE foo (a, b)")); - ASSERT_TRUE(db().IsSQLValid("SELECT a FROM foo")); - ASSERT_FALSE(db().IsSQLValid("SELECT no_exist FROM foo")); -} - - - -TEST_F(SQLConnectionTest, DoesStuffExist) { - // Test DoesTableExist. - EXPECT_FALSE(db().DoesTableExist("foo")); - ASSERT_TRUE(db().Execute("CREATE TABLE foo (a, b)")); - EXPECT_TRUE(db().DoesTableExist("foo")); - - // Should be case sensitive. - EXPECT_FALSE(db().DoesTableExist("FOO")); - - // Test DoesColumnExist. - EXPECT_FALSE(db().DoesColumnExist("foo", "bar")); - EXPECT_TRUE(db().DoesColumnExist("foo", "a")); - - // Testing for a column on a nonexistent table. - EXPECT_FALSE(db().DoesColumnExist("bar", "b")); -} - -TEST_F(SQLConnectionTest, GetLastInsertRowId) { - ASSERT_TRUE(db().Execute("CREATE TABLE foo (id INTEGER PRIMARY KEY, value)")); - - ASSERT_TRUE(db().Execute("INSERT INTO foo (value) VALUES (12)")); - - // Last insert row ID should be valid. - int64_t row = db().GetLastInsertRowId(); - EXPECT_LT(0, row); - - // It should be the primary key of the row we just inserted. - Statement s(db(), "SELECT value FROM foo WHERE id=?"); - s.BindInt64(0, row); - ASSERT_TRUE(s.Step()); - EXPECT_EQ(12, s.ColumnInt(0)); -} - -TEST_F(SQLConnectionTest, Rollback) { - ASSERT_TRUE(db().BeginTransaction()); - ASSERT_TRUE(db().BeginTransaction()); - EXPECT_EQ(2, db().GetTransactionNesting()); - db().RollbackTransaction(); - EXPECT_FALSE(db().CommitTransaction()); - EXPECT_TRUE(db().BeginTransaction()); -} - - - - -/******************************************************************** - ** Tests from - ** http://src.chromium.org/viewvc/chrome/trunk/src/sql/statement_unittest.cc - ********************************************************************/ - -namespace Orthanc -{ - namespace SQLite - { - class SQLStatementTest : public SQLConnectionTest - { - }; - - TEST_F(SQLStatementTest, Run) { - ASSERT_TRUE(db().Execute("CREATE TABLE foo (a, b)")); - ASSERT_TRUE(db().Execute("INSERT INTO foo (a, b) VALUES (3, 12)")); - - Statement s(db(), "SELECT b FROM foo WHERE a=?"); - // Stepping it won't work since we haven't bound the value. - EXPECT_FALSE(s.Step()); - - // Run should fail since this produces output, and we should use Step(). This - // gets a bit wonky since sqlite says this is OK so succeeded is set. - s.Reset(true); - s.BindInt(0, 3); - EXPECT_FALSE(s.Run()); - EXPECT_EQ(SQLITE_ROW, db().GetErrorCode()); - - // Resetting it should put it back to the previous state (not runnable). - s.Reset(true); - - // Binding and stepping should produce one row. - s.BindInt(0, 3); - EXPECT_TRUE(s.Step()); - EXPECT_EQ(12, s.ColumnInt(0)); - EXPECT_FALSE(s.Step()); - } - - TEST_F(SQLStatementTest, BasicErrorCallback) { - ASSERT_TRUE(db().Execute("CREATE TABLE foo (a INTEGER PRIMARY KEY, b)")); - // Insert in the foo table the primary key. It is an error to insert - // something other than an number. This error causes the error callback - // handler to be called with SQLITE_MISMATCH as error code. - Statement s(db(), "INSERT INTO foo (a) VALUES (?)"); - s.BindCString(0, "bad bad"); - EXPECT_THROW(s.Run(), OrthancException); - } - - TEST_F(SQLStatementTest, Reset) { - ASSERT_TRUE(db().Execute("CREATE TABLE foo (a, b)")); - ASSERT_TRUE(db().Execute("INSERT INTO foo (a, b) VALUES (3, 12)")); - ASSERT_TRUE(db().Execute("INSERT INTO foo (a, b) VALUES (4, 13)")); - - Statement s(db(), "SELECT b FROM foo WHERE a = ? "); - s.BindInt(0, 3); - ASSERT_TRUE(s.Step()); - EXPECT_EQ(12, s.ColumnInt(0)); - ASSERT_FALSE(s.Step()); - - s.Reset(false); - // Verify that we can get all rows again. - ASSERT_TRUE(s.Step()); - EXPECT_EQ(12, s.ColumnInt(0)); - EXPECT_FALSE(s.Step()); - - s.Reset(true); - ASSERT_FALSE(s.Step()); - } - } -} - - - - - - -/******************************************************************** - ** Tests from - ** http://src.chromium.org/viewvc/chrome/trunk/src/sql/transaction_unittest.cc - ********************************************************************/ - -class SQLTransactionTest : public SQLConnectionTest -{ -public: - virtual void SetUp() - { - SQLConnectionTest::SetUp(); - ASSERT_TRUE(db().Execute("CREATE TABLE foo (a, b)")); - } - - // Returns the number of rows in table "foo". - int CountFoo() - { - Statement count(db(), "SELECT count(*) FROM foo"); - count.Step(); - return count.ColumnInt(0); - } -}; - - -TEST_F(SQLTransactionTest, Commit) { - { - Transaction t(db()); - EXPECT_FALSE(t.IsOpen()); - t.Begin(); - EXPECT_TRUE(t.IsOpen()); - - EXPECT_TRUE(db().Execute("INSERT INTO foo (a, b) VALUES (1, 2)")); - - t.Commit(); - EXPECT_FALSE(t.IsOpen()); - } - - EXPECT_EQ(1, CountFoo()); -} - -TEST_F(SQLTransactionTest, Rollback) { - // Test some basic initialization, and that rollback runs when you exit the - // scope. - { - Transaction t(db()); - EXPECT_FALSE(t.IsOpen()); - t.Begin(); - EXPECT_TRUE(t.IsOpen()); - - EXPECT_TRUE(db().Execute("INSERT INTO foo (a, b) VALUES (1, 2)")); - } - - // Nothing should have been committed since it was implicitly rolled back. - EXPECT_EQ(0, CountFoo()); - - // Test explicit rollback. - Transaction t2(db()); - EXPECT_FALSE(t2.IsOpen()); - t2.Begin(); - - EXPECT_TRUE(db().Execute("INSERT INTO foo (a, b) VALUES (1, 2)")); - t2.Rollback(); - EXPECT_FALSE(t2.IsOpen()); - - // Nothing should have been committed since it was explicitly rolled back. - EXPECT_EQ(0, CountFoo()); -} - -// Rolling back any part of a transaction should roll back all of them. -TEST_F(SQLTransactionTest, NestedRollback) { - EXPECT_EQ(0, db().GetTransactionNesting()); - - // Outermost transaction. - { - Transaction outer(db()); - outer.Begin(); - EXPECT_EQ(1, db().GetTransactionNesting()); - - // The first inner one gets committed. - { - Transaction inner1(db()); - inner1.Begin(); - EXPECT_TRUE(db().Execute("INSERT INTO foo (a, b) VALUES (1, 2)")); - EXPECT_EQ(2, db().GetTransactionNesting()); - - inner1.Commit(); - EXPECT_EQ(1, db().GetTransactionNesting()); - } - - // One row should have gotten inserted. - EXPECT_EQ(1, CountFoo()); - - // The second inner one gets rolled back. - { - Transaction inner2(db()); - inner2.Begin(); - EXPECT_TRUE(db().Execute("INSERT INTO foo (a, b) VALUES (1, 2)")); - EXPECT_EQ(2, db().GetTransactionNesting()); - - inner2.Rollback(); - EXPECT_EQ(1, db().GetTransactionNesting()); - } - - // A third inner one will fail in Begin since one has already been rolled - // back. - EXPECT_EQ(1, db().GetTransactionNesting()); - { - Transaction inner3(db()); - EXPECT_THROW(inner3.Begin(), OrthancException); - EXPECT_EQ(1, db().GetTransactionNesting()); - } - } - EXPECT_EQ(0, db().GetTransactionNesting()); - EXPECT_EQ(0, CountFoo()); -} diff -r fc6ad5b97219 -r 17815b9d4280 UnitTests/ServerIndex.cpp --- a/UnitTests/ServerIndex.cpp Fri Oct 25 14:52:26 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,497 +0,0 @@ -#include "gtest/gtest.h" - -#include "../OrthancServer/DatabaseWrapper.h" -#include "../Core/Uuid.h" - -#include -#include -#include - -using namespace Orthanc; - -namespace -{ - class ServerIndexListener : public IServerIndexListener - { - public: - std::vector deletedFiles_; - std::string ancestorId_; - ResourceType ancestorType_; - - void Reset() - { - ancestorId_ = ""; - deletedFiles_.clear(); - } - - virtual void SignalRemainingAncestor(ResourceType type, - const std::string& publicId) - { - ancestorId_ = publicId; - ancestorType_ = type; - } - - virtual void SignalFileDeleted(const FileInfo& info) - { - const std::string fileUuid = info.GetUuid(); - deletedFiles_.push_back(fileUuid); - LOG(INFO) << "A file must be removed: " << fileUuid; - } - }; -} - - -TEST(DatabaseWrapper, Simple) -{ - ServerIndexListener listener; - DatabaseWrapper index(listener); - - int64_t a[] = { - index.CreateResource("a", ResourceType_Patient), // 0 - index.CreateResource("b", ResourceType_Study), // 1 - index.CreateResource("c", ResourceType_Series), // 2 - index.CreateResource("d", ResourceType_Instance), // 3 - index.CreateResource("e", ResourceType_Instance), // 4 - index.CreateResource("f", ResourceType_Instance), // 5 - index.CreateResource("g", ResourceType_Study) // 6 - }; - - ASSERT_EQ("a", index.GetPublicId(a[0])); - ASSERT_EQ("b", index.GetPublicId(a[1])); - ASSERT_EQ("c", index.GetPublicId(a[2])); - ASSERT_EQ("d", index.GetPublicId(a[3])); - ASSERT_EQ("e", index.GetPublicId(a[4])); - ASSERT_EQ("f", index.GetPublicId(a[5])); - ASSERT_EQ("g", index.GetPublicId(a[6])); - - ASSERT_EQ(ResourceType_Patient, index.GetResourceType(a[0])); - ASSERT_EQ(ResourceType_Study, index.GetResourceType(a[1])); - ASSERT_EQ(ResourceType_Series, index.GetResourceType(a[2])); - ASSERT_EQ(ResourceType_Instance, index.GetResourceType(a[3])); - ASSERT_EQ(ResourceType_Instance, index.GetResourceType(a[4])); - ASSERT_EQ(ResourceType_Instance, index.GetResourceType(a[5])); - ASSERT_EQ(ResourceType_Study, index.GetResourceType(a[6])); - - { - Json::Value t; - index.GetAllPublicIds(t, ResourceType_Patient); - - ASSERT_EQ(1u, t.size()); - ASSERT_EQ("a", t[0u].asString()); - - index.GetAllPublicIds(t, ResourceType_Series); - ASSERT_EQ(1u, t.size()); - ASSERT_EQ("c", t[0u].asString()); - - index.GetAllPublicIds(t, ResourceType_Study); - ASSERT_EQ(2u, t.size()); - - index.GetAllPublicIds(t, ResourceType_Instance); - ASSERT_EQ(3u, t.size()); - } - - index.SetGlobalProperty(GlobalProperty_FlushSleep, "World"); - - index.AttachChild(a[0], a[1]); - index.AttachChild(a[1], a[2]); - index.AttachChild(a[2], a[3]); - index.AttachChild(a[2], a[4]); - index.AttachChild(a[6], a[5]); - - int64_t parent; - ASSERT_FALSE(index.LookupParent(parent, a[0])); - ASSERT_TRUE(index.LookupParent(parent, a[1])); ASSERT_EQ(a[0], parent); - ASSERT_TRUE(index.LookupParent(parent, a[2])); ASSERT_EQ(a[1], parent); - ASSERT_TRUE(index.LookupParent(parent, a[3])); ASSERT_EQ(a[2], parent); - ASSERT_TRUE(index.LookupParent(parent, a[4])); ASSERT_EQ(a[2], parent); - ASSERT_TRUE(index.LookupParent(parent, a[5])); ASSERT_EQ(a[6], parent); - ASSERT_FALSE(index.LookupParent(parent, a[6])); - - std::string s; - - ASSERT_FALSE(index.GetParentPublicId(s, a[0])); - ASSERT_FALSE(index.GetParentPublicId(s, a[6])); - ASSERT_TRUE(index.GetParentPublicId(s, a[1])); ASSERT_EQ("a", s); - ASSERT_TRUE(index.GetParentPublicId(s, a[2])); ASSERT_EQ("b", s); - ASSERT_TRUE(index.GetParentPublicId(s, a[3])); ASSERT_EQ("c", s); - ASSERT_TRUE(index.GetParentPublicId(s, a[4])); ASSERT_EQ("c", s); - ASSERT_TRUE(index.GetParentPublicId(s, a[5])); ASSERT_EQ("g", s); - - std::list l; - index.GetChildrenPublicId(l, a[0]); ASSERT_EQ(1u, l.size()); ASSERT_EQ("b", l.front()); - index.GetChildrenPublicId(l, a[1]); ASSERT_EQ(1u, l.size()); ASSERT_EQ("c", l.front()); - index.GetChildrenPublicId(l, a[3]); ASSERT_EQ(0u, l.size()); - index.GetChildrenPublicId(l, a[4]); ASSERT_EQ(0u, l.size()); - index.GetChildrenPublicId(l, a[5]); ASSERT_EQ(0u, l.size()); - index.GetChildrenPublicId(l, a[6]); ASSERT_EQ(1u, l.size()); ASSERT_EQ("f", l.front()); - - index.GetChildrenPublicId(l, a[2]); ASSERT_EQ(2u, l.size()); - if (l.front() == "d") - { - ASSERT_EQ("e", l.back()); - } - else - { - ASSERT_EQ("d", l.back()); - ASSERT_EQ("e", l.front()); - } - - std::list md; - index.ListAvailableMetadata(md, a[4]); - ASSERT_EQ(0u, md.size()); - - index.AddAttachment(a[4], FileInfo("my json file", FileContentType_Json, 42, CompressionType_Zlib, 21)); - index.AddAttachment(a[4], FileInfo("my dicom file", FileContentType_Dicom, 42)); - index.AddAttachment(a[6], FileInfo("world", FileContentType_Dicom, 44)); - index.SetMetadata(a[4], MetadataType_Instance_RemoteAet, "PINNACLE"); - - index.ListAvailableMetadata(md, a[4]); - ASSERT_EQ(1u, md.size()); - ASSERT_EQ(MetadataType_Instance_RemoteAet, md.front()); - index.SetMetadata(a[4], MetadataType_ModifiedFrom, "TUTU"); - index.ListAvailableMetadata(md, a[4]); - ASSERT_EQ(2u, md.size()); - index.DeleteMetadata(a[4], MetadataType_ModifiedFrom); - index.ListAvailableMetadata(md, a[4]); - ASSERT_EQ(1u, md.size()); - ASSERT_EQ(MetadataType_Instance_RemoteAet, md.front()); - - ASSERT_EQ(21u + 42u + 44u, index.GetTotalCompressedSize()); - ASSERT_EQ(42u + 42u + 44u, index.GetTotalUncompressedSize()); - - DicomMap m; - m.SetValue(0x0010, 0x0010, "PatientName"); - index.SetMainDicomTags(a[3], m); - - int64_t b; - ResourceType t; - ASSERT_TRUE(index.LookupResource("g", b, t)); - ASSERT_EQ(7, b); - ASSERT_EQ(ResourceType_Study, t); - - ASSERT_TRUE(index.LookupMetadata(s, a[4], MetadataType_Instance_RemoteAet)); - ASSERT_FALSE(index.LookupMetadata(s, a[4], MetadataType_Instance_IndexInSeries)); - ASSERT_EQ("PINNACLE", s); - ASSERT_EQ("PINNACLE", index.GetMetadata(a[4], MetadataType_Instance_RemoteAet)); - ASSERT_EQ("None", index.GetMetadata(a[4], MetadataType_Instance_IndexInSeries, "None")); - - ASSERT_TRUE(index.LookupGlobalProperty(s, GlobalProperty_FlushSleep)); - ASSERT_FALSE(index.LookupGlobalProperty(s, static_cast(42))); - ASSERT_EQ("World", s); - ASSERT_EQ("World", index.GetGlobalProperty(GlobalProperty_FlushSleep)); - ASSERT_EQ("None", index.GetGlobalProperty(static_cast(42), "None")); - - FileInfo att; - ASSERT_TRUE(index.LookupAttachment(att, a[4], FileContentType_Json)); - ASSERT_EQ("my json file", att.GetUuid()); - ASSERT_EQ(21u, att.GetCompressedSize()); - ASSERT_EQ(42u, att.GetUncompressedSize()); - ASSERT_EQ(CompressionType_Zlib, att.GetCompressionType()); - - ASSERT_EQ(0u, listener.deletedFiles_.size()); - ASSERT_EQ(7u, index.GetTableRecordCount("Resources")); - ASSERT_EQ(3u, index.GetTableRecordCount("AttachedFiles")); - ASSERT_EQ(1u, index.GetTableRecordCount("Metadata")); - ASSERT_EQ(1u, index.GetTableRecordCount("MainDicomTags")); - index.DeleteResource(a[0]); - - ASSERT_EQ(2u, listener.deletedFiles_.size()); - ASSERT_FALSE(std::find(listener.deletedFiles_.begin(), - listener.deletedFiles_.end(), - "my json file") == listener.deletedFiles_.end()); - ASSERT_FALSE(std::find(listener.deletedFiles_.begin(), - listener.deletedFiles_.end(), - "my dicom file") == listener.deletedFiles_.end()); - - ASSERT_EQ(2u, index.GetTableRecordCount("Resources")); - ASSERT_EQ(0u, index.GetTableRecordCount("Metadata")); - ASSERT_EQ(1u, index.GetTableRecordCount("AttachedFiles")); - ASSERT_EQ(0u, index.GetTableRecordCount("MainDicomTags")); - index.DeleteResource(a[5]); - ASSERT_EQ(0u, index.GetTableRecordCount("Resources")); - ASSERT_EQ(0u, index.GetTableRecordCount("AttachedFiles")); - ASSERT_EQ(2u, index.GetTableRecordCount("GlobalProperties")); - - ASSERT_EQ(3u, listener.deletedFiles_.size()); - ASSERT_FALSE(std::find(listener.deletedFiles_.begin(), - listener.deletedFiles_.end(), - "world") == listener.deletedFiles_.end()); -} - - - - -TEST(DatabaseWrapper, Upward) -{ - ServerIndexListener listener; - DatabaseWrapper index(listener); - - int64_t a[] = { - index.CreateResource("a", ResourceType_Patient), // 0 - index.CreateResource("b", ResourceType_Study), // 1 - index.CreateResource("c", ResourceType_Series), // 2 - index.CreateResource("d", ResourceType_Instance), // 3 - index.CreateResource("e", ResourceType_Instance), // 4 - index.CreateResource("f", ResourceType_Study), // 5 - index.CreateResource("g", ResourceType_Series), // 6 - index.CreateResource("h", ResourceType_Series) // 7 - }; - - index.AttachChild(a[0], a[1]); - index.AttachChild(a[1], a[2]); - index.AttachChild(a[2], a[3]); - index.AttachChild(a[2], a[4]); - index.AttachChild(a[1], a[6]); - index.AttachChild(a[0], a[5]); - index.AttachChild(a[5], a[7]); - - { - Json::Value j; - index.GetChildren(j, a[0]); - ASSERT_EQ(2u, j.size()); - ASSERT_TRUE((j[0u] == "b" && j[1u] == "f") || - (j[1u] == "b" && j[0u] == "f")); - - index.GetChildren(j, a[1]); - ASSERT_EQ(2u, j.size()); - ASSERT_TRUE((j[0u] == "c" && j[1u] == "g") || - (j[1u] == "c" && j[0u] == "g")); - - index.GetChildren(j, a[2]); - ASSERT_EQ(2u, j.size()); - ASSERT_TRUE((j[0u] == "d" && j[1u] == "e") || - (j[1u] == "d" && j[0u] == "e")); - - index.GetChildren(j, a[3]); ASSERT_EQ(0u, j.size()); - index.GetChildren(j, a[4]); ASSERT_EQ(0u, j.size()); - index.GetChildren(j, a[5]); ASSERT_EQ(1u, j.size()); ASSERT_EQ("h", j[0u].asString()); - index.GetChildren(j, a[6]); ASSERT_EQ(0u, j.size()); - index.GetChildren(j, a[7]); ASSERT_EQ(0u, j.size()); - } - - listener.Reset(); - index.DeleteResource(a[3]); - ASSERT_EQ("c", listener.ancestorId_); - ASSERT_EQ(ResourceType_Series, listener.ancestorType_); - - listener.Reset(); - index.DeleteResource(a[4]); - ASSERT_EQ("b", listener.ancestorId_); - ASSERT_EQ(ResourceType_Study, listener.ancestorType_); - - listener.Reset(); - index.DeleteResource(a[7]); - ASSERT_EQ("a", listener.ancestorId_); - ASSERT_EQ(ResourceType_Patient, listener.ancestorType_); - - listener.Reset(); - index.DeleteResource(a[6]); - ASSERT_EQ("", listener.ancestorId_); // No more ancestor -} - - -TEST(DatabaseWrapper, PatientRecycling) -{ - ServerIndexListener listener; - DatabaseWrapper index(listener); - - std::vector patients; - for (int i = 0; i < 10; i++) - { - std::string p = "Patient " + boost::lexical_cast(i); - patients.push_back(index.CreateResource(p, ResourceType_Patient)); - index.AddAttachment(patients[i], FileInfo(p, FileContentType_Dicom, i + 10)); - ASSERT_FALSE(index.IsProtectedPatient(patients[i])); - } - - ASSERT_EQ(10u, index.GetTableRecordCount("Resources")); - ASSERT_EQ(10u, index.GetTableRecordCount("PatientRecyclingOrder")); - - listener.Reset(); - - index.DeleteResource(patients[5]); - index.DeleteResource(patients[0]); - ASSERT_EQ(8u, index.GetTableRecordCount("Resources")); - ASSERT_EQ(8u, index.GetTableRecordCount("PatientRecyclingOrder")); - - ASSERT_EQ(2u, listener.deletedFiles_.size()); - ASSERT_EQ("Patient 5", listener.deletedFiles_[0]); - ASSERT_EQ("Patient 0", listener.deletedFiles_[1]); - - int64_t p; - ASSERT_TRUE(index.SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[1]); - index.DeleteResource(p); - ASSERT_TRUE(index.SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[2]); - index.DeleteResource(p); - ASSERT_TRUE(index.SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[3]); - index.DeleteResource(p); - ASSERT_TRUE(index.SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[4]); - index.DeleteResource(p); - ASSERT_TRUE(index.SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[6]); - index.DeleteResource(p); - index.DeleteResource(patients[8]); - ASSERT_TRUE(index.SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[7]); - index.DeleteResource(p); - ASSERT_TRUE(index.SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[9]); - index.DeleteResource(p); - ASSERT_FALSE(index.SelectPatientToRecycle(p)); - - ASSERT_EQ(10u, listener.deletedFiles_.size()); - ASSERT_EQ(0u, index.GetTableRecordCount("Resources")); - ASSERT_EQ(0u, index.GetTableRecordCount("PatientRecyclingOrder")); -} - - -TEST(DatabaseWrapper, PatientProtection) -{ - ServerIndexListener listener; - DatabaseWrapper index(listener); - - std::vector patients; - for (int i = 0; i < 5; i++) - { - std::string p = "Patient " + boost::lexical_cast(i); - patients.push_back(index.CreateResource(p, ResourceType_Patient)); - index.AddAttachment(patients[i], FileInfo(p, FileContentType_Dicom, i + 10)); - ASSERT_FALSE(index.IsProtectedPatient(patients[i])); - } - - ASSERT_EQ(5u, index.GetTableRecordCount("Resources")); - ASSERT_EQ(5u, index.GetTableRecordCount("PatientRecyclingOrder")); - - ASSERT_FALSE(index.IsProtectedPatient(patients[2])); - index.SetProtectedPatient(patients[2], true); - ASSERT_TRUE(index.IsProtectedPatient(patients[2])); - ASSERT_EQ(4u, index.GetTableRecordCount("PatientRecyclingOrder")); - ASSERT_EQ(5u, index.GetTableRecordCount("Resources")); - - index.SetProtectedPatient(patients[2], true); - ASSERT_TRUE(index.IsProtectedPatient(patients[2])); - ASSERT_EQ(4u, index.GetTableRecordCount("PatientRecyclingOrder")); - index.SetProtectedPatient(patients[2], false); - ASSERT_FALSE(index.IsProtectedPatient(patients[2])); - ASSERT_EQ(5u, index.GetTableRecordCount("PatientRecyclingOrder")); - index.SetProtectedPatient(patients[2], false); - ASSERT_FALSE(index.IsProtectedPatient(patients[2])); - ASSERT_EQ(5u, index.GetTableRecordCount("PatientRecyclingOrder")); - - ASSERT_EQ(5u, index.GetTableRecordCount("Resources")); - ASSERT_EQ(5u, index.GetTableRecordCount("PatientRecyclingOrder")); - index.SetProtectedPatient(patients[2], true); - ASSERT_TRUE(index.IsProtectedPatient(patients[2])); - ASSERT_EQ(4u, index.GetTableRecordCount("PatientRecyclingOrder")); - index.SetProtectedPatient(patients[2], false); - ASSERT_FALSE(index.IsProtectedPatient(patients[2])); - ASSERT_EQ(5u, index.GetTableRecordCount("PatientRecyclingOrder")); - index.SetProtectedPatient(patients[3], true); - ASSERT_TRUE(index.IsProtectedPatient(patients[3])); - ASSERT_EQ(4u, index.GetTableRecordCount("PatientRecyclingOrder")); - - ASSERT_EQ(5u, index.GetTableRecordCount("Resources")); - ASSERT_EQ(0u, listener.deletedFiles_.size()); - - // Unprotecting a patient puts it at the last position in the recycling queue - int64_t p; - ASSERT_TRUE(index.SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[0]); - index.DeleteResource(p); - ASSERT_TRUE(index.SelectPatientToRecycle(p, patients[1])); ASSERT_EQ(p, patients[4]); - ASSERT_TRUE(index.SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[1]); - index.DeleteResource(p); - ASSERT_TRUE(index.SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[4]); - index.DeleteResource(p); - ASSERT_FALSE(index.SelectPatientToRecycle(p, patients[2])); - ASSERT_TRUE(index.SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[2]); - index.DeleteResource(p); - // "patients[3]" is still protected - ASSERT_FALSE(index.SelectPatientToRecycle(p)); - - ASSERT_EQ(4u, listener.deletedFiles_.size()); - ASSERT_EQ(1u, index.GetTableRecordCount("Resources")); - ASSERT_EQ(0u, index.GetTableRecordCount("PatientRecyclingOrder")); - - index.SetProtectedPatient(patients[3], false); - ASSERT_EQ(1u, index.GetTableRecordCount("PatientRecyclingOrder")); - ASSERT_FALSE(index.SelectPatientToRecycle(p, patients[3])); - ASSERT_TRUE(index.SelectPatientToRecycle(p, patients[2])); - ASSERT_TRUE(index.SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[3]); - index.DeleteResource(p); - - ASSERT_EQ(5u, listener.deletedFiles_.size()); - ASSERT_EQ(0u, index.GetTableRecordCount("Resources")); - ASSERT_EQ(0u, index.GetTableRecordCount("PatientRecyclingOrder")); -} - - - -TEST(DatabaseWrapper, Sequence) -{ - ServerIndexListener listener; - DatabaseWrapper index(listener); - - ASSERT_EQ(1u, index.IncrementGlobalSequence(GlobalProperty_AnonymizationSequence)); - ASSERT_EQ(2u, index.IncrementGlobalSequence(GlobalProperty_AnonymizationSequence)); - ASSERT_EQ(3u, index.IncrementGlobalSequence(GlobalProperty_AnonymizationSequence)); - ASSERT_EQ(4u, index.IncrementGlobalSequence(GlobalProperty_AnonymizationSequence)); -} - - - -TEST(DatabaseWrapper, LookupTagValue) -{ - ServerIndexListener listener; - DatabaseWrapper index(listener); - - int64_t a[] = { - index.CreateResource("a", ResourceType_Study), // 0 - index.CreateResource("b", ResourceType_Study), // 1 - index.CreateResource("c", ResourceType_Study), // 2 - index.CreateResource("d", ResourceType_Series) // 3 - }; - - DicomMap m; - m.Clear(); m.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, "0"); index.SetMainDicomTags(a[0], m); - m.Clear(); m.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, "1"); index.SetMainDicomTags(a[1], m); - m.Clear(); m.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, "0"); index.SetMainDicomTags(a[2], m); - m.Clear(); m.SetValue(DICOM_TAG_SERIES_INSTANCE_UID, "0"); index.SetMainDicomTags(a[3], m); - - std::list s; - - index.LookupTagValue(s, DICOM_TAG_STUDY_INSTANCE_UID, "0"); - ASSERT_EQ(2u, s.size()); - ASSERT_TRUE(std::find(s.begin(), s.end(), a[0]) != s.end()); - ASSERT_TRUE(std::find(s.begin(), s.end(), a[2]) != s.end()); - - index.LookupTagValue(s, "0"); - ASSERT_EQ(3u, s.size()); - ASSERT_TRUE(std::find(s.begin(), s.end(), a[0]) != s.end()); - ASSERT_TRUE(std::find(s.begin(), s.end(), a[2]) != s.end()); - ASSERT_TRUE(std::find(s.begin(), s.end(), a[3]) != s.end()); - - index.LookupTagValue(s, DICOM_TAG_STUDY_INSTANCE_UID, "1"); - ASSERT_EQ(1u, s.size()); - ASSERT_TRUE(std::find(s.begin(), s.end(), a[1]) != s.end()); - - index.LookupTagValue(s, "1"); - ASSERT_EQ(1u, s.size()); - ASSERT_TRUE(std::find(s.begin(), s.end(), a[1]) != s.end()); - - - /*{ - std::list s; - context.GetIndex().LookupTagValue(s, DICOM_TAG_STUDY_INSTANCE_UID, "1.2.250.1.74.20130819132500.29000036381059"); - for (std::list::iterator i = s.begin(); i != s.end(); i++) - { - std::cout << "*** " << *i << std::endl;; - } - }*/ - - -} - - -TEST(DicomMap, MainTags) -{ - ASSERT_TRUE(DicomMap::IsMainDicomTag(DICOM_TAG_PATIENT_ID)); - ASSERT_TRUE(DicomMap::IsMainDicomTag(DICOM_TAG_PATIENT_ID, ResourceType_Patient)); - ASSERT_FALSE(DicomMap::IsMainDicomTag(DICOM_TAG_PATIENT_ID, ResourceType_Study)); -} diff -r fc6ad5b97219 -r 17815b9d4280 UnitTests/Versions.cpp --- a/UnitTests/Versions.cpp Fri Oct 25 14:52:26 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,94 +0,0 @@ -#include "gtest/gtest.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -TEST(Versions, Zlib) -{ - ASSERT_STREQ(zlibVersion(), ZLIB_VERSION); -} - -TEST(Versions, Curl) -{ - curl_version_info_data* v = curl_version_info(CURLVERSION_NOW); - ASSERT_STREQ(LIBCURL_VERSION, v->version); -} - -TEST(Versions, Png) -{ - ASSERT_EQ(PNG_LIBPNG_VER_MAJOR * 10000 + PNG_LIBPNG_VER_MINOR * 100 + PNG_LIBPNG_VER_RELEASE, - png_access_version_number()); -} - -TEST(Versions, SQLite) -{ - // http://www.sqlite.org/capi3ref.html#sqlite3_libversion - assert(sqlite3_libversion_number() == SQLITE_VERSION_NUMBER ); - assert(strcmp(sqlite3_sourceid(), SQLITE_SOURCE_ID) == 0); - assert(strcmp(sqlite3_libversion(), SQLITE_VERSION) == 0); - - // Ensure that the SQLite version is above 3.7.0. - // "sqlite3_create_function_v2" is not defined in previous versions. - ASSERT_GE(SQLITE_VERSION_NUMBER, 3007000); -} - - -TEST(Versions, Lua) -{ - // Ensure that the Lua version is above 5.1.0. This version has - // introduced some API changes. - ASSERT_GE(LUA_VERSION_NUM, 501); -} - - -#if ORTHANC_STATIC == 1 -TEST(Versions, ZlibStatic) -{ - ASSERT_STREQ("1.2.7", zlibVersion()); -} - -TEST(Versions, BoostStatic) -{ - ASSERT_STREQ("1_54", BOOST_LIB_VERSION); -} - -TEST(Versions, CurlStatic) -{ - curl_version_info_data* v = curl_version_info(CURLVERSION_NOW); - ASSERT_STREQ("7.26.0", v->version); -} - -TEST(Versions, PngStatic) -{ - ASSERT_EQ(10512, png_access_version_number()); - ASSERT_STREQ("1.5.12", PNG_LIBPNG_VER_STRING); -} - -TEST(Versions, CurlSslStatic) -{ - curl_version_info_data * vinfo = curl_version_info(CURLVERSION_NOW); - - // Check that SSL support is enabled when required - bool curlSupportsSsl = vinfo->features & CURL_VERSION_SSL; - -#if ORTHANC_SSL_ENABLED == 0 - ASSERT_FALSE(curlSupportsSsl); -#else - ASSERT_TRUE(curlSupportsSsl); -#endif -} - -TEST(Version, LuaStatic) -{ - ASSERT_STREQ("Lua 5.1.5", LUA_RELEASE); -} -#endif - diff -r fc6ad5b97219 -r 17815b9d4280 UnitTests/Zip.cpp --- a/UnitTests/Zip.cpp Fri Oct 25 14:52:26 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,122 +0,0 @@ -#include "gtest/gtest.h" - -#include "../Core/OrthancException.h" -#include "../Core/Compression/ZipWriter.h" -#include "../Core/Compression/HierarchicalZipWriter.h" -#include "../Core/Toolbox.h" - - -using namespace Orthanc; - -TEST(ZipWriter, Basic) -{ - Orthanc::ZipWriter w; - w.SetOutputPath("hello.zip"); - w.Open(); - w.OpenFile("world/hello"); - w.Write("Hello world"); -} - - -TEST(ZipWriter, Exceptions) -{ - Orthanc::ZipWriter w; - ASSERT_THROW(w.Open(), Orthanc::OrthancException); - w.SetOutputPath("hello.zip"); - w.Open(); - ASSERT_THROW(w.Write("hello world"), Orthanc::OrthancException); -} - - - - - -namespace Orthanc -{ - // The namespace is necessary - // http://code.google.com/p/googletest/wiki/AdvancedGuide#Private_Class_Members - - TEST(HierarchicalZipWriter, Index) - { - HierarchicalZipWriter::Index i; - ASSERT_EQ("hello", i.OpenFile("hello")); - ASSERT_EQ("hello-2", i.OpenFile("hello")); - ASSERT_EQ("coucou", i.OpenFile("coucou")); - ASSERT_EQ("hello-3", i.OpenFile("hello")); - - i.OpenDirectory("coucou"); - - ASSERT_EQ("coucou-2/world", i.OpenFile("world")); - ASSERT_EQ("coucou-2/world-2", i.OpenFile("world")); - - i.OpenDirectory("world"); - - ASSERT_EQ("coucou-2/world-3/hello", i.OpenFile("hello")); - ASSERT_EQ("coucou-2/world-3/hello-2", i.OpenFile("hello")); - - i.CloseDirectory(); - - ASSERT_EQ("coucou-2/world-4", i.OpenFile("world")); - - i.CloseDirectory(); - - ASSERT_EQ("coucou-3", i.OpenFile("coucou")); - - ASSERT_THROW(i.CloseDirectory(), OrthancException); - } - - - TEST(HierarchicalZipWriter, Filenames) - { - ASSERT_EQ("trE hell", HierarchicalZipWriter::Index::KeepAlphanumeric(" ÊtrE hellô ")); - - // The "^" character is considered as a space in DICOM - ASSERT_EQ("Hel lo world", HierarchicalZipWriter::Index::KeepAlphanumeric(" Hel^^ ^\r\n\t^^lo \t ")); - } -} - - -TEST(HierarchicalZipWriter, Basic) -{ - static const std::string SPACES = " "; - - HierarchicalZipWriter w("hello2.zip"); - - w.SetCompressionLevel(0); - - // Inside "/" - w.OpenFile("hello"); - w.Write(SPACES + "hello\n"); - w.OpenFile("hello"); - w.Write(SPACES + "hello-2\n"); - w.OpenDirectory("hello"); - - // Inside "/hello-3" - w.OpenFile("hello"); - w.Write(SPACES + "hello\n"); - w.OpenDirectory("hello"); - - w.SetCompressionLevel(9); - - // Inside "/hello-3/hello-2" - w.OpenFile("hello"); - w.Write(SPACES + "hello\n"); - w.OpenFile("hello"); - w.Write(SPACES + "hello-2\n"); - w.CloseDirectory(); - - // Inside "/hello-3" - w.OpenFile("hello"); - w.Write(SPACES + "hello-3\n"); - - /** - - TO CHECK THE CONTENT OF THE "hello2.zip" FILE: - - # unzip -v hello2.zip - - => There must be 6 files. The first 3 files must have a negative - compression ratio. - - **/ -} diff -r fc6ad5b97219 -r 17815b9d4280 UnitTests/main.cpp --- a/UnitTests/main.cpp Fri Oct 25 14:52:26 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,543 +0,0 @@ -#include "../Core/EnumerationDictionary.h" - -#include "gtest/gtest.h" - -#include - -#include "../Core/Compression/ZlibCompressor.h" -#include "../Core/DicomFormat/DicomTag.h" -#include "../Core/HttpServer/HttpHandler.h" -#include "../Core/OrthancException.h" -#include "../Core/Toolbox.h" -#include "../Core/Uuid.h" -#include "../OrthancServer/FromDcmtkBridge.h" -#include "../OrthancServer/OrthancInitialization.h" -#include "../Core/MultiThreading/SharedMessageQueue.h" - -using namespace Orthanc; - - -TEST(Uuid, Generation) -{ - for (int i = 0; i < 10; i++) - { - std::string s = Toolbox::GenerateUuid(); - ASSERT_TRUE(Toolbox::IsUuid(s)); - } -} - -TEST(Uuid, Test) -{ - ASSERT_FALSE(Toolbox::IsUuid("")); - ASSERT_FALSE(Toolbox::IsUuid("012345678901234567890123456789012345")); - ASSERT_TRUE(Toolbox::IsUuid("550e8400-e29b-41d4-a716-446655440000")); - ASSERT_FALSE(Toolbox::StartsWithUuid("550e8400-e29b-41d4-a716-44665544000")); - ASSERT_TRUE(Toolbox::StartsWithUuid("550e8400-e29b-41d4-a716-446655440000")); - ASSERT_TRUE(Toolbox::StartsWithUuid("550e8400-e29b-41d4-a716-446655440000 ok")); - ASSERT_FALSE(Toolbox::StartsWithUuid("550e8400-e29b-41d4-a716-446655440000ok")); -} - -TEST(Toolbox, IsSHA1) -{ - ASSERT_FALSE(Toolbox::IsSHA1("")); - ASSERT_FALSE(Toolbox::IsSHA1("01234567890123456789012345678901234567890123")); - ASSERT_FALSE(Toolbox::IsSHA1("012345678901234567890123456789012345678901234")); - ASSERT_TRUE(Toolbox::IsSHA1("b5ed549f-956400ce-69a8c063-bf5b78be-2732a4b9")); - - std::string s; - Toolbox::ComputeSHA1(s, "The quick brown fox jumps over the lazy dog"); - ASSERT_TRUE(Toolbox::IsSHA1(s)); - ASSERT_EQ("2fd4e1c6-7a2d28fc-ed849ee1-bb76e739-1b93eb12", s); -} - -TEST(Zlib, Basic) -{ - std::string s = Toolbox::GenerateUuid(); - s = s + s + s + s; - - std::string compressed; - ZlibCompressor c; - c.Compress(compressed, s); - - std::string uncompressed; - c.Uncompress(uncompressed, compressed); - - ASSERT_EQ(s.size(), uncompressed.size()); - ASSERT_EQ(0, memcmp(&s[0], &uncompressed[0], s.size())); -} - -TEST(Zlib, Empty) -{ - std::string s = ""; - - std::string compressed; - ZlibCompressor c; - c.Compress(compressed, s); - - std::string uncompressed; - c.Uncompress(uncompressed, compressed); - - ASSERT_EQ(0u, uncompressed.size()); -} - -TEST(ParseGetQuery, Basic) -{ - HttpHandler::Arguments a; - HttpHandler::ParseGetQuery(a, "aaa=baaa&bb=a&aa=c"); - ASSERT_EQ(3u, a.size()); - ASSERT_EQ(a["aaa"], "baaa"); - ASSERT_EQ(a["bb"], "a"); - ASSERT_EQ(a["aa"], "c"); -} - -TEST(ParseGetQuery, BasicEmpty) -{ - HttpHandler::Arguments a; - HttpHandler::ParseGetQuery(a, "aaa&bb=aa&aa"); - ASSERT_EQ(3u, a.size()); - ASSERT_EQ(a["aaa"], ""); - ASSERT_EQ(a["bb"], "aa"); - ASSERT_EQ(a["aa"], ""); -} - -TEST(ParseGetQuery, Single) -{ - HttpHandler::Arguments a; - HttpHandler::ParseGetQuery(a, "aaa=baaa"); - ASSERT_EQ(1u, a.size()); - ASSERT_EQ(a["aaa"], "baaa"); -} - -TEST(ParseGetQuery, SingleEmpty) -{ - HttpHandler::Arguments a; - HttpHandler::ParseGetQuery(a, "aaa"); - ASSERT_EQ(1u, a.size()); - ASSERT_EQ(a["aaa"], ""); -} - -TEST(DicomFormat, Tag) -{ - ASSERT_EQ("PatientName", FromDcmtkBridge::GetName(DicomTag(0x0010, 0x0010))); - - DicomTag t = FromDcmtkBridge::ParseTag("SeriesDescription"); - ASSERT_EQ(0x0008, t.GetGroup()); - ASSERT_EQ(0x103E, t.GetElement()); - - t = FromDcmtkBridge::ParseTag("0020-e040"); - ASSERT_EQ(0x0020, t.GetGroup()); - ASSERT_EQ(0xe040, t.GetElement()); - - // Test ==() and !=() operators - ASSERT_TRUE(DICOM_TAG_PATIENT_ID == DicomTag(0x0010, 0x0020)); - ASSERT_FALSE(DICOM_TAG_PATIENT_ID != DicomTag(0x0010, 0x0020)); -} - - -TEST(Uri, SplitUriComponents) -{ - UriComponents c; - Toolbox::SplitUriComponents(c, "/cou/hello/world"); - ASSERT_EQ(3u, c.size()); - ASSERT_EQ("cou", c[0]); - ASSERT_EQ("hello", c[1]); - ASSERT_EQ("world", c[2]); - - Toolbox::SplitUriComponents(c, "/cou/hello/world/"); - ASSERT_EQ(3u, c.size()); - ASSERT_EQ("cou", c[0]); - ASSERT_EQ("hello", c[1]); - ASSERT_EQ("world", c[2]); - - Toolbox::SplitUriComponents(c, "/cou/hello/world/a"); - ASSERT_EQ(4u, c.size()); - ASSERT_EQ("cou", c[0]); - ASSERT_EQ("hello", c[1]); - ASSERT_EQ("world", c[2]); - ASSERT_EQ("a", c[3]); - - Toolbox::SplitUriComponents(c, "/"); - ASSERT_EQ(0u, c.size()); - - Toolbox::SplitUriComponents(c, "/hello"); - ASSERT_EQ(1u, c.size()); - ASSERT_EQ("hello", c[0]); - - Toolbox::SplitUriComponents(c, "/hello/"); - ASSERT_EQ(1u, c.size()); - ASSERT_EQ("hello", c[0]); - - ASSERT_THROW(Toolbox::SplitUriComponents(c, ""), OrthancException); - ASSERT_THROW(Toolbox::SplitUriComponents(c, "a"), OrthancException); - ASSERT_THROW(Toolbox::SplitUriComponents(c, "/coucou//coucou"), OrthancException); -} - - -TEST(Uri, Child) -{ - UriComponents c1; Toolbox::SplitUriComponents(c1, "/hello/world"); - UriComponents c2; Toolbox::SplitUriComponents(c2, "/hello/hello"); - UriComponents c3; Toolbox::SplitUriComponents(c3, "/hello"); - UriComponents c4; Toolbox::SplitUriComponents(c4, "/world"); - UriComponents c5; Toolbox::SplitUriComponents(c5, "/"); - - ASSERT_TRUE(Toolbox::IsChildUri(c1, c1)); - ASSERT_FALSE(Toolbox::IsChildUri(c1, c2)); - ASSERT_FALSE(Toolbox::IsChildUri(c1, c3)); - ASSERT_FALSE(Toolbox::IsChildUri(c1, c4)); - ASSERT_FALSE(Toolbox::IsChildUri(c1, c5)); - - ASSERT_FALSE(Toolbox::IsChildUri(c2, c1)); - ASSERT_TRUE(Toolbox::IsChildUri(c2, c2)); - ASSERT_FALSE(Toolbox::IsChildUri(c2, c3)); - ASSERT_FALSE(Toolbox::IsChildUri(c2, c4)); - ASSERT_FALSE(Toolbox::IsChildUri(c2, c5)); - - ASSERT_TRUE(Toolbox::IsChildUri(c3, c1)); - ASSERT_TRUE(Toolbox::IsChildUri(c3, c2)); - ASSERT_TRUE(Toolbox::IsChildUri(c3, c3)); - ASSERT_FALSE(Toolbox::IsChildUri(c3, c4)); - ASSERT_FALSE(Toolbox::IsChildUri(c3, c5)); - - ASSERT_FALSE(Toolbox::IsChildUri(c4, c1)); - ASSERT_FALSE(Toolbox::IsChildUri(c4, c2)); - ASSERT_FALSE(Toolbox::IsChildUri(c4, c3)); - ASSERT_TRUE(Toolbox::IsChildUri(c4, c4)); - ASSERT_FALSE(Toolbox::IsChildUri(c4, c5)); - - ASSERT_TRUE(Toolbox::IsChildUri(c5, c1)); - ASSERT_TRUE(Toolbox::IsChildUri(c5, c2)); - ASSERT_TRUE(Toolbox::IsChildUri(c5, c3)); - ASSERT_TRUE(Toolbox::IsChildUri(c5, c4)); - ASSERT_TRUE(Toolbox::IsChildUri(c5, c5)); -} - -TEST(Uri, AutodetectMimeType) -{ - ASSERT_EQ("", Toolbox::AutodetectMimeType("../NOTES")); - ASSERT_EQ("", Toolbox::AutodetectMimeType("")); - ASSERT_EQ("", Toolbox::AutodetectMimeType("/")); - ASSERT_EQ("", Toolbox::AutodetectMimeType("a/a")); - - ASSERT_EQ("text/plain", Toolbox::AutodetectMimeType("../NOTES.txt")); - ASSERT_EQ("text/plain", Toolbox::AutodetectMimeType("../coucou.xml/NOTES.txt")); - ASSERT_EQ("text/xml", Toolbox::AutodetectMimeType("../.xml")); - - ASSERT_EQ("application/javascript", Toolbox::AutodetectMimeType("NOTES.js")); - ASSERT_EQ("application/json", Toolbox::AutodetectMimeType("NOTES.json")); - ASSERT_EQ("application/pdf", Toolbox::AutodetectMimeType("NOTES.pdf")); - ASSERT_EQ("text/css", Toolbox::AutodetectMimeType("NOTES.css")); - ASSERT_EQ("text/html", Toolbox::AutodetectMimeType("NOTES.html")); - ASSERT_EQ("text/plain", Toolbox::AutodetectMimeType("NOTES.txt")); - ASSERT_EQ("text/xml", Toolbox::AutodetectMimeType("NOTES.xml")); - ASSERT_EQ("image/gif", Toolbox::AutodetectMimeType("NOTES.gif")); - ASSERT_EQ("image/jpeg", Toolbox::AutodetectMimeType("NOTES.jpg")); - ASSERT_EQ("image/jpeg", Toolbox::AutodetectMimeType("NOTES.jpeg")); - ASSERT_EQ("image/png", Toolbox::AutodetectMimeType("NOTES.png")); -} - -TEST(Toolbox, ComputeMD5) -{ - std::string s; - - // # echo -n "Hello" | md5sum - - Toolbox::ComputeMD5(s, "Hello"); - ASSERT_EQ("8b1a9953c4611296a827abf8c47804d7", s); - Toolbox::ComputeMD5(s, ""); - ASSERT_EQ("d41d8cd98f00b204e9800998ecf8427e", s); -} - -TEST(Toolbox, ComputeSHA1) -{ - std::string s; - - Toolbox::ComputeSHA1(s, "The quick brown fox jumps over the lazy dog"); - ASSERT_EQ("2fd4e1c6-7a2d28fc-ed849ee1-bb76e739-1b93eb12", s); - Toolbox::ComputeSHA1(s, ""); - ASSERT_EQ("da39a3ee-5e6b4b0d-3255bfef-95601890-afd80709", s); -} - - -TEST(Toolbox, Base64) -{ - ASSERT_EQ("", Toolbox::EncodeBase64("")); - ASSERT_EQ("YQ==", Toolbox::EncodeBase64("a")); - ASSERT_EQ("SGVsbG8gd29ybGQ=", Toolbox::EncodeBase64("Hello world")); -} - -TEST(Toolbox, PathToExecutable) -{ - printf("[%s]\n", Toolbox::GetPathToExecutable().c_str()); - printf("[%s]\n", Toolbox::GetDirectoryOfExecutable().c_str()); -} - -TEST(Toolbox, StripSpaces) -{ - ASSERT_EQ("", Toolbox::StripSpaces(" \t \r \n ")); - ASSERT_EQ("coucou", Toolbox::StripSpaces(" coucou \t \r \n ")); - ASSERT_EQ("cou cou", Toolbox::StripSpaces(" cou cou \n ")); - ASSERT_EQ("c", Toolbox::StripSpaces(" \n\t c\r \n ")); -} - - -#include - -TEST(Logger, Basic) -{ - LOG(INFO) << "I say hello"; -} - -TEST(Toolbox, ConvertFromLatin1) -{ - // This is a Latin-1 test string - const unsigned char data[10] = { 0xe0, 0xe9, 0xea, 0xe7, 0x26, 0xc6, 0x61, 0x62, 0x63, 0x00 }; - - /*FILE* f = fopen("/tmp/tutu", "w"); - fwrite(&data[0], 9, 1, f); - fclose(f);*/ - - std::string s((char*) &data[0], 10); - ASSERT_EQ("&abc", Toolbox::ConvertToAscii(s)); - - // Open in Emacs, then save with UTF-8 encoding, then "hexdump -C" - std::string utf8 = Toolbox::ConvertToUtf8(s, "ISO-8859-1"); - ASSERT_EQ(15u, utf8.size()); - ASSERT_EQ(0xc3, static_cast(utf8[0])); - ASSERT_EQ(0xa0, static_cast(utf8[1])); - ASSERT_EQ(0xc3, static_cast(utf8[2])); - ASSERT_EQ(0xa9, static_cast(utf8[3])); - ASSERT_EQ(0xc3, static_cast(utf8[4])); - ASSERT_EQ(0xaa, static_cast(utf8[5])); - ASSERT_EQ(0xc3, static_cast(utf8[6])); - ASSERT_EQ(0xa7, static_cast(utf8[7])); - ASSERT_EQ(0x26, static_cast(utf8[8])); - ASSERT_EQ(0xc3, static_cast(utf8[9])); - ASSERT_EQ(0x86, static_cast(utf8[10])); - ASSERT_EQ(0x61, static_cast(utf8[11])); - ASSERT_EQ(0x62, static_cast(utf8[12])); - ASSERT_EQ(0x63, static_cast(utf8[13])); - ASSERT_EQ(0x00, static_cast(utf8[14])); // Null-terminated string -} - -TEST(Toolbox, UrlDecode) -{ - std::string s; - - s = "Hello%20World"; - Toolbox::UrlDecode(s); - ASSERT_EQ("Hello World", s); - - s = "%21%23%24%26%27%28%29%2A%2B%2c%2f%3A%3b%3d%3f%40%5B%5D%90%ff"; - Toolbox::UrlDecode(s); - std::string ss = "!#$&'()*+,/:;=?@[]"; - ss.push_back((char) 144); - ss.push_back((char) 255); - ASSERT_EQ(ss, s); - - s = "(2000%2C00A4)+Other"; - Toolbox::UrlDecode(s); - ASSERT_EQ("(2000,00A4) Other", s); -} - - -#if defined(__linux) -TEST(OrthancInitialization, AbsoluteDirectory) -{ - ASSERT_EQ("/tmp/hello", InterpretRelativePath("/tmp", "hello")); - ASSERT_EQ("/tmp", InterpretRelativePath("/tmp", "/tmp")); -} -#endif - - - -#include "../OrthancServer/ServerEnumerations.h" - -TEST(EnumerationDictionary, Simple) -{ - Toolbox::EnumerationDictionary d; - - ASSERT_THROW(d.Translate("ReceptionDate"), OrthancException); - ASSERT_EQ(MetadataType_ModifiedFrom, d.Translate("5")); - ASSERT_EQ(256, d.Translate("256")); - - d.Add(MetadataType_Instance_ReceptionDate, "ReceptionDate"); - - ASSERT_EQ(MetadataType_Instance_ReceptionDate, d.Translate("ReceptionDate")); - ASSERT_EQ(MetadataType_Instance_ReceptionDate, d.Translate("2")); - ASSERT_EQ("ReceptionDate", d.Translate(MetadataType_Instance_ReceptionDate)); - - ASSERT_THROW(d.Add(MetadataType_Instance_ReceptionDate, "Hello"), OrthancException); - ASSERT_THROW(d.Add(MetadataType_ModifiedFrom, "ReceptionDate"), OrthancException); // already used - ASSERT_THROW(d.Add(MetadataType_ModifiedFrom, "1024"), OrthancException); // cannot register numbers - d.Add(MetadataType_ModifiedFrom, "ModifiedFrom"); // ok -} - - -TEST(EnumerationDictionary, ServerEnumerations) -{ - ASSERT_STREQ("Patient", EnumerationToString(ResourceType_Patient)); - ASSERT_STREQ("Study", EnumerationToString(ResourceType_Study)); - ASSERT_STREQ("Series", EnumerationToString(ResourceType_Series)); - ASSERT_STREQ("Instance", EnumerationToString(ResourceType_Instance)); - - ASSERT_STREQ("ModifiedSeries", EnumerationToString(ChangeType_ModifiedSeries)); - - ASSERT_STREQ("Failure", EnumerationToString(StoreStatus_Failure)); - ASSERT_STREQ("Success", EnumerationToString(StoreStatus_Success)); - - ASSERT_STREQ("CompletedSeries", EnumerationToString(ChangeType_CompletedSeries)); - - ASSERT_EQ("IndexInSeries", EnumerationToString(MetadataType_Instance_IndexInSeries)); - ASSERT_EQ("LastUpdate", EnumerationToString(MetadataType_LastUpdate)); - - ASSERT_EQ(ResourceType_Patient, StringToResourceType("PATienT")); - ASSERT_EQ(ResourceType_Study, StringToResourceType("STudy")); - ASSERT_EQ(ResourceType_Series, StringToResourceType("SeRiEs")); - ASSERT_EQ(ResourceType_Instance, StringToResourceType("INStance")); - ASSERT_EQ(ResourceType_Instance, StringToResourceType("IMagE")); - ASSERT_THROW(StringToResourceType("heLLo"), OrthancException); - - ASSERT_EQ(2047, StringToMetadata("2047")); - ASSERT_THROW(StringToMetadata("Ceci est un test"), OrthancException); - ASSERT_THROW(RegisterUserMetadata(128, ""), OrthancException); // too low (< 1024) - ASSERT_THROW(RegisterUserMetadata(128000, ""), OrthancException); // too high (> 65535) - RegisterUserMetadata(2047, "Ceci est un test"); - ASSERT_EQ(2047, StringToMetadata("2047")); - ASSERT_EQ(2047, StringToMetadata("Ceci est un test")); -} - - - -class DynamicInteger : public IDynamicObject -{ -private: - int value_; - -public: - DynamicInteger(int value) : value_(value) - { - } - - int GetValue() const - { - return value_; - } -}; - - -TEST(SharedMessageQueue, Basic) -{ - SharedMessageQueue q; - ASSERT_TRUE(q.WaitEmpty(0)); - q.Enqueue(new DynamicInteger(10)); - ASSERT_FALSE(q.WaitEmpty(1)); - q.Enqueue(new DynamicInteger(20)); - q.Enqueue(new DynamicInteger(30)); - q.Enqueue(new DynamicInteger(40)); - - std::auto_ptr i; - i.reset(dynamic_cast(q.Dequeue(1))); ASSERT_EQ(10, i->GetValue()); - i.reset(dynamic_cast(q.Dequeue(1))); ASSERT_EQ(20, i->GetValue()); - i.reset(dynamic_cast(q.Dequeue(1))); ASSERT_EQ(30, i->GetValue()); - ASSERT_FALSE(q.WaitEmpty(1)); - i.reset(dynamic_cast(q.Dequeue(1))); ASSERT_EQ(40, i->GetValue()); - ASSERT_TRUE(q.WaitEmpty(0)); - ASSERT_EQ(NULL, q.Dequeue(1)); -} - - -TEST(SharedMessageQueue, Clean) -{ - try - { - SharedMessageQueue q; - q.Enqueue(new DynamicInteger(10)); - q.Enqueue(new DynamicInteger(20)); - throw OrthancException("Nope"); - } - catch (OrthancException&) - { - } -} - - -TEST(Toolbox, WriteFile) -{ - std::string path; - - { - Toolbox::TemporaryFile tmp; - path = tmp.GetPath(); - - std::string s; - s.append("Hello"); - s.push_back('\0'); - s.append("World"); - ASSERT_EQ(11u, s.size()); - - Toolbox::WriteFile(s, path.c_str()); - - std::string t; - Toolbox::ReadFile(t, path.c_str()); - - ASSERT_EQ(11u, t.size()); - ASSERT_EQ(0, t[5]); - ASSERT_EQ(0, memcmp(s.c_str(), t.c_str(), s.size())); - } - - std::string u; - ASSERT_THROW(Toolbox::ReadFile(u, path.c_str()), OrthancException); -} - - -TEST(Toolbox, Wildcard) -{ - ASSERT_EQ("abcd", Toolbox::WildcardToRegularExpression("abcd")); - ASSERT_EQ("ab.*cd", Toolbox::WildcardToRegularExpression("ab*cd")); - ASSERT_EQ("ab..cd", Toolbox::WildcardToRegularExpression("ab??cd")); - ASSERT_EQ("a.*b.c.*d", Toolbox::WildcardToRegularExpression("a*b?c*d")); - ASSERT_EQ("a\\{b\\]", Toolbox::WildcardToRegularExpression("a{b]")); -} - - -TEST(Toolbox, Tokenize) -{ - std::vector t; - - Toolbox::TokenizeString(t, "", ','); - ASSERT_EQ(1, t.size()); - ASSERT_EQ("", t[0]); - - Toolbox::TokenizeString(t, "abc", ','); - ASSERT_EQ(1, t.size()); - ASSERT_EQ("abc", t[0]); - - Toolbox::TokenizeString(t, "ab,cd,ef,", ','); - ASSERT_EQ(4, t.size()); - ASSERT_EQ("ab", t[0]); - ASSERT_EQ("cd", t[1]); - ASSERT_EQ("ef", t[2]); - ASSERT_EQ("", t[3]); -} - - -int main(int argc, char **argv) -{ - // Initialize Google's logging library. - FLAGS_logtostderr = true; - FLAGS_minloglevel = 0; - - // Go to trace-level verbosity - //FLAGS_v = 1; - - Toolbox::DetectEndianness(); - - google::InitGoogleLogging("Orthanc"); - - OrthancInitialize(); - ::testing::InitGoogleTest(&argc, argv); - int result = RUN_ALL_TESTS(); - OrthancFinalize(); - return result; -} diff -r fc6ad5b97219 -r 17815b9d4280 UnitTestsSources/FileStorage.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/UnitTestsSources/FileStorage.cpp Mon Oct 28 16:26:51 2013 +0100 @@ -0,0 +1,149 @@ +#include "gtest/gtest.h" + +#include +#include + +#include "../Core/FileStorage/FileStorage.h" +#include "../OrthancServer/ServerIndex.h" +#include "../Core/Toolbox.h" +#include "../Core/OrthancException.h" +#include "../Core/Uuid.h" +#include "../Core/HttpServer/FilesystemHttpSender.h" +#include "../Core/HttpServer/BufferHttpSender.h" +#include "../Core/FileStorage/FileStorageAccessor.h" +#include "../Core/FileStorage/CompressedFileStorageAccessor.h" + +using namespace Orthanc; + +TEST(FileStorage, Basic) +{ + FileStorage s("FileStorageUnitTests"); + + std::string data = Toolbox::GenerateUuid(); + std::string uid = s.Create(data); + std::string d; + s.ReadFile(d, uid); + ASSERT_EQ(d.size(), data.size()); + ASSERT_FALSE(memcmp(&d[0], &data[0], data.size())); +} + +TEST(FileStorage, EndToEnd) +{ + FileStorage s("FileStorageUnitTests"); + s.Clear(); + + std::list u; + for (unsigned int i = 0; i < 10; i++) + { + u.push_back(s.Create(Toolbox::GenerateUuid())); + } + + std::set ss; + s.ListAllFiles(ss); + ASSERT_EQ(10u, ss.size()); + + unsigned int c = 0; + for (std::list::iterator + i = u.begin(); i != u.end(); i++, c++) + { + ASSERT_TRUE(ss.find(*i) != ss.end()); + if (c < 5) + s.Remove(*i); + } + + s.ListAllFiles(ss); + ASSERT_EQ(5u, ss.size()); + + s.Clear(); + s.ListAllFiles(ss); + ASSERT_EQ(0u, ss.size()); +} + + +TEST(FileStorageAccessor, Simple) +{ + FileStorage s("FileStorageUnitTests"); + FileStorageAccessor accessor(s); + + std::string data = "Hello world"; + FileInfo info = accessor.Write(data, FileContentType_Dicom); + + std::string r; + accessor.Read(r, info.GetUuid()); + + ASSERT_EQ(data, r); + ASSERT_EQ(CompressionType_None, info.GetCompressionType()); + ASSERT_EQ(11u, info.GetUncompressedSize()); + ASSERT_EQ(11u, info.GetCompressedSize()); + ASSERT_EQ(FileContentType_Dicom, info.GetContentType()); +} + + +TEST(FileStorageAccessor, NoCompression) +{ + FileStorage s("FileStorageUnitTests"); + CompressedFileStorageAccessor accessor(s); + + accessor.SetCompressionForNextOperations(CompressionType_None); + std::string data = "Hello world"; + FileInfo info = accessor.Write(data, FileContentType_Dicom); + + std::string r; + accessor.Read(r, info.GetUuid()); + + ASSERT_EQ(data, r); + ASSERT_EQ(CompressionType_None, info.GetCompressionType()); + ASSERT_EQ(11u, info.GetUncompressedSize()); + ASSERT_EQ(11u, info.GetCompressedSize()); + ASSERT_EQ(FileContentType_Dicom, info.GetContentType()); +} + + +TEST(FileStorageAccessor, Compression) +{ + FileStorage s("FileStorageUnitTests"); + CompressedFileStorageAccessor accessor(s); + + accessor.SetCompressionForNextOperations(CompressionType_Zlib); + std::string data = "Hello world"; + FileInfo info = accessor.Write(data, FileContentType_Dicom); + + std::string r; + accessor.Read(r, info.GetUuid()); + + ASSERT_EQ(data, r); + ASSERT_EQ(CompressionType_Zlib, info.GetCompressionType()); + ASSERT_EQ(11u, info.GetUncompressedSize()); + ASSERT_EQ(FileContentType_Dicom, info.GetContentType()); +} + + +TEST(FileStorageAccessor, Mix) +{ + FileStorage s("FileStorageUnitTests"); + CompressedFileStorageAccessor accessor(s); + + std::string r; + std::string compressedData = "Hello"; + std::string uncompressedData = "HelloWorld"; + + accessor.SetCompressionForNextOperations(CompressionType_Zlib); + FileInfo compressedInfo = accessor.Write(compressedData, FileContentType_Dicom); + + accessor.SetCompressionForNextOperations(CompressionType_None); + FileInfo uncompressedInfo = accessor.Write(uncompressedData, FileContentType_Dicom); + + accessor.SetCompressionForNextOperations(CompressionType_Zlib); + accessor.Read(r, compressedInfo.GetUuid()); + ASSERT_EQ(compressedData, r); + + accessor.SetCompressionForNextOperations(CompressionType_None); + accessor.Read(r, compressedInfo.GetUuid()); + ASSERT_NE(compressedData, r); + + /* + // This test is too slow on Windows + accessor.SetCompressionForNextOperations(CompressionType_Zlib); + ASSERT_THROW(accessor.Read(r, uncompressedInfo.GetUuid()), OrthancException); + */ +} diff -r fc6ad5b97219 -r 17815b9d4280 UnitTestsSources/Lua.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/UnitTestsSources/Lua.cpp Mon Oct 28 16:26:51 2013 +0100 @@ -0,0 +1,72 @@ +#include "gtest/gtest.h" + +#include "../Core/Lua/LuaFunctionCall.h" + + +TEST(Lua, Simple) +{ + Orthanc::LuaContext lua; + lua.Execute(Orthanc::EmbeddedResources::LUA_TOOLBOX); + lua.Execute("a={}"); + lua.Execute("a['x'] = 10"); + lua.Execute("a['y'] = {}"); + lua.Execute("a['y'][1] = 20"); + lua.Execute("a['y'][2] = 20"); + lua.Execute("PrintRecursive(a)"); + + lua.Execute("function f(a) print(a.bool) return a.bool,20,30,40,50,60 end"); + + Json::Value v, vv, o; + //v["a"] = "b"; + v.append("hello"); + v.append("world"); + v.append("42"); + vv.append("sub"); + vv.append("set"); + v.append(vv); + o = Json::objectValue; + o["x"] = 10; + o["y"] = 20; + o["z"] = 20.5f; + v.append(o); + + { + Orthanc::LuaFunctionCall f(lua, "PrintRecursive"); + f.PushJSON(v); + f.Execute(); + } + + { + Orthanc::LuaFunctionCall f(lua, "f"); + f.PushJSON(o); + ASSERT_THROW(f.ExecutePredicate(), Orthanc::LuaException); + } + + o["bool"] = false; + + { + Orthanc::LuaFunctionCall f(lua, "f"); + f.PushJSON(o); + ASSERT_FALSE(f.ExecutePredicate()); + } + + o["bool"] = true; + + { + Orthanc::LuaFunctionCall f(lua, "f"); + f.PushJSON(o); + ASSERT_TRUE(f.ExecutePredicate()); + } +} + + +TEST(Lua, Existing) +{ + Orthanc::LuaContext lua; + lua.Execute("a={}"); + lua.Execute("function f() end"); + + ASSERT_TRUE(lua.IsExistingFunction("f")); + ASSERT_FALSE(lua.IsExistingFunction("a")); + ASSERT_FALSE(lua.IsExistingFunction("Dummy")); +} diff -r fc6ad5b97219 -r 17815b9d4280 UnitTestsSources/MemoryCache.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/UnitTestsSources/MemoryCache.cpp Mon Oct 28 16:26:51 2013 +0100 @@ -0,0 +1,197 @@ +#include "gtest/gtest.h" + +#include +#include +#include +#include +#include "../Core/IDynamicObject.h" +#include "../Core/Cache/MemoryCache.h" + + +TEST(LRU, Basic) +{ + Orthanc::LeastRecentlyUsedIndex r; + + r.Add("d"); + r.Add("a"); + r.Add("c"); + r.Add("b"); + + r.MakeMostRecent("a"); + r.MakeMostRecent("d"); + r.MakeMostRecent("b"); + r.MakeMostRecent("c"); + r.MakeMostRecent("d"); + r.MakeMostRecent("c"); + + ASSERT_EQ("a", r.GetOldest()); + ASSERT_EQ("a", r.RemoveOldest()); + ASSERT_EQ("b", r.GetOldest()); + ASSERT_EQ("b", r.RemoveOldest()); + ASSERT_EQ("d", r.GetOldest()); + ASSERT_EQ("d", r.RemoveOldest()); + ASSERT_EQ("c", r.GetOldest()); + ASSERT_EQ("c", r.RemoveOldest()); + + ASSERT_TRUE(r.IsEmpty()); + + ASSERT_THROW(r.GetOldest(), Orthanc::OrthancException); + ASSERT_THROW(r.RemoveOldest(), Orthanc::OrthancException); +} + + +TEST(LRU, Payload) +{ + Orthanc::LeastRecentlyUsedIndex r; + + r.Add("a", 420); + r.Add("b", 421); + r.Add("c", 422); + r.Add("d", 423); + + r.MakeMostRecent("a"); + r.MakeMostRecent("d"); + r.MakeMostRecent("b"); + r.MakeMostRecent("c"); + r.MakeMostRecent("d"); + r.MakeMostRecent("c"); + + ASSERT_TRUE(r.Contains("b")); + ASSERT_EQ(421, r.Invalidate("b")); + ASSERT_FALSE(r.Contains("b")); + + int p; + ASSERT_TRUE(r.Contains("a", p)); ASSERT_EQ(420, p); + ASSERT_TRUE(r.Contains("c", p)); ASSERT_EQ(422, p); + ASSERT_TRUE(r.Contains("d", p)); ASSERT_EQ(423, p); + + ASSERT_EQ("a", r.GetOldest()); + ASSERT_EQ(420, r.GetOldestPayload()); + ASSERT_EQ("a", r.RemoveOldest(p)); ASSERT_EQ(420, p); + + ASSERT_EQ("d", r.GetOldest()); + ASSERT_EQ(423, r.GetOldestPayload()); + ASSERT_EQ("d", r.RemoveOldest(p)); ASSERT_EQ(423, p); + + ASSERT_EQ("c", r.GetOldest()); + ASSERT_EQ(422, r.GetOldestPayload()); + ASSERT_EQ("c", r.RemoveOldest(p)); ASSERT_EQ(422, p); + + ASSERT_TRUE(r.IsEmpty()); +} + + +TEST(LRU, PayloadUpdate) +{ + Orthanc::LeastRecentlyUsedIndex r; + + r.Add("a", 420); + r.Add("b", 421); + r.Add("d", 423); + + r.MakeMostRecent("a", 424); + r.MakeMostRecent("d", 421); + + ASSERT_EQ("b", r.GetOldest()); + ASSERT_EQ(421, r.GetOldestPayload()); + r.RemoveOldest(); + + ASSERT_EQ("a", r.GetOldest()); + ASSERT_EQ(424, r.GetOldestPayload()); + r.RemoveOldest(); + + ASSERT_EQ("d", r.GetOldest()); + ASSERT_EQ(421, r.GetOldestPayload()); + r.RemoveOldest(); + + ASSERT_TRUE(r.IsEmpty()); +} + + + +TEST(LRU, PayloadUpdateBis) +{ + Orthanc::LeastRecentlyUsedIndex r; + + r.AddOrMakeMostRecent("a", 420); + r.AddOrMakeMostRecent("b", 421); + r.AddOrMakeMostRecent("d", 423); + r.AddOrMakeMostRecent("a", 424); + r.AddOrMakeMostRecent("d", 421); + + ASSERT_EQ("b", r.GetOldest()); + ASSERT_EQ(421, r.GetOldestPayload()); + r.RemoveOldest(); + + ASSERT_EQ("a", r.GetOldest()); + ASSERT_EQ(424, r.GetOldestPayload()); + r.RemoveOldest(); + + ASSERT_EQ("d", r.GetOldest()); + ASSERT_EQ(421, r.GetOldestPayload()); + r.RemoveOldest(); + + ASSERT_TRUE(r.IsEmpty()); +} + + + + +namespace +{ + class Integer : public Orthanc::IDynamicObject + { + private: + std::string& log_; + int value_; + + public: + Integer(std::string& log, int v) : log_(log), value_(v) + { + } + + virtual ~Integer() + { + LOG(INFO) << "Removing cache entry for " << value_; + log_ += boost::lexical_cast(value_) + " "; + } + + int GetValue() const + { + return value_; + } + }; + + class IntegerProvider : public Orthanc::ICachePageProvider + { + public: + std::string log_; + + Orthanc::IDynamicObject* Provide(const std::string& s) + { + LOG(INFO) << "Providing " << s; + return new Integer(log_, boost::lexical_cast(s)); + } + }; +} + + +TEST(MemoryCache, Basic) +{ + IntegerProvider provider; + + { + Orthanc::MemoryCache cache(provider, 3); + cache.Access("42"); // 42 -> exit + cache.Access("43"); // 43, 42 -> exit + cache.Access("45"); // 45, 43, 42 -> exit + cache.Access("42"); // 42, 45, 43 -> exit + cache.Access("43"); // 43, 42, 45 -> exit + cache.Access("47"); // 45 is removed; 47, 43, 42 -> exit + cache.Access("44"); // 42 is removed; 44, 47, 43 -> exit + cache.Access("42"); // 43 is removed; 42, 44, 47 -> exit + // Closing the cache: 47, 44, 42 are successively removed + } + + ASSERT_EQ("45 42 43 47 44 42 ", provider.log_); +} diff -r fc6ad5b97219 -r 17815b9d4280 UnitTestsSources/Png.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/UnitTestsSources/Png.cpp Mon Oct 28 16:26:51 2013 +0100 @@ -0,0 +1,109 @@ +#include "gtest/gtest.h" + +#include +#include "../Core/FileFormats/PngReader.h" +#include "../Core/FileFormats/PngWriter.h" + +TEST(PngWriter, ColorPattern) +{ + Orthanc::PngWriter w; + int width = 17; + int height = 61; + int pitch = width * 3; + + std::vector image(height * pitch); + for (int y = 0; y < height; y++) + { + uint8_t *p = &image[0] + y * pitch; + for (int x = 0; x < width; x++, p += 3) + { + p[0] = (y % 3 == 0) ? 255 : 0; + p[1] = (y % 3 == 1) ? 255 : 0; + p[2] = (y % 3 == 2) ? 255 : 0; + } + } + + w.WriteToFile("ColorPattern.png", width, height, pitch, Orthanc::PixelFormat_RGB24, &image[0]); +} + +TEST(PngWriter, Gray8Pattern) +{ + Orthanc::PngWriter w; + int width = 17; + int height = 256; + int pitch = width; + + std::vector image(height * pitch); + for (int y = 0; y < height; y++) + { + uint8_t *p = &image[0] + y * pitch; + for (int x = 0; x < width; x++, p++) + { + *p = y; + } + } + + w.WriteToFile("Gray8Pattern.png", width, height, pitch, Orthanc::PixelFormat_Grayscale8, &image[0]); +} + +TEST(PngWriter, Gray16Pattern) +{ + Orthanc::PngWriter w; + int width = 256; + int height = 256; + int pitch = width * 2 + 16; + + std::vector image(height * pitch); + + int v = 0; + for (int y = 0; y < height; y++) + { + uint16_t *p = reinterpret_cast(&image[0] + y * pitch); + for (int x = 0; x < width; x++, p++, v++) + { + *p = v; + } + } + + w.WriteToFile("Gray16Pattern.png", width, height, pitch, Orthanc::PixelFormat_Grayscale16, &image[0]); +} + +TEST(PngWriter, EndToEnd) +{ + Orthanc::PngWriter w; + int width = 256; + int height = 256; + int pitch = width * 2 + 16; + + std::vector image(height * pitch); + + int v = 0; + for (int y = 0; y < height; y++) + { + uint16_t *p = reinterpret_cast(&image[0] + y * pitch); + for (int x = 0; x < width; x++, p++, v++) + { + *p = v; + } + } + + std::string s; + w.WriteToMemory(s, width, height, pitch, Orthanc::PixelFormat_Grayscale16, &image[0]); + + Orthanc::PngReader r; + r.ReadFromMemory(s); + + ASSERT_EQ(r.GetWidth(), width); + ASSERT_EQ(r.GetHeight(), height); + + v = 0; + for (int y = 0; y < height; y++) + { + uint16_t *p = reinterpret_cast((uint8_t*) r.GetBuffer() + y * r.GetPitch()); + for (int x = 0; x < width; x++, p++, v++) + { + ASSERT_EQ(*p, v); + } + } + +} diff -r fc6ad5b97219 -r 17815b9d4280 UnitTestsSources/RestApi.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/UnitTestsSources/RestApi.cpp Mon Oct 28 16:26:51 2013 +0100 @@ -0,0 +1,81 @@ +#include "gtest/gtest.h" + +#include +#include + +#include "../Core/RestApi/RestApi.h" +#include "../Core/Uuid.h" +#include "../Core/OrthancException.h" +#include "../Core/Compression/ZlibCompressor.h" + +using namespace Orthanc; + +TEST(RestApi, ParseCookies) +{ + HttpHandler::Arguments headers; + HttpHandler::Arguments cookies; + + headers["cookie"] = "a=b;c=d;;;e=f;;g=h;"; + HttpHandler::ParseCookies(cookies, headers); + ASSERT_EQ(4u, cookies.size()); + ASSERT_EQ("b", cookies["a"]); + ASSERT_EQ("d", cookies["c"]); + ASSERT_EQ("f", cookies["e"]); + ASSERT_EQ("h", cookies["g"]); + + headers["cookie"] = " name = value ; name2=value2"; + HttpHandler::ParseCookies(cookies, headers); + ASSERT_EQ(2u, cookies.size()); + ASSERT_EQ("value", cookies["name"]); + ASSERT_EQ("value2", cookies["name2"]); + + headers["cookie"] = " ;;; "; + HttpHandler::ParseCookies(cookies, headers); + ASSERT_EQ(0u, cookies.size()); + + headers["cookie"] = " ; n=v ;; "; + HttpHandler::ParseCookies(cookies, headers); + ASSERT_EQ(1u, cookies.size()); + ASSERT_EQ("v", cookies["n"]); +} + +TEST(RestApi, RestApiPath) +{ + RestApiPath::Components args; + UriComponents trail; + + { + RestApiPath uri("/coucou/{abc}/d/*"); + ASSERT_TRUE(uri.Match(args, trail, "/coucou/moi/d/e/f/g")); + ASSERT_EQ(1u, args.size()); + ASSERT_EQ(3u, trail.size()); + ASSERT_EQ("moi", args["abc"]); + ASSERT_EQ("e", trail[0]); + ASSERT_EQ("f", trail[1]); + ASSERT_EQ("g", trail[2]); + + ASSERT_FALSE(uri.Match(args, trail, "/coucou/moi/f")); + ASSERT_TRUE(uri.Match(args, trail, "/coucou/moi/d/")); + ASSERT_FALSE(uri.Match(args, trail, "/a/moi/d")); + ASSERT_FALSE(uri.Match(args, trail, "/coucou/moi")); + } + + { + RestApiPath uri("/coucou/{abc}/d"); + ASSERT_FALSE(uri.Match(args, trail, "/coucou/moi/d/e/f/g")); + ASSERT_TRUE(uri.Match(args, trail, "/coucou/moi/d")); + ASSERT_EQ(1u, args.size()); + ASSERT_EQ(0u, trail.size()); + ASSERT_EQ("moi", args["abc"]); + } + + { + RestApiPath uri("/*"); + ASSERT_TRUE(uri.Match(args, trail, "/a/b/c")); + ASSERT_EQ(0u, args.size()); + ASSERT_EQ(3u, trail.size()); + ASSERT_EQ("a", trail[0]); + ASSERT_EQ("b", trail[1]); + ASSERT_EQ("c", trail[2]); + } +} diff -r fc6ad5b97219 -r 17815b9d4280 UnitTestsSources/SQLite.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/UnitTestsSources/SQLite.cpp Mon Oct 28 16:26:51 2013 +0100 @@ -0,0 +1,238 @@ +#include "gtest/gtest.h" + +#include "../Core/Toolbox.h" +#include "../Core/SQLite/Connection.h" +#include "../Core/SQLite/Statement.h" +#include "../Core/SQLite/Transaction.h" + +#include + +using namespace Orthanc; + + +TEST(SQLite, Configuration) +{ + ASSERT_EQ(1, sqlite3_threadsafe()); +} + + +TEST(SQLite, Connection) +{ + Toolbox::RemoveFile("coucou"); + SQLite::Connection c; + c.Open("coucou"); + c.Execute("CREATE TABLE c(k INTEGER PRIMARY KEY AUTOINCREMENT, v INTEGER)"); + c.Execute("INSERT INTO c VALUES(NULL, 42);"); +} + + +TEST(SQLite, StatementReferenceBasic) +{ + sqlite3* db; + sqlite3_open(":memory:", &db); + + { + SQLite::StatementReference r(db, "SELECT * FROM sqlite_master"); + ASSERT_EQ(0u, r.GetReferenceCount()); + + { + SQLite::StatementReference r1(r); + ASSERT_EQ(1u, r.GetReferenceCount()); + ASSERT_EQ(0u, r1.GetReferenceCount()); + + { + SQLite::StatementReference r2(r); + ASSERT_EQ(2u, r.GetReferenceCount()); + ASSERT_EQ(0u, r1.GetReferenceCount()); + ASSERT_EQ(0u, r2.GetReferenceCount()); + + SQLite::StatementReference r3(r2); + ASSERT_EQ(3u, r.GetReferenceCount()); + ASSERT_EQ(0u, r1.GetReferenceCount()); + ASSERT_EQ(0u, r2.GetReferenceCount()); + ASSERT_EQ(0u, r3.GetReferenceCount()); + } + + ASSERT_EQ(1u, r.GetReferenceCount()); + ASSERT_EQ(0u, r1.GetReferenceCount()); + + { + SQLite::StatementReference r2(r); + ASSERT_EQ(2u, r.GetReferenceCount()); + ASSERT_EQ(0u, r1.GetReferenceCount()); + ASSERT_EQ(0u, r2.GetReferenceCount()); + } + + ASSERT_EQ(1u, r.GetReferenceCount()); + ASSERT_EQ(0u, r1.GetReferenceCount()); + } + + ASSERT_EQ(0u, r.GetReferenceCount()); + } + + sqlite3_close(db); +} + +TEST(SQLite, StatementBasic) +{ + SQLite::Connection c; + c.OpenInMemory(); + + SQLite::Statement s(c, "SELECT * from sqlite_master"); + s.Run(); + + for (unsigned int i = 0; i < 5; i++) + { + SQLite::Statement cs(c, SQLITE_FROM_HERE, "SELECT * from sqlite_master"); + cs.Step(); + } +} + + +namespace +{ + static bool destroyed; + + class MyFunc : public SQLite::IScalarFunction + { + public: + MyFunc() + { + destroyed = false; + } + + virtual ~MyFunc() + { + destroyed = true; + } + + virtual const char* GetName() const + { + return "MYFUNC"; + } + + virtual unsigned int GetCardinality() const + { + return 2; + } + + virtual void Compute(SQLite::FunctionContext& context) + { + context.SetIntResult(1000 + context.GetIntValue(0) * context.GetIntValue(1)); + } + }; + + class MyDelete : public SQLite::IScalarFunction + { + public: + std::set deleted_; + + virtual const char* GetName() const + { + return "MYDELETE"; + } + + virtual unsigned int GetCardinality() const + { + return 1; + } + + virtual void Compute(SQLite::FunctionContext& context) + { + deleted_.insert(context.GetIntValue(0)); + context.SetNullResult(); + } + }; +} + +TEST(SQLite, ScalarFunction) +{ + { + SQLite::Connection c; + c.OpenInMemory(); + c.Register(new MyFunc()); + c.Execute("CREATE TABLE t(id INTEGER PRIMARY KEY, v1 INTEGER, v2 INTEGER);"); + c.Execute("INSERT INTO t VALUES(NULL, 2, 3);"); + c.Execute("INSERT INTO t VALUES(NULL, 4, 4);"); + c.Execute("INSERT INTO t VALUES(NULL, 6, 5);"); + SQLite::Statement t(c, "SELECT MYFUNC(v1, v2), v1, v2 FROM t"); + int i = 0; + while (t.Step()) + { + ASSERT_EQ(t.ColumnInt(0), 1000 + t.ColumnInt(1) * t.ColumnInt(2)); + i++; + } + ASSERT_EQ(3, i); + ASSERT_FALSE(destroyed); + } + ASSERT_TRUE(destroyed); +} + +TEST(SQLite, CascadedDeleteCallback) +{ + SQLite::Connection c; + c.OpenInMemory(); + MyDelete *func = new MyDelete(); + c.Register(func); + c.Execute("CREATE TABLE parent(id INTEGER PRIMARY KEY, dummy INTEGER);"); + c.Execute("CREATE TABLE child(" + " id INTEGER PRIMARY KEY, " + " parent INTEGER REFERENCES parent(id) ON DELETE CASCADE, " + " value INTEGER);"); + c.Execute("CREATE TRIGGER childRemoved " + "AFTER DELETE ON child " + "FOR EACH ROW BEGIN " + " SELECT MYDELETE(old.value); " + "END;"); + + c.Execute("INSERT INTO parent VALUES(42, 100);"); + c.Execute("INSERT INTO parent VALUES(43, 101);"); + + c.Execute("INSERT INTO child VALUES(NULL, 42, 4200);"); + c.Execute("INSERT INTO child VALUES(NULL, 42, 4201);"); + + c.Execute("INSERT INTO child VALUES(NULL, 43, 4300);"); + c.Execute("INSERT INTO child VALUES(NULL, 43, 4301);"); + + // The following command deletes "parent(43, 101)", then in turns + // "child(NULL, 43, 4300/4301)", then calls the MyDelete on 4300 and + // 4301 + c.Execute("DELETE FROM parent WHERE dummy=101"); + + ASSERT_EQ(2u, func->deleted_.size()); + ASSERT_TRUE(func->deleted_.find(4300) != func->deleted_.end()); + ASSERT_TRUE(func->deleted_.find(4301) != func->deleted_.end()); +} + + +TEST(SQLite, EmptyTransactions) +{ + try + { + SQLite::Connection c; + c.OpenInMemory(); + + c.Execute("CREATE TABLE a(id INTEGER PRIMARY KEY);"); + c.Execute("INSERT INTO a VALUES(NULL)"); + + { + SQLite::Transaction t(c); + t.Begin(); + { + SQLite::Statement s(c, SQLITE_FROM_HERE, "SELECT * FROM a"); + s.Step(); + } + //t.Commit(); + } + + { + SQLite::Statement s(c, SQLITE_FROM_HERE, "SELECT * FROM a"); + s.Step(); + } + } + catch (OrthancException& e) + { + fprintf(stderr, "Exception: [%s]\n", e.What()); + throw e; + } +} diff -r fc6ad5b97219 -r 17815b9d4280 UnitTestsSources/SQLiteChromium.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/UnitTestsSources/SQLiteChromium.cpp Mon Oct 28 16:26:51 2013 +0100 @@ -0,0 +1,344 @@ +#include "gtest/gtest.h" + +#include "../Core/Toolbox.h" +#include "../Core/SQLite/Connection.h" +#include "../Core/SQLite/Statement.h" +#include "../Core/SQLite/Transaction.h" + +#include + + +using namespace Orthanc; +using namespace Orthanc::SQLite; + + +/******************************************************************** + ** Tests from + ** http://src.chromium.org/viewvc/chrome/trunk/src/sql/connection_unittest.cc + ********************************************************************/ + +class SQLConnectionTest : public testing::Test +{ +public: + SQLConnectionTest() + { + } + + virtual ~SQLConnectionTest() + { + } + + virtual void SetUp() + { + db_.OpenInMemory(); + } + + virtual void TearDown() + { + db_.Close(); + } + + Connection& db() + { + return db_; + } + +private: + Connection db_; +}; + + + +TEST_F(SQLConnectionTest, Execute) +{ + // Valid statement should return true. + ASSERT_TRUE(db().Execute("CREATE TABLE foo (a, b)")); + EXPECT_EQ(SQLITE_OK, db().GetErrorCode()); + + // Invalid statement should fail. + ASSERT_EQ(SQLITE_ERROR, + db().ExecuteAndReturnErrorCode("CREATE TAB foo (a, b")); + EXPECT_EQ(SQLITE_ERROR, db().GetErrorCode()); +} + +TEST_F(SQLConnectionTest, ExecuteWithErrorCode) { + ASSERT_EQ(SQLITE_OK, + db().ExecuteAndReturnErrorCode("CREATE TABLE foo (a, b)")); + ASSERT_EQ(SQLITE_ERROR, + db().ExecuteAndReturnErrorCode("CREATE TABLE TABLE")); + ASSERT_EQ(SQLITE_ERROR, + db().ExecuteAndReturnErrorCode( + "INSERT INTO foo(a, b) VALUES (1, 2, 3, 4)")); +} + +TEST_F(SQLConnectionTest, CachedStatement) { + StatementId id1("foo", 12); + ASSERT_TRUE(db().Execute("CREATE TABLE foo (a, b)")); + ASSERT_TRUE(db().Execute("INSERT INTO foo(a, b) VALUES (12, 13)")); + + // Create a new cached statement. + { + Statement s(db(), id1, "SELECT a FROM foo"); + ASSERT_TRUE(s.Step()); + EXPECT_EQ(12, s.ColumnInt(0)); + } + + // The statement should be cached still. + EXPECT_TRUE(db().HasCachedStatement(id1)); + + { + // Get the same statement using different SQL. This should ignore our + // SQL and use the cached one (so it will be valid). + Statement s(db(), id1, "something invalid("); + ASSERT_TRUE(s.Step()); + EXPECT_EQ(12, s.ColumnInt(0)); + } + + // Make sure other statements aren't marked as cached. + EXPECT_FALSE(db().HasCachedStatement(SQLITE_FROM_HERE)); +} + +TEST_F(SQLConnectionTest, IsSQLValidTest) { + ASSERT_TRUE(db().Execute("CREATE TABLE foo (a, b)")); + ASSERT_TRUE(db().IsSQLValid("SELECT a FROM foo")); + ASSERT_FALSE(db().IsSQLValid("SELECT no_exist FROM foo")); +} + + + +TEST_F(SQLConnectionTest, DoesStuffExist) { + // Test DoesTableExist. + EXPECT_FALSE(db().DoesTableExist("foo")); + ASSERT_TRUE(db().Execute("CREATE TABLE foo (a, b)")); + EXPECT_TRUE(db().DoesTableExist("foo")); + + // Should be case sensitive. + EXPECT_FALSE(db().DoesTableExist("FOO")); + + // Test DoesColumnExist. + EXPECT_FALSE(db().DoesColumnExist("foo", "bar")); + EXPECT_TRUE(db().DoesColumnExist("foo", "a")); + + // Testing for a column on a nonexistent table. + EXPECT_FALSE(db().DoesColumnExist("bar", "b")); +} + +TEST_F(SQLConnectionTest, GetLastInsertRowId) { + ASSERT_TRUE(db().Execute("CREATE TABLE foo (id INTEGER PRIMARY KEY, value)")); + + ASSERT_TRUE(db().Execute("INSERT INTO foo (value) VALUES (12)")); + + // Last insert row ID should be valid. + int64_t row = db().GetLastInsertRowId(); + EXPECT_LT(0, row); + + // It should be the primary key of the row we just inserted. + Statement s(db(), "SELECT value FROM foo WHERE id=?"); + s.BindInt64(0, row); + ASSERT_TRUE(s.Step()); + EXPECT_EQ(12, s.ColumnInt(0)); +} + +TEST_F(SQLConnectionTest, Rollback) { + ASSERT_TRUE(db().BeginTransaction()); + ASSERT_TRUE(db().BeginTransaction()); + EXPECT_EQ(2, db().GetTransactionNesting()); + db().RollbackTransaction(); + EXPECT_FALSE(db().CommitTransaction()); + EXPECT_TRUE(db().BeginTransaction()); +} + + + + +/******************************************************************** + ** Tests from + ** http://src.chromium.org/viewvc/chrome/trunk/src/sql/statement_unittest.cc + ********************************************************************/ + +namespace Orthanc +{ + namespace SQLite + { + class SQLStatementTest : public SQLConnectionTest + { + }; + + TEST_F(SQLStatementTest, Run) { + ASSERT_TRUE(db().Execute("CREATE TABLE foo (a, b)")); + ASSERT_TRUE(db().Execute("INSERT INTO foo (a, b) VALUES (3, 12)")); + + Statement s(db(), "SELECT b FROM foo WHERE a=?"); + // Stepping it won't work since we haven't bound the value. + EXPECT_FALSE(s.Step()); + + // Run should fail since this produces output, and we should use Step(). This + // gets a bit wonky since sqlite says this is OK so succeeded is set. + s.Reset(true); + s.BindInt(0, 3); + EXPECT_FALSE(s.Run()); + EXPECT_EQ(SQLITE_ROW, db().GetErrorCode()); + + // Resetting it should put it back to the previous state (not runnable). + s.Reset(true); + + // Binding and stepping should produce one row. + s.BindInt(0, 3); + EXPECT_TRUE(s.Step()); + EXPECT_EQ(12, s.ColumnInt(0)); + EXPECT_FALSE(s.Step()); + } + + TEST_F(SQLStatementTest, BasicErrorCallback) { + ASSERT_TRUE(db().Execute("CREATE TABLE foo (a INTEGER PRIMARY KEY, b)")); + // Insert in the foo table the primary key. It is an error to insert + // something other than an number. This error causes the error callback + // handler to be called with SQLITE_MISMATCH as error code. + Statement s(db(), "INSERT INTO foo (a) VALUES (?)"); + s.BindCString(0, "bad bad"); + EXPECT_THROW(s.Run(), OrthancException); + } + + TEST_F(SQLStatementTest, Reset) { + ASSERT_TRUE(db().Execute("CREATE TABLE foo (a, b)")); + ASSERT_TRUE(db().Execute("INSERT INTO foo (a, b) VALUES (3, 12)")); + ASSERT_TRUE(db().Execute("INSERT INTO foo (a, b) VALUES (4, 13)")); + + Statement s(db(), "SELECT b FROM foo WHERE a = ? "); + s.BindInt(0, 3); + ASSERT_TRUE(s.Step()); + EXPECT_EQ(12, s.ColumnInt(0)); + ASSERT_FALSE(s.Step()); + + s.Reset(false); + // Verify that we can get all rows again. + ASSERT_TRUE(s.Step()); + EXPECT_EQ(12, s.ColumnInt(0)); + EXPECT_FALSE(s.Step()); + + s.Reset(true); + ASSERT_FALSE(s.Step()); + } + } +} + + + + + + +/******************************************************************** + ** Tests from + ** http://src.chromium.org/viewvc/chrome/trunk/src/sql/transaction_unittest.cc + ********************************************************************/ + +class SQLTransactionTest : public SQLConnectionTest +{ +public: + virtual void SetUp() + { + SQLConnectionTest::SetUp(); + ASSERT_TRUE(db().Execute("CREATE TABLE foo (a, b)")); + } + + // Returns the number of rows in table "foo". + int CountFoo() + { + Statement count(db(), "SELECT count(*) FROM foo"); + count.Step(); + return count.ColumnInt(0); + } +}; + + +TEST_F(SQLTransactionTest, Commit) { + { + Transaction t(db()); + EXPECT_FALSE(t.IsOpen()); + t.Begin(); + EXPECT_TRUE(t.IsOpen()); + + EXPECT_TRUE(db().Execute("INSERT INTO foo (a, b) VALUES (1, 2)")); + + t.Commit(); + EXPECT_FALSE(t.IsOpen()); + } + + EXPECT_EQ(1, CountFoo()); +} + +TEST_F(SQLTransactionTest, Rollback) { + // Test some basic initialization, and that rollback runs when you exit the + // scope. + { + Transaction t(db()); + EXPECT_FALSE(t.IsOpen()); + t.Begin(); + EXPECT_TRUE(t.IsOpen()); + + EXPECT_TRUE(db().Execute("INSERT INTO foo (a, b) VALUES (1, 2)")); + } + + // Nothing should have been committed since it was implicitly rolled back. + EXPECT_EQ(0, CountFoo()); + + // Test explicit rollback. + Transaction t2(db()); + EXPECT_FALSE(t2.IsOpen()); + t2.Begin(); + + EXPECT_TRUE(db().Execute("INSERT INTO foo (a, b) VALUES (1, 2)")); + t2.Rollback(); + EXPECT_FALSE(t2.IsOpen()); + + // Nothing should have been committed since it was explicitly rolled back. + EXPECT_EQ(0, CountFoo()); +} + +// Rolling back any part of a transaction should roll back all of them. +TEST_F(SQLTransactionTest, NestedRollback) { + EXPECT_EQ(0, db().GetTransactionNesting()); + + // Outermost transaction. + { + Transaction outer(db()); + outer.Begin(); + EXPECT_EQ(1, db().GetTransactionNesting()); + + // The first inner one gets committed. + { + Transaction inner1(db()); + inner1.Begin(); + EXPECT_TRUE(db().Execute("INSERT INTO foo (a, b) VALUES (1, 2)")); + EXPECT_EQ(2, db().GetTransactionNesting()); + + inner1.Commit(); + EXPECT_EQ(1, db().GetTransactionNesting()); + } + + // One row should have gotten inserted. + EXPECT_EQ(1, CountFoo()); + + // The second inner one gets rolled back. + { + Transaction inner2(db()); + inner2.Begin(); + EXPECT_TRUE(db().Execute("INSERT INTO foo (a, b) VALUES (1, 2)")); + EXPECT_EQ(2, db().GetTransactionNesting()); + + inner2.Rollback(); + EXPECT_EQ(1, db().GetTransactionNesting()); + } + + // A third inner one will fail in Begin since one has already been rolled + // back. + EXPECT_EQ(1, db().GetTransactionNesting()); + { + Transaction inner3(db()); + EXPECT_THROW(inner3.Begin(), OrthancException); + EXPECT_EQ(1, db().GetTransactionNesting()); + } + } + EXPECT_EQ(0, db().GetTransactionNesting()); + EXPECT_EQ(0, CountFoo()); +} diff -r fc6ad5b97219 -r 17815b9d4280 UnitTestsSources/ServerIndex.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/UnitTestsSources/ServerIndex.cpp Mon Oct 28 16:26:51 2013 +0100 @@ -0,0 +1,497 @@ +#include "gtest/gtest.h" + +#include "../OrthancServer/DatabaseWrapper.h" +#include "../Core/Uuid.h" + +#include +#include +#include + +using namespace Orthanc; + +namespace +{ + class ServerIndexListener : public IServerIndexListener + { + public: + std::vector deletedFiles_; + std::string ancestorId_; + ResourceType ancestorType_; + + void Reset() + { + ancestorId_ = ""; + deletedFiles_.clear(); + } + + virtual void SignalRemainingAncestor(ResourceType type, + const std::string& publicId) + { + ancestorId_ = publicId; + ancestorType_ = type; + } + + virtual void SignalFileDeleted(const FileInfo& info) + { + const std::string fileUuid = info.GetUuid(); + deletedFiles_.push_back(fileUuid); + LOG(INFO) << "A file must be removed: " << fileUuid; + } + }; +} + + +TEST(DatabaseWrapper, Simple) +{ + ServerIndexListener listener; + DatabaseWrapper index(listener); + + int64_t a[] = { + index.CreateResource("a", ResourceType_Patient), // 0 + index.CreateResource("b", ResourceType_Study), // 1 + index.CreateResource("c", ResourceType_Series), // 2 + index.CreateResource("d", ResourceType_Instance), // 3 + index.CreateResource("e", ResourceType_Instance), // 4 + index.CreateResource("f", ResourceType_Instance), // 5 + index.CreateResource("g", ResourceType_Study) // 6 + }; + + ASSERT_EQ("a", index.GetPublicId(a[0])); + ASSERT_EQ("b", index.GetPublicId(a[1])); + ASSERT_EQ("c", index.GetPublicId(a[2])); + ASSERT_EQ("d", index.GetPublicId(a[3])); + ASSERT_EQ("e", index.GetPublicId(a[4])); + ASSERT_EQ("f", index.GetPublicId(a[5])); + ASSERT_EQ("g", index.GetPublicId(a[6])); + + ASSERT_EQ(ResourceType_Patient, index.GetResourceType(a[0])); + ASSERT_EQ(ResourceType_Study, index.GetResourceType(a[1])); + ASSERT_EQ(ResourceType_Series, index.GetResourceType(a[2])); + ASSERT_EQ(ResourceType_Instance, index.GetResourceType(a[3])); + ASSERT_EQ(ResourceType_Instance, index.GetResourceType(a[4])); + ASSERT_EQ(ResourceType_Instance, index.GetResourceType(a[5])); + ASSERT_EQ(ResourceType_Study, index.GetResourceType(a[6])); + + { + Json::Value t; + index.GetAllPublicIds(t, ResourceType_Patient); + + ASSERT_EQ(1u, t.size()); + ASSERT_EQ("a", t[0u].asString()); + + index.GetAllPublicIds(t, ResourceType_Series); + ASSERT_EQ(1u, t.size()); + ASSERT_EQ("c", t[0u].asString()); + + index.GetAllPublicIds(t, ResourceType_Study); + ASSERT_EQ(2u, t.size()); + + index.GetAllPublicIds(t, ResourceType_Instance); + ASSERT_EQ(3u, t.size()); + } + + index.SetGlobalProperty(GlobalProperty_FlushSleep, "World"); + + index.AttachChild(a[0], a[1]); + index.AttachChild(a[1], a[2]); + index.AttachChild(a[2], a[3]); + index.AttachChild(a[2], a[4]); + index.AttachChild(a[6], a[5]); + + int64_t parent; + ASSERT_FALSE(index.LookupParent(parent, a[0])); + ASSERT_TRUE(index.LookupParent(parent, a[1])); ASSERT_EQ(a[0], parent); + ASSERT_TRUE(index.LookupParent(parent, a[2])); ASSERT_EQ(a[1], parent); + ASSERT_TRUE(index.LookupParent(parent, a[3])); ASSERT_EQ(a[2], parent); + ASSERT_TRUE(index.LookupParent(parent, a[4])); ASSERT_EQ(a[2], parent); + ASSERT_TRUE(index.LookupParent(parent, a[5])); ASSERT_EQ(a[6], parent); + ASSERT_FALSE(index.LookupParent(parent, a[6])); + + std::string s; + + ASSERT_FALSE(index.GetParentPublicId(s, a[0])); + ASSERT_FALSE(index.GetParentPublicId(s, a[6])); + ASSERT_TRUE(index.GetParentPublicId(s, a[1])); ASSERT_EQ("a", s); + ASSERT_TRUE(index.GetParentPublicId(s, a[2])); ASSERT_EQ("b", s); + ASSERT_TRUE(index.GetParentPublicId(s, a[3])); ASSERT_EQ("c", s); + ASSERT_TRUE(index.GetParentPublicId(s, a[4])); ASSERT_EQ("c", s); + ASSERT_TRUE(index.GetParentPublicId(s, a[5])); ASSERT_EQ("g", s); + + std::list l; + index.GetChildrenPublicId(l, a[0]); ASSERT_EQ(1u, l.size()); ASSERT_EQ("b", l.front()); + index.GetChildrenPublicId(l, a[1]); ASSERT_EQ(1u, l.size()); ASSERT_EQ("c", l.front()); + index.GetChildrenPublicId(l, a[3]); ASSERT_EQ(0u, l.size()); + index.GetChildrenPublicId(l, a[4]); ASSERT_EQ(0u, l.size()); + index.GetChildrenPublicId(l, a[5]); ASSERT_EQ(0u, l.size()); + index.GetChildrenPublicId(l, a[6]); ASSERT_EQ(1u, l.size()); ASSERT_EQ("f", l.front()); + + index.GetChildrenPublicId(l, a[2]); ASSERT_EQ(2u, l.size()); + if (l.front() == "d") + { + ASSERT_EQ("e", l.back()); + } + else + { + ASSERT_EQ("d", l.back()); + ASSERT_EQ("e", l.front()); + } + + std::list md; + index.ListAvailableMetadata(md, a[4]); + ASSERT_EQ(0u, md.size()); + + index.AddAttachment(a[4], FileInfo("my json file", FileContentType_Json, 42, CompressionType_Zlib, 21)); + index.AddAttachment(a[4], FileInfo("my dicom file", FileContentType_Dicom, 42)); + index.AddAttachment(a[6], FileInfo("world", FileContentType_Dicom, 44)); + index.SetMetadata(a[4], MetadataType_Instance_RemoteAet, "PINNACLE"); + + index.ListAvailableMetadata(md, a[4]); + ASSERT_EQ(1u, md.size()); + ASSERT_EQ(MetadataType_Instance_RemoteAet, md.front()); + index.SetMetadata(a[4], MetadataType_ModifiedFrom, "TUTU"); + index.ListAvailableMetadata(md, a[4]); + ASSERT_EQ(2u, md.size()); + index.DeleteMetadata(a[4], MetadataType_ModifiedFrom); + index.ListAvailableMetadata(md, a[4]); + ASSERT_EQ(1u, md.size()); + ASSERT_EQ(MetadataType_Instance_RemoteAet, md.front()); + + ASSERT_EQ(21u + 42u + 44u, index.GetTotalCompressedSize()); + ASSERT_EQ(42u + 42u + 44u, index.GetTotalUncompressedSize()); + + DicomMap m; + m.SetValue(0x0010, 0x0010, "PatientName"); + index.SetMainDicomTags(a[3], m); + + int64_t b; + ResourceType t; + ASSERT_TRUE(index.LookupResource("g", b, t)); + ASSERT_EQ(7, b); + ASSERT_EQ(ResourceType_Study, t); + + ASSERT_TRUE(index.LookupMetadata(s, a[4], MetadataType_Instance_RemoteAet)); + ASSERT_FALSE(index.LookupMetadata(s, a[4], MetadataType_Instance_IndexInSeries)); + ASSERT_EQ("PINNACLE", s); + ASSERT_EQ("PINNACLE", index.GetMetadata(a[4], MetadataType_Instance_RemoteAet)); + ASSERT_EQ("None", index.GetMetadata(a[4], MetadataType_Instance_IndexInSeries, "None")); + + ASSERT_TRUE(index.LookupGlobalProperty(s, GlobalProperty_FlushSleep)); + ASSERT_FALSE(index.LookupGlobalProperty(s, static_cast(42))); + ASSERT_EQ("World", s); + ASSERT_EQ("World", index.GetGlobalProperty(GlobalProperty_FlushSleep)); + ASSERT_EQ("None", index.GetGlobalProperty(static_cast(42), "None")); + + FileInfo att; + ASSERT_TRUE(index.LookupAttachment(att, a[4], FileContentType_Json)); + ASSERT_EQ("my json file", att.GetUuid()); + ASSERT_EQ(21u, att.GetCompressedSize()); + ASSERT_EQ(42u, att.GetUncompressedSize()); + ASSERT_EQ(CompressionType_Zlib, att.GetCompressionType()); + + ASSERT_EQ(0u, listener.deletedFiles_.size()); + ASSERT_EQ(7u, index.GetTableRecordCount("Resources")); + ASSERT_EQ(3u, index.GetTableRecordCount("AttachedFiles")); + ASSERT_EQ(1u, index.GetTableRecordCount("Metadata")); + ASSERT_EQ(1u, index.GetTableRecordCount("MainDicomTags")); + index.DeleteResource(a[0]); + + ASSERT_EQ(2u, listener.deletedFiles_.size()); + ASSERT_FALSE(std::find(listener.deletedFiles_.begin(), + listener.deletedFiles_.end(), + "my json file") == listener.deletedFiles_.end()); + ASSERT_FALSE(std::find(listener.deletedFiles_.begin(), + listener.deletedFiles_.end(), + "my dicom file") == listener.deletedFiles_.end()); + + ASSERT_EQ(2u, index.GetTableRecordCount("Resources")); + ASSERT_EQ(0u, index.GetTableRecordCount("Metadata")); + ASSERT_EQ(1u, index.GetTableRecordCount("AttachedFiles")); + ASSERT_EQ(0u, index.GetTableRecordCount("MainDicomTags")); + index.DeleteResource(a[5]); + ASSERT_EQ(0u, index.GetTableRecordCount("Resources")); + ASSERT_EQ(0u, index.GetTableRecordCount("AttachedFiles")); + ASSERT_EQ(2u, index.GetTableRecordCount("GlobalProperties")); + + ASSERT_EQ(3u, listener.deletedFiles_.size()); + ASSERT_FALSE(std::find(listener.deletedFiles_.begin(), + listener.deletedFiles_.end(), + "world") == listener.deletedFiles_.end()); +} + + + + +TEST(DatabaseWrapper, Upward) +{ + ServerIndexListener listener; + DatabaseWrapper index(listener); + + int64_t a[] = { + index.CreateResource("a", ResourceType_Patient), // 0 + index.CreateResource("b", ResourceType_Study), // 1 + index.CreateResource("c", ResourceType_Series), // 2 + index.CreateResource("d", ResourceType_Instance), // 3 + index.CreateResource("e", ResourceType_Instance), // 4 + index.CreateResource("f", ResourceType_Study), // 5 + index.CreateResource("g", ResourceType_Series), // 6 + index.CreateResource("h", ResourceType_Series) // 7 + }; + + index.AttachChild(a[0], a[1]); + index.AttachChild(a[1], a[2]); + index.AttachChild(a[2], a[3]); + index.AttachChild(a[2], a[4]); + index.AttachChild(a[1], a[6]); + index.AttachChild(a[0], a[5]); + index.AttachChild(a[5], a[7]); + + { + Json::Value j; + index.GetChildren(j, a[0]); + ASSERT_EQ(2u, j.size()); + ASSERT_TRUE((j[0u] == "b" && j[1u] == "f") || + (j[1u] == "b" && j[0u] == "f")); + + index.GetChildren(j, a[1]); + ASSERT_EQ(2u, j.size()); + ASSERT_TRUE((j[0u] == "c" && j[1u] == "g") || + (j[1u] == "c" && j[0u] == "g")); + + index.GetChildren(j, a[2]); + ASSERT_EQ(2u, j.size()); + ASSERT_TRUE((j[0u] == "d" && j[1u] == "e") || + (j[1u] == "d" && j[0u] == "e")); + + index.GetChildren(j, a[3]); ASSERT_EQ(0u, j.size()); + index.GetChildren(j, a[4]); ASSERT_EQ(0u, j.size()); + index.GetChildren(j, a[5]); ASSERT_EQ(1u, j.size()); ASSERT_EQ("h", j[0u].asString()); + index.GetChildren(j, a[6]); ASSERT_EQ(0u, j.size()); + index.GetChildren(j, a[7]); ASSERT_EQ(0u, j.size()); + } + + listener.Reset(); + index.DeleteResource(a[3]); + ASSERT_EQ("c", listener.ancestorId_); + ASSERT_EQ(ResourceType_Series, listener.ancestorType_); + + listener.Reset(); + index.DeleteResource(a[4]); + ASSERT_EQ("b", listener.ancestorId_); + ASSERT_EQ(ResourceType_Study, listener.ancestorType_); + + listener.Reset(); + index.DeleteResource(a[7]); + ASSERT_EQ("a", listener.ancestorId_); + ASSERT_EQ(ResourceType_Patient, listener.ancestorType_); + + listener.Reset(); + index.DeleteResource(a[6]); + ASSERT_EQ("", listener.ancestorId_); // No more ancestor +} + + +TEST(DatabaseWrapper, PatientRecycling) +{ + ServerIndexListener listener; + DatabaseWrapper index(listener); + + std::vector patients; + for (int i = 0; i < 10; i++) + { + std::string p = "Patient " + boost::lexical_cast(i); + patients.push_back(index.CreateResource(p, ResourceType_Patient)); + index.AddAttachment(patients[i], FileInfo(p, FileContentType_Dicom, i + 10)); + ASSERT_FALSE(index.IsProtectedPatient(patients[i])); + } + + ASSERT_EQ(10u, index.GetTableRecordCount("Resources")); + ASSERT_EQ(10u, index.GetTableRecordCount("PatientRecyclingOrder")); + + listener.Reset(); + + index.DeleteResource(patients[5]); + index.DeleteResource(patients[0]); + ASSERT_EQ(8u, index.GetTableRecordCount("Resources")); + ASSERT_EQ(8u, index.GetTableRecordCount("PatientRecyclingOrder")); + + ASSERT_EQ(2u, listener.deletedFiles_.size()); + ASSERT_EQ("Patient 5", listener.deletedFiles_[0]); + ASSERT_EQ("Patient 0", listener.deletedFiles_[1]); + + int64_t p; + ASSERT_TRUE(index.SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[1]); + index.DeleteResource(p); + ASSERT_TRUE(index.SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[2]); + index.DeleteResource(p); + ASSERT_TRUE(index.SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[3]); + index.DeleteResource(p); + ASSERT_TRUE(index.SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[4]); + index.DeleteResource(p); + ASSERT_TRUE(index.SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[6]); + index.DeleteResource(p); + index.DeleteResource(patients[8]); + ASSERT_TRUE(index.SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[7]); + index.DeleteResource(p); + ASSERT_TRUE(index.SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[9]); + index.DeleteResource(p); + ASSERT_FALSE(index.SelectPatientToRecycle(p)); + + ASSERT_EQ(10u, listener.deletedFiles_.size()); + ASSERT_EQ(0u, index.GetTableRecordCount("Resources")); + ASSERT_EQ(0u, index.GetTableRecordCount("PatientRecyclingOrder")); +} + + +TEST(DatabaseWrapper, PatientProtection) +{ + ServerIndexListener listener; + DatabaseWrapper index(listener); + + std::vector patients; + for (int i = 0; i < 5; i++) + { + std::string p = "Patient " + boost::lexical_cast(i); + patients.push_back(index.CreateResource(p, ResourceType_Patient)); + index.AddAttachment(patients[i], FileInfo(p, FileContentType_Dicom, i + 10)); + ASSERT_FALSE(index.IsProtectedPatient(patients[i])); + } + + ASSERT_EQ(5u, index.GetTableRecordCount("Resources")); + ASSERT_EQ(5u, index.GetTableRecordCount("PatientRecyclingOrder")); + + ASSERT_FALSE(index.IsProtectedPatient(patients[2])); + index.SetProtectedPatient(patients[2], true); + ASSERT_TRUE(index.IsProtectedPatient(patients[2])); + ASSERT_EQ(4u, index.GetTableRecordCount("PatientRecyclingOrder")); + ASSERT_EQ(5u, index.GetTableRecordCount("Resources")); + + index.SetProtectedPatient(patients[2], true); + ASSERT_TRUE(index.IsProtectedPatient(patients[2])); + ASSERT_EQ(4u, index.GetTableRecordCount("PatientRecyclingOrder")); + index.SetProtectedPatient(patients[2], false); + ASSERT_FALSE(index.IsProtectedPatient(patients[2])); + ASSERT_EQ(5u, index.GetTableRecordCount("PatientRecyclingOrder")); + index.SetProtectedPatient(patients[2], false); + ASSERT_FALSE(index.IsProtectedPatient(patients[2])); + ASSERT_EQ(5u, index.GetTableRecordCount("PatientRecyclingOrder")); + + ASSERT_EQ(5u, index.GetTableRecordCount("Resources")); + ASSERT_EQ(5u, index.GetTableRecordCount("PatientRecyclingOrder")); + index.SetProtectedPatient(patients[2], true); + ASSERT_TRUE(index.IsProtectedPatient(patients[2])); + ASSERT_EQ(4u, index.GetTableRecordCount("PatientRecyclingOrder")); + index.SetProtectedPatient(patients[2], false); + ASSERT_FALSE(index.IsProtectedPatient(patients[2])); + ASSERT_EQ(5u, index.GetTableRecordCount("PatientRecyclingOrder")); + index.SetProtectedPatient(patients[3], true); + ASSERT_TRUE(index.IsProtectedPatient(patients[3])); + ASSERT_EQ(4u, index.GetTableRecordCount("PatientRecyclingOrder")); + + ASSERT_EQ(5u, index.GetTableRecordCount("Resources")); + ASSERT_EQ(0u, listener.deletedFiles_.size()); + + // Unprotecting a patient puts it at the last position in the recycling queue + int64_t p; + ASSERT_TRUE(index.SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[0]); + index.DeleteResource(p); + ASSERT_TRUE(index.SelectPatientToRecycle(p, patients[1])); ASSERT_EQ(p, patients[4]); + ASSERT_TRUE(index.SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[1]); + index.DeleteResource(p); + ASSERT_TRUE(index.SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[4]); + index.DeleteResource(p); + ASSERT_FALSE(index.SelectPatientToRecycle(p, patients[2])); + ASSERT_TRUE(index.SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[2]); + index.DeleteResource(p); + // "patients[3]" is still protected + ASSERT_FALSE(index.SelectPatientToRecycle(p)); + + ASSERT_EQ(4u, listener.deletedFiles_.size()); + ASSERT_EQ(1u, index.GetTableRecordCount("Resources")); + ASSERT_EQ(0u, index.GetTableRecordCount("PatientRecyclingOrder")); + + index.SetProtectedPatient(patients[3], false); + ASSERT_EQ(1u, index.GetTableRecordCount("PatientRecyclingOrder")); + ASSERT_FALSE(index.SelectPatientToRecycle(p, patients[3])); + ASSERT_TRUE(index.SelectPatientToRecycle(p, patients[2])); + ASSERT_TRUE(index.SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[3]); + index.DeleteResource(p); + + ASSERT_EQ(5u, listener.deletedFiles_.size()); + ASSERT_EQ(0u, index.GetTableRecordCount("Resources")); + ASSERT_EQ(0u, index.GetTableRecordCount("PatientRecyclingOrder")); +} + + + +TEST(DatabaseWrapper, Sequence) +{ + ServerIndexListener listener; + DatabaseWrapper index(listener); + + ASSERT_EQ(1u, index.IncrementGlobalSequence(GlobalProperty_AnonymizationSequence)); + ASSERT_EQ(2u, index.IncrementGlobalSequence(GlobalProperty_AnonymizationSequence)); + ASSERT_EQ(3u, index.IncrementGlobalSequence(GlobalProperty_AnonymizationSequence)); + ASSERT_EQ(4u, index.IncrementGlobalSequence(GlobalProperty_AnonymizationSequence)); +} + + + +TEST(DatabaseWrapper, LookupTagValue) +{ + ServerIndexListener listener; + DatabaseWrapper index(listener); + + int64_t a[] = { + index.CreateResource("a", ResourceType_Study), // 0 + index.CreateResource("b", ResourceType_Study), // 1 + index.CreateResource("c", ResourceType_Study), // 2 + index.CreateResource("d", ResourceType_Series) // 3 + }; + + DicomMap m; + m.Clear(); m.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, "0"); index.SetMainDicomTags(a[0], m); + m.Clear(); m.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, "1"); index.SetMainDicomTags(a[1], m); + m.Clear(); m.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, "0"); index.SetMainDicomTags(a[2], m); + m.Clear(); m.SetValue(DICOM_TAG_SERIES_INSTANCE_UID, "0"); index.SetMainDicomTags(a[3], m); + + std::list s; + + index.LookupTagValue(s, DICOM_TAG_STUDY_INSTANCE_UID, "0"); + ASSERT_EQ(2u, s.size()); + ASSERT_TRUE(std::find(s.begin(), s.end(), a[0]) != s.end()); + ASSERT_TRUE(std::find(s.begin(), s.end(), a[2]) != s.end()); + + index.LookupTagValue(s, "0"); + ASSERT_EQ(3u, s.size()); + ASSERT_TRUE(std::find(s.begin(), s.end(), a[0]) != s.end()); + ASSERT_TRUE(std::find(s.begin(), s.end(), a[2]) != s.end()); + ASSERT_TRUE(std::find(s.begin(), s.end(), a[3]) != s.end()); + + index.LookupTagValue(s, DICOM_TAG_STUDY_INSTANCE_UID, "1"); + ASSERT_EQ(1u, s.size()); + ASSERT_TRUE(std::find(s.begin(), s.end(), a[1]) != s.end()); + + index.LookupTagValue(s, "1"); + ASSERT_EQ(1u, s.size()); + ASSERT_TRUE(std::find(s.begin(), s.end(), a[1]) != s.end()); + + + /*{ + std::list s; + context.GetIndex().LookupTagValue(s, DICOM_TAG_STUDY_INSTANCE_UID, "1.2.250.1.74.20130819132500.29000036381059"); + for (std::list::iterator i = s.begin(); i != s.end(); i++) + { + std::cout << "*** " << *i << std::endl;; + } + }*/ + + +} + + +TEST(DicomMap, MainTags) +{ + ASSERT_TRUE(DicomMap::IsMainDicomTag(DICOM_TAG_PATIENT_ID)); + ASSERT_TRUE(DicomMap::IsMainDicomTag(DICOM_TAG_PATIENT_ID, ResourceType_Patient)); + ASSERT_FALSE(DicomMap::IsMainDicomTag(DICOM_TAG_PATIENT_ID, ResourceType_Study)); +} diff -r fc6ad5b97219 -r 17815b9d4280 UnitTestsSources/Versions.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/UnitTestsSources/Versions.cpp Mon Oct 28 16:26:51 2013 +0100 @@ -0,0 +1,94 @@ +#include "gtest/gtest.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +TEST(Versions, Zlib) +{ + ASSERT_STREQ(zlibVersion(), ZLIB_VERSION); +} + +TEST(Versions, Curl) +{ + curl_version_info_data* v = curl_version_info(CURLVERSION_NOW); + ASSERT_STREQ(LIBCURL_VERSION, v->version); +} + +TEST(Versions, Png) +{ + ASSERT_EQ(PNG_LIBPNG_VER_MAJOR * 10000 + PNG_LIBPNG_VER_MINOR * 100 + PNG_LIBPNG_VER_RELEASE, + png_access_version_number()); +} + +TEST(Versions, SQLite) +{ + // http://www.sqlite.org/capi3ref.html#sqlite3_libversion + assert(sqlite3_libversion_number() == SQLITE_VERSION_NUMBER ); + assert(strcmp(sqlite3_sourceid(), SQLITE_SOURCE_ID) == 0); + assert(strcmp(sqlite3_libversion(), SQLITE_VERSION) == 0); + + // Ensure that the SQLite version is above 3.7.0. + // "sqlite3_create_function_v2" is not defined in previous versions. + ASSERT_GE(SQLITE_VERSION_NUMBER, 3007000); +} + + +TEST(Versions, Lua) +{ + // Ensure that the Lua version is above 5.1.0. This version has + // introduced some API changes. + ASSERT_GE(LUA_VERSION_NUM, 501); +} + + +#if ORTHANC_STATIC == 1 +TEST(Versions, ZlibStatic) +{ + ASSERT_STREQ("1.2.7", zlibVersion()); +} + +TEST(Versions, BoostStatic) +{ + ASSERT_STREQ("1_54", BOOST_LIB_VERSION); +} + +TEST(Versions, CurlStatic) +{ + curl_version_info_data* v = curl_version_info(CURLVERSION_NOW); + ASSERT_STREQ("7.26.0", v->version); +} + +TEST(Versions, PngStatic) +{ + ASSERT_EQ(10512, png_access_version_number()); + ASSERT_STREQ("1.5.12", PNG_LIBPNG_VER_STRING); +} + +TEST(Versions, CurlSslStatic) +{ + curl_version_info_data * vinfo = curl_version_info(CURLVERSION_NOW); + + // Check that SSL support is enabled when required + bool curlSupportsSsl = vinfo->features & CURL_VERSION_SSL; + +#if ORTHANC_SSL_ENABLED == 0 + ASSERT_FALSE(curlSupportsSsl); +#else + ASSERT_TRUE(curlSupportsSsl); +#endif +} + +TEST(Version, LuaStatic) +{ + ASSERT_STREQ("Lua 5.1.5", LUA_RELEASE); +} +#endif + diff -r fc6ad5b97219 -r 17815b9d4280 UnitTestsSources/Zip.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/UnitTestsSources/Zip.cpp Mon Oct 28 16:26:51 2013 +0100 @@ -0,0 +1,122 @@ +#include "gtest/gtest.h" + +#include "../Core/OrthancException.h" +#include "../Core/Compression/ZipWriter.h" +#include "../Core/Compression/HierarchicalZipWriter.h" +#include "../Core/Toolbox.h" + + +using namespace Orthanc; + +TEST(ZipWriter, Basic) +{ + Orthanc::ZipWriter w; + w.SetOutputPath("hello.zip"); + w.Open(); + w.OpenFile("world/hello"); + w.Write("Hello world"); +} + + +TEST(ZipWriter, Exceptions) +{ + Orthanc::ZipWriter w; + ASSERT_THROW(w.Open(), Orthanc::OrthancException); + w.SetOutputPath("hello.zip"); + w.Open(); + ASSERT_THROW(w.Write("hello world"), Orthanc::OrthancException); +} + + + + + +namespace Orthanc +{ + // The namespace is necessary + // http://code.google.com/p/googletest/wiki/AdvancedGuide#Private_Class_Members + + TEST(HierarchicalZipWriter, Index) + { + HierarchicalZipWriter::Index i; + ASSERT_EQ("hello", i.OpenFile("hello")); + ASSERT_EQ("hello-2", i.OpenFile("hello")); + ASSERT_EQ("coucou", i.OpenFile("coucou")); + ASSERT_EQ("hello-3", i.OpenFile("hello")); + + i.OpenDirectory("coucou"); + + ASSERT_EQ("coucou-2/world", i.OpenFile("world")); + ASSERT_EQ("coucou-2/world-2", i.OpenFile("world")); + + i.OpenDirectory("world"); + + ASSERT_EQ("coucou-2/world-3/hello", i.OpenFile("hello")); + ASSERT_EQ("coucou-2/world-3/hello-2", i.OpenFile("hello")); + + i.CloseDirectory(); + + ASSERT_EQ("coucou-2/world-4", i.OpenFile("world")); + + i.CloseDirectory(); + + ASSERT_EQ("coucou-3", i.OpenFile("coucou")); + + ASSERT_THROW(i.CloseDirectory(), OrthancException); + } + + + TEST(HierarchicalZipWriter, Filenames) + { + ASSERT_EQ("trE hell", HierarchicalZipWriter::Index::KeepAlphanumeric(" ÊtrE hellô ")); + + // The "^" character is considered as a space in DICOM + ASSERT_EQ("Hel lo world", HierarchicalZipWriter::Index::KeepAlphanumeric(" Hel^^ ^\r\n\t^^lo \t ")); + } +} + + +TEST(HierarchicalZipWriter, Basic) +{ + static const std::string SPACES = " "; + + HierarchicalZipWriter w("hello2.zip"); + + w.SetCompressionLevel(0); + + // Inside "/" + w.OpenFile("hello"); + w.Write(SPACES + "hello\n"); + w.OpenFile("hello"); + w.Write(SPACES + "hello-2\n"); + w.OpenDirectory("hello"); + + // Inside "/hello-3" + w.OpenFile("hello"); + w.Write(SPACES + "hello\n"); + w.OpenDirectory("hello"); + + w.SetCompressionLevel(9); + + // Inside "/hello-3/hello-2" + w.OpenFile("hello"); + w.Write(SPACES + "hello\n"); + w.OpenFile("hello"); + w.Write(SPACES + "hello-2\n"); + w.CloseDirectory(); + + // Inside "/hello-3" + w.OpenFile("hello"); + w.Write(SPACES + "hello-3\n"); + + /** + + TO CHECK THE CONTENT OF THE "hello2.zip" FILE: + + # unzip -v hello2.zip + + => There must be 6 files. The first 3 files must have a negative + compression ratio. + + **/ +} diff -r fc6ad5b97219 -r 17815b9d4280 UnitTestsSources/main.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/UnitTestsSources/main.cpp Mon Oct 28 16:26:51 2013 +0100 @@ -0,0 +1,543 @@ +#include "../Core/EnumerationDictionary.h" + +#include "gtest/gtest.h" + +#include + +#include "../Core/Compression/ZlibCompressor.h" +#include "../Core/DicomFormat/DicomTag.h" +#include "../Core/HttpServer/HttpHandler.h" +#include "../Core/OrthancException.h" +#include "../Core/Toolbox.h" +#include "../Core/Uuid.h" +#include "../OrthancServer/FromDcmtkBridge.h" +#include "../OrthancServer/OrthancInitialization.h" +#include "../Core/MultiThreading/SharedMessageQueue.h" + +using namespace Orthanc; + + +TEST(Uuid, Generation) +{ + for (int i = 0; i < 10; i++) + { + std::string s = Toolbox::GenerateUuid(); + ASSERT_TRUE(Toolbox::IsUuid(s)); + } +} + +TEST(Uuid, Test) +{ + ASSERT_FALSE(Toolbox::IsUuid("")); + ASSERT_FALSE(Toolbox::IsUuid("012345678901234567890123456789012345")); + ASSERT_TRUE(Toolbox::IsUuid("550e8400-e29b-41d4-a716-446655440000")); + ASSERT_FALSE(Toolbox::StartsWithUuid("550e8400-e29b-41d4-a716-44665544000")); + ASSERT_TRUE(Toolbox::StartsWithUuid("550e8400-e29b-41d4-a716-446655440000")); + ASSERT_TRUE(Toolbox::StartsWithUuid("550e8400-e29b-41d4-a716-446655440000 ok")); + ASSERT_FALSE(Toolbox::StartsWithUuid("550e8400-e29b-41d4-a716-446655440000ok")); +} + +TEST(Toolbox, IsSHA1) +{ + ASSERT_FALSE(Toolbox::IsSHA1("")); + ASSERT_FALSE(Toolbox::IsSHA1("01234567890123456789012345678901234567890123")); + ASSERT_FALSE(Toolbox::IsSHA1("012345678901234567890123456789012345678901234")); + ASSERT_TRUE(Toolbox::IsSHA1("b5ed549f-956400ce-69a8c063-bf5b78be-2732a4b9")); + + std::string s; + Toolbox::ComputeSHA1(s, "The quick brown fox jumps over the lazy dog"); + ASSERT_TRUE(Toolbox::IsSHA1(s)); + ASSERT_EQ("2fd4e1c6-7a2d28fc-ed849ee1-bb76e739-1b93eb12", s); +} + +TEST(Zlib, Basic) +{ + std::string s = Toolbox::GenerateUuid(); + s = s + s + s + s; + + std::string compressed; + ZlibCompressor c; + c.Compress(compressed, s); + + std::string uncompressed; + c.Uncompress(uncompressed, compressed); + + ASSERT_EQ(s.size(), uncompressed.size()); + ASSERT_EQ(0, memcmp(&s[0], &uncompressed[0], s.size())); +} + +TEST(Zlib, Empty) +{ + std::string s = ""; + + std::string compressed; + ZlibCompressor c; + c.Compress(compressed, s); + + std::string uncompressed; + c.Uncompress(uncompressed, compressed); + + ASSERT_EQ(0u, uncompressed.size()); +} + +TEST(ParseGetQuery, Basic) +{ + HttpHandler::Arguments a; + HttpHandler::ParseGetQuery(a, "aaa=baaa&bb=a&aa=c"); + ASSERT_EQ(3u, a.size()); + ASSERT_EQ(a["aaa"], "baaa"); + ASSERT_EQ(a["bb"], "a"); + ASSERT_EQ(a["aa"], "c"); +} + +TEST(ParseGetQuery, BasicEmpty) +{ + HttpHandler::Arguments a; + HttpHandler::ParseGetQuery(a, "aaa&bb=aa&aa"); + ASSERT_EQ(3u, a.size()); + ASSERT_EQ(a["aaa"], ""); + ASSERT_EQ(a["bb"], "aa"); + ASSERT_EQ(a["aa"], ""); +} + +TEST(ParseGetQuery, Single) +{ + HttpHandler::Arguments a; + HttpHandler::ParseGetQuery(a, "aaa=baaa"); + ASSERT_EQ(1u, a.size()); + ASSERT_EQ(a["aaa"], "baaa"); +} + +TEST(ParseGetQuery, SingleEmpty) +{ + HttpHandler::Arguments a; + HttpHandler::ParseGetQuery(a, "aaa"); + ASSERT_EQ(1u, a.size()); + ASSERT_EQ(a["aaa"], ""); +} + +TEST(DicomFormat, Tag) +{ + ASSERT_EQ("PatientName", FromDcmtkBridge::GetName(DicomTag(0x0010, 0x0010))); + + DicomTag t = FromDcmtkBridge::ParseTag("SeriesDescription"); + ASSERT_EQ(0x0008, t.GetGroup()); + ASSERT_EQ(0x103E, t.GetElement()); + + t = FromDcmtkBridge::ParseTag("0020-e040"); + ASSERT_EQ(0x0020, t.GetGroup()); + ASSERT_EQ(0xe040, t.GetElement()); + + // Test ==() and !=() operators + ASSERT_TRUE(DICOM_TAG_PATIENT_ID == DicomTag(0x0010, 0x0020)); + ASSERT_FALSE(DICOM_TAG_PATIENT_ID != DicomTag(0x0010, 0x0020)); +} + + +TEST(Uri, SplitUriComponents) +{ + UriComponents c; + Toolbox::SplitUriComponents(c, "/cou/hello/world"); + ASSERT_EQ(3u, c.size()); + ASSERT_EQ("cou", c[0]); + ASSERT_EQ("hello", c[1]); + ASSERT_EQ("world", c[2]); + + Toolbox::SplitUriComponents(c, "/cou/hello/world/"); + ASSERT_EQ(3u, c.size()); + ASSERT_EQ("cou", c[0]); + ASSERT_EQ("hello", c[1]); + ASSERT_EQ("world", c[2]); + + Toolbox::SplitUriComponents(c, "/cou/hello/world/a"); + ASSERT_EQ(4u, c.size()); + ASSERT_EQ("cou", c[0]); + ASSERT_EQ("hello", c[1]); + ASSERT_EQ("world", c[2]); + ASSERT_EQ("a", c[3]); + + Toolbox::SplitUriComponents(c, "/"); + ASSERT_EQ(0u, c.size()); + + Toolbox::SplitUriComponents(c, "/hello"); + ASSERT_EQ(1u, c.size()); + ASSERT_EQ("hello", c[0]); + + Toolbox::SplitUriComponents(c, "/hello/"); + ASSERT_EQ(1u, c.size()); + ASSERT_EQ("hello", c[0]); + + ASSERT_THROW(Toolbox::SplitUriComponents(c, ""), OrthancException); + ASSERT_THROW(Toolbox::SplitUriComponents(c, "a"), OrthancException); + ASSERT_THROW(Toolbox::SplitUriComponents(c, "/coucou//coucou"), OrthancException); +} + + +TEST(Uri, Child) +{ + UriComponents c1; Toolbox::SplitUriComponents(c1, "/hello/world"); + UriComponents c2; Toolbox::SplitUriComponents(c2, "/hello/hello"); + UriComponents c3; Toolbox::SplitUriComponents(c3, "/hello"); + UriComponents c4; Toolbox::SplitUriComponents(c4, "/world"); + UriComponents c5; Toolbox::SplitUriComponents(c5, "/"); + + ASSERT_TRUE(Toolbox::IsChildUri(c1, c1)); + ASSERT_FALSE(Toolbox::IsChildUri(c1, c2)); + ASSERT_FALSE(Toolbox::IsChildUri(c1, c3)); + ASSERT_FALSE(Toolbox::IsChildUri(c1, c4)); + ASSERT_FALSE(Toolbox::IsChildUri(c1, c5)); + + ASSERT_FALSE(Toolbox::IsChildUri(c2, c1)); + ASSERT_TRUE(Toolbox::IsChildUri(c2, c2)); + ASSERT_FALSE(Toolbox::IsChildUri(c2, c3)); + ASSERT_FALSE(Toolbox::IsChildUri(c2, c4)); + ASSERT_FALSE(Toolbox::IsChildUri(c2, c5)); + + ASSERT_TRUE(Toolbox::IsChildUri(c3, c1)); + ASSERT_TRUE(Toolbox::IsChildUri(c3, c2)); + ASSERT_TRUE(Toolbox::IsChildUri(c3, c3)); + ASSERT_FALSE(Toolbox::IsChildUri(c3, c4)); + ASSERT_FALSE(Toolbox::IsChildUri(c3, c5)); + + ASSERT_FALSE(Toolbox::IsChildUri(c4, c1)); + ASSERT_FALSE(Toolbox::IsChildUri(c4, c2)); + ASSERT_FALSE(Toolbox::IsChildUri(c4, c3)); + ASSERT_TRUE(Toolbox::IsChildUri(c4, c4)); + ASSERT_FALSE(Toolbox::IsChildUri(c4, c5)); + + ASSERT_TRUE(Toolbox::IsChildUri(c5, c1)); + ASSERT_TRUE(Toolbox::IsChildUri(c5, c2)); + ASSERT_TRUE(Toolbox::IsChildUri(c5, c3)); + ASSERT_TRUE(Toolbox::IsChildUri(c5, c4)); + ASSERT_TRUE(Toolbox::IsChildUri(c5, c5)); +} + +TEST(Uri, AutodetectMimeType) +{ + ASSERT_EQ("", Toolbox::AutodetectMimeType("../NOTES")); + ASSERT_EQ("", Toolbox::AutodetectMimeType("")); + ASSERT_EQ("", Toolbox::AutodetectMimeType("/")); + ASSERT_EQ("", Toolbox::AutodetectMimeType("a/a")); + + ASSERT_EQ("text/plain", Toolbox::AutodetectMimeType("../NOTES.txt")); + ASSERT_EQ("text/plain", Toolbox::AutodetectMimeType("../coucou.xml/NOTES.txt")); + ASSERT_EQ("text/xml", Toolbox::AutodetectMimeType("../.xml")); + + ASSERT_EQ("application/javascript", Toolbox::AutodetectMimeType("NOTES.js")); + ASSERT_EQ("application/json", Toolbox::AutodetectMimeType("NOTES.json")); + ASSERT_EQ("application/pdf", Toolbox::AutodetectMimeType("NOTES.pdf")); + ASSERT_EQ("text/css", Toolbox::AutodetectMimeType("NOTES.css")); + ASSERT_EQ("text/html", Toolbox::AutodetectMimeType("NOTES.html")); + ASSERT_EQ("text/plain", Toolbox::AutodetectMimeType("NOTES.txt")); + ASSERT_EQ("text/xml", Toolbox::AutodetectMimeType("NOTES.xml")); + ASSERT_EQ("image/gif", Toolbox::AutodetectMimeType("NOTES.gif")); + ASSERT_EQ("image/jpeg", Toolbox::AutodetectMimeType("NOTES.jpg")); + ASSERT_EQ("image/jpeg", Toolbox::AutodetectMimeType("NOTES.jpeg")); + ASSERT_EQ("image/png", Toolbox::AutodetectMimeType("NOTES.png")); +} + +TEST(Toolbox, ComputeMD5) +{ + std::string s; + + // # echo -n "Hello" | md5sum + + Toolbox::ComputeMD5(s, "Hello"); + ASSERT_EQ("8b1a9953c4611296a827abf8c47804d7", s); + Toolbox::ComputeMD5(s, ""); + ASSERT_EQ("d41d8cd98f00b204e9800998ecf8427e", s); +} + +TEST(Toolbox, ComputeSHA1) +{ + std::string s; + + Toolbox::ComputeSHA1(s, "The quick brown fox jumps over the lazy dog"); + ASSERT_EQ("2fd4e1c6-7a2d28fc-ed849ee1-bb76e739-1b93eb12", s); + Toolbox::ComputeSHA1(s, ""); + ASSERT_EQ("da39a3ee-5e6b4b0d-3255bfef-95601890-afd80709", s); +} + + +TEST(Toolbox, Base64) +{ + ASSERT_EQ("", Toolbox::EncodeBase64("")); + ASSERT_EQ("YQ==", Toolbox::EncodeBase64("a")); + ASSERT_EQ("SGVsbG8gd29ybGQ=", Toolbox::EncodeBase64("Hello world")); +} + +TEST(Toolbox, PathToExecutable) +{ + printf("[%s]\n", Toolbox::GetPathToExecutable().c_str()); + printf("[%s]\n", Toolbox::GetDirectoryOfExecutable().c_str()); +} + +TEST(Toolbox, StripSpaces) +{ + ASSERT_EQ("", Toolbox::StripSpaces(" \t \r \n ")); + ASSERT_EQ("coucou", Toolbox::StripSpaces(" coucou \t \r \n ")); + ASSERT_EQ("cou cou", Toolbox::StripSpaces(" cou cou \n ")); + ASSERT_EQ("c", Toolbox::StripSpaces(" \n\t c\r \n ")); +} + + +#include + +TEST(Logger, Basic) +{ + LOG(INFO) << "I say hello"; +} + +TEST(Toolbox, ConvertFromLatin1) +{ + // This is a Latin-1 test string + const unsigned char data[10] = { 0xe0, 0xe9, 0xea, 0xe7, 0x26, 0xc6, 0x61, 0x62, 0x63, 0x00 }; + + /*FILE* f = fopen("/tmp/tutu", "w"); + fwrite(&data[0], 9, 1, f); + fclose(f);*/ + + std::string s((char*) &data[0], 10); + ASSERT_EQ("&abc", Toolbox::ConvertToAscii(s)); + + // Open in Emacs, then save with UTF-8 encoding, then "hexdump -C" + std::string utf8 = Toolbox::ConvertToUtf8(s, "ISO-8859-1"); + ASSERT_EQ(15u, utf8.size()); + ASSERT_EQ(0xc3, static_cast(utf8[0])); + ASSERT_EQ(0xa0, static_cast(utf8[1])); + ASSERT_EQ(0xc3, static_cast(utf8[2])); + ASSERT_EQ(0xa9, static_cast(utf8[3])); + ASSERT_EQ(0xc3, static_cast(utf8[4])); + ASSERT_EQ(0xaa, static_cast(utf8[5])); + ASSERT_EQ(0xc3, static_cast(utf8[6])); + ASSERT_EQ(0xa7, static_cast(utf8[7])); + ASSERT_EQ(0x26, static_cast(utf8[8])); + ASSERT_EQ(0xc3, static_cast(utf8[9])); + ASSERT_EQ(0x86, static_cast(utf8[10])); + ASSERT_EQ(0x61, static_cast(utf8[11])); + ASSERT_EQ(0x62, static_cast(utf8[12])); + ASSERT_EQ(0x63, static_cast(utf8[13])); + ASSERT_EQ(0x00, static_cast(utf8[14])); // Null-terminated string +} + +TEST(Toolbox, UrlDecode) +{ + std::string s; + + s = "Hello%20World"; + Toolbox::UrlDecode(s); + ASSERT_EQ("Hello World", s); + + s = "%21%23%24%26%27%28%29%2A%2B%2c%2f%3A%3b%3d%3f%40%5B%5D%90%ff"; + Toolbox::UrlDecode(s); + std::string ss = "!#$&'()*+,/:;=?@[]"; + ss.push_back((char) 144); + ss.push_back((char) 255); + ASSERT_EQ(ss, s); + + s = "(2000%2C00A4)+Other"; + Toolbox::UrlDecode(s); + ASSERT_EQ("(2000,00A4) Other", s); +} + + +#if defined(__linux) +TEST(OrthancInitialization, AbsoluteDirectory) +{ + ASSERT_EQ("/tmp/hello", InterpretRelativePath("/tmp", "hello")); + ASSERT_EQ("/tmp", InterpretRelativePath("/tmp", "/tmp")); +} +#endif + + + +#include "../OrthancServer/ServerEnumerations.h" + +TEST(EnumerationDictionary, Simple) +{ + Toolbox::EnumerationDictionary d; + + ASSERT_THROW(d.Translate("ReceptionDate"), OrthancException); + ASSERT_EQ(MetadataType_ModifiedFrom, d.Translate("5")); + ASSERT_EQ(256, d.Translate("256")); + + d.Add(MetadataType_Instance_ReceptionDate, "ReceptionDate"); + + ASSERT_EQ(MetadataType_Instance_ReceptionDate, d.Translate("ReceptionDate")); + ASSERT_EQ(MetadataType_Instance_ReceptionDate, d.Translate("2")); + ASSERT_EQ("ReceptionDate", d.Translate(MetadataType_Instance_ReceptionDate)); + + ASSERT_THROW(d.Add(MetadataType_Instance_ReceptionDate, "Hello"), OrthancException); + ASSERT_THROW(d.Add(MetadataType_ModifiedFrom, "ReceptionDate"), OrthancException); // already used + ASSERT_THROW(d.Add(MetadataType_ModifiedFrom, "1024"), OrthancException); // cannot register numbers + d.Add(MetadataType_ModifiedFrom, "ModifiedFrom"); // ok +} + + +TEST(EnumerationDictionary, ServerEnumerations) +{ + ASSERT_STREQ("Patient", EnumerationToString(ResourceType_Patient)); + ASSERT_STREQ("Study", EnumerationToString(ResourceType_Study)); + ASSERT_STREQ("Series", EnumerationToString(ResourceType_Series)); + ASSERT_STREQ("Instance", EnumerationToString(ResourceType_Instance)); + + ASSERT_STREQ("ModifiedSeries", EnumerationToString(ChangeType_ModifiedSeries)); + + ASSERT_STREQ("Failure", EnumerationToString(StoreStatus_Failure)); + ASSERT_STREQ("Success", EnumerationToString(StoreStatus_Success)); + + ASSERT_STREQ("CompletedSeries", EnumerationToString(ChangeType_CompletedSeries)); + + ASSERT_EQ("IndexInSeries", EnumerationToString(MetadataType_Instance_IndexInSeries)); + ASSERT_EQ("LastUpdate", EnumerationToString(MetadataType_LastUpdate)); + + ASSERT_EQ(ResourceType_Patient, StringToResourceType("PATienT")); + ASSERT_EQ(ResourceType_Study, StringToResourceType("STudy")); + ASSERT_EQ(ResourceType_Series, StringToResourceType("SeRiEs")); + ASSERT_EQ(ResourceType_Instance, StringToResourceType("INStance")); + ASSERT_EQ(ResourceType_Instance, StringToResourceType("IMagE")); + ASSERT_THROW(StringToResourceType("heLLo"), OrthancException); + + ASSERT_EQ(2047, StringToMetadata("2047")); + ASSERT_THROW(StringToMetadata("Ceci est un test"), OrthancException); + ASSERT_THROW(RegisterUserMetadata(128, ""), OrthancException); // too low (< 1024) + ASSERT_THROW(RegisterUserMetadata(128000, ""), OrthancException); // too high (> 65535) + RegisterUserMetadata(2047, "Ceci est un test"); + ASSERT_EQ(2047, StringToMetadata("2047")); + ASSERT_EQ(2047, StringToMetadata("Ceci est un test")); +} + + + +class DynamicInteger : public IDynamicObject +{ +private: + int value_; + +public: + DynamicInteger(int value) : value_(value) + { + } + + int GetValue() const + { + return value_; + } +}; + + +TEST(SharedMessageQueue, Basic) +{ + SharedMessageQueue q; + ASSERT_TRUE(q.WaitEmpty(0)); + q.Enqueue(new DynamicInteger(10)); + ASSERT_FALSE(q.WaitEmpty(1)); + q.Enqueue(new DynamicInteger(20)); + q.Enqueue(new DynamicInteger(30)); + q.Enqueue(new DynamicInteger(40)); + + std::auto_ptr i; + i.reset(dynamic_cast(q.Dequeue(1))); ASSERT_EQ(10, i->GetValue()); + i.reset(dynamic_cast(q.Dequeue(1))); ASSERT_EQ(20, i->GetValue()); + i.reset(dynamic_cast(q.Dequeue(1))); ASSERT_EQ(30, i->GetValue()); + ASSERT_FALSE(q.WaitEmpty(1)); + i.reset(dynamic_cast(q.Dequeue(1))); ASSERT_EQ(40, i->GetValue()); + ASSERT_TRUE(q.WaitEmpty(0)); + ASSERT_EQ(NULL, q.Dequeue(1)); +} + + +TEST(SharedMessageQueue, Clean) +{ + try + { + SharedMessageQueue q; + q.Enqueue(new DynamicInteger(10)); + q.Enqueue(new DynamicInteger(20)); + throw OrthancException("Nope"); + } + catch (OrthancException&) + { + } +} + + +TEST(Toolbox, WriteFile) +{ + std::string path; + + { + Toolbox::TemporaryFile tmp; + path = tmp.GetPath(); + + std::string s; + s.append("Hello"); + s.push_back('\0'); + s.append("World"); + ASSERT_EQ(11u, s.size()); + + Toolbox::WriteFile(s, path.c_str()); + + std::string t; + Toolbox::ReadFile(t, path.c_str()); + + ASSERT_EQ(11u, t.size()); + ASSERT_EQ(0, t[5]); + ASSERT_EQ(0, memcmp(s.c_str(), t.c_str(), s.size())); + } + + std::string u; + ASSERT_THROW(Toolbox::ReadFile(u, path.c_str()), OrthancException); +} + + +TEST(Toolbox, Wildcard) +{ + ASSERT_EQ("abcd", Toolbox::WildcardToRegularExpression("abcd")); + ASSERT_EQ("ab.*cd", Toolbox::WildcardToRegularExpression("ab*cd")); + ASSERT_EQ("ab..cd", Toolbox::WildcardToRegularExpression("ab??cd")); + ASSERT_EQ("a.*b.c.*d", Toolbox::WildcardToRegularExpression("a*b?c*d")); + ASSERT_EQ("a\\{b\\]", Toolbox::WildcardToRegularExpression("a{b]")); +} + + +TEST(Toolbox, Tokenize) +{ + std::vector t; + + Toolbox::TokenizeString(t, "", ','); + ASSERT_EQ(1, t.size()); + ASSERT_EQ("", t[0]); + + Toolbox::TokenizeString(t, "abc", ','); + ASSERT_EQ(1, t.size()); + ASSERT_EQ("abc", t[0]); + + Toolbox::TokenizeString(t, "ab,cd,ef,", ','); + ASSERT_EQ(4, t.size()); + ASSERT_EQ("ab", t[0]); + ASSERT_EQ("cd", t[1]); + ASSERT_EQ("ef", t[2]); + ASSERT_EQ("", t[3]); +} + + +int main(int argc, char **argv) +{ + // Initialize Google's logging library. + FLAGS_logtostderr = true; + FLAGS_minloglevel = 0; + + // Go to trace-level verbosity + //FLAGS_v = 1; + + Toolbox::DetectEndianness(); + + google::InitGoogleLogging("Orthanc"); + + OrthancInitialize(); + ::testing::InitGoogleTest(&argc, argv); + int result = RUN_ALL_TESTS(); + OrthancFinalize(); + return result; +}