comparison OrthancServer/UnitTestsSources/ServerJobsTests.cpp @ 4060:149172a06b4d framework

splitting MultiThreadingTests.cpp into JobsTests.cpp and ServerJobsTests.cpp
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 11 Jun 2020 14:29:14 +0200
parents OrthancServer/UnitTestsSources/MultiThreadingTests.cpp@05b8fd21089c
children 0953b3dc3261
comparison
equal deleted inserted replaced
4059:e241e5f3f088 4060:149172a06b4d
1 /**
2 * Orthanc - A Lightweight, RESTful DICOM Store
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
4 * Department, University Hospital of Liege, Belgium
5 * Copyright (C) 2017-2020 Osimis S.A., Belgium
6 *
7 * This program is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation, either version 3 of the
10 * License, or (at your option) any later version.
11 *
12 * In addition, as a special exception, the copyright holders of this
13 * program give permission to link the code of its release with the
14 * OpenSSL project's "OpenSSL" library (or with modified versions of it
15 * that use the same license as the "OpenSSL" library), and distribute
16 * the linked executables. You must obey the GNU General Public License
17 * in all respects for all of the code used other than "OpenSSL". If you
18 * modify file(s) with this exception, you may extend this exception to
19 * your version of the file(s), but you are not obligated to do so. If
20 * you do not wish to do so, delete this exception statement from your
21 * version. If you delete this exception statement from all source files
22 * in the program, then also delete it here.
23 *
24 * This program is distributed in the hope that it will be useful, but
25 * WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27 * General Public License for more details.
28 *
29 * You should have received a copy of the GNU General Public License
30 * along with this program. If not, see <http://www.gnu.org/licenses/>.
31 **/
32
33
34 #include "PrecompiledHeadersUnitTests.h"
35 #include "gtest/gtest.h"
36
37 #include "../../OrthancFramework/Sources/Compatibility.h"
38 #include "../../OrthancFramework/Sources/FileStorage/MemoryStorageArea.h"
39 #include "../../OrthancFramework/Sources/SerializationToolbox.h"
40
41 #include "../Sources/Database/SQLiteDatabaseWrapper.h"
42 #include "../Sources/ServerContext.h"
43 #include "../Sources/ServerJobs/LuaJobManager.h"
44 #include "../Sources/ServerJobs/OrthancJobUnserializer.h"
45
46 #include "../../OrthancFramework/Sources/JobsEngine/Operations/LogJobOperation.h"
47 #include "../Sources/ServerJobs/Operations/DicomInstanceOperationValue.h"
48 #include "../Sources/ServerJobs/Operations/DeleteResourceOperation.h"
49 #include "../Sources/ServerJobs/Operations/ModifyInstanceOperation.h"
50 #include "../Sources/ServerJobs/Operations/StorePeerOperation.h"
51 #include "../Sources/ServerJobs/Operations/StoreScuOperation.h"
52 #include "../Sources/ServerJobs/Operations/SystemCallOperation.h"
53
54 #include "../Sources/ServerJobs/ArchiveJob.h"
55 #include "../Sources/ServerJobs/DicomModalityStoreJob.h"
56 #include "../Sources/ServerJobs/DicomMoveScuJob.h"
57 #include "../Sources/ServerJobs/MergeStudyJob.h"
58 #include "../Sources/ServerJobs/OrthancPeerStoreJob.h"
59 #include "../Sources/ServerJobs/ResourceModificationJob.h"
60 #include "../Sources/ServerJobs/SplitStudyJob.h"
61
62
63 using namespace Orthanc;
64
65 namespace
66 {
67 class DummyJob : public IJob
68 {
69 private:
70 bool fails_;
71 unsigned int count_;
72 unsigned int steps_;
73
74 public:
75 DummyJob() :
76 fails_(false),
77 count_(0),
78 steps_(4)
79 {
80 }
81
82 explicit DummyJob(bool fails) :
83 fails_(fails),
84 count_(0),
85 steps_(4)
86 {
87 }
88
89 virtual void Start() ORTHANC_OVERRIDE
90 {
91 }
92
93 virtual void Reset() ORTHANC_OVERRIDE
94 {
95 }
96
97 virtual JobStepResult Step(const std::string& jobId) ORTHANC_OVERRIDE
98 {
99 if (fails_)
100 {
101 return JobStepResult::Failure(ErrorCode_ParameterOutOfRange, NULL);
102 }
103 else if (count_ == steps_ - 1)
104 {
105 return JobStepResult::Success();
106 }
107 else
108 {
109 count_++;
110 return JobStepResult::Continue();
111 }
112 }
113
114 virtual void Stop(JobStopReason reason) ORTHANC_OVERRIDE
115 {
116 }
117
118 virtual float GetProgress() ORTHANC_OVERRIDE
119 {
120 return static_cast<float>(count_) / static_cast<float>(steps_ - 1);
121 }
122
123 virtual void GetJobType(std::string& type) ORTHANC_OVERRIDE
124 {
125 type = "DummyJob";
126 }
127
128 virtual bool Serialize(Json::Value& value) ORTHANC_OVERRIDE
129 {
130 value = Json::objectValue;
131 value["Type"] = "DummyJob";
132 return true;
133 }
134
135 virtual void GetPublicContent(Json::Value& value) ORTHANC_OVERRIDE
136 {
137 value["hello"] = "world";
138 }
139
140 virtual bool GetOutput(std::string& output,
141 MimeType& mime,
142 const std::string& key) ORTHANC_OVERRIDE
143 {
144 return false;
145 }
146 };
147
148
149 class DummyInstancesJob : public SetOfInstancesJob
150 {
151 private:
152 bool trailingStepDone_;
153
154 protected:
155 virtual bool HandleInstance(const std::string& instance) ORTHANC_OVERRIDE
156 {
157 return (instance != "nope");
158 }
159
160 virtual bool HandleTrailingStep() ORTHANC_OVERRIDE
161 {
162 if (HasTrailingStep())
163 {
164 if (trailingStepDone_)
165 {
166 throw OrthancException(ErrorCode_InternalError);
167 }
168 else
169 {
170 trailingStepDone_ = true;
171 return true;
172 }
173 }
174 else
175 {
176 throw OrthancException(ErrorCode_InternalError);
177 }
178 }
179
180 public:
181 DummyInstancesJob() :
182 trailingStepDone_(false)
183 {
184 }
185
186 DummyInstancesJob(const Json::Value& value) :
187 SetOfInstancesJob(value)
188 {
189 if (HasTrailingStep())
190 {
191 trailingStepDone_ = (GetPosition() == GetCommandsCount());
192 }
193 else
194 {
195 trailingStepDone_ = false;
196 }
197 }
198
199 bool IsTrailingStepDone() const
200 {
201 return trailingStepDone_;
202 }
203
204 virtual void Stop(JobStopReason reason) ORTHANC_OVERRIDE
205 {
206 }
207
208 virtual void GetJobType(std::string& s) ORTHANC_OVERRIDE
209 {
210 s = "DummyInstancesJob";
211 }
212 };
213
214
215 class DummyUnserializer : public GenericJobUnserializer
216 {
217 public:
218 virtual IJob* UnserializeJob(const Json::Value& value) ORTHANC_OVERRIDE
219 {
220 if (SerializationToolbox::ReadString(value, "Type") == "DummyInstancesJob")
221 {
222 return new DummyInstancesJob(value);
223 }
224 else if (SerializationToolbox::ReadString(value, "Type") == "DummyJob")
225 {
226 return new DummyJob;
227 }
228 else
229 {
230 return GenericJobUnserializer::UnserializeJob(value);
231 }
232 }
233 };
234
235
236 class DynamicInteger : public IDynamicObject
237 {
238 private:
239 int value_;
240 std::set<int>& target_;
241
242 public:
243 DynamicInteger(int value, std::set<int>& target) :
244 value_(value), target_(target)
245 {
246 }
247
248 int GetValue() const
249 {
250 return value_;
251 }
252 };
253 }
254
255
256 TEST(JobsEngine, DISABLED_Lua)
257 {
258 JobsEngine engine(10);
259 engine.SetThreadSleep(10);
260 engine.SetWorkersCount(2);
261 engine.Start();
262
263 LuaJobManager lua;
264 lua.SetMaxOperationsPerJob(5);
265 lua.SetTrailingOperationTimeout(200);
266
267 for (size_t i = 0; i < 30; i++)
268 {
269 boost::this_thread::sleep(boost::posix_time::milliseconds(150));
270
271 LuaJobManager::Lock lock(lua, engine);
272 size_t a = lock.AddLogOperation();
273 size_t b = lock.AddLogOperation();
274 size_t c = lock.AddSystemCallOperation("echo");
275 lock.AddStringInput(a, boost::lexical_cast<std::string>(i));
276 lock.AddNullInput(a);
277 lock.Connect(a, b);
278 lock.Connect(a, c);
279 }
280
281 boost::this_thread::sleep(boost::posix_time::milliseconds(2000));
282
283 engine.Stop();
284 }
285
286
287 static bool CheckSameJson(const Json::Value& a,
288 const Json::Value& b)
289 {
290 std::string s = a.toStyledString();
291 std::string t = b.toStyledString();
292
293 if (s == t)
294 {
295 return true;
296 }
297 else
298 {
299 LOG(ERROR) << "Expected serialization: " << s;
300 LOG(ERROR) << "Actual serialization: " << t;
301 return false;
302 }
303 }
304
305
306 static bool CheckIdempotentSetOfInstances(IJobUnserializer& unserializer,
307 SetOfInstancesJob& job)
308 {
309 Json::Value a = 42;
310
311 if (!job.Serialize(a))
312 {
313 return false;
314 }
315 else
316 {
317 std::unique_ptr<SetOfInstancesJob> unserialized
318 (dynamic_cast<SetOfInstancesJob*>(unserializer.UnserializeJob(a)));
319
320 Json::Value b = 43;
321 if (unserialized->Serialize(b))
322 {
323 return (CheckSameJson(a, b) &&
324 job.HasTrailingStep() == unserialized->HasTrailingStep() &&
325 job.GetPosition() == unserialized->GetPosition() &&
326 job.GetInstancesCount() == unserialized->GetInstancesCount() &&
327 job.GetCommandsCount() == unserialized->GetCommandsCount());
328 }
329 else
330 {
331 return false;
332 }
333 }
334 }
335
336
337 static bool CheckIdempotentSerialization(IJobUnserializer& unserializer,
338 IJobOperation& operation)
339 {
340 Json::Value a = 42;
341 operation.Serialize(a);
342
343 std::unique_ptr<IJobOperation> unserialized(unserializer.UnserializeOperation(a));
344
345 Json::Value b = 43;
346 unserialized->Serialize(b);
347
348 return CheckSameJson(a, b);
349 }
350
351
352 static bool CheckIdempotentSerialization(IJobUnserializer& unserializer,
353 JobOperationValue& value)
354 {
355 Json::Value a = 42;
356 value.Serialize(a);
357
358 std::unique_ptr<JobOperationValue> unserialized(unserializer.UnserializeValue(a));
359
360 Json::Value b = 43;
361 unserialized->Serialize(b);
362
363 return CheckSameJson(a, b);
364 }
365
366
367 TEST(JobsSerialization, GenericOperations)
368 {
369 DummyUnserializer unserializer;
370 Json::Value s;
371
372 {
373 LogJobOperation operation;
374
375 ASSERT_TRUE(CheckIdempotentSerialization(unserializer, operation));
376 operation.Serialize(s);
377 }
378
379 ASSERT_THROW(unserializer.UnserializeJob(s), OrthancException);
380 ASSERT_THROW(unserializer.UnserializeValue(s), OrthancException);
381
382 {
383 std::unique_ptr<IJobOperation> operation;
384 operation.reset(unserializer.UnserializeOperation(s));
385
386 // Make sure that we have indeed unserialized a log operation
387 Json::Value dummy;
388 ASSERT_THROW(dynamic_cast<DeleteResourceOperation&>(*operation).Serialize(dummy), std::bad_cast);
389 dynamic_cast<LogJobOperation&>(*operation).Serialize(dummy);
390 }
391 }
392
393
394 TEST(JobsSerialization, DicomInstanceOrigin)
395 {
396 Json::Value s;
397 std::string t;
398
399 {
400 DicomInstanceOrigin origin;
401
402 s = 42;
403 origin.Serialize(s);
404 }
405
406 {
407 DicomInstanceOrigin origin(s);
408 ASSERT_EQ(RequestOrigin_Unknown, origin.GetRequestOrigin());
409 ASSERT_EQ("", std::string(origin.GetRemoteAetC()));
410 ASSERT_FALSE(origin.LookupRemoteIp(t));
411 ASSERT_FALSE(origin.LookupRemoteAet(t));
412 ASSERT_FALSE(origin.LookupCalledAet(t));
413 ASSERT_FALSE(origin.LookupHttpUsername(t));
414 }
415
416 {
417 DicomInstanceOrigin origin(DicomInstanceOrigin::FromDicomProtocol("host", "aet", "called"));
418
419 s = 42;
420 origin.Serialize(s);
421 }
422
423 {
424 DicomInstanceOrigin origin(s);
425 ASSERT_EQ(RequestOrigin_DicomProtocol, origin.GetRequestOrigin());
426 ASSERT_EQ("aet", std::string(origin.GetRemoteAetC()));
427 ASSERT_TRUE(origin.LookupRemoteIp(t)); ASSERT_EQ("host", t);
428 ASSERT_TRUE(origin.LookupRemoteAet(t)); ASSERT_EQ("aet", t);
429 ASSERT_TRUE(origin.LookupCalledAet(t)); ASSERT_EQ("called", t);
430 ASSERT_FALSE(origin.LookupHttpUsername(t));
431 }
432
433 {
434 DicomInstanceOrigin origin(DicomInstanceOrigin::FromHttp("host", "username"));
435
436 s = 42;
437 origin.Serialize(s);
438 }
439
440 {
441 DicomInstanceOrigin origin(s);
442 ASSERT_EQ(RequestOrigin_RestApi, origin.GetRequestOrigin());
443 ASSERT_EQ("", std::string(origin.GetRemoteAetC()));
444 ASSERT_TRUE(origin.LookupRemoteIp(t)); ASSERT_EQ("host", t);
445 ASSERT_FALSE(origin.LookupRemoteAet(t));
446 ASSERT_FALSE(origin.LookupCalledAet(t));
447 ASSERT_TRUE(origin.LookupHttpUsername(t)); ASSERT_EQ("username", t);
448 }
449
450 {
451 DicomInstanceOrigin origin(DicomInstanceOrigin::FromLua());
452
453 s = 42;
454 origin.Serialize(s);
455 }
456
457 {
458 DicomInstanceOrigin origin(s);
459 ASSERT_EQ(RequestOrigin_Lua, origin.GetRequestOrigin());
460 ASSERT_FALSE(origin.LookupRemoteIp(t));
461 ASSERT_FALSE(origin.LookupRemoteAet(t));
462 ASSERT_FALSE(origin.LookupCalledAet(t));
463 ASSERT_FALSE(origin.LookupHttpUsername(t));
464 }
465
466 {
467 DicomInstanceOrigin origin(DicomInstanceOrigin::FromPlugins());
468
469 s = 42;
470 origin.Serialize(s);
471 }
472
473 {
474 DicomInstanceOrigin origin(s);
475 ASSERT_EQ(RequestOrigin_Plugins, origin.GetRequestOrigin());
476 ASSERT_FALSE(origin.LookupRemoteIp(t));
477 ASSERT_FALSE(origin.LookupRemoteAet(t));
478 ASSERT_FALSE(origin.LookupCalledAet(t));
479 ASSERT_FALSE(origin.LookupHttpUsername(t));
480 }
481 }
482
483
484 namespace
485 {
486 class OrthancJobsSerialization : public testing::Test
487 {
488 private:
489 MemoryStorageArea storage_;
490 SQLiteDatabaseWrapper db_; // The SQLite DB is in memory
491 std::unique_ptr<ServerContext> context_;
492
493 public:
494 OrthancJobsSerialization()
495 {
496 db_.Open();
497 context_.reset(new ServerContext(db_, storage_, true /* running unit tests */, 10));
498 context_->SetupJobsEngine(true, false);
499 }
500
501 virtual ~OrthancJobsSerialization() ORTHANC_OVERRIDE
502 {
503 context_->Stop();
504 context_.reset(NULL);
505 db_.Close();
506 }
507
508 ServerContext& GetContext()
509 {
510 return *context_;
511 }
512
513 bool CreateInstance(std::string& id)
514 {
515 // Create a sample DICOM file
516 ParsedDicomFile dicom(true);
517 dicom.Replace(DICOM_TAG_PATIENT_NAME, std::string("JODOGNE"),
518 false, DicomReplaceMode_InsertIfAbsent, "");
519
520 DicomInstanceToStore toStore;
521 toStore.SetParsedDicomFile(dicom);
522
523 return (context_->Store(id, toStore, StoreInstanceMode_Default) == StoreStatus_Success);
524 }
525 };
526 }
527
528
529 TEST_F(OrthancJobsSerialization, Values)
530 {
531 std::string id;
532 ASSERT_TRUE(CreateInstance(id));
533
534 Json::Value s;
535 OrthancJobUnserializer unserializer(GetContext());
536
537 {
538 DicomInstanceOperationValue instance(GetContext(), id);
539
540 ASSERT_TRUE(CheckIdempotentSerialization(unserializer, instance));
541 instance.Serialize(s);
542 }
543
544 std::unique_ptr<JobOperationValue> value;
545 value.reset(unserializer.UnserializeValue(s));
546 ASSERT_EQ(JobOperationValue::Type_DicomInstance, value->GetType());
547 ASSERT_EQ(id, dynamic_cast<DicomInstanceOperationValue&>(*value).GetId());
548
549 {
550 std::string content;
551 dynamic_cast<DicomInstanceOperationValue&>(*value).ReadDicom(content);
552
553 ParsedDicomFile dicom(content);
554 ASSERT_TRUE(dicom.GetTagValue(content, DICOM_TAG_PATIENT_NAME));
555 ASSERT_EQ("JODOGNE", content);
556 }
557 }
558
559
560 TEST_F(OrthancJobsSerialization, Operations)
561 {
562 std::string id;
563 ASSERT_TRUE(CreateInstance(id));
564
565 Json::Value s;
566 OrthancJobUnserializer unserializer(GetContext());
567
568 // DeleteResourceOperation
569
570 {
571 DeleteResourceOperation operation(GetContext());
572
573 ASSERT_TRUE(CheckIdempotentSerialization(unserializer, operation));
574 operation.Serialize(s);
575 }
576
577 std::unique_ptr<IJobOperation> operation;
578
579 {
580 operation.reset(unserializer.UnserializeOperation(s));
581
582 Json::Value dummy;
583 ASSERT_THROW(dynamic_cast<LogJobOperation&>(*operation).Serialize(dummy), std::bad_cast);
584 dynamic_cast<DeleteResourceOperation&>(*operation).Serialize(dummy);
585 }
586
587 // StorePeerOperation
588
589 {
590 WebServiceParameters peer;
591 peer.SetUrl("http://localhost/");
592 peer.SetCredentials("username", "password");
593 peer.SetPkcs11Enabled(true);
594
595 StorePeerOperation operation(peer);
596
597 ASSERT_TRUE(CheckIdempotentSerialization(unserializer, operation));
598 operation.Serialize(s);
599 }
600
601 {
602 operation.reset(unserializer.UnserializeOperation(s));
603
604 const StorePeerOperation& tmp = dynamic_cast<StorePeerOperation&>(*operation);
605 ASSERT_EQ("http://localhost/", tmp.GetPeer().GetUrl());
606 ASSERT_EQ("username", tmp.GetPeer().GetUsername());
607 ASSERT_EQ("password", tmp.GetPeer().GetPassword());
608 ASSERT_TRUE(tmp.GetPeer().IsPkcs11Enabled());
609 }
610
611 // StoreScuOperation
612
613 {
614 TimeoutDicomConnectionManager luaManager;
615
616 {
617 RemoteModalityParameters modality;
618 modality.SetApplicationEntityTitle("REMOTE");
619 modality.SetHost("192.168.1.1");
620 modality.SetPortNumber(1000);
621 modality.SetManufacturer(ModalityManufacturer_StoreScp);
622
623 StoreScuOperation operation(GetContext(), luaManager, "TEST", modality);
624
625 ASSERT_TRUE(CheckIdempotentSerialization(unserializer, operation));
626 operation.Serialize(s);
627 }
628
629 {
630 operation.reset(unserializer.UnserializeOperation(s));
631
632 const StoreScuOperation& tmp = dynamic_cast<StoreScuOperation&>(*operation);
633 ASSERT_EQ("REMOTE", tmp.GetRemoteModality().GetApplicationEntityTitle());
634 ASSERT_EQ("192.168.1.1", tmp.GetRemoteModality().GetHost());
635 ASSERT_EQ(1000, tmp.GetRemoteModality().GetPortNumber());
636 ASSERT_EQ(ModalityManufacturer_StoreScp, tmp.GetRemoteModality().GetManufacturer());
637 ASSERT_EQ("TEST", tmp.GetLocalAet());
638 }
639 }
640
641 // SystemCallOperation
642
643 {
644 SystemCallOperation operation(std::string("echo"));
645 operation.AddPreArgument("a");
646 operation.AddPreArgument("b");
647 operation.AddPostArgument("c");
648
649 ASSERT_TRUE(CheckIdempotentSerialization(unserializer, operation));
650 operation.Serialize(s);
651 }
652
653 {
654 operation.reset(unserializer.UnserializeOperation(s));
655
656 const SystemCallOperation& tmp = dynamic_cast<SystemCallOperation&>(*operation);
657 ASSERT_EQ("echo", tmp.GetCommand());
658 ASSERT_EQ(2u, tmp.GetPreArgumentsCount());
659 ASSERT_EQ(1u, tmp.GetPostArgumentsCount());
660 ASSERT_EQ("a", tmp.GetPreArgument(0));
661 ASSERT_EQ("b", tmp.GetPreArgument(1));
662 ASSERT_EQ("c", tmp.GetPostArgument(0));
663 }
664
665 // ModifyInstanceOperation
666
667 {
668 std::unique_ptr<DicomModification> modification(new DicomModification);
669 modification->SetupAnonymization(DicomVersion_2008);
670
671 ModifyInstanceOperation operation(GetContext(), RequestOrigin_Lua, modification.release());
672
673 ASSERT_TRUE(CheckIdempotentSerialization(unserializer, operation));
674 operation.Serialize(s);
675 }
676
677 {
678 operation.reset(unserializer.UnserializeOperation(s));
679
680 const ModifyInstanceOperation& tmp = dynamic_cast<ModifyInstanceOperation&>(*operation);
681 ASSERT_EQ(RequestOrigin_Lua, tmp.GetRequestOrigin());
682 ASSERT_TRUE(tmp.GetModification().IsRemoved(DICOM_TAG_STUDY_DESCRIPTION));
683 }
684 }
685
686
687 TEST_F(OrthancJobsSerialization, Jobs)
688 {
689 Json::Value s;
690
691 // ArchiveJob
692
693 {
694 ArchiveJob job(GetContext(), false, false);
695 ASSERT_FALSE(job.Serialize(s)); // Cannot serialize this
696 }
697
698 // DicomModalityStoreJob
699
700 OrthancJobUnserializer unserializer(GetContext());
701
702 {
703 RemoteModalityParameters modality;
704 modality.SetApplicationEntityTitle("REMOTE");
705 modality.SetHost("192.168.1.1");
706 modality.SetPortNumber(1000);
707 modality.SetManufacturer(ModalityManufacturer_StoreScp);
708
709 DicomModalityStoreJob job(GetContext());
710 job.SetLocalAet("LOCAL");
711 job.SetRemoteModality(modality);
712 job.SetMoveOriginator("MOVESCU", 42);
713
714 ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job));
715 ASSERT_TRUE(job.Serialize(s));
716 }
717
718 {
719 std::unique_ptr<IJob> job;
720 job.reset(unserializer.UnserializeJob(s));
721
722 DicomModalityStoreJob& tmp = dynamic_cast<DicomModalityStoreJob&>(*job);
723 ASSERT_EQ("LOCAL", tmp.GetParameters().GetLocalApplicationEntityTitle());
724 ASSERT_EQ("REMOTE", tmp.GetParameters().GetRemoteModality().GetApplicationEntityTitle());
725 ASSERT_EQ("192.168.1.1", tmp.GetParameters().GetRemoteModality().GetHost());
726 ASSERT_EQ(1000, tmp.GetParameters().GetRemoteModality().GetPortNumber());
727 ASSERT_EQ(ModalityManufacturer_StoreScp, tmp.GetParameters().GetRemoteModality().GetManufacturer());
728 ASSERT_TRUE(tmp.HasMoveOriginator());
729 ASSERT_EQ("MOVESCU", tmp.GetMoveOriginatorAet());
730 ASSERT_EQ(42, tmp.GetMoveOriginatorId());
731 }
732
733 // OrthancPeerStoreJob
734
735 {
736 WebServiceParameters peer;
737 peer.SetUrl("http://localhost/");
738 peer.SetCredentials("username", "password");
739 peer.SetPkcs11Enabled(true);
740
741 OrthancPeerStoreJob job(GetContext());
742 job.SetPeer(peer);
743
744 ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job));
745 ASSERT_TRUE(job.Serialize(s));
746 }
747
748 {
749 std::unique_ptr<IJob> job;
750 job.reset(unserializer.UnserializeJob(s));
751
752 OrthancPeerStoreJob& tmp = dynamic_cast<OrthancPeerStoreJob&>(*job);
753 ASSERT_EQ("http://localhost/", tmp.GetPeer().GetUrl());
754 ASSERT_EQ("username", tmp.GetPeer().GetUsername());
755 ASSERT_EQ("password", tmp.GetPeer().GetPassword());
756 ASSERT_TRUE(tmp.GetPeer().IsPkcs11Enabled());
757 ASSERT_FALSE(tmp.IsTranscode());
758 ASSERT_THROW(tmp.GetTransferSyntax(), OrthancException);
759 }
760
761 {
762 OrthancPeerStoreJob job(GetContext());
763 ASSERT_THROW(job.SetTranscode("nope"), OrthancException);
764 job.SetTranscode("1.2.840.10008.1.2.4.50");
765
766 ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job));
767 ASSERT_TRUE(job.Serialize(s));
768 }
769
770 {
771 std::unique_ptr<IJob> job;
772 job.reset(unserializer.UnserializeJob(s));
773
774 OrthancPeerStoreJob& tmp = dynamic_cast<OrthancPeerStoreJob&>(*job);
775 ASSERT_EQ("http://127.0.0.1:8042/", tmp.GetPeer().GetUrl());
776 ASSERT_EQ("", tmp.GetPeer().GetUsername());
777 ASSERT_EQ("", tmp.GetPeer().GetPassword());
778 ASSERT_FALSE(tmp.GetPeer().IsPkcs11Enabled());
779 ASSERT_TRUE(tmp.IsTranscode());
780 ASSERT_EQ(DicomTransferSyntax_JPEGProcess1, tmp.GetTransferSyntax());
781 }
782
783 // ResourceModificationJob
784
785 {
786 std::unique_ptr<DicomModification> modification(new DicomModification);
787 modification->SetupAnonymization(DicomVersion_2008);
788
789 ResourceModificationJob job(GetContext());
790 job.SetModification(modification.release(), ResourceType_Patient, true);
791 job.SetOrigin(DicomInstanceOrigin::FromLua());
792
793 job.AddTrailingStep(); // Necessary since 1.7.0
794 ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job));
795 ASSERT_TRUE(job.Serialize(s));
796 }
797
798 {
799 std::unique_ptr<IJob> job;
800 job.reset(unserializer.UnserializeJob(s));
801
802 ResourceModificationJob& tmp = dynamic_cast<ResourceModificationJob&>(*job);
803 ASSERT_TRUE(tmp.IsAnonymization());
804 ASSERT_FALSE(tmp.IsTranscode());
805 ASSERT_THROW(tmp.GetTransferSyntax(), OrthancException);
806 ASSERT_EQ(RequestOrigin_Lua, tmp.GetOrigin().GetRequestOrigin());
807 ASSERT_TRUE(tmp.GetModification().IsRemoved(DICOM_TAG_STUDY_DESCRIPTION));
808 }
809
810 {
811 ResourceModificationJob job(GetContext());
812 ASSERT_THROW(job.SetTranscode("nope"), OrthancException);
813 job.SetTranscode(DicomTransferSyntax_JPEGProcess1);
814
815 job.AddTrailingStep(); // Necessary since 1.7.0
816 ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job));
817 ASSERT_TRUE(job.Serialize(s));
818 }
819
820 {
821 std::unique_ptr<IJob> job;
822 job.reset(unserializer.UnserializeJob(s));
823
824 ResourceModificationJob& tmp = dynamic_cast<ResourceModificationJob&>(*job);
825 ASSERT_FALSE(tmp.IsAnonymization());
826 ASSERT_TRUE(tmp.IsTranscode());
827 ASSERT_EQ(DicomTransferSyntax_JPEGProcess1, tmp.GetTransferSyntax());
828 ASSERT_EQ(RequestOrigin_Unknown, tmp.GetOrigin().GetRequestOrigin());
829 }
830
831 // SplitStudyJob
832
833 std::string instance;
834 ASSERT_TRUE(CreateInstance(instance));
835
836 std::string study, series;
837
838 {
839 ServerContext::DicomCacheLocker lock(GetContext(), instance);
840 study = lock.GetDicom().GetHasher().HashStudy();
841 series = lock.GetDicom().GetHasher().HashSeries();
842 }
843
844 {
845 std::list<std::string> tmp;
846 GetContext().GetIndex().GetAllUuids(tmp, ResourceType_Study);
847 ASSERT_EQ(1u, tmp.size());
848 ASSERT_EQ(study, tmp.front());
849 GetContext().GetIndex().GetAllUuids(tmp, ResourceType_Series);
850 ASSERT_EQ(1u, tmp.size());
851 ASSERT_EQ(series, tmp.front());
852 }
853
854 std::string study2;
855
856 {
857 std::string a, b;
858
859 {
860 ASSERT_THROW(SplitStudyJob(GetContext(), std::string("nope")), OrthancException);
861
862 SplitStudyJob job(GetContext(), study);
863 job.SetKeepSource(true);
864 job.AddSourceSeries(series);
865 ASSERT_THROW(job.AddSourceSeries("nope"), OrthancException);
866 job.SetOrigin(DicomInstanceOrigin::FromLua());
867 job.Replace(DICOM_TAG_PATIENT_NAME, "hello");
868 job.Remove(DICOM_TAG_PATIENT_BIRTH_DATE);
869 ASSERT_THROW(job.Replace(DICOM_TAG_SERIES_DESCRIPTION, "nope"), OrthancException);
870 ASSERT_THROW(job.Remove(DICOM_TAG_SERIES_DESCRIPTION), OrthancException);
871
872 ASSERT_TRUE(job.GetTargetStudy().empty());
873 a = job.GetTargetStudyUid();
874 ASSERT_TRUE(job.LookupTargetSeriesUid(b, series));
875
876 job.AddTrailingStep();
877 job.Start();
878 ASSERT_EQ(JobStepCode_Continue, job.Step("jobId").GetCode());
879 ASSERT_EQ(JobStepCode_Success, job.Step("jobId").GetCode());
880
881 study2 = job.GetTargetStudy();
882 ASSERT_FALSE(study2.empty());
883
884 ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job));
885 ASSERT_TRUE(job.Serialize(s));
886 }
887
888 {
889 std::unique_ptr<IJob> job;
890 job.reset(unserializer.UnserializeJob(s));
891
892 SplitStudyJob& tmp = dynamic_cast<SplitStudyJob&>(*job);
893 ASSERT_TRUE(tmp.IsKeepSource());
894 ASSERT_EQ(study, tmp.GetSourceStudy());
895 ASSERT_EQ(a, tmp.GetTargetStudyUid());
896 ASSERT_EQ(RequestOrigin_Lua, tmp.GetOrigin().GetRequestOrigin());
897
898 std::string s;
899 ASSERT_EQ(study2, tmp.GetTargetStudy());
900 ASSERT_FALSE(tmp.LookupTargetSeriesUid(s, "nope"));
901 ASSERT_TRUE(tmp.LookupTargetSeriesUid(s, series));
902 ASSERT_EQ(b, s);
903
904 ASSERT_FALSE(tmp.LookupReplacement(s, DICOM_TAG_STUDY_DESCRIPTION));
905 ASSERT_TRUE(tmp.LookupReplacement(s, DICOM_TAG_PATIENT_NAME));
906 ASSERT_EQ("hello", s);
907 ASSERT_FALSE(tmp.IsRemoved(DICOM_TAG_PATIENT_NAME));
908 ASSERT_TRUE(tmp.IsRemoved(DICOM_TAG_PATIENT_BIRTH_DATE));
909 }
910 }
911
912 {
913 std::list<std::string> tmp;
914 GetContext().GetIndex().GetAllUuids(tmp, ResourceType_Study);
915 ASSERT_EQ(2u, tmp.size());
916 GetContext().GetIndex().GetAllUuids(tmp, ResourceType_Series);
917 ASSERT_EQ(2u, tmp.size());
918 }
919
920 // MergeStudyJob
921
922 {
923 ASSERT_THROW(SplitStudyJob(GetContext(), std::string("nope")), OrthancException);
924
925 MergeStudyJob job(GetContext(), study);
926 job.SetKeepSource(true);
927 job.AddSource(study2);
928 ASSERT_THROW(job.AddSourceSeries("nope"), OrthancException);
929 ASSERT_THROW(job.AddSourceStudy("nope"), OrthancException);
930 ASSERT_THROW(job.AddSource("nope"), OrthancException);
931 job.SetOrigin(DicomInstanceOrigin::FromLua());
932
933 ASSERT_EQ(job.GetTargetStudy(), study);
934
935 job.AddTrailingStep();
936 job.Start();
937 ASSERT_EQ(JobStepCode_Continue, job.Step("jobId").GetCode());
938 ASSERT_EQ(JobStepCode_Success, job.Step("jobId").GetCode());
939
940 ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job));
941 ASSERT_TRUE(job.Serialize(s));
942 }
943
944 {
945 std::list<std::string> tmp;
946 GetContext().GetIndex().GetAllUuids(tmp, ResourceType_Study);
947 ASSERT_EQ(2u, tmp.size());
948 GetContext().GetIndex().GetAllUuids(tmp, ResourceType_Series);
949 ASSERT_EQ(3u, tmp.size());
950 }
951
952 {
953 std::unique_ptr<IJob> job;
954 job.reset(unserializer.UnserializeJob(s));
955
956 MergeStudyJob& tmp = dynamic_cast<MergeStudyJob&>(*job);
957 ASSERT_TRUE(tmp.IsKeepSource());
958 ASSERT_EQ(study, tmp.GetTargetStudy());
959 ASSERT_EQ(RequestOrigin_Lua, tmp.GetOrigin().GetRequestOrigin());
960 }
961 }
962
963
964
965 TEST_F(OrthancJobsSerialization, DicomAssociationParameters)
966 {
967 Json::Value v;
968
969 {
970 v = Json::objectValue;
971 DicomAssociationParameters p;
972 p.SerializeJob(v);
973 }
974
975 {
976 DicomAssociationParameters p = DicomAssociationParameters::UnserializeJob(v);
977 ASSERT_EQ("ORTHANC", p.GetLocalApplicationEntityTitle());
978 ASSERT_EQ("ANY-SCP", p.GetRemoteModality().GetApplicationEntityTitle());
979 ASSERT_EQ(104u, p.GetRemoteModality().GetPortNumber());
980 ASSERT_EQ(ModalityManufacturer_Generic, p.GetRemoteModality().GetManufacturer());
981 ASSERT_EQ("127.0.0.1", p.GetRemoteModality().GetHost());
982 ASSERT_EQ(DicomAssociationParameters::GetDefaultTimeout(), p.GetTimeout());
983 }
984
985 {
986 v = Json::objectValue;
987 DicomAssociationParameters p;
988 p.SetLocalApplicationEntityTitle("HELLO");
989 p.SetRemoteApplicationEntityTitle("WORLD");
990 p.SetRemotePort(42);
991 p.SetRemoteHost("MY_HOST");
992 p.SetTimeout(43);
993 p.SerializeJob(v);
994 }
995
996 {
997 DicomAssociationParameters p = DicomAssociationParameters::UnserializeJob(v);
998 ASSERT_EQ("HELLO", p.GetLocalApplicationEntityTitle());
999 ASSERT_EQ("WORLD", p.GetRemoteModality().GetApplicationEntityTitle());
1000 ASSERT_EQ(42u, p.GetRemoteModality().GetPortNumber());
1001 ASSERT_EQ(ModalityManufacturer_Generic, p.GetRemoteModality().GetManufacturer());
1002 ASSERT_EQ("MY_HOST", p.GetRemoteModality().GetHost());
1003 ASSERT_EQ(43u, p.GetTimeout());
1004 }
1005
1006 {
1007 DicomModalityStoreJob job(GetContext());
1008 job.Serialize(v);
1009 }
1010
1011 {
1012 OrthancJobUnserializer unserializer(GetContext());
1013 std::unique_ptr<DicomModalityStoreJob> job(
1014 dynamic_cast<DicomModalityStoreJob*>(unserializer.UnserializeJob(v)));
1015 ASSERT_EQ("ORTHANC", job->GetParameters().GetLocalApplicationEntityTitle());
1016 ASSERT_EQ("ANY-SCP", job->GetParameters().GetRemoteModality().GetApplicationEntityTitle());
1017 ASSERT_EQ("127.0.0.1", job->GetParameters().GetRemoteModality().GetHost());
1018 ASSERT_EQ(104u, job->GetParameters().GetRemoteModality().GetPortNumber());
1019 ASSERT_EQ(ModalityManufacturer_Generic, job->GetParameters().GetRemoteModality().GetManufacturer());
1020 ASSERT_EQ(DicomAssociationParameters::GetDefaultTimeout(), job->GetParameters().GetTimeout());
1021 ASSERT_FALSE(job->HasMoveOriginator());
1022 ASSERT_THROW(job->GetMoveOriginatorAet(), OrthancException);
1023 ASSERT_THROW(job->GetMoveOriginatorId(), OrthancException);
1024 ASSERT_FALSE(job->HasStorageCommitment());
1025 }
1026
1027 {
1028 RemoteModalityParameters r;
1029 r.SetApplicationEntityTitle("HELLO");
1030 r.SetPortNumber(42);
1031 r.SetHost("MY_HOST");
1032
1033 DicomModalityStoreJob job(GetContext());
1034 job.SetLocalAet("WORLD");
1035 job.SetRemoteModality(r);
1036 job.SetTimeout(43);
1037 job.SetMoveOriginator("ORIGINATOR", 100);
1038 job.EnableStorageCommitment(true);
1039 job.Serialize(v);
1040 }
1041
1042 {
1043 OrthancJobUnserializer unserializer(GetContext());
1044 std::unique_ptr<DicomModalityStoreJob> job(
1045 dynamic_cast<DicomModalityStoreJob*>(unserializer.UnserializeJob(v)));
1046 ASSERT_EQ("WORLD", job->GetParameters().GetLocalApplicationEntityTitle());
1047 ASSERT_EQ("HELLO", job->GetParameters().GetRemoteModality().GetApplicationEntityTitle());
1048 ASSERT_EQ("MY_HOST", job->GetParameters().GetRemoteModality().GetHost());
1049 ASSERT_EQ(42u, job->GetParameters().GetRemoteModality().GetPortNumber());
1050 ASSERT_EQ(ModalityManufacturer_Generic, job->GetParameters().GetRemoteModality().GetManufacturer());
1051 ASSERT_EQ(43u, job->GetParameters().GetTimeout());
1052 ASSERT_TRUE(job->HasMoveOriginator());
1053 ASSERT_EQ("ORIGINATOR", job->GetMoveOriginatorAet());
1054 ASSERT_EQ(100, job->GetMoveOriginatorId());
1055 ASSERT_TRUE(job->HasStorageCommitment());
1056 }
1057
1058 {
1059 DicomMoveScuJob job(GetContext());
1060 job.Serialize(v);
1061 }
1062
1063 {
1064 OrthancJobUnserializer unserializer(GetContext());
1065 std::unique_ptr<DicomMoveScuJob> job(
1066 dynamic_cast<DicomMoveScuJob*>(unserializer.UnserializeJob(v)));
1067 ASSERT_EQ("ORTHANC", job->GetParameters().GetLocalApplicationEntityTitle());
1068 ASSERT_EQ("ANY-SCP", job->GetParameters().GetRemoteModality().GetApplicationEntityTitle());
1069 ASSERT_EQ("127.0.0.1", job->GetParameters().GetRemoteModality().GetHost());
1070 ASSERT_EQ(104u, job->GetParameters().GetRemoteModality().GetPortNumber());
1071 ASSERT_EQ(ModalityManufacturer_Generic, job->GetParameters().GetRemoteModality().GetManufacturer());
1072 ASSERT_EQ(DicomAssociationParameters::GetDefaultTimeout(), job->GetParameters().GetTimeout());
1073 }
1074
1075 {
1076 RemoteModalityParameters r;
1077 r.SetApplicationEntityTitle("HELLO");
1078 r.SetPortNumber(42);
1079 r.SetHost("MY_HOST");
1080
1081 DicomMoveScuJob job(GetContext());
1082 job.SetLocalAet("WORLD");
1083 job.SetRemoteModality(r);
1084 job.SetTimeout(43);
1085 job.Serialize(v);
1086 }
1087
1088 {
1089 OrthancJobUnserializer unserializer(GetContext());
1090 std::unique_ptr<DicomMoveScuJob> job(
1091 dynamic_cast<DicomMoveScuJob*>(unserializer.UnserializeJob(v)));
1092 ASSERT_EQ("WORLD", job->GetParameters().GetLocalApplicationEntityTitle());
1093 ASSERT_EQ("HELLO", job->GetParameters().GetRemoteModality().GetApplicationEntityTitle());
1094 ASSERT_EQ("MY_HOST", job->GetParameters().GetRemoteModality().GetHost());
1095 ASSERT_EQ(42u, job->GetParameters().GetRemoteModality().GetPortNumber());
1096 ASSERT_EQ(ModalityManufacturer_Generic, job->GetParameters().GetRemoteModality().GetManufacturer());
1097 ASSERT_EQ(43u, job->GetParameters().GetTimeout());
1098 }
1099 }