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-2021 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 Lesser General Public License
9 * as published by the Free Software Foundation, either version 3 of
10 * the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this program. If not, see
19 * <http://www.gnu.org/licenses/>.
20 **/
21
22
23 #if ORTHANC_UNIT_TESTS_LINK_FRAMEWORK == 1
24 // Must be the first to be sure to use the Orthanc framework shared library
25 # include <OrthancFramework.h>
26 #endif
27
28 #include <gtest/gtest.h>
29
30 #include "../../OrthancFramework/Sources/Compatibility.h"
31 #include "../../OrthancFramework/Sources/DicomNetworking/DicomAssociationParameters.h"
32 #include "../../OrthancFramework/Sources/DicomNetworking/RemoteModalityParameters.h"
33 #include "../../OrthancFramework/Sources/DicomParsing/DicomModification.h"
34 #include "../../OrthancFramework/Sources/DicomParsing/ParsedDicomFile.h"
35 #include "../../OrthancFramework/Sources/JobsEngine/GenericJobUnserializer.h"
36 #include "../../OrthancFramework/Sources/JobsEngine/JobsEngine.h"
37 #include "../../OrthancFramework/Sources/JobsEngine/Operations/JobOperationValues.h"
38 #include "../../OrthancFramework/Sources/JobsEngine/Operations/LogJobOperation.h"
39 #include "../../OrthancFramework/Sources/JobsEngine/Operations/NullOperationValue.h"
40 #include "../../OrthancFramework/Sources/JobsEngine/Operations/SequenceOfOperationsJob.h"
41 #include "../../OrthancFramework/Sources/JobsEngine/Operations/StringOperationValue.h"
42 #include "../../OrthancFramework/Sources/JobsEngine/SetOfInstancesJob.h"
43 #include "../../OrthancFramework/Sources/Logging.h"
44 #include "../../OrthancFramework/Sources/MultiThreading/SharedMessageQueue.h"
45 #include "../../OrthancFramework/Sources/OrthancException.h"
46 #include "../../OrthancFramework/Sources/SerializationToolbox.h"
47
48
49 using namespace Orthanc;
50
51 namespace
52 {
53 class DummyJob : public IJob
54 {
55 private:
56 bool fails_;
57 unsigned int count_;
58 unsigned int steps_;
59
60 public:
DummyJob()61 DummyJob() :
62 fails_(false),
63 count_(0),
64 steps_(4)
65 {
66 }
67
DummyJob(bool fails)68 explicit DummyJob(bool fails) :
69 fails_(fails),
70 count_(0),
71 steps_(4)
72 {
73 }
74
Start()75 virtual void Start() ORTHANC_OVERRIDE
76 {
77 }
78
Reset()79 virtual void Reset() ORTHANC_OVERRIDE
80 {
81 }
82
Step(const std::string & jobId)83 virtual JobStepResult Step(const std::string& jobId) ORTHANC_OVERRIDE
84 {
85 if (fails_)
86 {
87 return JobStepResult::Failure(ErrorCode_ParameterOutOfRange, NULL);
88 }
89 else if (count_ == steps_ - 1)
90 {
91 return JobStepResult::Success();
92 }
93 else
94 {
95 count_++;
96 return JobStepResult::Continue();
97 }
98 }
99
Stop(JobStopReason reason)100 virtual void Stop(JobStopReason reason) ORTHANC_OVERRIDE
101 {
102 }
103
GetProgress()104 virtual float GetProgress() ORTHANC_OVERRIDE
105 {
106 return static_cast<float>(count_) / static_cast<float>(steps_ - 1);
107 }
108
GetJobType(std::string & type)109 virtual void GetJobType(std::string& type) ORTHANC_OVERRIDE
110 {
111 type = "DummyJob";
112 }
113
Serialize(Json::Value & value)114 virtual bool Serialize(Json::Value& value) ORTHANC_OVERRIDE
115 {
116 value = Json::objectValue;
117 value["Type"] = "DummyJob";
118 return true;
119 }
120
GetPublicContent(Json::Value & value)121 virtual void GetPublicContent(Json::Value& value) ORTHANC_OVERRIDE
122 {
123 value["hello"] = "world";
124 }
125
GetOutput(std::string & output,MimeType & mime,const std::string & key)126 virtual bool GetOutput(std::string& output,
127 MimeType& mime,
128 const std::string& key) ORTHANC_OVERRIDE
129 {
130 return false;
131 }
132 };
133
134
135 class DummyInstancesJob : public SetOfInstancesJob
136 {
137 private:
138 bool trailingStepDone_;
139
140 protected:
HandleInstance(const std::string & instance)141 virtual bool HandleInstance(const std::string& instance) ORTHANC_OVERRIDE
142 {
143 return (instance != "nope");
144 }
145
HandleTrailingStep()146 virtual bool HandleTrailingStep() ORTHANC_OVERRIDE
147 {
148 if (HasTrailingStep())
149 {
150 if (trailingStepDone_)
151 {
152 throw OrthancException(ErrorCode_InternalError);
153 }
154 else
155 {
156 trailingStepDone_ = true;
157 return true;
158 }
159 }
160 else
161 {
162 throw OrthancException(ErrorCode_InternalError);
163 }
164 }
165
166 public:
DummyInstancesJob()167 DummyInstancesJob() :
168 trailingStepDone_(false)
169 {
170 }
171
DummyInstancesJob(const Json::Value & value)172 explicit DummyInstancesJob(const Json::Value& value) :
173 SetOfInstancesJob(value)
174 {
175 if (HasTrailingStep())
176 {
177 trailingStepDone_ = (GetPosition() == GetCommandsCount());
178 }
179 else
180 {
181 trailingStepDone_ = false;
182 }
183 }
184
IsTrailingStepDone() const185 bool IsTrailingStepDone() const
186 {
187 return trailingStepDone_;
188 }
189
Stop(JobStopReason reason)190 virtual void Stop(JobStopReason reason) ORTHANC_OVERRIDE
191 {
192 }
193
GetJobType(std::string & s)194 virtual void GetJobType(std::string& s) ORTHANC_OVERRIDE
195 {
196 s = "DummyInstancesJob";
197 }
198 };
199
200
201 class DummyUnserializer : public GenericJobUnserializer
202 {
203 public:
UnserializeJob(const Json::Value & value)204 virtual IJob* UnserializeJob(const Json::Value& value) ORTHANC_OVERRIDE
205 {
206 if (SerializationToolbox::ReadString(value, "Type") == "DummyInstancesJob")
207 {
208 return new DummyInstancesJob(value);
209 }
210 else if (SerializationToolbox::ReadString(value, "Type") == "DummyJob")
211 {
212 return new DummyJob;
213 }
214 else
215 {
216 return GenericJobUnserializer::UnserializeJob(value);
217 }
218 }
219 };
220
221
222 class DynamicInteger : public IDynamicObject
223 {
224 private:
225 int value_;
226 std::set<int>& target_;
227
228 public:
DynamicInteger(int value,std::set<int> & target)229 DynamicInteger(int value, std::set<int>& target) :
230 value_(value), target_(target)
231 {
232 }
233
GetValue() const234 int GetValue() const
235 {
236 return value_;
237 }
238 };
239 }
240
241
TEST(MultiThreading,SharedMessageQueueBasic)242 TEST(MultiThreading, SharedMessageQueueBasic)
243 {
244 std::set<int> s;
245
246 SharedMessageQueue q;
247 ASSERT_TRUE(q.WaitEmpty(0));
248 q.Enqueue(new DynamicInteger(10, s));
249 ASSERT_FALSE(q.WaitEmpty(1));
250 q.Enqueue(new DynamicInteger(20, s));
251 q.Enqueue(new DynamicInteger(30, s));
252 q.Enqueue(new DynamicInteger(40, s));
253
254 std::unique_ptr<DynamicInteger> i;
255 i.reset(dynamic_cast<DynamicInteger*>(q.Dequeue(1))); ASSERT_EQ(10, i->GetValue());
256 i.reset(dynamic_cast<DynamicInteger*>(q.Dequeue(1))); ASSERT_EQ(20, i->GetValue());
257 i.reset(dynamic_cast<DynamicInteger*>(q.Dequeue(1))); ASSERT_EQ(30, i->GetValue());
258 ASSERT_FALSE(q.WaitEmpty(1));
259 i.reset(dynamic_cast<DynamicInteger*>(q.Dequeue(1))); ASSERT_EQ(40, i->GetValue());
260 ASSERT_TRUE(q.WaitEmpty(0));
261 ASSERT_EQ(NULL, q.Dequeue(1));
262 }
263
264
TEST(MultiThreading,SharedMessageQueueClean)265 TEST(MultiThreading, SharedMessageQueueClean)
266 {
267 std::set<int> s;
268
269 try
270 {
271 SharedMessageQueue q;
272 q.Enqueue(new DynamicInteger(10, s));
273 q.Enqueue(new DynamicInteger(20, s));
274 throw OrthancException(ErrorCode_InternalError);
275 }
276 catch (OrthancException&)
277 {
278 }
279 }
280
281
282
283
CheckState(JobsRegistry & registry,const std::string & id,JobState state)284 static bool CheckState(JobsRegistry& registry,
285 const std::string& id,
286 JobState state)
287 {
288 JobState s;
289 if (registry.GetState(s, id))
290 {
291 return state == s;
292 }
293 else
294 {
295 return false;
296 }
297 }
298
299
CheckErrorCode(JobsRegistry & registry,const std::string & id,ErrorCode code)300 static bool CheckErrorCode(JobsRegistry& registry,
301 const std::string& id,
302 ErrorCode code)
303 {
304 JobInfo s;
305 if (registry.GetJobInfo(s, id))
306 {
307 return code == s.GetStatus().GetErrorCode();
308 }
309 else
310 {
311 return false;
312 }
313 }
314
315
TEST(JobsRegistry,Priority)316 TEST(JobsRegistry, Priority)
317 {
318 JobsRegistry registry(10);
319
320 std::string i1, i2, i3, i4;
321 registry.Submit(i1, new DummyJob(), 10);
322 registry.Submit(i2, new DummyJob(), 30);
323 registry.Submit(i3, new DummyJob(), 20);
324 registry.Submit(i4, new DummyJob(), 5);
325
326 registry.SetMaxCompletedJobs(2);
327
328 std::set<std::string> id;
329 registry.ListJobs(id);
330
331 ASSERT_EQ(4u, id.size());
332 ASSERT_TRUE(id.find(i1) != id.end());
333 ASSERT_TRUE(id.find(i2) != id.end());
334 ASSERT_TRUE(id.find(i3) != id.end());
335 ASSERT_TRUE(id.find(i4) != id.end());
336
337 ASSERT_TRUE(CheckState(registry, i2, JobState_Pending));
338
339 {
340 JobsRegistry::RunningJob job(registry, 0);
341 ASSERT_TRUE(job.IsValid());
342 ASSERT_EQ(30, job.GetPriority());
343 ASSERT_EQ(i2, job.GetId());
344
345 ASSERT_TRUE(CheckState(registry, i2, JobState_Running));
346 }
347
348 ASSERT_TRUE(CheckState(registry, i2, JobState_Failure));
349 ASSERT_TRUE(CheckState(registry, i3, JobState_Pending));
350
351 {
352 JobsRegistry::RunningJob job(registry, 0);
353 ASSERT_TRUE(job.IsValid());
354 ASSERT_EQ(20, job.GetPriority());
355 ASSERT_EQ(i3, job.GetId());
356
357 job.MarkSuccess();
358
359 ASSERT_TRUE(CheckState(registry, i3, JobState_Running));
360 }
361
362 ASSERT_TRUE(CheckState(registry, i3, JobState_Success));
363
364 {
365 JobsRegistry::RunningJob job(registry, 0);
366 ASSERT_TRUE(job.IsValid());
367 ASSERT_EQ(10, job.GetPriority());
368 ASSERT_EQ(i1, job.GetId());
369 }
370
371 {
372 JobsRegistry::RunningJob job(registry, 0);
373 ASSERT_TRUE(job.IsValid());
374 ASSERT_EQ(5, job.GetPriority());
375 ASSERT_EQ(i4, job.GetId());
376 }
377
378 {
379 JobsRegistry::RunningJob job(registry, 1);
380 ASSERT_FALSE(job.IsValid());
381 }
382
383 JobState s;
384 ASSERT_TRUE(registry.GetState(s, i1));
385 ASSERT_FALSE(registry.GetState(s, i2)); // Removed because oldest
386 ASSERT_FALSE(registry.GetState(s, i3)); // Removed because second oldest
387 ASSERT_TRUE(registry.GetState(s, i4));
388
389 registry.SetMaxCompletedJobs(1); // (*)
390 ASSERT_FALSE(registry.GetState(s, i1)); // Just discarded by (*)
391 ASSERT_TRUE(registry.GetState(s, i4));
392 }
393
394
TEST(JobsRegistry,Simultaneous)395 TEST(JobsRegistry, Simultaneous)
396 {
397 JobsRegistry registry(10);
398
399 std::string i1, i2;
400 registry.Submit(i1, new DummyJob(), 20);
401 registry.Submit(i2, new DummyJob(), 10);
402
403 ASSERT_TRUE(CheckState(registry, i1, JobState_Pending));
404 ASSERT_TRUE(CheckState(registry, i2, JobState_Pending));
405
406 {
407 JobsRegistry::RunningJob job1(registry, 0);
408 JobsRegistry::RunningJob job2(registry, 0);
409
410 ASSERT_TRUE(job1.IsValid());
411 ASSERT_TRUE(job2.IsValid());
412
413 job1.MarkFailure();
414 job2.MarkSuccess();
415
416 ASSERT_TRUE(CheckState(registry, i1, JobState_Running));
417 ASSERT_TRUE(CheckState(registry, i2, JobState_Running));
418 }
419
420 ASSERT_TRUE(CheckState(registry, i1, JobState_Failure));
421 ASSERT_TRUE(CheckState(registry, i2, JobState_Success));
422 }
423
424
TEST(JobsRegistry,Resubmit)425 TEST(JobsRegistry, Resubmit)
426 {
427 JobsRegistry registry(10);
428
429 std::string id;
430 registry.Submit(id, new DummyJob(), 10);
431
432 ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
433
434 registry.Resubmit(id);
435 ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
436
437 {
438 JobsRegistry::RunningJob job(registry, 0);
439 ASSERT_TRUE(job.IsValid());
440 job.MarkFailure();
441
442 ASSERT_TRUE(CheckState(registry, id, JobState_Running));
443
444 registry.Resubmit(id);
445 ASSERT_TRUE(CheckState(registry, id, JobState_Running));
446 }
447
448 ASSERT_TRUE(CheckState(registry, id, JobState_Failure));
449
450 registry.Resubmit(id);
451 ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
452
453 {
454 JobsRegistry::RunningJob job(registry, 0);
455 ASSERT_TRUE(job.IsValid());
456 ASSERT_EQ(id, job.GetId());
457
458 job.MarkSuccess();
459 ASSERT_TRUE(CheckState(registry, id, JobState_Running));
460 }
461
462 ASSERT_TRUE(CheckState(registry, id, JobState_Success));
463
464 registry.Resubmit(id);
465 ASSERT_TRUE(CheckState(registry, id, JobState_Success));
466 }
467
468
TEST(JobsRegistry,Retry)469 TEST(JobsRegistry, Retry)
470 {
471 JobsRegistry registry(10);
472
473 std::string id;
474 registry.Submit(id, new DummyJob(), 10);
475
476 ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
477
478 {
479 JobsRegistry::RunningJob job(registry, 0);
480 ASSERT_TRUE(job.IsValid());
481 job.MarkRetry(0);
482
483 ASSERT_TRUE(CheckState(registry, id, JobState_Running));
484 }
485
486 ASSERT_TRUE(CheckState(registry, id, JobState_Retry));
487
488 registry.Resubmit(id);
489 ASSERT_TRUE(CheckState(registry, id, JobState_Retry));
490
491 registry.ScheduleRetries();
492 ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
493
494 {
495 JobsRegistry::RunningJob job(registry, 0);
496 ASSERT_TRUE(job.IsValid());
497 job.MarkSuccess();
498
499 ASSERT_TRUE(CheckState(registry, id, JobState_Running));
500 }
501
502 ASSERT_TRUE(CheckState(registry, id, JobState_Success));
503 }
504
505
TEST(JobsRegistry,PausePending)506 TEST(JobsRegistry, PausePending)
507 {
508 JobsRegistry registry(10);
509
510 std::string id;
511 registry.Submit(id, new DummyJob(), 10);
512
513 ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
514
515 registry.Pause(id);
516 ASSERT_TRUE(CheckState(registry, id, JobState_Paused));
517
518 registry.Pause(id);
519 ASSERT_TRUE(CheckState(registry, id, JobState_Paused));
520
521 registry.Resubmit(id);
522 ASSERT_TRUE(CheckState(registry, id, JobState_Paused));
523
524 registry.Resume(id);
525 ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
526 }
527
528
TEST(JobsRegistry,PauseRunning)529 TEST(JobsRegistry, PauseRunning)
530 {
531 JobsRegistry registry(10);
532
533 std::string id;
534 registry.Submit(id, new DummyJob(), 10);
535
536 ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
537
538 {
539 JobsRegistry::RunningJob job(registry, 0);
540 ASSERT_TRUE(job.IsValid());
541
542 registry.Resubmit(id);
543 job.MarkPause();
544 ASSERT_TRUE(CheckState(registry, id, JobState_Running));
545 }
546
547 ASSERT_TRUE(CheckState(registry, id, JobState_Paused));
548
549 registry.Resubmit(id);
550 ASSERT_TRUE(CheckState(registry, id, JobState_Paused));
551
552 registry.Resume(id);
553 ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
554
555 {
556 JobsRegistry::RunningJob job(registry, 0);
557 ASSERT_TRUE(job.IsValid());
558
559 job.MarkSuccess();
560 ASSERT_TRUE(CheckState(registry, id, JobState_Running));
561 }
562
563 ASSERT_TRUE(CheckState(registry, id, JobState_Success));
564 }
565
566
TEST(JobsRegistry,PauseRetry)567 TEST(JobsRegistry, PauseRetry)
568 {
569 JobsRegistry registry(10);
570
571 std::string id;
572 registry.Submit(id, new DummyJob(), 10);
573
574 ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
575
576 {
577 JobsRegistry::RunningJob job(registry, 0);
578 ASSERT_TRUE(job.IsValid());
579
580 job.MarkRetry(0);
581 ASSERT_TRUE(CheckState(registry, id, JobState_Running));
582 }
583
584 ASSERT_TRUE(CheckState(registry, id, JobState_Retry));
585
586 registry.Pause(id);
587 ASSERT_TRUE(CheckState(registry, id, JobState_Paused));
588
589 registry.Resume(id);
590 ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
591
592 {
593 JobsRegistry::RunningJob job(registry, 0);
594 ASSERT_TRUE(job.IsValid());
595
596 job.MarkSuccess();
597 ASSERT_TRUE(CheckState(registry, id, JobState_Running));
598 }
599
600 ASSERT_TRUE(CheckState(registry, id, JobState_Success));
601 }
602
603
TEST(JobsRegistry,Cancel)604 TEST(JobsRegistry, Cancel)
605 {
606 JobsRegistry registry(10);
607
608 std::string id;
609 registry.Submit(id, new DummyJob(), 10);
610
611 ASSERT_FALSE(registry.Cancel("nope"));
612
613 ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
614 ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_Success));
615
616 ASSERT_TRUE(registry.Cancel(id));
617 ASSERT_TRUE(CheckState(registry, id, JobState_Failure));
618 ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_CanceledJob));
619
620 ASSERT_TRUE(registry.Cancel(id));
621 ASSERT_TRUE(CheckState(registry, id, JobState_Failure));
622 ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_CanceledJob));
623
624 ASSERT_TRUE(registry.Resubmit(id));
625 ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
626 ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_CanceledJob));
627
628 {
629 JobsRegistry::RunningJob job(registry, 0);
630 ASSERT_TRUE(job.IsValid());
631
632 ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_Success));
633
634 job.MarkSuccess();
635 ASSERT_TRUE(CheckState(registry, id, JobState_Running));
636 }
637
638 ASSERT_TRUE(CheckState(registry, id, JobState_Success));
639 ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_Success));
640
641 ASSERT_TRUE(registry.Cancel(id));
642 ASSERT_TRUE(CheckState(registry, id, JobState_Success));
643 ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_Success));
644
645 registry.Submit(id, new DummyJob(), 10);
646
647 {
648 JobsRegistry::RunningJob job(registry, 0);
649 ASSERT_TRUE(job.IsValid());
650 ASSERT_EQ(id, job.GetId());
651
652 ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_Success));
653 ASSERT_TRUE(CheckState(registry, id, JobState_Running));
654
655 job.MarkCanceled();
656 }
657
658 ASSERT_TRUE(CheckState(registry, id, JobState_Failure));
659 ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_CanceledJob));
660
661 ASSERT_TRUE(registry.Resubmit(id));
662 ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
663 ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_CanceledJob));
664
665 ASSERT_TRUE(registry.Pause(id));
666 ASSERT_TRUE(CheckState(registry, id, JobState_Paused));
667 ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_CanceledJob));
668
669 ASSERT_TRUE(registry.Cancel(id));
670 ASSERT_TRUE(CheckState(registry, id, JobState_Failure));
671 ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_CanceledJob));
672
673 ASSERT_TRUE(registry.Resubmit(id));
674 ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
675 ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_CanceledJob));
676
677 {
678 JobsRegistry::RunningJob job(registry, 0);
679 ASSERT_TRUE(job.IsValid());
680 ASSERT_EQ(id, job.GetId());
681
682 ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_Success));
683 ASSERT_TRUE(CheckState(registry, id, JobState_Running));
684
685 job.MarkRetry(500);
686 }
687
688 ASSERT_TRUE(CheckState(registry, id, JobState_Retry));
689 ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_Success));
690
691 ASSERT_TRUE(registry.Cancel(id));
692 ASSERT_TRUE(CheckState(registry, id, JobState_Failure));
693 ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_CanceledJob));
694 }
695
696
697
TEST(JobsEngine,SubmitAndWait)698 TEST(JobsEngine, SubmitAndWait)
699 {
700 JobsEngine engine(10);
701 engine.SetThreadSleep(10);
702 engine.SetWorkersCount(3);
703 engine.Start();
704
705 Json::Value content = Json::nullValue;
706 engine.GetRegistry().SubmitAndWait(content, new DummyJob(), rand() % 10);
707 ASSERT_EQ(Json::objectValue, content.type());
708 ASSERT_EQ("world", content["hello"].asString());
709
710 content = Json::nullValue;
711 ASSERT_THROW(engine.GetRegistry().SubmitAndWait(content, new DummyJob(true), rand() % 10), OrthancException);
712 ASSERT_EQ(Json::nullValue, content.type());
713
714 engine.Stop();
715 }
716
717
TEST(JobsEngine,DISABLED_SequenceOfOperationsJob)718 TEST(JobsEngine, DISABLED_SequenceOfOperationsJob)
719 {
720 JobsEngine engine(10);
721 engine.SetThreadSleep(10);
722 engine.SetWorkersCount(3);
723 engine.Start();
724
725 std::string id;
726 SequenceOfOperationsJob* job = NULL;
727
728 {
729 std::unique_ptr<SequenceOfOperationsJob> a(new SequenceOfOperationsJob);
730 job = a.get();
731 engine.GetRegistry().Submit(id, a.release(), 0);
732 }
733
734 boost::this_thread::sleep(boost::posix_time::milliseconds(500));
735
736 {
737 SequenceOfOperationsJob::Lock lock(*job);
738 size_t i = lock.AddOperation(new LogJobOperation);
739 size_t j = lock.AddOperation(new LogJobOperation);
740 size_t k = lock.AddOperation(new LogJobOperation);
741
742 StringOperationValue a("Hello");
743 StringOperationValue b("World");
744 lock.AddInput(i, a);
745 lock.AddInput(i, b);
746
747 lock.Connect(i, j);
748 lock.Connect(j, k);
749 }
750
751 boost::this_thread::sleep(boost::posix_time::milliseconds(2000));
752
753 engine.Stop();
754
755 }
756
757
CheckSameJson(const Json::Value & a,const Json::Value & b)758 static bool CheckSameJson(const Json::Value& a,
759 const Json::Value& b)
760 {
761 std::string s = a.toStyledString();
762 std::string t = b.toStyledString();
763
764 if (s == t)
765 {
766 return true;
767 }
768 else
769 {
770 LOG(ERROR) << "Expected serialization: " << s;
771 LOG(ERROR) << "Actual serialization: " << t;
772 return false;
773 }
774 }
775
776
CheckIdempotentSerialization(IJobUnserializer & unserializer,IJob & job)777 static bool CheckIdempotentSerialization(IJobUnserializer& unserializer,
778 IJob& job)
779 {
780 Json::Value a = 42;
781
782 if (!job.Serialize(a))
783 {
784 return false;
785 }
786 else
787 {
788 std::unique_ptr<IJob> unserialized(unserializer.UnserializeJob(a));
789
790 Json::Value b = 43;
791 if (unserialized->Serialize(b))
792 {
793 return (CheckSameJson(a, b));
794 }
795 else
796 {
797 return false;
798 }
799 }
800 }
801
802
CheckIdempotentSetOfInstances(IJobUnserializer & unserializer,SetOfInstancesJob & job)803 static bool CheckIdempotentSetOfInstances(IJobUnserializer& unserializer,
804 SetOfInstancesJob& job)
805 {
806 Json::Value a = 42;
807
808 if (!job.Serialize(a))
809 {
810 return false;
811 }
812 else
813 {
814 std::unique_ptr<SetOfInstancesJob> unserialized
815 (dynamic_cast<SetOfInstancesJob*>(unserializer.UnserializeJob(a)));
816
817 Json::Value b = 43;
818 if (unserialized->Serialize(b))
819 {
820 return (CheckSameJson(a, b) &&
821 job.HasTrailingStep() == unserialized->HasTrailingStep() &&
822 job.GetPosition() == unserialized->GetPosition() &&
823 job.GetInstancesCount() == unserialized->GetInstancesCount() &&
824 job.GetCommandsCount() == unserialized->GetCommandsCount());
825 }
826 else
827 {
828 return false;
829 }
830 }
831 }
832
833
CheckIdempotentSerialization(IJobUnserializer & unserializer,const IJobOperationValue & value)834 static bool CheckIdempotentSerialization(IJobUnserializer& unserializer,
835 const IJobOperationValue& value)
836 {
837 Json::Value a = 42;
838 value.Serialize(a);
839
840 std::unique_ptr<IJobOperationValue> unserialized(unserializer.UnserializeValue(a));
841
842 Json::Value b = 43;
843 unserialized->Serialize(b);
844
845 return CheckSameJson(a, b);
846 }
847
848
TEST(JobsSerialization,BadFileFormat)849 TEST(JobsSerialization, BadFileFormat)
850 {
851 GenericJobUnserializer unserializer;
852
853 Json::Value s;
854
855 s = Json::objectValue;
856 ASSERT_THROW(unserializer.UnserializeValue(s), OrthancException);
857 ASSERT_THROW(unserializer.UnserializeJob(s), OrthancException);
858 ASSERT_THROW(unserializer.UnserializeOperation(s), OrthancException);
859
860 s = Json::arrayValue;
861 ASSERT_THROW(unserializer.UnserializeValue(s), OrthancException);
862 ASSERT_THROW(unserializer.UnserializeJob(s), OrthancException);
863 ASSERT_THROW(unserializer.UnserializeOperation(s), OrthancException);
864
865 s = "hello";
866 ASSERT_THROW(unserializer.UnserializeValue(s), OrthancException);
867 ASSERT_THROW(unserializer.UnserializeJob(s), OrthancException);
868 ASSERT_THROW(unserializer.UnserializeOperation(s), OrthancException);
869
870 s = 42;
871 ASSERT_THROW(unserializer.UnserializeValue(s), OrthancException);
872 ASSERT_THROW(unserializer.UnserializeJob(s), OrthancException);
873 ASSERT_THROW(unserializer.UnserializeOperation(s), OrthancException);
874 }
875
876
TEST(JobsSerialization,JobOperationValues)877 TEST(JobsSerialization, JobOperationValues)
878 {
879 Json::Value s;
880
881 {
882 JobOperationValues values;
883 values.Append(new NullOperationValue);
884 values.Append(new StringOperationValue("hello"));
885 values.Append(new StringOperationValue("world"));
886
887 s = 42;
888 values.Serialize(s);
889 }
890
891 {
892 GenericJobUnserializer unserializer;
893 std::unique_ptr<JobOperationValues> values(JobOperationValues::Unserialize(unserializer, s));
894 ASSERT_EQ(3u, values->GetSize());
895 ASSERT_EQ(IJobOperationValue::Type_Null, values->GetValue(0).GetType());
896 ASSERT_EQ(IJobOperationValue::Type_String, values->GetValue(1).GetType());
897 ASSERT_EQ(IJobOperationValue::Type_String, values->GetValue(2).GetType());
898
899 ASSERT_EQ("hello", dynamic_cast<const StringOperationValue&>(values->GetValue(1)).GetContent());
900 ASSERT_EQ("world", dynamic_cast<const StringOperationValue&>(values->GetValue(2)).GetContent());
901 }
902 }
903
904
TEST(JobsSerialization,GenericValues)905 TEST(JobsSerialization, GenericValues)
906 {
907 GenericJobUnserializer unserializer;
908 Json::Value s;
909
910 {
911 NullOperationValue null;
912
913 ASSERT_TRUE(CheckIdempotentSerialization(unserializer, null));
914 null.Serialize(s);
915 }
916
917 ASSERT_THROW(unserializer.UnserializeJob(s), OrthancException);
918 ASSERT_THROW(unserializer.UnserializeOperation(s), OrthancException);
919
920 std::unique_ptr<IJobOperationValue> value;
921 value.reset(unserializer.UnserializeValue(s));
922
923 ASSERT_EQ(IJobOperationValue::Type_Null, value->GetType());
924
925 {
926 StringOperationValue str("Hello");
927
928 ASSERT_TRUE(CheckIdempotentSerialization(unserializer, str));
929 str.Serialize(s);
930 }
931
932 ASSERT_THROW(unserializer.UnserializeJob(s), OrthancException);
933 ASSERT_THROW(unserializer.UnserializeOperation(s), OrthancException);
934 value.reset(unserializer.UnserializeValue(s));
935
936 ASSERT_EQ(IJobOperationValue::Type_String, value->GetType());
937 ASSERT_EQ("Hello", dynamic_cast<StringOperationValue&>(*value).GetContent());
938 }
939
940
TEST(JobsSerialization,GenericJobs)941 TEST(JobsSerialization, GenericJobs)
942 {
943 Json::Value s;
944
945 // This tests SetOfInstancesJob
946
947 {
948 DummyInstancesJob job;
949 job.SetDescription("description");
950 job.AddInstance("hello");
951 job.AddInstance("nope");
952 job.AddInstance("world");
953 job.SetPermissive(true);
954 ASSERT_THROW(job.Step("jobId"), OrthancException); // Not started yet
955 ASSERT_FALSE(job.HasTrailingStep());
956 ASSERT_FALSE(job.IsTrailingStepDone());
957 job.Start();
958 ASSERT_EQ(JobStepCode_Continue, job.Step("jobId").GetCode());
959 ASSERT_EQ(JobStepCode_Continue, job.Step("jobId").GetCode());
960
961 {
962 DummyUnserializer unserializer;
963 ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job));
964 }
965
966 ASSERT_TRUE(job.Serialize(s));
967 }
968
969 {
970 DummyUnserializer unserializer;
971 ASSERT_THROW(unserializer.UnserializeValue(s), OrthancException);
972 ASSERT_THROW(unserializer.UnserializeOperation(s), OrthancException);
973
974 std::unique_ptr<IJob> job;
975 job.reset(unserializer.UnserializeJob(s));
976
977 const DummyInstancesJob& tmp = dynamic_cast<const DummyInstancesJob&>(*job);
978 ASSERT_FALSE(tmp.IsStarted());
979 ASSERT_TRUE(tmp.IsPermissive());
980 ASSERT_EQ("description", tmp.GetDescription());
981 ASSERT_EQ(3u, tmp.GetInstancesCount());
982 ASSERT_EQ(2u, tmp.GetPosition());
983 ASSERT_EQ(1u, tmp.GetFailedInstances().size());
984 ASSERT_EQ("hello", tmp.GetInstance(0));
985 ASSERT_EQ("nope", tmp.GetInstance(1));
986 ASSERT_EQ("world", tmp.GetInstance(2));
987 ASSERT_TRUE(tmp.IsFailedInstance("nope"));
988 }
989
990 // SequenceOfOperationsJob
991
992 {
993 SequenceOfOperationsJob job;
994 job.SetDescription("hello");
995
996 {
997 SequenceOfOperationsJob::Lock lock(job);
998 size_t a = lock.AddOperation(new LogJobOperation);
999 size_t b = lock.AddOperation(new LogJobOperation);
1000 lock.Connect(a, b);
1001
1002 StringOperationValue s1("hello");
1003 StringOperationValue s2("world");
1004 lock.AddInput(a, s1);
1005 lock.AddInput(a, s2);
1006 lock.SetTrailingOperationTimeout(300);
1007 }
1008
1009 ASSERT_EQ(JobStepCode_Continue, job.Step("jobId").GetCode());
1010
1011 {
1012 GenericJobUnserializer unserializer;
1013 ASSERT_TRUE(CheckIdempotentSerialization(unserializer, job));
1014 }
1015
1016 ASSERT_TRUE(job.Serialize(s));
1017 }
1018
1019 {
1020 GenericJobUnserializer unserializer;
1021 ASSERT_THROW(unserializer.UnserializeValue(s), OrthancException);
1022 ASSERT_THROW(unserializer.UnserializeOperation(s), OrthancException);
1023
1024 std::unique_ptr<IJob> job;
1025 job.reset(unserializer.UnserializeJob(s));
1026
1027 std::string tmp;
1028 dynamic_cast<SequenceOfOperationsJob&>(*job).GetDescription(tmp);
1029 ASSERT_EQ("hello", tmp);
1030 }
1031 }
1032
1033
IsSameTagValue(const ParsedDicomFile & dicom1,const ParsedDicomFile & dicom2,DicomTag tag)1034 static bool IsSameTagValue(const ParsedDicomFile& dicom1,
1035 const ParsedDicomFile& dicom2,
1036 DicomTag tag)
1037 {
1038 std::string a, b;
1039 return (dicom1.GetTagValue(a, tag) &&
1040 dicom2.GetTagValue(b, tag) &&
1041 (a == b));
1042 }
1043
1044
1045
TEST(JobsSerialization,DicomModification)1046 TEST(JobsSerialization, DicomModification)
1047 {
1048 Json::Value s;
1049
1050 ParsedDicomFile source(true);
1051 source.Insert(DICOM_TAG_STUDY_DESCRIPTION, "Test 1", false, "");
1052 source.Insert(DICOM_TAG_SERIES_DESCRIPTION, "Test 2", false, "");
1053 source.Insert(DICOM_TAG_PATIENT_NAME, "Test 3", false, "");
1054
1055 std::unique_ptr<ParsedDicomFile> modified(source.Clone(true));
1056
1057 {
1058 DicomModification modification;
1059 modification.SetLevel(ResourceType_Series);
1060 modification.Clear(DICOM_TAG_STUDY_DESCRIPTION);
1061 modification.Remove(DICOM_TAG_SERIES_DESCRIPTION);
1062 modification.Replace(DICOM_TAG_PATIENT_NAME, "Test 4", true);
1063
1064 modification.Apply(*modified);
1065
1066 s = 42;
1067 modification.Serialize(s);
1068 }
1069
1070 {
1071 DicomModification modification(s);
1072 ASSERT_EQ(ResourceType_Series, modification.GetLevel());
1073
1074 std::unique_ptr<ParsedDicomFile> second(source.Clone(true));
1075 modification.Apply(*second);
1076
1077 std::string t;
1078 ASSERT_TRUE(second->GetTagValue(t, DICOM_TAG_STUDY_DESCRIPTION));
1079 ASSERT_TRUE(t.empty());
1080 ASSERT_FALSE(second->GetTagValue(t, DICOM_TAG_SERIES_DESCRIPTION));
1081 ASSERT_TRUE(second->GetTagValue(t, DICOM_TAG_PATIENT_NAME));
1082 ASSERT_EQ("Test 4", t);
1083
1084 ASSERT_TRUE(IsSameTagValue(source, *modified, DICOM_TAG_STUDY_INSTANCE_UID));
1085 ASSERT_TRUE(IsSameTagValue(source, *second, DICOM_TAG_STUDY_INSTANCE_UID));
1086
1087 ASSERT_FALSE(IsSameTagValue(source, *second, DICOM_TAG_SERIES_INSTANCE_UID));
1088 ASSERT_TRUE(IsSameTagValue(*modified, *second, DICOM_TAG_SERIES_INSTANCE_UID));
1089 }
1090 }
1091
1092
TEST(JobsSerialization,DicomModification2)1093 TEST(JobsSerialization, DicomModification2)
1094 {
1095 Json::Value s;
1096
1097 {
1098 DicomModification modification;
1099 modification.SetupAnonymization(DicomVersion_2017c);
1100 modification.Remove(DicomPath(DICOM_TAG_REFERENCED_IMAGE_SEQUENCE, 1, DICOM_TAG_SOP_INSTANCE_UID));
1101 modification.Replace(DicomPath(DICOM_TAG_REFERENCED_IMAGE_SEQUENCE, 1, DICOM_TAG_SOP_CLASS_UID), "Hello", true);
1102 modification.Keep(DicomPath(DICOM_TAG_REFERENCED_IMAGE_SEQUENCE, 1, DICOM_TAG_PATIENT_NAME));
1103
1104 s = 42;
1105 modification.Serialize(s);
1106 }
1107
1108 {
1109 DicomModification modification(s);
1110
1111 // Check idempotent serialization
1112 Json::Value ss;
1113 modification.Serialize(ss);
1114 ASSERT_EQ(s.toStyledString(), ss.toStyledString());
1115 }
1116 }
1117
1118
TEST(JobsSerialization,Registry)1119 TEST(JobsSerialization, Registry)
1120 {
1121 Json::Value s;
1122 std::string i1, i2;
1123
1124 {
1125 JobsRegistry registry(10);
1126 registry.Submit(i1, new DummyJob(), 10);
1127 registry.Submit(i2, new SequenceOfOperationsJob(), 30);
1128 registry.Serialize(s);
1129 }
1130
1131 {
1132 DummyUnserializer unserializer;
1133 JobsRegistry registry(unserializer, s, 10);
1134
1135 Json::Value t;
1136 registry.Serialize(t);
1137 ASSERT_TRUE(CheckSameJson(s, t));
1138 }
1139 }
1140
1141
TEST(JobsSerialization,TrailingStep)1142 TEST(JobsSerialization, TrailingStep)
1143 {
1144 {
1145 Json::Value s;
1146
1147 DummyInstancesJob job;
1148 ASSERT_EQ(0u, job.GetCommandsCount());
1149 ASSERT_EQ(0u, job.GetInstancesCount());
1150
1151 job.Start();
1152 ASSERT_EQ(0u, job.GetPosition());
1153 ASSERT_FALSE(job.HasTrailingStep());
1154 ASSERT_FALSE(job.IsTrailingStepDone());
1155
1156 {
1157 DummyUnserializer unserializer;
1158 ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job));
1159 }
1160
1161 ASSERT_EQ(JobStepCode_Success, job.Step("jobId").GetCode());
1162 ASSERT_EQ(1u, job.GetPosition());
1163 ASSERT_FALSE(job.IsTrailingStepDone());
1164
1165 {
1166 DummyUnserializer unserializer;
1167 ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job));
1168 }
1169
1170 ASSERT_THROW(job.Step("jobId"), OrthancException);
1171 }
1172
1173 {
1174 Json::Value s;
1175
1176 DummyInstancesJob job;
1177 job.AddInstance("hello");
1178 job.AddInstance("world");
1179 ASSERT_EQ(2u, job.GetCommandsCount());
1180 ASSERT_EQ(2u, job.GetInstancesCount());
1181
1182 job.Start();
1183 ASSERT_EQ(0u, job.GetPosition());
1184 ASSERT_FALSE(job.HasTrailingStep());
1185 ASSERT_FALSE(job.IsTrailingStepDone());
1186
1187 {
1188 DummyUnserializer unserializer;
1189 ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job));
1190 }
1191
1192 ASSERT_EQ(JobStepCode_Continue, job.Step("jobId").GetCode());
1193 ASSERT_EQ(1u, job.GetPosition());
1194 ASSERT_FALSE(job.IsTrailingStepDone());
1195
1196 {
1197 DummyUnserializer unserializer;
1198 ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job));
1199 }
1200
1201 ASSERT_EQ(JobStepCode_Success, job.Step("jobId").GetCode());
1202 ASSERT_EQ(2u, job.GetPosition());
1203 ASSERT_FALSE(job.IsTrailingStepDone());
1204
1205 {
1206 DummyUnserializer unserializer;
1207 ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job));
1208 }
1209
1210 ASSERT_THROW(job.Step("jobId"), OrthancException);
1211 }
1212
1213 {
1214 Json::Value s;
1215
1216 DummyInstancesJob job;
1217 ASSERT_EQ(0u, job.GetInstancesCount());
1218 ASSERT_EQ(0u, job.GetCommandsCount());
1219 job.AddTrailingStep();
1220 ASSERT_EQ(0u, job.GetInstancesCount());
1221 ASSERT_EQ(1u, job.GetCommandsCount());
1222
1223 job.Start(); // This adds the trailing step
1224 ASSERT_EQ(0u, job.GetPosition());
1225 ASSERT_TRUE(job.HasTrailingStep());
1226 ASSERT_FALSE(job.IsTrailingStepDone());
1227
1228 {
1229 DummyUnserializer unserializer;
1230 ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job));
1231 }
1232
1233 ASSERT_EQ(JobStepCode_Success, job.Step("jobId").GetCode());
1234 ASSERT_EQ(1u, job.GetPosition());
1235 ASSERT_TRUE(job.IsTrailingStepDone());
1236
1237 {
1238 DummyUnserializer unserializer;
1239 ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job));
1240 }
1241
1242 ASSERT_THROW(job.Step("jobId"), OrthancException);
1243 }
1244
1245 {
1246 Json::Value s;
1247
1248 DummyInstancesJob job;
1249 job.AddInstance("hello");
1250 ASSERT_EQ(1u, job.GetInstancesCount());
1251 ASSERT_EQ(1u, job.GetCommandsCount());
1252 job.AddTrailingStep();
1253 ASSERT_EQ(1u, job.GetInstancesCount());
1254 ASSERT_EQ(2u, job.GetCommandsCount());
1255
1256 job.Start();
1257 ASSERT_EQ(2u, job.GetCommandsCount());
1258 ASSERT_EQ(0u, job.GetPosition());
1259 ASSERT_TRUE(job.HasTrailingStep());
1260 ASSERT_FALSE(job.IsTrailingStepDone());
1261
1262 {
1263 DummyUnserializer unserializer;
1264 ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job));
1265 }
1266
1267 ASSERT_EQ(JobStepCode_Continue, job.Step("jobId").GetCode());
1268 ASSERT_EQ(1u, job.GetPosition());
1269 ASSERT_FALSE(job.IsTrailingStepDone());
1270
1271 {
1272 DummyUnserializer unserializer;
1273 ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job));
1274 }
1275
1276 ASSERT_EQ(JobStepCode_Success, job.Step("jobId").GetCode());
1277 ASSERT_EQ(2u, job.GetPosition());
1278 ASSERT_TRUE(job.IsTrailingStepDone());
1279
1280 {
1281 DummyUnserializer unserializer;
1282 ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job));
1283 }
1284
1285 ASSERT_THROW(job.Step("jobId"), OrthancException);
1286 }
1287 }
1288
1289
TEST(JobsSerialization,RemoteModalityParameters)1290 TEST(JobsSerialization, RemoteModalityParameters)
1291 {
1292 Json::Value s;
1293
1294 {
1295 RemoteModalityParameters modality;
1296 ASSERT_FALSE(modality.IsAdvancedFormatNeeded());
1297 modality.Serialize(s, false);
1298 ASSERT_EQ(Json::arrayValue, s.type());
1299 ASSERT_FALSE(modality.IsDicomTlsEnabled());
1300 ASSERT_FALSE(modality.HasTimeout());
1301 ASSERT_EQ(0u, modality.GetTimeout());
1302 }
1303
1304 {
1305 RemoteModalityParameters modality(s);
1306 ASSERT_FALSE(modality.IsAdvancedFormatNeeded());
1307 ASSERT_EQ("ORTHANC", modality.GetApplicationEntityTitle());
1308 ASSERT_EQ("127.0.0.1", modality.GetHost());
1309 ASSERT_EQ(104u, modality.GetPortNumber());
1310 ASSERT_EQ(ModalityManufacturer_Generic, modality.GetManufacturer());
1311 ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_Echo));
1312 ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_Find));
1313 ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_Get));
1314 ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_Store));
1315 ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_Move));
1316 ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_NAction));
1317 ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_NEventReport));
1318 ASSERT_TRUE(modality.IsTranscodingAllowed());
1319 ASSERT_FALSE(modality.IsDicomTlsEnabled());
1320 ASSERT_FALSE(modality.HasLocalAet());
1321 ASSERT_THROW(modality.GetLocalAet(), OrthancException);
1322 ASSERT_FALSE(modality.HasTimeout());
1323 ASSERT_EQ(0u, modality.GetTimeout());
1324 }
1325
1326 s = Json::nullValue;
1327
1328 {
1329 RemoteModalityParameters modality;
1330 ASSERT_THROW(modality.SetPortNumber(0), OrthancException);
1331 ASSERT_THROW(modality.SetPortNumber(65535), OrthancException);
1332 modality.SetApplicationEntityTitle("HELLO");
1333 modality.SetHost("world");
1334 modality.SetPortNumber(45);
1335 modality.SetManufacturer(ModalityManufacturer_GenericNoWildcardInDates);
1336 ASSERT_FALSE(modality.IsAdvancedFormatNeeded());
1337 modality.Serialize(s, true);
1338 ASSERT_EQ(Json::objectValue, s.type());
1339 ASSERT_FALSE(modality.HasLocalAet());
1340 ASSERT_FALSE(modality.HasTimeout());
1341 ASSERT_EQ(0u, modality.GetTimeout());
1342 }
1343
1344 {
1345 RemoteModalityParameters modality(s);
1346 ASSERT_EQ("HELLO", modality.GetApplicationEntityTitle());
1347 ASSERT_EQ("world", modality.GetHost());
1348 ASSERT_EQ(45u, modality.GetPortNumber());
1349 ASSERT_EQ(ModalityManufacturer_GenericNoWildcardInDates, modality.GetManufacturer());
1350 ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_Echo));
1351 ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_Find));
1352 ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_Get));
1353 ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_Store));
1354 ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_Move));
1355 ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_NAction));
1356 ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_NEventReport));
1357 ASSERT_TRUE(modality.IsTranscodingAllowed());
1358 ASSERT_FALSE(modality.IsDicomTlsEnabled());
1359 ASSERT_FALSE(modality.HasLocalAet());
1360 ASSERT_FALSE(modality.HasTimeout());
1361 ASSERT_EQ(0u, modality.GetTimeout());
1362 }
1363
1364 s["Port"] = "46";
1365
1366 {
1367 RemoteModalityParameters modality(s);
1368 ASSERT_EQ(46u, modality.GetPortNumber());
1369 }
1370
1371 s["Port"] = -1; ASSERT_THROW(RemoteModalityParameters m(s), OrthancException);
1372 s["Port"] = 65535; ASSERT_THROW(RemoteModalityParameters m(s), OrthancException);
1373 s["Port"] = "nope"; ASSERT_THROW(RemoteModalityParameters m(s), OrthancException);
1374
1375 std::set<DicomRequestType> operations;
1376 operations.insert(DicomRequestType_Echo);
1377 operations.insert(DicomRequestType_Find);
1378 operations.insert(DicomRequestType_Get);
1379 operations.insert(DicomRequestType_Move);
1380 operations.insert(DicomRequestType_Store);
1381 operations.insert(DicomRequestType_NAction);
1382 operations.insert(DicomRequestType_NEventReport);
1383
1384 ASSERT_EQ(7u, operations.size());
1385
1386 for (std::set<DicomRequestType>::const_iterator
1387 it = operations.begin(); it != operations.end(); ++it)
1388 {
1389 {
1390 RemoteModalityParameters modality;
1391 modality.SetRequestAllowed(*it, false);
1392 ASSERT_TRUE(modality.IsAdvancedFormatNeeded());
1393
1394 modality.Serialize(s, false);
1395 ASSERT_EQ(Json::objectValue, s.type());
1396 }
1397
1398 {
1399 RemoteModalityParameters modality(s);
1400
1401 ASSERT_FALSE(modality.IsRequestAllowed(*it));
1402
1403 for (std::set<DicomRequestType>::const_iterator
1404 it2 = operations.begin(); it2 != operations.end(); ++it2)
1405 {
1406 if (*it2 != *it)
1407 {
1408 ASSERT_TRUE(modality.IsRequestAllowed(*it2));
1409 }
1410 }
1411 }
1412 }
1413
1414 s = Json::nullValue;
1415
1416 {
1417 RemoteModalityParameters modality;
1418 modality.SetLocalAet("hello");
1419 modality.SetTimeout(42);
1420 ASSERT_TRUE(modality.IsAdvancedFormatNeeded());
1421 modality.Serialize(s, true);
1422 ASSERT_EQ(Json::objectValue, s.type());
1423 ASSERT_TRUE(modality.HasLocalAet());
1424 ASSERT_TRUE(modality.HasTimeout());
1425 ASSERT_EQ(42u, modality.GetTimeout());
1426 }
1427
1428 {
1429 RemoteModalityParameters modality(s);
1430 ASSERT_TRUE(modality.HasLocalAet());
1431 ASSERT_EQ("hello", modality.GetLocalAet());
1432 ASSERT_TRUE(modality.HasTimeout());
1433 ASSERT_EQ(42u, modality.GetTimeout());
1434 }
1435
1436 {
1437 Json::Value t;
1438 t["AllowStorageCommitment"] = false;
1439 t["AET"] = "AET";
1440 t["Host"] = "host";
1441 t["Port"] = "104";
1442
1443 RemoteModalityParameters modality(t);
1444 ASSERT_TRUE(modality.IsAdvancedFormatNeeded());
1445 ASSERT_EQ("AET", modality.GetApplicationEntityTitle());
1446 ASSERT_EQ("host", modality.GetHost());
1447 ASSERT_EQ(104u, modality.GetPortNumber());
1448 ASSERT_FALSE(modality.IsRequestAllowed(DicomRequestType_NAction));
1449 ASSERT_FALSE(modality.IsRequestAllowed(DicomRequestType_NEventReport));
1450 ASSERT_TRUE(modality.IsTranscodingAllowed());
1451 ASSERT_FALSE(modality.IsDicomTlsEnabled());
1452 ASSERT_FALSE(modality.HasLocalAet());
1453 ASSERT_THROW(modality.GetLocalAet(), OrthancException);
1454 ASSERT_FALSE(modality.HasTimeout());
1455 ASSERT_EQ(0u, modality.GetTimeout());
1456 }
1457
1458 {
1459 Json::Value t;
1460 t["AllowNAction"] = false;
1461 t["AllowNEventReport"] = true;
1462 t["AET"] = "AET";
1463 t["Host"] = "host";
1464 t["Port"] = "104";
1465 t["AllowTranscoding"] = false;
1466 t["UseDicomTls"] = true;
1467 t["LocalAet"] = "world";
1468 t["Timeout"] = 20;
1469
1470 RemoteModalityParameters modality(t);
1471 ASSERT_TRUE(modality.IsAdvancedFormatNeeded());
1472 ASSERT_EQ("AET", modality.GetApplicationEntityTitle());
1473 ASSERT_EQ("host", modality.GetHost());
1474 ASSERT_EQ(104u, modality.GetPortNumber());
1475 ASSERT_FALSE(modality.IsRequestAllowed(DicomRequestType_NAction));
1476 ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_NEventReport));
1477 ASSERT_FALSE(modality.IsTranscodingAllowed());
1478 ASSERT_TRUE(modality.IsDicomTlsEnabled());
1479 ASSERT_TRUE(modality.HasLocalAet());
1480 ASSERT_EQ("world", modality.GetLocalAet());
1481 ASSERT_TRUE(modality.HasTimeout());
1482 ASSERT_EQ(20u, modality.GetTimeout());
1483 }
1484
1485 {
1486 Json::Value t;
1487 t["AllowNAction"] = true;
1488 t["AllowNEventReport"] = true;
1489 t["AET"] = "AET";
1490 t["Host"] = "host";
1491 t["Port"] = "104";
1492
1493 RemoteModalityParameters modality(t);
1494 ASSERT_FALSE(modality.IsAdvancedFormatNeeded());
1495 ASSERT_EQ("AET", modality.GetApplicationEntityTitle());
1496 ASSERT_EQ("host", modality.GetHost());
1497 ASSERT_EQ(104u, modality.GetPortNumber());
1498 ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_NAction));
1499 ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_NEventReport));
1500 ASSERT_TRUE(modality.IsTranscodingAllowed());
1501 ASSERT_FALSE(modality.IsDicomTlsEnabled());
1502 ASSERT_FALSE(modality.HasLocalAet());
1503 ASSERT_THROW(modality.GetLocalAet(), OrthancException);
1504 }
1505 }
1506
1507
1508
TEST(JobsSerialization,DicomAssociationParameters)1509 TEST(JobsSerialization, DicomAssociationParameters)
1510 {
1511 {
1512 DicomAssociationParameters a;
1513
1514 Json::Value v = Json::objectValue;
1515 a.SerializeJob(v);
1516 ASSERT_EQ(Json::objectValue, v.type());
1517 ASSERT_EQ("ORTHANC", v["LocalAet"].asString());
1518 ASSERT_EQ(DicomAssociationParameters::GetDefaultTimeout(), v["Timeout"].asUInt());
1519 ASSERT_TRUE(v.isMember("Remote"));
1520 ASSERT_TRUE(v.isMember("MaximumPduLength"));
1521
1522 ASSERT_EQ(5u, v.getMemberNames().size());
1523
1524 DicomAssociationParameters b;
1525 b.UnserializeJob(v);
1526 ASSERT_EQ("ANY-SCP", b.GetRemoteModality().GetApplicationEntityTitle());
1527 ASSERT_EQ("127.0.0.1", b.GetRemoteModality().GetHost());
1528 ASSERT_EQ(104u, b.GetRemoteModality().GetPortNumber());
1529 ASSERT_EQ("ORTHANC", b.GetLocalApplicationEntityTitle());
1530 ASSERT_EQ(DicomAssociationParameters::GetDefaultMaximumPduLength(), b.GetMaximumPduLength());
1531 ASSERT_FALSE(b.GetRemoteModality().IsDicomTlsEnabled());
1532 ASSERT_FALSE(b.GetRemoteModality().HasLocalAet());
1533 ASSERT_THROW(b.GetRemoteModality().GetLocalAet(), OrthancException);
1534 ASSERT_FALSE(b.GetRemoteModality().HasTimeout());
1535 ASSERT_EQ(0u, b.GetRemoteModality().GetTimeout());
1536 ASSERT_TRUE(b.IsRemoteCertificateRequired());
1537 }
1538
1539 {
1540 RemoteModalityParameters p;
1541 p.SetApplicationEntityTitle("WORLD");
1542 p.SetPortNumber(4242);
1543 p.SetHost("hello.world.com");
1544 p.SetDicomTlsEnabled(true);
1545 p.SetTimeout(42);
1546
1547 DicomAssociationParameters a("HELLO", p);
1548 a.SetOwnCertificatePath("key", "crt");
1549 a.SetTrustedCertificatesPath("trusted");
1550 a.SetRemoteCertificateRequired(false);
1551
1552 ASSERT_THROW(a.SetMaximumPduLength(4095), OrthancException);
1553 ASSERT_THROW(a.SetMaximumPduLength(131073), OrthancException);
1554 a.SetMaximumPduLength(4096);
1555 a.SetMaximumPduLength(131072);
1556
1557 Json::Value v = Json::objectValue;
1558 a.SerializeJob(v);
1559
1560 ASSERT_EQ(8u, v.getMemberNames().size());
1561
1562 DicomAssociationParameters b = DicomAssociationParameters::UnserializeJob(v);
1563
1564 ASSERT_EQ("WORLD", b.GetRemoteModality().GetApplicationEntityTitle());
1565 ASSERT_EQ("hello.world.com", b.GetRemoteModality().GetHost());
1566 ASSERT_EQ(4242u, b.GetRemoteModality().GetPortNumber());
1567 ASSERT_EQ("HELLO", b.GetLocalApplicationEntityTitle());
1568 ASSERT_TRUE(b.GetRemoteModality().IsDicomTlsEnabled());
1569 ASSERT_EQ("key", b.GetOwnPrivateKeyPath());
1570 ASSERT_EQ("crt", b.GetOwnCertificatePath());
1571 ASSERT_EQ("trusted", b.GetTrustedCertificatesPath());
1572 ASSERT_EQ(131072u, b.GetMaximumPduLength());
1573 ASSERT_TRUE(b.GetRemoteModality().HasTimeout());
1574 ASSERT_EQ(42u, b.GetRemoteModality().GetTimeout());
1575 ASSERT_FALSE(b.IsRemoteCertificateRequired());
1576 }
1577 }
1578
1579
TEST(SerializationToolbox,Numbers)1580 TEST(SerializationToolbox, Numbers)
1581 {
1582 {
1583 int32_t i;
1584 ASSERT_FALSE(SerializationToolbox::ParseInteger32(i, ""));
1585 ASSERT_FALSE(SerializationToolbox::ParseInteger32(i, "ee"));
1586 ASSERT_TRUE(SerializationToolbox::ParseInteger32(i, "42")); ASSERT_EQ(42, i);
1587 ASSERT_TRUE(SerializationToolbox::ParseInteger32(i, "-42")); ASSERT_EQ(-42, i);
1588 ASSERT_TRUE(SerializationToolbox::ParseInteger32(i, "-2147483648")); ASSERT_EQ(-2147483648l, i);
1589 ASSERT_TRUE(SerializationToolbox::ParseInteger32(i, "2147483647")); ASSERT_EQ(2147483647l, i);
1590 ASSERT_FALSE(SerializationToolbox::ParseInteger32(i, "-2147483649"));
1591 ASSERT_FALSE(SerializationToolbox::ParseInteger32(i, "2147483648"));
1592 ASSERT_FALSE(SerializationToolbox::ParseInteger32(i, "-2\\-3\\-4"));
1593 ASSERT_TRUE(SerializationToolbox::ParseFirstInteger32(i, "-2\\-3\\-4")); ASSERT_EQ(-2, i);
1594 }
1595
1596 {
1597 uint32_t i;
1598 ASSERT_FALSE(SerializationToolbox::ParseUnsignedInteger32(i, ""));
1599 ASSERT_FALSE(SerializationToolbox::ParseUnsignedInteger32(i, "ee"));
1600 ASSERT_TRUE(SerializationToolbox::ParseUnsignedInteger32(i, "42")); ASSERT_EQ(42u, i);
1601 ASSERT_FALSE(SerializationToolbox::ParseUnsignedInteger32(i, "-42"));
1602 ASSERT_TRUE(SerializationToolbox::ParseUnsignedInteger32(i, "4294967295")); ASSERT_EQ(4294967295u, i);
1603 ASSERT_FALSE(SerializationToolbox::ParseUnsignedInteger32(i, "4294967296"));
1604 ASSERT_FALSE(SerializationToolbox::ParseUnsignedInteger32(i, "2\\3\\4"));
1605 ASSERT_TRUE(SerializationToolbox::ParseFirstUnsignedInteger32(i, "2\\3\\4")); ASSERT_EQ(2u, i);
1606 }
1607
1608 {
1609 int64_t i;
1610 ASSERT_FALSE(SerializationToolbox::ParseInteger64(i, ""));
1611 ASSERT_FALSE(SerializationToolbox::ParseInteger64(i, "ee"));
1612 ASSERT_TRUE(SerializationToolbox::ParseInteger64(i, "42")); ASSERT_EQ(42, i);
1613 ASSERT_TRUE(SerializationToolbox::ParseInteger64(i, "-42")); ASSERT_EQ(-42, i);
1614 ASSERT_TRUE(SerializationToolbox::ParseInteger64(i, "-2147483649")); ASSERT_EQ(-2147483649ll, i);
1615 ASSERT_TRUE(SerializationToolbox::ParseInteger64(i, "2147483648")); ASSERT_EQ(2147483648ll, i);
1616 ASSERT_FALSE(SerializationToolbox::ParseInteger64(i, "-2\\-3\\-4"));
1617 ASSERT_TRUE(SerializationToolbox::ParseFirstInteger64(i, "-2\\-3\\-4")); ASSERT_EQ(-2, i);
1618 }
1619
1620 {
1621 uint64_t i;
1622 ASSERT_FALSE(SerializationToolbox::ParseUnsignedInteger64(i, ""));
1623 ASSERT_FALSE(SerializationToolbox::ParseUnsignedInteger64(i, "ee"));
1624 ASSERT_TRUE(SerializationToolbox::ParseUnsignedInteger64(i, "42")); ASSERT_EQ(42u, i);
1625 ASSERT_FALSE(SerializationToolbox::ParseUnsignedInteger64(i, "-42"));
1626 ASSERT_TRUE(SerializationToolbox::ParseUnsignedInteger64(i, "4294967296")); ASSERT_EQ(4294967296lu, i);
1627 ASSERT_FALSE(SerializationToolbox::ParseUnsignedInteger64(i, "2\\3\\4"));
1628 ASSERT_TRUE(SerializationToolbox::ParseFirstUnsignedInteger64(i, "2\\3\\4")); ASSERT_EQ(2u, i);
1629 }
1630
1631 {
1632 float i;
1633 ASSERT_FALSE(SerializationToolbox::ParseFloat(i, ""));
1634 ASSERT_FALSE(SerializationToolbox::ParseFloat(i, "ee"));
1635 ASSERT_TRUE(SerializationToolbox::ParseFloat(i, "42")); ASSERT_FLOAT_EQ(42.0f, i);
1636 ASSERT_TRUE(SerializationToolbox::ParseFloat(i, "-42")); ASSERT_FLOAT_EQ(-42.0f, i);
1637 ASSERT_FALSE(SerializationToolbox::ParseFloat(i, "2\\3\\4"));
1638 ASSERT_TRUE(SerializationToolbox::ParseFirstFloat(i, "1.367\\2.367\\3.367")); ASSERT_FLOAT_EQ(1.367f, i);
1639
1640 ASSERT_TRUE(SerializationToolbox::ParseFloat(i, "1.2")); ASSERT_FLOAT_EQ(1.2f, i);
1641 ASSERT_TRUE(SerializationToolbox::ParseFloat(i, "-1.2e+2")); ASSERT_FLOAT_EQ(-120.0f, i);
1642 ASSERT_TRUE(SerializationToolbox::ParseFloat(i, "-1e-2")); ASSERT_FLOAT_EQ(-0.01f, i);
1643 ASSERT_TRUE(SerializationToolbox::ParseFloat(i, "1.3671875")); ASSERT_FLOAT_EQ(1.3671875f, i);
1644 }
1645
1646 {
1647 double i;
1648 ASSERT_FALSE(SerializationToolbox::ParseDouble(i, ""));
1649 ASSERT_FALSE(SerializationToolbox::ParseDouble(i, "ee"));
1650 ASSERT_TRUE(SerializationToolbox::ParseDouble(i, "42")); ASSERT_DOUBLE_EQ(42.0, i);
1651 ASSERT_TRUE(SerializationToolbox::ParseDouble(i, "-42")); ASSERT_DOUBLE_EQ(-42.0, i);
1652 ASSERT_FALSE(SerializationToolbox::ParseDouble(i, "2\\3\\4"));
1653 ASSERT_TRUE(SerializationToolbox::ParseFirstDouble(i, "1.367\\2.367\\3.367")); ASSERT_DOUBLE_EQ(1.367, i);
1654
1655 ASSERT_TRUE(SerializationToolbox::ParseDouble(i, "1.2")); ASSERT_DOUBLE_EQ(1.2, i);
1656 ASSERT_TRUE(SerializationToolbox::ParseDouble(i, "-1.2e+2")); ASSERT_DOUBLE_EQ(-120.0, i);
1657 ASSERT_TRUE(SerializationToolbox::ParseDouble(i, "-1e-2")); ASSERT_DOUBLE_EQ(-0.01, i);
1658 ASSERT_TRUE(SerializationToolbox::ParseDouble(i, "1.3671875")); ASSERT_DOUBLE_EQ(1.3671875, i);
1659 }
1660 }
1661