Mercurial > hg > orthanc-indexer
view Sources/UnitTestsMain.cpp @ 24:7da7815d2cce OrthancIndexer-1.1
OrthancIndexer-1.1
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Tue, 26 Mar 2024 14:21:19 +0100 |
parents | ca40236fda57 |
children |
line wrap: on
line source
/** * Indexer plugin for Orthanc * Copyright (C) 2021-2024 Sebastien Jodogne, UCLouvain, Belgium * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. **/ #include <gtest/gtest.h> #include "IndexerDatabase.h" #include "StorageArea.h" #include <Logging.h> #include <OrthancException.h> #include <SystemToolbox.h> #include <Toolbox.h> TEST(StorageArea, Basic) { const std::string uuid = Orthanc::Toolbox::GenerateUuid(); ASSERT_TRUE(Orthanc::Toolbox::IsUuid(uuid)); StorageArea area("StorageAreaTests"); area.Create(uuid, "Hello", 5); ASSERT_TRUE(Orthanc::SystemToolbox::IsRegularFile(area.GetPath(uuid))); std::string s; area.ReadWhole(s, uuid); ASSERT_EQ(5u, s.size()); ASSERT_EQ("Hello", s); char data[10]; OrthancPluginMemoryBuffer64 buffer; buffer.data = data; buffer.size = 1; area.ReadRange(&buffer, uuid, 0); ASSERT_EQ('H', data[0]); area.ReadRange(&buffer, uuid, 4); ASSERT_EQ('o', data[0]); ASSERT_THROW(area.ReadRange(&buffer, uuid, 5), Orthanc::OrthancException); buffer.size = 2; area.ReadRange(&buffer, uuid, 0); ASSERT_EQ('H', data[0]); ASSERT_EQ('e', data[1]); area.ReadRange(&buffer, uuid, 1); ASSERT_EQ('e', data[0]); ASSERT_EQ('l', data[1]); area.ReadRange(&buffer, uuid, 2); ASSERT_EQ('l', data[0]); ASSERT_EQ('l', data[1]); area.ReadRange(&buffer, uuid, 3); ASSERT_EQ('l', data[0]); ASSERT_EQ('o', data[1]); ASSERT_THROW(area.ReadRange(&buffer, uuid, 4), Orthanc::OrthancException); buffer.size = 3; area.ReadRange(&buffer, uuid, 0); ASSERT_EQ('H', data[0]); ASSERT_EQ('e', data[1]); ASSERT_EQ('l', data[2]); area.ReadRange(&buffer, uuid, 1); ASSERT_EQ('e', data[0]); ASSERT_EQ('l', data[1]); ASSERT_EQ('l', data[2]); area.ReadRange(&buffer, uuid, 2); ASSERT_EQ('l', data[0]); ASSERT_EQ('l', data[1]); ASSERT_EQ('o', data[2]); ASSERT_THROW(area.ReadRange(&buffer, uuid, 3), Orthanc::OrthancException); buffer.size = 5; area.ReadRange(&buffer, uuid, 0); ASSERT_EQ('H', data[0]); ASSERT_EQ('e', data[1]); ASSERT_EQ('l', data[2]); ASSERT_EQ('l', data[3]); ASSERT_EQ('o', data[4]); ASSERT_THROW(area.ReadRange(&buffer, uuid, 1), Orthanc::OrthancException); area.RemoveAttachment(uuid); ASSERT_FALSE(Orthanc::SystemToolbox::IsRegularFile(area.GetPath(uuid))); ASSERT_THROW(area.ReadWhole(&buffer, uuid), Orthanc::OrthancException); ASSERT_THROW(area.ReadRange(&buffer, uuid, 0), Orthanc::OrthancException); } class Visitor : public IndexerDatabase::IFileVisitor { private: std::vector<std::string> path_; std::vector<bool> isDicom_; std::vector<std::string> instanceId_; public: virtual void VisitInstance(const std::string& path, bool isDicom, const std::string& instanceId) ORTHANC_OVERRIDE { path_.push_back(path); isDicom_.push_back(isDicom); instanceId_.push_back(instanceId); } void Clear() { path_.clear(); isDicom_.clear(); instanceId_.clear(); } size_t GetSize() const { assert(path_.size() == isDicom_.size() && path_.size() == instanceId_.size()); return path_.size(); } const std::string& GetPath(size_t i) const { assert(i < path_.size()); return path_[i]; } bool IsDicom(size_t i) const { assert(i < isDicom_.size()); return isDicom_[i]; } const std::string& GetInstanceId(size_t i) const { assert(i < instanceId_.size()); return instanceId_[i]; } void GetDicomPath(std::set<std::string>& target) const { target.clear(); for (size_t i = 0; i < GetSize(); i++) { target.insert(GetPath(i)); } } void GetInstanceId(std::set<std::string>& target) const { target.clear(); for (size_t i = 0; i < GetSize(); i++) { target.insert(GetInstanceId(i)); } } }; TEST(IndexerDatabase, Files) { Visitor v; IndexerDatabase db; ASSERT_THROW(db.Apply(v), Orthanc::OrthancException); // DB is not opened db.OpenInMemory(); db.Apply(v); ASSERT_EQ(0u, v.GetSize()); ASSERT_EQ(0u, db.GetFilesCount()); ASSERT_EQ(0u, db.GetAttachmentsCount()); std::string s; ASSERT_EQ(IndexerDatabase::FileStatus_New, db.LookupFile(s, "some/path/to/dicom", 42 /* time */, 5 /* size */)); db.AddDicomInstance("some/path/to/dicom", 42 /* time */, 5 /* size */, "instance1"); db.Apply(v); ASSERT_EQ(1u, v.GetSize()); ASSERT_EQ("some/path/to/dicom", v.GetPath(0)); ASSERT_TRUE(v.IsDicom(0)); ASSERT_EQ("instance1", v.GetInstanceId(0)); ASSERT_EQ(IndexerDatabase::FileStatus_AlreadyStored, db.LookupFile(s, "some/path/to/dicom", 42 /* time */, 5 /* size */)); ASSERT_EQ(IndexerDatabase::FileStatus_Modified, db.LookupFile(s, "some/path/to/dicom", 43 /* time */, 5 /* size */)); ASSERT_EQ(IndexerDatabase::FileStatus_Modified, db.LookupFile(s, "some/path/to/dicom", 42 /* time */, 6 /* size */)); ASSERT_EQ(1u, db.GetFilesCount()); ASSERT_EQ(0u, db.GetAttachmentsCount()); ASSERT_THROW(db.RemoveFile("nope"), Orthanc::OrthancException); ASSERT_TRUE(db.RemoveFile("some/path/to/dicom")); ASSERT_THROW(db.RemoveFile("some/path/to/dicom"), Orthanc::OrthancException); ASSERT_EQ(0u, db.GetFilesCount()); ASSERT_EQ(0u, db.GetAttachmentsCount()); v.Clear(); db.Apply(v); ASSERT_EQ(0u, v.GetSize()); ASSERT_EQ(IndexerDatabase::FileStatus_New, db.LookupFile(s, "some/path/to/text", 42 /* time */, 5 /* size */)); db.AddNonDicomFile("some/path/to/text", 42 /* time */, 5 /* size */); db.Apply(v); ASSERT_EQ(1u, v.GetSize()); ASSERT_EQ("some/path/to/text", v.GetPath(0)); ASSERT_FALSE(v.IsDicom(0)); ASSERT_TRUE(v.GetInstanceId(0).empty()); ASSERT_EQ(1u, db.GetFilesCount()); ASSERT_EQ(0u, db.GetAttachmentsCount()); ASSERT_EQ(IndexerDatabase::FileStatus_NotDicom, db.LookupFile(s, "some/path/to/text", 42 /* time */, 5 /* size */)); ASSERT_EQ(IndexerDatabase::FileStatus_Modified, db.LookupFile(s, "some/path/to/text", 43 /* time */, 5 /* size */)); ASSERT_EQ(IndexerDatabase::FileStatus_Modified, db.LookupFile(s, "some/path/to/text", 42 /* time */, 6 /* size */)); ASSERT_TRUE(db.RemoveFile("some/path/to/text")); ASSERT_THROW(db.RemoveFile("some/path/to/text"), Orthanc::OrthancException); v.Clear(); db.Apply(v); ASSERT_EQ(0u, v.GetSize()); ASSERT_EQ(0u, db.GetFilesCount()); ASSERT_EQ(0u, db.GetAttachmentsCount()); } TEST(IndexerDatabase, RemoveUsingRestApi) { Visitor v; IndexerDatabase db; db.OpenInMemory(); // Firstly, the plugin detects the new instances on the filesystem // and uploads them into Orthanc in the "ProcessFile()" function db.AddDicomInstance("sample.dcm", 42 /* time */, 5 /* size */, "instance1"); db.Apply(v); ASSERT_EQ(1u, v.GetSize()); ASSERT_EQ("sample.dcm", v.GetPath(0)); ASSERT_TRUE(v.IsDicom(0)); ASSERT_EQ("instance1", v.GetInstanceId(0)); std::string path; ASSERT_FALSE(db.LookupAttachment(path, "uuid1")); ASSERT_FALSE(db.LookupAttachment(path, "uuid2")); ASSERT_EQ(1u, db.GetFilesCount()); ASSERT_EQ(0u, db.GetAttachmentsCount()); // Secondly, the storage area plugin parses DICOM attachments in // order to extract their Orthanc ID, and indexes them in the DB ASSERT_TRUE(db.AddAttachment("uuid1", "instance1")); ASSERT_FALSE(db.AddAttachment("uuid2", "instance2")); ASSERT_TRUE(db.LookupAttachment(path, "uuid1")); ASSERT_EQ("sample.dcm", path); ASSERT_FALSE(db.LookupAttachment(path, "uuid2")); db.RemoveAttachment("uuid2"); ASSERT_EQ(1u, db.GetFilesCount()); ASSERT_EQ(1u, db.GetAttachmentsCount()); // Thirdly, the user deletes the DICOM instance using the REST API, // which causes the storage area to remove the attachment db.RemoveAttachment("uuid1"); db.RemoveAttachment("uuid1"); v.Clear(); db.Apply(v); ASSERT_EQ(1u, v.GetSize()); ASSERT_EQ("sample.dcm", v.GetPath(0)); ASSERT_TRUE(v.IsDicom(0)); ASSERT_EQ("instance1", v.GetInstanceId(0)); ASSERT_EQ(1u, db.GetFilesCount()); ASSERT_EQ(0u, db.GetAttachmentsCount()); db.RemoveFile("sample.dcm"); ASSERT_THROW(db.RemoveFile("sample.dcm"), Orthanc::OrthancException); ASSERT_EQ(0u, db.GetFilesCount()); ASSERT_EQ(0u, db.GetAttachmentsCount()); } TEST(IndexerDatabase, RemoveFromFilesystem) { Visitor v; IndexerDatabase db; db.OpenInMemory(); // Firstly, the plugin finds two copies of the same DICOM instance db.AddDicomInstance("copy1.dcm", 42 /* time */, 5 /* size */, "instance1"); db.AddDicomInstance("copy2.dcm", 43 /* time */, 6 /* size */, "instance1"); std::set<std::string> s; db.Apply(v); v.GetDicomPath(s); ASSERT_EQ(2u, s.size()); ASSERT_TRUE(s.find("copy1.dcm") != s.end()); ASSERT_TRUE(s.find("copy2.dcm") != s.end()); v.GetInstanceId(s); ASSERT_EQ(1u, s.size()); ASSERT_TRUE(s.find("instance1") != s.end()); std::string path; ASSERT_FALSE(db.LookupAttachment(path, "uuid1")); ASSERT_EQ(2u, db.GetFilesCount()); ASSERT_EQ(0u, db.GetAttachmentsCount()); // Secondly, the storage area plugin parses DICOM attachments in // order to extract their Orthanc ID, and indexes them in the DB ASSERT_TRUE(db.AddAttachment("uuid1", "instance1")); ASSERT_THROW(db.AddAttachment("uuid1", "instance1"), Orthanc::OrthancException); // Constraint violation ASSERT_TRUE(db.LookupAttachment(path, "uuid1")); ASSERT_TRUE(path == "copy1.dcm" || path == "copy2.dcm"); ASSERT_FALSE(db.LookupAttachment(path, "uuid2")); ASSERT_EQ(2u, db.GetFilesCount()); ASSERT_EQ(1u, db.GetAttachmentsCount()); // Thirdly, one of the two copies is removed from the filesystem ASSERT_FALSE(db.RemoveFile("copy1.dcm")); // "false" means that there is still one copy ASSERT_TRUE(db.LookupAttachment(path, "uuid1")); ASSERT_EQ("copy2.dcm", path); ASSERT_EQ(1u, db.GetFilesCount()); ASSERT_EQ(1u, db.GetAttachmentsCount()); v.Clear(); db.Apply(v); ASSERT_EQ(1u, v.GetSize()); ASSERT_EQ("copy2.dcm", v.GetPath(0)); ASSERT_TRUE(v.IsDicom(0)); ASSERT_EQ("instance1", v.GetInstanceId(0)); // Fourthly, the second copy is also removed, which causes a call to // "OrthancPluginRestApiDelete()" ASSERT_TRUE(db.RemoveFile("copy2.dcm")); // "true" means this was the last copy ASSERT_FALSE(db.LookupAttachment(path, "uuid1")); v.Clear(); db.Apply(v); ASSERT_EQ(0u, v.GetSize()); ASSERT_EQ(0u, db.GetFilesCount()); ASSERT_EQ(1u, db.GetAttachmentsCount()); // Finally, because of the call to "OrthancPluginRestApiDelete()", // Orthanc removes the attachment from the file storage db.RemoveAttachment("uuid1"); ASSERT_EQ(0u, db.GetFilesCount()); ASSERT_EQ(0u, db.GetAttachmentsCount()); } int main(int argc, char **argv) { Orthanc::Logging::Initialize(); Orthanc::Logging::EnableInfoLevel(true); ::testing::InitGoogleTest(&argc, argv); int result = RUN_ALL_TESTS(); Orthanc::Logging::Finalize(); return result; }