/**
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
* Copyright (C) 2017-2023 Osimis S.A., Belgium
* Copyright (C) 2024-2025 Orthanc Team SRL, Belgium
* Copyright (C) 2021-2025 Sebastien Jodogne, ICTEAM UCLouvain, Belgium
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program. If not, see
* .
**/
#if ORTHANC_UNIT_TESTS_LINK_FRAMEWORK == 1
// Must be the first to be sure to use the Orthanc framework shared library
# include
#endif
#include
#include "../../OrthancFramework/Sources/Compatibility.h"
#include "../../OrthancFramework/Sources/DicomNetworking/DicomAssociationParameters.h"
#include "../../OrthancFramework/Sources/DicomNetworking/RemoteModalityParameters.h"
#include "../../OrthancFramework/Sources/DicomParsing/DicomModification.h"
#include "../../OrthancFramework/Sources/DicomParsing/ParsedDicomFile.h"
#include "../../OrthancFramework/Sources/JobsEngine/GenericJobUnserializer.h"
#include "../../OrthancFramework/Sources/JobsEngine/JobsEngine.h"
#include "../../OrthancFramework/Sources/JobsEngine/Operations/JobOperationValues.h"
#include "../../OrthancFramework/Sources/JobsEngine/Operations/LogJobOperation.h"
#include "../../OrthancFramework/Sources/JobsEngine/Operations/NullOperationValue.h"
#include "../../OrthancFramework/Sources/JobsEngine/Operations/SequenceOfOperationsJob.h"
#include "../../OrthancFramework/Sources/JobsEngine/Operations/StringOperationValue.h"
#include "../../OrthancFramework/Sources/JobsEngine/SetOfInstancesJob.h"
#include "../../OrthancFramework/Sources/Logging.h"
#include "../../OrthancFramework/Sources/MultiThreading/SharedMessageQueue.h"
#include "../../OrthancFramework/Sources/OrthancException.h"
#include "../../OrthancFramework/Sources/SerializationToolbox.h"
using namespace Orthanc;
namespace
{
class DummyJob : public IJob
{
private:
bool fails_;
unsigned int count_;
unsigned int steps_;
public:
DummyJob() :
fails_(false),
count_(0),
steps_(4)
{
}
explicit DummyJob(bool fails) :
fails_(fails),
count_(0),
steps_(4)
{
}
virtual void Start() ORTHANC_OVERRIDE
{
}
virtual void Reset() ORTHANC_OVERRIDE
{
}
virtual JobStepResult Step(const std::string& jobId) ORTHANC_OVERRIDE
{
if (fails_)
{
return JobStepResult::Failure(ErrorCode_ParameterOutOfRange, NULL);
}
else if (count_ == steps_ - 1)
{
return JobStepResult::Success();
}
else
{
count_++;
return JobStepResult::Continue();
}
}
virtual void Stop(JobStopReason reason) ORTHANC_OVERRIDE
{
}
virtual float GetProgress() const ORTHANC_OVERRIDE
{
return static_cast(count_) / static_cast(steps_ - 1);
}
virtual void GetJobType(std::string& type) const ORTHANC_OVERRIDE
{
type = "DummyJob";
}
virtual bool Serialize(Json::Value& value) const ORTHANC_OVERRIDE
{
value = Json::objectValue;
value["Type"] = "DummyJob";
return true;
}
virtual void GetPublicContent(Json::Value& value) const ORTHANC_OVERRIDE
{
value["hello"] = "world";
}
virtual bool GetOutput(std::string& output,
MimeType& mime,
std::string& filename,
const std::string& key) ORTHANC_OVERRIDE
{
return false;
}
virtual bool DeleteOutput(const std::string& key) ORTHANC_OVERRIDE
{
return false;
}
};
class DummyInstancesJob : public SetOfInstancesJob
{
private:
bool trailingStepDone_;
protected:
virtual bool HandleInstance(const std::string& instance) ORTHANC_OVERRIDE
{
return (instance != "nope");
}
virtual bool HandleTrailingStep() ORTHANC_OVERRIDE
{
if (HasTrailingStep())
{
if (trailingStepDone_)
{
throw OrthancException(ErrorCode_InternalError);
}
else
{
trailingStepDone_ = true;
return true;
}
}
else
{
throw OrthancException(ErrorCode_InternalError);
}
}
public:
DummyInstancesJob() :
trailingStepDone_(false)
{
}
explicit DummyInstancesJob(const Json::Value& value) :
SetOfInstancesJob(value)
{
if (HasTrailingStep())
{
trailingStepDone_ = (GetPosition() == GetCommandsCount());
}
else
{
trailingStepDone_ = false;
}
}
bool IsTrailingStepDone() const
{
return trailingStepDone_;
}
virtual void Stop(JobStopReason reason) ORTHANC_OVERRIDE
{
}
virtual void GetJobType(std::string& s) const ORTHANC_OVERRIDE
{
s = "DummyInstancesJob";
}
};
class DummyUnserializer : public GenericJobUnserializer
{
public:
virtual IJob* UnserializeJob(const Json::Value& value) ORTHANC_OVERRIDE
{
if (SerializationToolbox::ReadString(value, "Type") == "DummyInstancesJob")
{
return new DummyInstancesJob(value);
}
else if (SerializationToolbox::ReadString(value, "Type") == "DummyJob")
{
return new DummyJob;
}
else
{
return GenericJobUnserializer::UnserializeJob(value);
}
}
};
class DynamicInteger : public IDynamicObject
{
private:
int value_;
std::set& target_;
public:
DynamicInteger(int value, std::set& target) :
value_(value), target_(target)
{
}
int GetValue() const
{
return value_;
}
};
}
TEST(MultiThreading, SharedMessageQueueBasic)
{
std::set s;
SharedMessageQueue q;
ASSERT_TRUE(q.WaitEmpty(0));
q.Enqueue(new DynamicInteger(10, s));
ASSERT_FALSE(q.WaitEmpty(1));
q.Enqueue(new DynamicInteger(20, s));
q.Enqueue(new DynamicInteger(30, s));
q.Enqueue(new DynamicInteger(40, s));
std::unique_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(MultiThreading, SharedMessageQueueClean)
{
std::set s;
try
{
SharedMessageQueue q;
q.Enqueue(new DynamicInteger(10, s));
q.Enqueue(new DynamicInteger(20, s));
throw OrthancException(ErrorCode_InternalError);
}
catch (OrthancException&)
{
}
}
static bool CheckState(JobsRegistry& registry,
const std::string& id,
JobState state)
{
JobState s;
if (registry.GetState(s, id))
{
return state == s;
}
else
{
return false;
}
}
static bool CheckErrorCode(JobsRegistry& registry,
const std::string& id,
ErrorCode code)
{
JobInfo s;
if (registry.GetJobInfo(s, id))
{
return code == s.GetStatus().GetErrorCode();
}
else
{
return false;
}
}
TEST(JobsRegistry, Priority)
{
JobsRegistry registry(10);
std::string i1, i2, i3, i4;
registry.Submit(i1, new DummyJob(), 10);
registry.Submit(i2, new DummyJob(), 30);
registry.Submit(i3, new DummyJob(), 20);
registry.Submit(i4, new DummyJob(), 5);
registry.SetMaxCompletedJobs(2);
std::set id;
registry.ListJobs(id);
ASSERT_EQ(4u, id.size());
ASSERT_TRUE(id.find(i1) != id.end());
ASSERT_TRUE(id.find(i2) != id.end());
ASSERT_TRUE(id.find(i3) != id.end());
ASSERT_TRUE(id.find(i4) != id.end());
ASSERT_TRUE(CheckState(registry, i2, JobState_Pending));
{
JobsRegistry::RunningJob job(registry, 0);
ASSERT_TRUE(job.IsValid());
ASSERT_EQ(30, job.GetPriority());
ASSERT_EQ(i2, job.GetId());
ASSERT_TRUE(CheckState(registry, i2, JobState_Running));
}
ASSERT_TRUE(CheckState(registry, i2, JobState_Failure));
ASSERT_TRUE(CheckState(registry, i3, JobState_Pending));
{
JobsRegistry::RunningJob job(registry, 0);
ASSERT_TRUE(job.IsValid());
ASSERT_EQ(20, job.GetPriority());
ASSERT_EQ(i3, job.GetId());
job.MarkSuccess();
ASSERT_TRUE(CheckState(registry, i3, JobState_Running));
}
ASSERT_TRUE(CheckState(registry, i3, JobState_Success));
{
JobsRegistry::RunningJob job(registry, 0);
ASSERT_TRUE(job.IsValid());
ASSERT_EQ(10, job.GetPriority());
ASSERT_EQ(i1, job.GetId());
}
{
JobsRegistry::RunningJob job(registry, 0);
ASSERT_TRUE(job.IsValid());
ASSERT_EQ(5, job.GetPriority());
ASSERT_EQ(i4, job.GetId());
}
{
JobsRegistry::RunningJob job(registry, 1);
ASSERT_FALSE(job.IsValid());
}
JobState s;
ASSERT_TRUE(registry.GetState(s, i1));
ASSERT_FALSE(registry.GetState(s, i2)); // Removed because oldest
ASSERT_FALSE(registry.GetState(s, i3)); // Removed because second oldest
ASSERT_TRUE(registry.GetState(s, i4));
registry.SetMaxCompletedJobs(1); // (*)
ASSERT_FALSE(registry.GetState(s, i1)); // Just discarded by (*)
ASSERT_TRUE(registry.GetState(s, i4));
}
TEST(JobsRegistry, Simultaneous)
{
JobsRegistry registry(10);
std::string i1, i2;
registry.Submit(i1, new DummyJob(), 20);
registry.Submit(i2, new DummyJob(), 10);
ASSERT_TRUE(CheckState(registry, i1, JobState_Pending));
ASSERT_TRUE(CheckState(registry, i2, JobState_Pending));
{
JobsRegistry::RunningJob job1(registry, 0);
JobsRegistry::RunningJob job2(registry, 0);
ASSERT_TRUE(job1.IsValid());
ASSERT_TRUE(job2.IsValid());
job1.MarkFailure();
job2.MarkSuccess();
ASSERT_TRUE(CheckState(registry, i1, JobState_Running));
ASSERT_TRUE(CheckState(registry, i2, JobState_Running));
}
ASSERT_TRUE(CheckState(registry, i1, JobState_Failure));
ASSERT_TRUE(CheckState(registry, i2, JobState_Success));
}
TEST(JobsRegistry, Resubmit)
{
JobsRegistry registry(10);
std::string id;
registry.Submit(id, new DummyJob(), 10);
ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
registry.Resubmit(id);
ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
{
JobsRegistry::RunningJob job(registry, 0);
ASSERT_TRUE(job.IsValid());
job.MarkFailure();
ASSERT_TRUE(CheckState(registry, id, JobState_Running));
registry.Resubmit(id);
ASSERT_TRUE(CheckState(registry, id, JobState_Running));
}
ASSERT_TRUE(CheckState(registry, id, JobState_Failure));
registry.Resubmit(id);
ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
{
JobsRegistry::RunningJob job(registry, 0);
ASSERT_TRUE(job.IsValid());
ASSERT_EQ(id, job.GetId());
job.MarkSuccess();
ASSERT_TRUE(CheckState(registry, id, JobState_Running));
}
ASSERT_TRUE(CheckState(registry, id, JobState_Success));
registry.Resubmit(id);
ASSERT_TRUE(CheckState(registry, id, JobState_Success));
}
TEST(JobsRegistry, Retry)
{
JobsRegistry registry(10);
std::string id;
registry.Submit(id, new DummyJob(), 10);
ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
{
JobsRegistry::RunningJob job(registry, 0);
ASSERT_TRUE(job.IsValid());
job.MarkRetry(0);
ASSERT_TRUE(CheckState(registry, id, JobState_Running));
}
ASSERT_TRUE(CheckState(registry, id, JobState_Retry));
registry.Resubmit(id);
ASSERT_TRUE(CheckState(registry, id, JobState_Retry));
registry.ScheduleRetries();
ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
{
JobsRegistry::RunningJob job(registry, 0);
ASSERT_TRUE(job.IsValid());
job.MarkSuccess();
ASSERT_TRUE(CheckState(registry, id, JobState_Running));
}
ASSERT_TRUE(CheckState(registry, id, JobState_Success));
}
TEST(JobsRegistry, PausePending)
{
JobsRegistry registry(10);
std::string id;
registry.Submit(id, new DummyJob(), 10);
ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
registry.Pause(id);
ASSERT_TRUE(CheckState(registry, id, JobState_Paused));
registry.Pause(id);
ASSERT_TRUE(CheckState(registry, id, JobState_Paused));
registry.Resubmit(id);
ASSERT_TRUE(CheckState(registry, id, JobState_Paused));
registry.Resume(id);
ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
}
TEST(JobsRegistry, PauseRunning)
{
JobsRegistry registry(10);
std::string id;
registry.Submit(id, new DummyJob(), 10);
ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
{
JobsRegistry::RunningJob job(registry, 0);
ASSERT_TRUE(job.IsValid());
registry.Resubmit(id);
job.MarkPause();
ASSERT_TRUE(CheckState(registry, id, JobState_Running));
}
ASSERT_TRUE(CheckState(registry, id, JobState_Paused));
registry.Resubmit(id);
ASSERT_TRUE(CheckState(registry, id, JobState_Paused));
registry.Resume(id);
ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
{
JobsRegistry::RunningJob job(registry, 0);
ASSERT_TRUE(job.IsValid());
job.MarkSuccess();
ASSERT_TRUE(CheckState(registry, id, JobState_Running));
}
ASSERT_TRUE(CheckState(registry, id, JobState_Success));
}
TEST(JobsRegistry, PauseRetry)
{
JobsRegistry registry(10);
std::string id;
registry.Submit(id, new DummyJob(), 10);
ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
{
JobsRegistry::RunningJob job(registry, 0);
ASSERT_TRUE(job.IsValid());
job.MarkRetry(0);
ASSERT_TRUE(CheckState(registry, id, JobState_Running));
}
ASSERT_TRUE(CheckState(registry, id, JobState_Retry));
registry.Pause(id);
ASSERT_TRUE(CheckState(registry, id, JobState_Paused));
registry.Resume(id);
ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
{
JobsRegistry::RunningJob job(registry, 0);
ASSERT_TRUE(job.IsValid());
job.MarkSuccess();
ASSERT_TRUE(CheckState(registry, id, JobState_Running));
}
ASSERT_TRUE(CheckState(registry, id, JobState_Success));
}
TEST(JobsRegistry, Cancel)
{
JobsRegistry registry(10);
std::string id;
registry.Submit(id, new DummyJob(), 10);
ASSERT_FALSE(registry.Cancel("nope"));
ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_Success));
ASSERT_TRUE(registry.Cancel(id));
ASSERT_TRUE(CheckState(registry, id, JobState_Failure));
ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_CanceledJob));
ASSERT_TRUE(registry.Cancel(id));
ASSERT_TRUE(CheckState(registry, id, JobState_Failure));
ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_CanceledJob));
ASSERT_TRUE(registry.Resubmit(id));
ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_CanceledJob));
{
JobsRegistry::RunningJob job(registry, 0);
ASSERT_TRUE(job.IsValid());
ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_Success));
job.MarkSuccess();
ASSERT_TRUE(CheckState(registry, id, JobState_Running));
}
ASSERT_TRUE(CheckState(registry, id, JobState_Success));
ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_Success));
ASSERT_TRUE(registry.Cancel(id));
ASSERT_TRUE(CheckState(registry, id, JobState_Success));
ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_Success));
registry.Submit(id, new DummyJob(), 10);
{
JobsRegistry::RunningJob job(registry, 0);
ASSERT_TRUE(job.IsValid());
ASSERT_EQ(id, job.GetId());
ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_Success));
ASSERT_TRUE(CheckState(registry, id, JobState_Running));
job.MarkCanceled();
}
ASSERT_TRUE(CheckState(registry, id, JobState_Failure));
ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_CanceledJob));
ASSERT_TRUE(registry.Resubmit(id));
ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_CanceledJob));
ASSERT_TRUE(registry.Pause(id));
ASSERT_TRUE(CheckState(registry, id, JobState_Paused));
ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_CanceledJob));
ASSERT_TRUE(registry.Cancel(id));
ASSERT_TRUE(CheckState(registry, id, JobState_Failure));
ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_CanceledJob));
ASSERT_TRUE(registry.Resubmit(id));
ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_CanceledJob));
{
JobsRegistry::RunningJob job(registry, 0);
ASSERT_TRUE(job.IsValid());
ASSERT_EQ(id, job.GetId());
ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_Success));
ASSERT_TRUE(CheckState(registry, id, JobState_Running));
job.MarkRetry(500);
}
ASSERT_TRUE(CheckState(registry, id, JobState_Retry));
ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_Success));
ASSERT_TRUE(registry.Cancel(id));
ASSERT_TRUE(CheckState(registry, id, JobState_Failure));
ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_CanceledJob));
}
TEST(JobsEngine, SubmitAndWait)
{
JobsEngine engine(10);
engine.SetThreadSleep(10);
engine.SetWorkersCount(3);
engine.Start();
Json::Value content = Json::nullValue;
engine.GetRegistry().SubmitAndWait(content, new DummyJob(), rand() % 10);
ASSERT_EQ(Json::objectValue, content.type());
ASSERT_EQ("world", content["hello"].asString());
content = Json::nullValue;
ASSERT_THROW(engine.GetRegistry().SubmitAndWait(content, new DummyJob(true), rand() % 10), OrthancException);
ASSERT_EQ(Json::nullValue, content.type());
engine.Stop();
}
TEST(JobsEngine, DISABLED_SequenceOfOperationsJob)
{
JobsEngine engine(10);
engine.SetThreadSleep(10);
engine.SetWorkersCount(3);
engine.Start();
std::string id;
SequenceOfOperationsJob* job = NULL;
{
std::unique_ptr a(new SequenceOfOperationsJob);
job = a.get();
engine.GetRegistry().Submit(id, a.release(), 0);
}
boost::this_thread::sleep(boost::posix_time::milliseconds(500));
{
SequenceOfOperationsJob::Lock lock(*job);
size_t i = lock.AddOperation(new LogJobOperation);
size_t j = lock.AddOperation(new LogJobOperation);
size_t k = lock.AddOperation(new LogJobOperation);
StringOperationValue a("Hello");
StringOperationValue b("World");
lock.AddInput(i, a);
lock.AddInput(i, b);
lock.Connect(i, j);
lock.Connect(j, k);
}
boost::this_thread::sleep(boost::posix_time::milliseconds(2000));
engine.Stop();
}
static bool CheckSameJson(const Json::Value& a,
const Json::Value& b)
{
std::string s = a.toStyledString();
std::string t = b.toStyledString();
if (s == t)
{
return true;
}
else
{
LOG(ERROR) << "Expected serialization: " << s;
LOG(ERROR) << "Actual serialization: " << t;
return false;
}
}
static bool CheckIdempotentSerialization(IJobUnserializer& unserializer,
const IJob& job)
{
Json::Value a = 42;
if (!job.Serialize(a))
{
return false;
}
else
{
std::unique_ptr unserialized(unserializer.UnserializeJob(a));
Json::Value b = 43;
if (unserialized->Serialize(b))
{
return (CheckSameJson(a, b));
}
else
{
return false;
}
}
}
static bool CheckIdempotentSetOfInstances(IJobUnserializer& unserializer,
const SetOfInstancesJob& job)
{
Json::Value a = 42;
if (!job.Serialize(a))
{
return false;
}
else
{
std::unique_ptr unserialized
(dynamic_cast(unserializer.UnserializeJob(a)));
Json::Value b = 43;
if (unserialized->Serialize(b))
{
return (CheckSameJson(a, b) &&
job.HasTrailingStep() == unserialized->HasTrailingStep() &&
job.GetPosition() == unserialized->GetPosition() &&
job.GetInstancesCount() == unserialized->GetInstancesCount() &&
job.GetCommandsCount() == unserialized->GetCommandsCount());
}
else
{
return false;
}
}
}
static bool CheckIdempotentSerialization(IJobUnserializer& unserializer,
const IJobOperationValue& value)
{
Json::Value a = 42;
value.Serialize(a);
std::unique_ptr unserialized(unserializer.UnserializeValue(a));
Json::Value b = 43;
unserialized->Serialize(b);
return CheckSameJson(a, b);
}
TEST(JobsSerialization, BadFileFormat)
{
GenericJobUnserializer unserializer;
Json::Value s;
s = Json::objectValue;
ASSERT_THROW(unserializer.UnserializeValue(s), OrthancException);
ASSERT_THROW(unserializer.UnserializeJob(s), OrthancException);
ASSERT_THROW(unserializer.UnserializeOperation(s), OrthancException);
s = Json::arrayValue;
ASSERT_THROW(unserializer.UnserializeValue(s), OrthancException);
ASSERT_THROW(unserializer.UnserializeJob(s), OrthancException);
ASSERT_THROW(unserializer.UnserializeOperation(s), OrthancException);
s = "hello";
ASSERT_THROW(unserializer.UnserializeValue(s), OrthancException);
ASSERT_THROW(unserializer.UnserializeJob(s), OrthancException);
ASSERT_THROW(unserializer.UnserializeOperation(s), OrthancException);
s = 42;
ASSERT_THROW(unserializer.UnserializeValue(s), OrthancException);
ASSERT_THROW(unserializer.UnserializeJob(s), OrthancException);
ASSERT_THROW(unserializer.UnserializeOperation(s), OrthancException);
}
TEST(JobsSerialization, JobOperationValues)
{
Json::Value s;
{
JobOperationValues values;
values.Append(new NullOperationValue);
values.Append(new StringOperationValue("hello"));
values.Append(new StringOperationValue("world"));
s = 42;
values.Serialize(s);
}
{
GenericJobUnserializer unserializer;
std::unique_ptr values(JobOperationValues::Unserialize(unserializer, s));
ASSERT_EQ(3u, values->GetSize());
ASSERT_EQ(IJobOperationValue::Type_Null, values->GetValue(0).GetType());
ASSERT_EQ(IJobOperationValue::Type_String, values->GetValue(1).GetType());
ASSERT_EQ(IJobOperationValue::Type_String, values->GetValue(2).GetType());
ASSERT_EQ("hello", dynamic_cast(values->GetValue(1)).GetContent());
ASSERT_EQ("world", dynamic_cast(values->GetValue(2)).GetContent());
}
}
TEST(JobsSerialization, GenericValues)
{
GenericJobUnserializer unserializer;
Json::Value s;
{
NullOperationValue null;
ASSERT_TRUE(CheckIdempotentSerialization(unserializer, null));
null.Serialize(s);
}
ASSERT_THROW(unserializer.UnserializeJob(s), OrthancException);
ASSERT_THROW(unserializer.UnserializeOperation(s), OrthancException);
std::unique_ptr value;
value.reset(unserializer.UnserializeValue(s));
ASSERT_EQ(IJobOperationValue::Type_Null, value->GetType());
{
StringOperationValue str("Hello");
ASSERT_TRUE(CheckIdempotentSerialization(unserializer, str));
str.Serialize(s);
}
ASSERT_THROW(unserializer.UnserializeJob(s), OrthancException);
ASSERT_THROW(unserializer.UnserializeOperation(s), OrthancException);
value.reset(unserializer.UnserializeValue(s));
ASSERT_EQ(IJobOperationValue::Type_String, value->GetType());
ASSERT_EQ("Hello", dynamic_cast(*value).GetContent());
}
TEST(JobsSerialization, GenericJobs)
{
Json::Value s;
// This tests SetOfInstancesJob
{
DummyInstancesJob job;
job.SetDescription("description");
job.AddInstance("hello");
job.AddInstance("nope");
job.AddInstance("world");
job.SetPermissive(true);
ASSERT_THROW(job.Step("jobId"), OrthancException); // Not started yet
ASSERT_FALSE(job.HasTrailingStep());
ASSERT_FALSE(job.IsTrailingStepDone());
job.Start();
ASSERT_EQ(JobStepCode_Continue, job.Step("jobId").GetCode());
ASSERT_EQ(JobStepCode_Continue, job.Step("jobId").GetCode());
{
DummyUnserializer unserializer;
ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job));
}
ASSERT_TRUE(job.Serialize(s));
}
{
DummyUnserializer unserializer;
ASSERT_THROW(unserializer.UnserializeValue(s), OrthancException);
ASSERT_THROW(unserializer.UnserializeOperation(s), OrthancException);
std::unique_ptr job;
job.reset(unserializer.UnserializeJob(s));
const DummyInstancesJob& tmp = dynamic_cast(*job);
ASSERT_FALSE(tmp.IsStarted());
ASSERT_TRUE(tmp.IsPermissive());
ASSERT_EQ("description", tmp.GetDescription());
ASSERT_EQ(3u, tmp.GetInstancesCount());
ASSERT_EQ(2u, tmp.GetPosition());
ASSERT_EQ(1u, tmp.GetFailedInstances().size());
ASSERT_EQ("hello", tmp.GetInstance(0));
ASSERT_EQ("nope", tmp.GetInstance(1));
ASSERT_EQ("world", tmp.GetInstance(2));
ASSERT_TRUE(tmp.IsFailedInstance("nope"));
}
// SequenceOfOperationsJob
{
SequenceOfOperationsJob job;
job.SetDescription("hello");
{
SequenceOfOperationsJob::Lock lock(job);
size_t a = lock.AddOperation(new LogJobOperation);
size_t b = lock.AddOperation(new LogJobOperation);
lock.Connect(a, b);
StringOperationValue s1("hello");
StringOperationValue s2("world");
lock.AddInput(a, s1);
lock.AddInput(a, s2);
lock.SetTrailingOperationTimeout(300);
}
ASSERT_EQ(JobStepCode_Continue, job.Step("jobId").GetCode());
{
GenericJobUnserializer unserializer;
ASSERT_TRUE(CheckIdempotentSerialization(unserializer, job));
}
ASSERT_TRUE(job.Serialize(s));
}
{
GenericJobUnserializer unserializer;
ASSERT_THROW(unserializer.UnserializeValue(s), OrthancException);
ASSERT_THROW(unserializer.UnserializeOperation(s), OrthancException);
std::unique_ptr job;
job.reset(unserializer.UnserializeJob(s));
std::string tmp;
dynamic_cast(*job).GetDescription(tmp);
ASSERT_EQ("hello", tmp);
}
}
static bool IsSameTagValue(const ParsedDicomFile& dicom1,
const ParsedDicomFile& dicom2,
DicomTag tag)
{
std::string a, b;
return (dicom1.GetTagValue(a, tag) &&
dicom2.GetTagValue(b, tag) &&
(a == b));
}
TEST(JobsSerialization, DicomModification)
{
Json::Value s;
ParsedDicomFile source(true);
source.Insert(DICOM_TAG_STUDY_DESCRIPTION, "Test 1", false, "");
source.Insert(DICOM_TAG_SERIES_DESCRIPTION, "Test 2", false, "");
source.Insert(DICOM_TAG_PATIENT_NAME, "Test 3", false, "");
std::unique_ptr modified(source.Clone(true));
{
DicomModification modification;
modification.SetLevel(ResourceType_Series);
modification.Clear(DICOM_TAG_STUDY_DESCRIPTION);
modification.Remove(DICOM_TAG_SERIES_DESCRIPTION);
modification.Replace(DICOM_TAG_PATIENT_NAME, "Test 4", true);
modification.Apply(*modified);
s = 42;
modification.Serialize(s);
}
{
DicomModification modification(s);
ASSERT_EQ(ResourceType_Series, modification.GetLevel());
std::unique_ptr second(source.Clone(true));
modification.Apply(*second);
std::string t;
ASSERT_TRUE(second->GetTagValue(t, DICOM_TAG_STUDY_DESCRIPTION));
ASSERT_TRUE(t.empty());
ASSERT_FALSE(second->GetTagValue(t, DICOM_TAG_SERIES_DESCRIPTION));
ASSERT_TRUE(second->GetTagValue(t, DICOM_TAG_PATIENT_NAME));
ASSERT_EQ("Test 4", t);
ASSERT_TRUE(IsSameTagValue(source, *modified, DICOM_TAG_STUDY_INSTANCE_UID));
ASSERT_TRUE(IsSameTagValue(source, *second, DICOM_TAG_STUDY_INSTANCE_UID));
ASSERT_FALSE(IsSameTagValue(source, *second, DICOM_TAG_SERIES_INSTANCE_UID));
ASSERT_TRUE(IsSameTagValue(*modified, *second, DICOM_TAG_SERIES_INSTANCE_UID));
}
}
TEST(JobsSerialization, DicomModification2)
{
Json::Value s;
{
DicomModification modification;
modification.SetupAnonymization(DicomVersion_2017c);
modification.Remove(DicomPath(DICOM_TAG_REFERENCED_IMAGE_SEQUENCE, 1, DICOM_TAG_SOP_INSTANCE_UID));
modification.Replace(DicomPath(DICOM_TAG_REFERENCED_IMAGE_SEQUENCE, 1, DICOM_TAG_SOP_CLASS_UID), "Hello", true);
modification.Keep(DicomPath(DICOM_TAG_REFERENCED_IMAGE_SEQUENCE, 1, DICOM_TAG_PATIENT_NAME));
s = 42;
modification.Serialize(s);
}
{
DicomModification modification(s);
// Check idempotent serialization
Json::Value ss;
modification.Serialize(ss);
ASSERT_EQ(s.toStyledString(), ss.toStyledString());
}
}
TEST(JobsSerialization, Registry)
{
Json::Value s;
std::string i1, i2;
{
JobsRegistry registry(10);
registry.Submit(i1, new DummyJob(), 10);
registry.Submit(i2, new SequenceOfOperationsJob(), 30);
registry.Serialize(s);
}
{
DummyUnserializer unserializer;
JobsRegistry registry(unserializer, s, 10);
Json::Value t;
registry.Serialize(t);
ASSERT_TRUE(CheckSameJson(s, t));
}
}
TEST(JobsSerialization, TrailingStep)
{
{
Json::Value s;
DummyInstancesJob job;
ASSERT_EQ(0u, job.GetCommandsCount());
ASSERT_EQ(0u, job.GetInstancesCount());
job.Start();
ASSERT_EQ(0u, job.GetPosition());
ASSERT_FALSE(job.HasTrailingStep());
ASSERT_FALSE(job.IsTrailingStepDone());
{
DummyUnserializer unserializer;
ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job));
}
ASSERT_EQ(JobStepCode_Success, job.Step("jobId").GetCode());
ASSERT_EQ(1u, job.GetPosition());
ASSERT_FALSE(job.IsTrailingStepDone());
{
DummyUnserializer unserializer;
ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job));
}
ASSERT_THROW(job.Step("jobId"), OrthancException);
}
{
Json::Value s;
DummyInstancesJob job;
job.AddInstance("hello");
job.AddInstance("world");
ASSERT_EQ(2u, job.GetCommandsCount());
ASSERT_EQ(2u, job.GetInstancesCount());
job.Start();
ASSERT_EQ(0u, job.GetPosition());
ASSERT_FALSE(job.HasTrailingStep());
ASSERT_FALSE(job.IsTrailingStepDone());
{
DummyUnserializer unserializer;
ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job));
}
ASSERT_EQ(JobStepCode_Continue, job.Step("jobId").GetCode());
ASSERT_EQ(1u, job.GetPosition());
ASSERT_FALSE(job.IsTrailingStepDone());
{
DummyUnserializer unserializer;
ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job));
}
ASSERT_EQ(JobStepCode_Success, job.Step("jobId").GetCode());
ASSERT_EQ(2u, job.GetPosition());
ASSERT_FALSE(job.IsTrailingStepDone());
{
DummyUnserializer unserializer;
ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job));
}
ASSERT_THROW(job.Step("jobId"), OrthancException);
}
{
Json::Value s;
DummyInstancesJob job;
ASSERT_EQ(0u, job.GetInstancesCount());
ASSERT_EQ(0u, job.GetCommandsCount());
job.AddTrailingStep();
ASSERT_EQ(0u, job.GetInstancesCount());
ASSERT_EQ(1u, job.GetCommandsCount());
job.Start(); // This adds the trailing step
ASSERT_EQ(0u, job.GetPosition());
ASSERT_TRUE(job.HasTrailingStep());
ASSERT_FALSE(job.IsTrailingStepDone());
{
DummyUnserializer unserializer;
ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job));
}
ASSERT_EQ(JobStepCode_Success, job.Step("jobId").GetCode());
ASSERT_EQ(1u, job.GetPosition());
ASSERT_TRUE(job.IsTrailingStepDone());
{
DummyUnserializer unserializer;
ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job));
}
ASSERT_THROW(job.Step("jobId"), OrthancException);
}
{
Json::Value s;
DummyInstancesJob job;
job.AddInstance("hello");
ASSERT_EQ(1u, job.GetInstancesCount());
ASSERT_EQ(1u, job.GetCommandsCount());
job.AddTrailingStep();
ASSERT_EQ(1u, job.GetInstancesCount());
ASSERT_EQ(2u, job.GetCommandsCount());
job.Start();
ASSERT_EQ(2u, job.GetCommandsCount());
ASSERT_EQ(0u, job.GetPosition());
ASSERT_TRUE(job.HasTrailingStep());
ASSERT_FALSE(job.IsTrailingStepDone());
{
DummyUnserializer unserializer;
ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job));
}
ASSERT_EQ(JobStepCode_Continue, job.Step("jobId").GetCode());
ASSERT_EQ(1u, job.GetPosition());
ASSERT_FALSE(job.IsTrailingStepDone());
{
DummyUnserializer unserializer;
ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job));
}
ASSERT_EQ(JobStepCode_Success, job.Step("jobId").GetCode());
ASSERT_EQ(2u, job.GetPosition());
ASSERT_TRUE(job.IsTrailingStepDone());
{
DummyUnserializer unserializer;
ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job));
}
ASSERT_THROW(job.Step("jobId"), OrthancException);
}
}
TEST(JobsSerialization, RemoteModalityParameters)
{
Json::Value s;
{
RemoteModalityParameters modality;
ASSERT_FALSE(modality.IsAdvancedFormatNeeded());
modality.Serialize(s, false);
ASSERT_EQ(Json::arrayValue, s.type());
ASSERT_FALSE(modality.IsDicomTlsEnabled());
ASSERT_FALSE(modality.HasTimeout());
ASSERT_EQ(0u, modality.GetTimeout());
}
{
RemoteModalityParameters modality(s);
ASSERT_FALSE(modality.IsAdvancedFormatNeeded());
ASSERT_EQ("ORTHANC", modality.GetApplicationEntityTitle());
ASSERT_EQ("127.0.0.1", modality.GetHost());
ASSERT_EQ(104u, modality.GetPortNumber());
ASSERT_EQ(ModalityManufacturer_Generic, modality.GetManufacturer());
ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_Echo));
ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_Find));
ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_FindWorklist));
ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_Get));
ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_Store));
ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_Move));
ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_NAction));
ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_NEventReport));
ASSERT_TRUE(modality.IsTranscodingAllowed());
ASSERT_FALSE(modality.IsDicomTlsEnabled());
ASSERT_FALSE(modality.HasLocalAet());
ASSERT_THROW(modality.GetLocalAet(), OrthancException);
ASSERT_FALSE(modality.HasTimeout());
ASSERT_EQ(0u, modality.GetTimeout());
}
s = Json::nullValue;
{
RemoteModalityParameters modality;
ASSERT_THROW(modality.SetPortNumber(0), OrthancException);
ASSERT_THROW(modality.SetPortNumber(65535), OrthancException);
modality.SetApplicationEntityTitle("HELLO");
modality.SetHost("world");
modality.SetPortNumber(45);
modality.SetManufacturer(ModalityManufacturer_GenericNoWildcardInDates);
ASSERT_FALSE(modality.IsAdvancedFormatNeeded());
modality.Serialize(s, true);
ASSERT_EQ(Json::objectValue, s.type());
ASSERT_FALSE(modality.HasLocalAet());
ASSERT_FALSE(modality.HasTimeout());
ASSERT_EQ(0u, modality.GetTimeout());
}
{
RemoteModalityParameters modality(s);
ASSERT_EQ("HELLO", modality.GetApplicationEntityTitle());
ASSERT_EQ("world", modality.GetHost());
ASSERT_EQ(45u, modality.GetPortNumber());
ASSERT_EQ(ModalityManufacturer_GenericNoWildcardInDates, modality.GetManufacturer());
ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_Echo));
ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_Find));
ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_FindWorklist));
ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_Get));
ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_Store));
ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_Move));
ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_NAction));
ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_NEventReport));
ASSERT_TRUE(modality.IsTranscodingAllowed());
ASSERT_FALSE(modality.IsDicomTlsEnabled());
ASSERT_FALSE(modality.HasLocalAet());
ASSERT_FALSE(modality.HasTimeout());
ASSERT_EQ(0u, modality.GetTimeout());
}
s["Port"] = "46";
{
RemoteModalityParameters modality(s);
ASSERT_EQ(46u, modality.GetPortNumber());
}
s["Port"] = -1; ASSERT_THROW(RemoteModalityParameters m(s), OrthancException);
s["Port"] = 65535; ASSERT_THROW(RemoteModalityParameters m(s), OrthancException);
s["Port"] = "nope"; ASSERT_THROW(RemoteModalityParameters m(s), OrthancException);
std::set operations;
operations.insert(DicomRequestType_Echo);
operations.insert(DicomRequestType_Find);
operations.insert(DicomRequestType_FindWorklist);
operations.insert(DicomRequestType_Get);
operations.insert(DicomRequestType_Move);
operations.insert(DicomRequestType_Store);
operations.insert(DicomRequestType_NAction);
operations.insert(DicomRequestType_NEventReport);
ASSERT_EQ(8u, operations.size());
for (std::set::const_iterator
it = operations.begin(); it != operations.end(); ++it)
{
{
RemoteModalityParameters modality;
modality.SetRequestAllowed(*it, false);
ASSERT_TRUE(modality.IsAdvancedFormatNeeded());
modality.Serialize(s, false);
ASSERT_EQ(Json::objectValue, s.type());
}
{
RemoteModalityParameters modality(s);
ASSERT_FALSE(modality.IsRequestAllowed(*it));
for (std::set::const_iterator
it2 = operations.begin(); it2 != operations.end(); ++it2)
{
if (*it2 != *it)
{
ASSERT_TRUE(modality.IsRequestAllowed(*it2));
}
}
}
}
s = Json::nullValue;
{
RemoteModalityParameters modality;
modality.SetLocalAet("hello");
modality.SetTimeout(42);
ASSERT_TRUE(modality.IsAdvancedFormatNeeded());
modality.Serialize(s, true);
ASSERT_EQ(Json::objectValue, s.type());
ASSERT_TRUE(modality.HasLocalAet());
ASSERT_TRUE(modality.HasTimeout());
ASSERT_EQ(42u, modality.GetTimeout());
}
{
RemoteModalityParameters modality(s);
ASSERT_TRUE(modality.HasLocalAet());
ASSERT_EQ("hello", modality.GetLocalAet());
ASSERT_TRUE(modality.HasTimeout());
ASSERT_EQ(42u, modality.GetTimeout());
}
{
Json::Value t;
t["AllowStorageCommitment"] = false;
t["AET"] = "AET";
t["Host"] = "host";
t["Port"] = "104";
RemoteModalityParameters modality(t);
ASSERT_TRUE(modality.IsAdvancedFormatNeeded());
ASSERT_EQ("AET", modality.GetApplicationEntityTitle());
ASSERT_EQ("host", modality.GetHost());
ASSERT_EQ(104u, modality.GetPortNumber());
ASSERT_FALSE(modality.IsRequestAllowed(DicomRequestType_NAction));
ASSERT_FALSE(modality.IsRequestAllowed(DicomRequestType_NEventReport));
ASSERT_TRUE(modality.IsTranscodingAllowed());
ASSERT_FALSE(modality.IsDicomTlsEnabled());
ASSERT_FALSE(modality.HasLocalAet());
ASSERT_THROW(modality.GetLocalAet(), OrthancException);
ASSERT_FALSE(modality.HasTimeout());
ASSERT_EQ(0u, modality.GetTimeout());
}
{
Json::Value t;
t["AllowNAction"] = false;
t["AllowNEventReport"] = true;
t["AET"] = "AET";
t["Host"] = "host";
t["Port"] = "104";
t["AllowTranscoding"] = false;
t["UseDicomTls"] = true;
t["LocalAet"] = "world";
t["Timeout"] = 20;
RemoteModalityParameters modality(t);
ASSERT_TRUE(modality.IsAdvancedFormatNeeded());
ASSERT_EQ("AET", modality.GetApplicationEntityTitle());
ASSERT_EQ("host", modality.GetHost());
ASSERT_EQ(104u, modality.GetPortNumber());
ASSERT_FALSE(modality.IsRequestAllowed(DicomRequestType_NAction));
ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_NEventReport));
ASSERT_FALSE(modality.IsTranscodingAllowed());
ASSERT_TRUE(modality.IsDicomTlsEnabled());
ASSERT_TRUE(modality.HasLocalAet());
ASSERT_EQ("world", modality.GetLocalAet());
ASSERT_TRUE(modality.HasTimeout());
ASSERT_EQ(20u, modality.GetTimeout());
}
{
Json::Value t;
t["AllowNAction"] = true;
t["AllowNEventReport"] = true;
t["AET"] = "AET";
t["Host"] = "host";
t["Port"] = "104";
RemoteModalityParameters modality(t);
ASSERT_FALSE(modality.IsAdvancedFormatNeeded());
ASSERT_EQ("AET", modality.GetApplicationEntityTitle());
ASSERT_EQ("host", modality.GetHost());
ASSERT_EQ(104u, modality.GetPortNumber());
ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_NAction));
ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_NEventReport));
ASSERT_TRUE(modality.IsTranscodingAllowed());
ASSERT_FALSE(modality.IsDicomTlsEnabled());
ASSERT_FALSE(modality.HasLocalAet());
ASSERT_THROW(modality.GetLocalAet(), OrthancException);
}
}
TEST(JobsSerialization, DicomAssociationParameters)
{
{
DicomAssociationParameters a;
Json::Value v = Json::objectValue;
a.SerializeJob(v);
ASSERT_EQ(Json::objectValue, v.type());
ASSERT_EQ("ORTHANC", v["LocalAet"].asString());
ASSERT_EQ(DicomAssociationParameters::GetDefaultTimeout(), v["Timeout"].asUInt());
ASSERT_TRUE(v.isMember("Remote"));
ASSERT_TRUE(v.isMember("MaximumPduLength"));
ASSERT_EQ(5u, v.getMemberNames().size());
DicomAssociationParameters b;
b.UnserializeJob(v);
ASSERT_EQ("ANY-SCP", b.GetRemoteModality().GetApplicationEntityTitle());
ASSERT_EQ("127.0.0.1", b.GetRemoteModality().GetHost());
ASSERT_EQ(104u, b.GetRemoteModality().GetPortNumber());
ASSERT_EQ("ORTHANC", b.GetLocalApplicationEntityTitle());
ASSERT_EQ(DicomAssociationParameters::GetDefaultMaximumPduLength(), b.GetMaximumPduLength());
ASSERT_FALSE(b.GetRemoteModality().IsDicomTlsEnabled());
ASSERT_FALSE(b.GetRemoteModality().HasLocalAet());
ASSERT_THROW(b.GetRemoteModality().GetLocalAet(), OrthancException);
ASSERT_FALSE(b.GetRemoteModality().HasTimeout());
ASSERT_EQ(0u, b.GetRemoteModality().GetTimeout());
ASSERT_TRUE(b.IsRemoteCertificateRequired());
}
{
RemoteModalityParameters p;
p.SetApplicationEntityTitle("WORLD");
p.SetPortNumber(4242);
p.SetHost("hello.world.com");
p.SetDicomTlsEnabled(true);
p.SetTimeout(42);
DicomAssociationParameters a("HELLO", p);
a.SetOwnCertificatePath("key", "crt");
a.SetTrustedCertificatesPath("trusted");
a.SetRemoteCertificateRequired(false);
ASSERT_THROW(a.SetMaximumPduLength(4095), OrthancException);
ASSERT_THROW(a.SetMaximumPduLength(131073), OrthancException);
a.SetMaximumPduLength(4096);
a.SetMaximumPduLength(131072);
Json::Value v = Json::objectValue;
a.SerializeJob(v);
ASSERT_EQ(8u, v.getMemberNames().size());
DicomAssociationParameters b = DicomAssociationParameters::UnserializeJob(v);
ASSERT_EQ("WORLD", b.GetRemoteModality().GetApplicationEntityTitle());
ASSERT_EQ("hello.world.com", b.GetRemoteModality().GetHost());
ASSERT_EQ(4242u, b.GetRemoteModality().GetPortNumber());
ASSERT_EQ("HELLO", b.GetLocalApplicationEntityTitle());
ASSERT_TRUE(b.GetRemoteModality().IsDicomTlsEnabled());
ASSERT_EQ("key", b.GetOwnPrivateKeyPath());
ASSERT_EQ("crt", b.GetOwnCertificatePath());
ASSERT_EQ("trusted", b.GetTrustedCertificatesPath());
ASSERT_EQ(131072u, b.GetMaximumPduLength());
ASSERT_TRUE(b.GetRemoteModality().HasTimeout());
ASSERT_EQ(42u, b.GetRemoteModality().GetTimeout());
ASSERT_FALSE(b.IsRemoteCertificateRequired());
}
}
TEST(SerializationToolbox, Numbers)
{
{
int32_t i;
ASSERT_FALSE(SerializationToolbox::ParseInteger32(i, ""));
ASSERT_FALSE(SerializationToolbox::ParseInteger32(i, "ee"));
ASSERT_TRUE(SerializationToolbox::ParseInteger32(i, "42")); ASSERT_EQ(42, i);
ASSERT_TRUE(SerializationToolbox::ParseInteger32(i, "-42")); ASSERT_EQ(-42, i);
ASSERT_TRUE(SerializationToolbox::ParseInteger32(i, "-2147483648")); ASSERT_EQ(-2147483648l, i);
ASSERT_TRUE(SerializationToolbox::ParseInteger32(i, "2147483647")); ASSERT_EQ(2147483647l, i);
ASSERT_FALSE(SerializationToolbox::ParseInteger32(i, "-2147483649"));
ASSERT_FALSE(SerializationToolbox::ParseInteger32(i, "2147483648"));
ASSERT_FALSE(SerializationToolbox::ParseInteger32(i, "-2\\-3\\-4"));
ASSERT_TRUE(SerializationToolbox::ParseFirstInteger32(i, "-2\\-3\\-4")); ASSERT_EQ(-2, i);
}
{
uint32_t i;
ASSERT_FALSE(SerializationToolbox::ParseUnsignedInteger32(i, ""));
ASSERT_FALSE(SerializationToolbox::ParseUnsignedInteger32(i, "ee"));
ASSERT_TRUE(SerializationToolbox::ParseUnsignedInteger32(i, "42")); ASSERT_EQ(42u, i);
ASSERT_FALSE(SerializationToolbox::ParseUnsignedInteger32(i, "-42"));
ASSERT_TRUE(SerializationToolbox::ParseUnsignedInteger32(i, "4294967295")); ASSERT_EQ(4294967295u, i);
ASSERT_FALSE(SerializationToolbox::ParseUnsignedInteger32(i, "4294967296"));
ASSERT_FALSE(SerializationToolbox::ParseUnsignedInteger32(i, "2\\3\\4"));
ASSERT_TRUE(SerializationToolbox::ParseFirstUnsignedInteger32(i, "2\\3\\4")); ASSERT_EQ(2u, i);
}
{
int64_t i;
ASSERT_FALSE(SerializationToolbox::ParseInteger64(i, ""));
ASSERT_FALSE(SerializationToolbox::ParseInteger64(i, "ee"));
ASSERT_TRUE(SerializationToolbox::ParseInteger64(i, "42")); ASSERT_EQ(42, i);
ASSERT_TRUE(SerializationToolbox::ParseInteger64(i, "-42")); ASSERT_EQ(-42, i);
ASSERT_TRUE(SerializationToolbox::ParseInteger64(i, "-2147483649")); ASSERT_EQ(-2147483649ll, i);
ASSERT_TRUE(SerializationToolbox::ParseInteger64(i, "2147483648")); ASSERT_EQ(2147483648ll, i);
ASSERT_FALSE(SerializationToolbox::ParseInteger64(i, "-2\\-3\\-4"));
ASSERT_TRUE(SerializationToolbox::ParseFirstInteger64(i, "-2\\-3\\-4")); ASSERT_EQ(-2, i);
}
{
uint64_t i;
ASSERT_FALSE(SerializationToolbox::ParseUnsignedInteger64(i, ""));
ASSERT_FALSE(SerializationToolbox::ParseUnsignedInteger64(i, "ee"));
ASSERT_TRUE(SerializationToolbox::ParseUnsignedInteger64(i, "42")); ASSERT_EQ(42u, i);
ASSERT_FALSE(SerializationToolbox::ParseUnsignedInteger64(i, "-42"));
ASSERT_TRUE(SerializationToolbox::ParseUnsignedInteger64(i, "4294967296")); ASSERT_EQ(4294967296lu, i);
ASSERT_FALSE(SerializationToolbox::ParseUnsignedInteger64(i, "2\\3\\4"));
ASSERT_TRUE(SerializationToolbox::ParseFirstUnsignedInteger64(i, "2\\3\\4")); ASSERT_EQ(2u, i);
}
{
float i;
ASSERT_FALSE(SerializationToolbox::ParseFloat(i, ""));
ASSERT_FALSE(SerializationToolbox::ParseFloat(i, "ee"));
ASSERT_TRUE(SerializationToolbox::ParseFloat(i, "42")); ASSERT_FLOAT_EQ(42.0f, i);
ASSERT_TRUE(SerializationToolbox::ParseFloat(i, "-42")); ASSERT_FLOAT_EQ(-42.0f, i);
ASSERT_FALSE(SerializationToolbox::ParseFloat(i, "2\\3\\4"));
ASSERT_TRUE(SerializationToolbox::ParseFirstFloat(i, "1.367\\2.367\\3.367")); ASSERT_FLOAT_EQ(1.367f, i);
ASSERT_TRUE(SerializationToolbox::ParseFloat(i, "1.2")); ASSERT_FLOAT_EQ(1.2f, i);
ASSERT_TRUE(SerializationToolbox::ParseFloat(i, "-1.2e+2")); ASSERT_FLOAT_EQ(-120.0f, i);
ASSERT_TRUE(SerializationToolbox::ParseFloat(i, "-1e-2")); ASSERT_FLOAT_EQ(-0.01f, i);
ASSERT_TRUE(SerializationToolbox::ParseFloat(i, "1.3671875")); ASSERT_FLOAT_EQ(1.3671875f, i);
}
{
double i;
ASSERT_FALSE(SerializationToolbox::ParseDouble(i, ""));
ASSERT_FALSE(SerializationToolbox::ParseDouble(i, "ee"));
ASSERT_TRUE(SerializationToolbox::ParseDouble(i, "42")); ASSERT_DOUBLE_EQ(42.0, i);
ASSERT_TRUE(SerializationToolbox::ParseDouble(i, "-42")); ASSERT_DOUBLE_EQ(-42.0, i);
ASSERT_FALSE(SerializationToolbox::ParseDouble(i, "2\\3\\4"));
ASSERT_TRUE(SerializationToolbox::ParseFirstDouble(i, "1.367\\2.367\\3.367")); ASSERT_DOUBLE_EQ(1.367, i);
ASSERT_TRUE(SerializationToolbox::ParseDouble(i, "1.2")); ASSERT_DOUBLE_EQ(1.2, i);
ASSERT_TRUE(SerializationToolbox::ParseDouble(i, "-1.2e+2")); ASSERT_DOUBLE_EQ(-120.0, i);
ASSERT_TRUE(SerializationToolbox::ParseDouble(i, "-1e-2")); ASSERT_DOUBLE_EQ(-0.01, i);
ASSERT_TRUE(SerializationToolbox::ParseDouble(i, "1.3671875")); ASSERT_DOUBLE_EQ(1.3671875, i);
}
}