comparison OrthancServer/UnitTestsSources/MultiThreadingTests.cpp @ 4044:d25f4c0fa160 framework

splitting code into OrthancFramework and OrthancServer
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 10 Jun 2020 20:30:34 +0200
parents UnitTestsSources/MultiThreadingTests.cpp@ef696db8426f
children 05b8fd21089c
comparison
equal deleted inserted replaced
4043:6c6239aec462 4044:d25f4c0fa160
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 "../Core/Compatibility.h"
38 #include "../Core/FileStorage/MemoryStorageArea.h"
39 #include "../Core/JobsEngine/JobsEngine.h"
40 #include "../Core/Logging.h"
41 #include "../Core/MultiThreading/SharedMessageQueue.h"
42 #include "../Core/OrthancException.h"
43 #include "../Core/SerializationToolbox.h"
44 #include "../Core/SystemToolbox.h"
45 #include "../Core/Toolbox.h"
46 #include "../OrthancServer/Database/SQLiteDatabaseWrapper.h"
47 #include "../OrthancServer/ServerContext.h"
48 #include "../OrthancServer/ServerJobs/LuaJobManager.h"
49 #include "../OrthancServer/ServerJobs/OrthancJobUnserializer.h"
50
51 #include "../Core/JobsEngine/Operations/JobOperationValues.h"
52 #include "../Core/JobsEngine/Operations/NullOperationValue.h"
53 #include "../Core/JobsEngine/Operations/StringOperationValue.h"
54 #include "../OrthancServer/ServerJobs/Operations/DicomInstanceOperationValue.h"
55
56 #include "../Core/JobsEngine/Operations/LogJobOperation.h"
57 #include "../OrthancServer/ServerJobs/Operations/DeleteResourceOperation.h"
58 #include "../OrthancServer/ServerJobs/Operations/ModifyInstanceOperation.h"
59 #include "../OrthancServer/ServerJobs/Operations/StorePeerOperation.h"
60 #include "../OrthancServer/ServerJobs/Operations/StoreScuOperation.h"
61 #include "../OrthancServer/ServerJobs/Operations/SystemCallOperation.h"
62
63 #include "../OrthancServer/ServerJobs/ArchiveJob.h"
64 #include "../OrthancServer/ServerJobs/DicomModalityStoreJob.h"
65 #include "../OrthancServer/ServerJobs/DicomMoveScuJob.h"
66 #include "../OrthancServer/ServerJobs/MergeStudyJob.h"
67 #include "../OrthancServer/ServerJobs/OrthancPeerStoreJob.h"
68 #include "../OrthancServer/ServerJobs/ResourceModificationJob.h"
69 #include "../OrthancServer/ServerJobs/SplitStudyJob.h"
70
71
72 using namespace Orthanc;
73
74 namespace
75 {
76 class DummyJob : public IJob
77 {
78 private:
79 bool fails_;
80 unsigned int count_;
81 unsigned int steps_;
82
83 public:
84 DummyJob() :
85 fails_(false),
86 count_(0),
87 steps_(4)
88 {
89 }
90
91 explicit DummyJob(bool fails) :
92 fails_(fails),
93 count_(0),
94 steps_(4)
95 {
96 }
97
98 virtual void Start() ORTHANC_OVERRIDE
99 {
100 }
101
102 virtual void Reset() ORTHANC_OVERRIDE
103 {
104 }
105
106 virtual JobStepResult Step(const std::string& jobId) ORTHANC_OVERRIDE
107 {
108 if (fails_)
109 {
110 return JobStepResult::Failure(ErrorCode_ParameterOutOfRange, NULL);
111 }
112 else if (count_ == steps_ - 1)
113 {
114 return JobStepResult::Success();
115 }
116 else
117 {
118 count_++;
119 return JobStepResult::Continue();
120 }
121 }
122
123 virtual void Stop(JobStopReason reason) ORTHANC_OVERRIDE
124 {
125 }
126
127 virtual float GetProgress() ORTHANC_OVERRIDE
128 {
129 return static_cast<float>(count_) / static_cast<float>(steps_ - 1);
130 }
131
132 virtual void GetJobType(std::string& type) ORTHANC_OVERRIDE
133 {
134 type = "DummyJob";
135 }
136
137 virtual bool Serialize(Json::Value& value) ORTHANC_OVERRIDE
138 {
139 value = Json::objectValue;
140 value["Type"] = "DummyJob";
141 return true;
142 }
143
144 virtual void GetPublicContent(Json::Value& value) ORTHANC_OVERRIDE
145 {
146 value["hello"] = "world";
147 }
148
149 virtual bool GetOutput(std::string& output,
150 MimeType& mime,
151 const std::string& key) ORTHANC_OVERRIDE
152 {
153 return false;
154 }
155 };
156
157
158 class DummyInstancesJob : public SetOfInstancesJob
159 {
160 private:
161 bool trailingStepDone_;
162
163 protected:
164 virtual bool HandleInstance(const std::string& instance) ORTHANC_OVERRIDE
165 {
166 return (instance != "nope");
167 }
168
169 virtual bool HandleTrailingStep() ORTHANC_OVERRIDE
170 {
171 if (HasTrailingStep())
172 {
173 if (trailingStepDone_)
174 {
175 throw OrthancException(ErrorCode_InternalError);
176 }
177 else
178 {
179 trailingStepDone_ = true;
180 return true;
181 }
182 }
183 else
184 {
185 throw OrthancException(ErrorCode_InternalError);
186 }
187 }
188
189 public:
190 DummyInstancesJob() :
191 trailingStepDone_(false)
192 {
193 }
194
195 DummyInstancesJob(const Json::Value& value) :
196 SetOfInstancesJob(value)
197 {
198 if (HasTrailingStep())
199 {
200 trailingStepDone_ = (GetPosition() == GetCommandsCount());
201 }
202 else
203 {
204 trailingStepDone_ = false;
205 }
206 }
207
208 bool IsTrailingStepDone() const
209 {
210 return trailingStepDone_;
211 }
212
213 virtual void Stop(JobStopReason reason) ORTHANC_OVERRIDE
214 {
215 }
216
217 virtual void GetJobType(std::string& s) ORTHANC_OVERRIDE
218 {
219 s = "DummyInstancesJob";
220 }
221 };
222
223
224 class DummyUnserializer : public GenericJobUnserializer
225 {
226 public:
227 virtual IJob* UnserializeJob(const Json::Value& value) ORTHANC_OVERRIDE
228 {
229 if (SerializationToolbox::ReadString(value, "Type") == "DummyInstancesJob")
230 {
231 return new DummyInstancesJob(value);
232 }
233 else if (SerializationToolbox::ReadString(value, "Type") == "DummyJob")
234 {
235 return new DummyJob;
236 }
237 else
238 {
239 return GenericJobUnserializer::UnserializeJob(value);
240 }
241 }
242 };
243
244
245 class DynamicInteger : public IDynamicObject
246 {
247 private:
248 int value_;
249 std::set<int>& target_;
250
251 public:
252 DynamicInteger(int value, std::set<int>& target) :
253 value_(value), target_(target)
254 {
255 }
256
257 int GetValue() const
258 {
259 return value_;
260 }
261 };
262 }
263
264
265 TEST(MultiThreading, SharedMessageQueueBasic)
266 {
267 std::set<int> s;
268
269 SharedMessageQueue q;
270 ASSERT_TRUE(q.WaitEmpty(0));
271 q.Enqueue(new DynamicInteger(10, s));
272 ASSERT_FALSE(q.WaitEmpty(1));
273 q.Enqueue(new DynamicInteger(20, s));
274 q.Enqueue(new DynamicInteger(30, s));
275 q.Enqueue(new DynamicInteger(40, s));
276
277 std::unique_ptr<DynamicInteger> i;
278 i.reset(dynamic_cast<DynamicInteger*>(q.Dequeue(1))); ASSERT_EQ(10, i->GetValue());
279 i.reset(dynamic_cast<DynamicInteger*>(q.Dequeue(1))); ASSERT_EQ(20, i->GetValue());
280 i.reset(dynamic_cast<DynamicInteger*>(q.Dequeue(1))); ASSERT_EQ(30, i->GetValue());
281 ASSERT_FALSE(q.WaitEmpty(1));
282 i.reset(dynamic_cast<DynamicInteger*>(q.Dequeue(1))); ASSERT_EQ(40, i->GetValue());
283 ASSERT_TRUE(q.WaitEmpty(0));
284 ASSERT_EQ(NULL, q.Dequeue(1));
285 }
286
287
288 TEST(MultiThreading, SharedMessageQueueClean)
289 {
290 std::set<int> s;
291
292 try
293 {
294 SharedMessageQueue q;
295 q.Enqueue(new DynamicInteger(10, s));
296 q.Enqueue(new DynamicInteger(20, s));
297 throw OrthancException(ErrorCode_InternalError);
298 }
299 catch (OrthancException&)
300 {
301 }
302 }
303
304
305
306
307 static bool CheckState(JobsRegistry& registry,
308 const std::string& id,
309 JobState state)
310 {
311 JobState s;
312 if (registry.GetState(s, id))
313 {
314 return state == s;
315 }
316 else
317 {
318 return false;
319 }
320 }
321
322
323 static bool CheckErrorCode(JobsRegistry& registry,
324 const std::string& id,
325 ErrorCode code)
326 {
327 JobInfo s;
328 if (registry.GetJobInfo(s, id))
329 {
330 return code == s.GetStatus().GetErrorCode();
331 }
332 else
333 {
334 return false;
335 }
336 }
337
338
339 TEST(JobsRegistry, Priority)
340 {
341 JobsRegistry registry(10);
342
343 std::string i1, i2, i3, i4;
344 registry.Submit(i1, new DummyJob(), 10);
345 registry.Submit(i2, new DummyJob(), 30);
346 registry.Submit(i3, new DummyJob(), 20);
347 registry.Submit(i4, new DummyJob(), 5);
348
349 registry.SetMaxCompletedJobs(2);
350
351 std::set<std::string> id;
352 registry.ListJobs(id);
353
354 ASSERT_EQ(4u, id.size());
355 ASSERT_TRUE(id.find(i1) != id.end());
356 ASSERT_TRUE(id.find(i2) != id.end());
357 ASSERT_TRUE(id.find(i3) != id.end());
358 ASSERT_TRUE(id.find(i4) != id.end());
359
360 ASSERT_TRUE(CheckState(registry, i2, JobState_Pending));
361
362 {
363 JobsRegistry::RunningJob job(registry, 0);
364 ASSERT_TRUE(job.IsValid());
365 ASSERT_EQ(30, job.GetPriority());
366 ASSERT_EQ(i2, job.GetId());
367
368 ASSERT_TRUE(CheckState(registry, i2, JobState_Running));
369 }
370
371 ASSERT_TRUE(CheckState(registry, i2, JobState_Failure));
372 ASSERT_TRUE(CheckState(registry, i3, JobState_Pending));
373
374 {
375 JobsRegistry::RunningJob job(registry, 0);
376 ASSERT_TRUE(job.IsValid());
377 ASSERT_EQ(20, job.GetPriority());
378 ASSERT_EQ(i3, job.GetId());
379
380 job.MarkSuccess();
381
382 ASSERT_TRUE(CheckState(registry, i3, JobState_Running));
383 }
384
385 ASSERT_TRUE(CheckState(registry, i3, JobState_Success));
386
387 {
388 JobsRegistry::RunningJob job(registry, 0);
389 ASSERT_TRUE(job.IsValid());
390 ASSERT_EQ(10, job.GetPriority());
391 ASSERT_EQ(i1, job.GetId());
392 }
393
394 {
395 JobsRegistry::RunningJob job(registry, 0);
396 ASSERT_TRUE(job.IsValid());
397 ASSERT_EQ(5, job.GetPriority());
398 ASSERT_EQ(i4, job.GetId());
399 }
400
401 {
402 JobsRegistry::RunningJob job(registry, 1);
403 ASSERT_FALSE(job.IsValid());
404 }
405
406 JobState s;
407 ASSERT_TRUE(registry.GetState(s, i1));
408 ASSERT_FALSE(registry.GetState(s, i2)); // Removed because oldest
409 ASSERT_FALSE(registry.GetState(s, i3)); // Removed because second oldest
410 ASSERT_TRUE(registry.GetState(s, i4));
411
412 registry.SetMaxCompletedJobs(1); // (*)
413 ASSERT_FALSE(registry.GetState(s, i1)); // Just discarded by (*)
414 ASSERT_TRUE(registry.GetState(s, i4));
415 }
416
417
418 TEST(JobsRegistry, Simultaneous)
419 {
420 JobsRegistry registry(10);
421
422 std::string i1, i2;
423 registry.Submit(i1, new DummyJob(), 20);
424 registry.Submit(i2, new DummyJob(), 10);
425
426 ASSERT_TRUE(CheckState(registry, i1, JobState_Pending));
427 ASSERT_TRUE(CheckState(registry, i2, JobState_Pending));
428
429 {
430 JobsRegistry::RunningJob job1(registry, 0);
431 JobsRegistry::RunningJob job2(registry, 0);
432
433 ASSERT_TRUE(job1.IsValid());
434 ASSERT_TRUE(job2.IsValid());
435
436 job1.MarkFailure();
437 job2.MarkSuccess();
438
439 ASSERT_TRUE(CheckState(registry, i1, JobState_Running));
440 ASSERT_TRUE(CheckState(registry, i2, JobState_Running));
441 }
442
443 ASSERT_TRUE(CheckState(registry, i1, JobState_Failure));
444 ASSERT_TRUE(CheckState(registry, i2, JobState_Success));
445 }
446
447
448 TEST(JobsRegistry, Resubmit)
449 {
450 JobsRegistry registry(10);
451
452 std::string id;
453 registry.Submit(id, new DummyJob(), 10);
454
455 ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
456
457 registry.Resubmit(id);
458 ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
459
460 {
461 JobsRegistry::RunningJob job(registry, 0);
462 ASSERT_TRUE(job.IsValid());
463 job.MarkFailure();
464
465 ASSERT_TRUE(CheckState(registry, id, JobState_Running));
466
467 registry.Resubmit(id);
468 ASSERT_TRUE(CheckState(registry, id, JobState_Running));
469 }
470
471 ASSERT_TRUE(CheckState(registry, id, JobState_Failure));
472
473 registry.Resubmit(id);
474 ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
475
476 {
477 JobsRegistry::RunningJob job(registry, 0);
478 ASSERT_TRUE(job.IsValid());
479 ASSERT_EQ(id, job.GetId());
480
481 job.MarkSuccess();
482 ASSERT_TRUE(CheckState(registry, id, JobState_Running));
483 }
484
485 ASSERT_TRUE(CheckState(registry, id, JobState_Success));
486
487 registry.Resubmit(id);
488 ASSERT_TRUE(CheckState(registry, id, JobState_Success));
489 }
490
491
492 TEST(JobsRegistry, Retry)
493 {
494 JobsRegistry registry(10);
495
496 std::string id;
497 registry.Submit(id, new DummyJob(), 10);
498
499 ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
500
501 {
502 JobsRegistry::RunningJob job(registry, 0);
503 ASSERT_TRUE(job.IsValid());
504 job.MarkRetry(0);
505
506 ASSERT_TRUE(CheckState(registry, id, JobState_Running));
507 }
508
509 ASSERT_TRUE(CheckState(registry, id, JobState_Retry));
510
511 registry.Resubmit(id);
512 ASSERT_TRUE(CheckState(registry, id, JobState_Retry));
513
514 registry.ScheduleRetries();
515 ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
516
517 {
518 JobsRegistry::RunningJob job(registry, 0);
519 ASSERT_TRUE(job.IsValid());
520 job.MarkSuccess();
521
522 ASSERT_TRUE(CheckState(registry, id, JobState_Running));
523 }
524
525 ASSERT_TRUE(CheckState(registry, id, JobState_Success));
526 }
527
528
529 TEST(JobsRegistry, PausePending)
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 registry.Pause(id);
539 ASSERT_TRUE(CheckState(registry, id, JobState_Paused));
540
541 registry.Pause(id);
542 ASSERT_TRUE(CheckState(registry, id, JobState_Paused));
543
544 registry.Resubmit(id);
545 ASSERT_TRUE(CheckState(registry, id, JobState_Paused));
546
547 registry.Resume(id);
548 ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
549 }
550
551
552 TEST(JobsRegistry, PauseRunning)
553 {
554 JobsRegistry registry(10);
555
556 std::string id;
557 registry.Submit(id, new DummyJob(), 10);
558
559 ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
560
561 {
562 JobsRegistry::RunningJob job(registry, 0);
563 ASSERT_TRUE(job.IsValid());
564
565 registry.Resubmit(id);
566 job.MarkPause();
567 ASSERT_TRUE(CheckState(registry, id, JobState_Running));
568 }
569
570 ASSERT_TRUE(CheckState(registry, id, JobState_Paused));
571
572 registry.Resubmit(id);
573 ASSERT_TRUE(CheckState(registry, id, JobState_Paused));
574
575 registry.Resume(id);
576 ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
577
578 {
579 JobsRegistry::RunningJob job(registry, 0);
580 ASSERT_TRUE(job.IsValid());
581
582 job.MarkSuccess();
583 ASSERT_TRUE(CheckState(registry, id, JobState_Running));
584 }
585
586 ASSERT_TRUE(CheckState(registry, id, JobState_Success));
587 }
588
589
590 TEST(JobsRegistry, PauseRetry)
591 {
592 JobsRegistry registry(10);
593
594 std::string id;
595 registry.Submit(id, new DummyJob(), 10);
596
597 ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
598
599 {
600 JobsRegistry::RunningJob job(registry, 0);
601 ASSERT_TRUE(job.IsValid());
602
603 job.MarkRetry(0);
604 ASSERT_TRUE(CheckState(registry, id, JobState_Running));
605 }
606
607 ASSERT_TRUE(CheckState(registry, id, JobState_Retry));
608
609 registry.Pause(id);
610 ASSERT_TRUE(CheckState(registry, id, JobState_Paused));
611
612 registry.Resume(id);
613 ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
614
615 {
616 JobsRegistry::RunningJob job(registry, 0);
617 ASSERT_TRUE(job.IsValid());
618
619 job.MarkSuccess();
620 ASSERT_TRUE(CheckState(registry, id, JobState_Running));
621 }
622
623 ASSERT_TRUE(CheckState(registry, id, JobState_Success));
624 }
625
626
627 TEST(JobsRegistry, Cancel)
628 {
629 JobsRegistry registry(10);
630
631 std::string id;
632 registry.Submit(id, new DummyJob(), 10);
633
634 ASSERT_FALSE(registry.Cancel("nope"));
635
636 ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
637 ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_Success));
638
639 ASSERT_TRUE(registry.Cancel(id));
640 ASSERT_TRUE(CheckState(registry, id, JobState_Failure));
641 ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_CanceledJob));
642
643 ASSERT_TRUE(registry.Cancel(id));
644 ASSERT_TRUE(CheckState(registry, id, JobState_Failure));
645 ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_CanceledJob));
646
647 ASSERT_TRUE(registry.Resubmit(id));
648 ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
649 ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_CanceledJob));
650
651 {
652 JobsRegistry::RunningJob job(registry, 0);
653 ASSERT_TRUE(job.IsValid());
654
655 ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_Success));
656
657 job.MarkSuccess();
658 ASSERT_TRUE(CheckState(registry, id, JobState_Running));
659 }
660
661 ASSERT_TRUE(CheckState(registry, id, JobState_Success));
662 ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_Success));
663
664 ASSERT_TRUE(registry.Cancel(id));
665 ASSERT_TRUE(CheckState(registry, id, JobState_Success));
666 ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_Success));
667
668 registry.Submit(id, new DummyJob(), 10);
669
670 {
671 JobsRegistry::RunningJob job(registry, 0);
672 ASSERT_TRUE(job.IsValid());
673 ASSERT_EQ(id, job.GetId());
674
675 ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_Success));
676 ASSERT_TRUE(CheckState(registry, id, JobState_Running));
677
678 job.MarkCanceled();
679 }
680
681 ASSERT_TRUE(CheckState(registry, id, JobState_Failure));
682 ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_CanceledJob));
683
684 ASSERT_TRUE(registry.Resubmit(id));
685 ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
686 ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_CanceledJob));
687
688 ASSERT_TRUE(registry.Pause(id));
689 ASSERT_TRUE(CheckState(registry, id, JobState_Paused));
690 ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_CanceledJob));
691
692 ASSERT_TRUE(registry.Cancel(id));
693 ASSERT_TRUE(CheckState(registry, id, JobState_Failure));
694 ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_CanceledJob));
695
696 ASSERT_TRUE(registry.Resubmit(id));
697 ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
698 ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_CanceledJob));
699
700 {
701 JobsRegistry::RunningJob job(registry, 0);
702 ASSERT_TRUE(job.IsValid());
703 ASSERT_EQ(id, job.GetId());
704
705 ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_Success));
706 ASSERT_TRUE(CheckState(registry, id, JobState_Running));
707
708 job.MarkRetry(500);
709 }
710
711 ASSERT_TRUE(CheckState(registry, id, JobState_Retry));
712 ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_Success));
713
714 ASSERT_TRUE(registry.Cancel(id));
715 ASSERT_TRUE(CheckState(registry, id, JobState_Failure));
716 ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_CanceledJob));
717 }
718
719
720
721 TEST(JobsEngine, SubmitAndWait)
722 {
723 JobsEngine engine(10);
724 engine.SetThreadSleep(10);
725 engine.SetWorkersCount(3);
726 engine.Start();
727
728 Json::Value content = Json::nullValue;
729 engine.GetRegistry().SubmitAndWait(content, new DummyJob(), rand() % 10);
730 ASSERT_EQ(Json::objectValue, content.type());
731 ASSERT_EQ("world", content["hello"].asString());
732
733 content = Json::nullValue;
734 ASSERT_THROW(engine.GetRegistry().SubmitAndWait(content, new DummyJob(true), rand() % 10), OrthancException);
735 ASSERT_EQ(Json::nullValue, content.type());
736
737 engine.Stop();
738 }
739
740
741 TEST(JobsEngine, DISABLED_SequenceOfOperationsJob)
742 {
743 JobsEngine engine(10);
744 engine.SetThreadSleep(10);
745 engine.SetWorkersCount(3);
746 engine.Start();
747
748 std::string id;
749 SequenceOfOperationsJob* job = NULL;
750
751 {
752 std::unique_ptr<SequenceOfOperationsJob> a(new SequenceOfOperationsJob);
753 job = a.get();
754 engine.GetRegistry().Submit(id, a.release(), 0);
755 }
756
757 boost::this_thread::sleep(boost::posix_time::milliseconds(500));
758
759 {
760 SequenceOfOperationsJob::Lock lock(*job);
761 size_t i = lock.AddOperation(new LogJobOperation);
762 size_t j = lock.AddOperation(new LogJobOperation);
763 size_t k = lock.AddOperation(new LogJobOperation);
764
765 StringOperationValue a("Hello");
766 StringOperationValue b("World");
767 lock.AddInput(i, a);
768 lock.AddInput(i, b);
769
770 lock.Connect(i, j);
771 lock.Connect(j, k);
772 }
773
774 boost::this_thread::sleep(boost::posix_time::milliseconds(2000));
775
776 engine.Stop();
777
778 }
779
780
781 TEST(JobsEngine, DISABLED_Lua)
782 {
783 JobsEngine engine(10);
784 engine.SetThreadSleep(10);
785 engine.SetWorkersCount(2);
786 engine.Start();
787
788 LuaJobManager lua;
789 lua.SetMaxOperationsPerJob(5);
790 lua.SetTrailingOperationTimeout(200);
791
792 for (size_t i = 0; i < 30; i++)
793 {
794 boost::this_thread::sleep(boost::posix_time::milliseconds(150));
795
796 LuaJobManager::Lock lock(lua, engine);
797 size_t a = lock.AddLogOperation();
798 size_t b = lock.AddLogOperation();
799 size_t c = lock.AddSystemCallOperation("echo");
800 lock.AddStringInput(a, boost::lexical_cast<std::string>(i));
801 lock.AddNullInput(a);
802 lock.Connect(a, b);
803 lock.Connect(a, c);
804 }
805
806 boost::this_thread::sleep(boost::posix_time::milliseconds(2000));
807
808 engine.Stop();
809 }
810
811
812 static bool CheckSameJson(const Json::Value& a,
813 const Json::Value& b)
814 {
815 std::string s = a.toStyledString();
816 std::string t = b.toStyledString();
817
818 if (s == t)
819 {
820 return true;
821 }
822 else
823 {
824 LOG(ERROR) << "Expected serialization: " << s;
825 LOG(ERROR) << "Actual serialization: " << t;
826 return false;
827 }
828 }
829
830
831 static bool CheckIdempotentSerialization(IJobUnserializer& unserializer,
832 IJob& job)
833 {
834 Json::Value a = 42;
835
836 if (!job.Serialize(a))
837 {
838 return false;
839 }
840 else
841 {
842 std::unique_ptr<IJob> unserialized(unserializer.UnserializeJob(a));
843
844 Json::Value b = 43;
845 if (unserialized->Serialize(b))
846 {
847 return (CheckSameJson(a, b));
848 }
849 else
850 {
851 return false;
852 }
853 }
854 }
855
856
857 static bool CheckIdempotentSetOfInstances(IJobUnserializer& unserializer,
858 SetOfInstancesJob& job)
859 {
860 Json::Value a = 42;
861
862 if (!job.Serialize(a))
863 {
864 return false;
865 }
866 else
867 {
868 std::unique_ptr<SetOfInstancesJob> unserialized
869 (dynamic_cast<SetOfInstancesJob*>(unserializer.UnserializeJob(a)));
870
871 Json::Value b = 43;
872 if (unserialized->Serialize(b))
873 {
874 return (CheckSameJson(a, b) &&
875 job.HasTrailingStep() == unserialized->HasTrailingStep() &&
876 job.GetPosition() == unserialized->GetPosition() &&
877 job.GetInstancesCount() == unserialized->GetInstancesCount() &&
878 job.GetCommandsCount() == unserialized->GetCommandsCount());
879 }
880 else
881 {
882 return false;
883 }
884 }
885 }
886
887
888 static bool CheckIdempotentSerialization(IJobUnserializer& unserializer,
889 IJobOperation& operation)
890 {
891 Json::Value a = 42;
892 operation.Serialize(a);
893
894 std::unique_ptr<IJobOperation> unserialized(unserializer.UnserializeOperation(a));
895
896 Json::Value b = 43;
897 unserialized->Serialize(b);
898
899 return CheckSameJson(a, b);
900 }
901
902
903 static bool CheckIdempotentSerialization(IJobUnserializer& unserializer,
904 JobOperationValue& value)
905 {
906 Json::Value a = 42;
907 value.Serialize(a);
908
909 std::unique_ptr<JobOperationValue> unserialized(unserializer.UnserializeValue(a));
910
911 Json::Value b = 43;
912 unserialized->Serialize(b);
913
914 return CheckSameJson(a, b);
915 }
916
917
918 TEST(JobsSerialization, BadFileFormat)
919 {
920 GenericJobUnserializer unserializer;
921
922 Json::Value s;
923
924 s = Json::objectValue;
925 ASSERT_THROW(unserializer.UnserializeValue(s), OrthancException);
926 ASSERT_THROW(unserializer.UnserializeJob(s), OrthancException);
927 ASSERT_THROW(unserializer.UnserializeOperation(s), OrthancException);
928
929 s = Json::arrayValue;
930 ASSERT_THROW(unserializer.UnserializeValue(s), OrthancException);
931 ASSERT_THROW(unserializer.UnserializeJob(s), OrthancException);
932 ASSERT_THROW(unserializer.UnserializeOperation(s), OrthancException);
933
934 s = "hello";
935 ASSERT_THROW(unserializer.UnserializeValue(s), OrthancException);
936 ASSERT_THROW(unserializer.UnserializeJob(s), OrthancException);
937 ASSERT_THROW(unserializer.UnserializeOperation(s), OrthancException);
938
939 s = 42;
940 ASSERT_THROW(unserializer.UnserializeValue(s), OrthancException);
941 ASSERT_THROW(unserializer.UnserializeJob(s), OrthancException);
942 ASSERT_THROW(unserializer.UnserializeOperation(s), OrthancException);
943 }
944
945
946 TEST(JobsSerialization, JobOperationValues)
947 {
948 Json::Value s;
949
950 {
951 JobOperationValues values;
952 values.Append(new NullOperationValue);
953 values.Append(new StringOperationValue("hello"));
954 values.Append(new StringOperationValue("world"));
955
956 s = 42;
957 values.Serialize(s);
958 }
959
960 {
961 GenericJobUnserializer unserializer;
962 std::unique_ptr<JobOperationValues> values(JobOperationValues::Unserialize(unserializer, s));
963 ASSERT_EQ(3u, values->GetSize());
964 ASSERT_EQ(JobOperationValue::Type_Null, values->GetValue(0).GetType());
965 ASSERT_EQ(JobOperationValue::Type_String, values->GetValue(1).GetType());
966 ASSERT_EQ(JobOperationValue::Type_String, values->GetValue(2).GetType());
967
968 ASSERT_EQ("hello", dynamic_cast<const StringOperationValue&>(values->GetValue(1)).GetContent());
969 ASSERT_EQ("world", dynamic_cast<const StringOperationValue&>(values->GetValue(2)).GetContent());
970 }
971 }
972
973
974 TEST(JobsSerialization, GenericValues)
975 {
976 GenericJobUnserializer unserializer;
977 Json::Value s;
978
979 {
980 NullOperationValue null;
981
982 ASSERT_TRUE(CheckIdempotentSerialization(unserializer, null));
983 null.Serialize(s);
984 }
985
986 ASSERT_THROW(unserializer.UnserializeJob(s), OrthancException);
987 ASSERT_THROW(unserializer.UnserializeOperation(s), OrthancException);
988
989 std::unique_ptr<JobOperationValue> value;
990 value.reset(unserializer.UnserializeValue(s));
991
992 ASSERT_EQ(JobOperationValue::Type_Null, value->GetType());
993
994 {
995 StringOperationValue str("Hello");
996
997 ASSERT_TRUE(CheckIdempotentSerialization(unserializer, str));
998 str.Serialize(s);
999 }
1000
1001 ASSERT_THROW(unserializer.UnserializeJob(s), OrthancException);
1002 ASSERT_THROW(unserializer.UnserializeOperation(s), OrthancException);
1003 value.reset(unserializer.UnserializeValue(s));
1004
1005 ASSERT_EQ(JobOperationValue::Type_String, value->GetType());
1006 ASSERT_EQ("Hello", dynamic_cast<StringOperationValue&>(*value).GetContent());
1007 }
1008
1009
1010 TEST(JobsSerialization, GenericOperations)
1011 {
1012 DummyUnserializer unserializer;
1013 Json::Value s;
1014
1015 {
1016 LogJobOperation operation;
1017
1018 ASSERT_TRUE(CheckIdempotentSerialization(unserializer, operation));
1019 operation.Serialize(s);
1020 }
1021
1022 ASSERT_THROW(unserializer.UnserializeJob(s), OrthancException);
1023 ASSERT_THROW(unserializer.UnserializeValue(s), OrthancException);
1024
1025 {
1026 std::unique_ptr<IJobOperation> operation;
1027 operation.reset(unserializer.UnserializeOperation(s));
1028
1029 // Make sure that we have indeed unserialized a log operation
1030 Json::Value dummy;
1031 ASSERT_THROW(dynamic_cast<DeleteResourceOperation&>(*operation).Serialize(dummy), std::bad_cast);
1032 dynamic_cast<LogJobOperation&>(*operation).Serialize(dummy);
1033 }
1034 }
1035
1036
1037 TEST(JobsSerialization, GenericJobs)
1038 {
1039 Json::Value s;
1040
1041 // This tests SetOfInstancesJob
1042
1043 {
1044 DummyInstancesJob job;
1045 job.SetDescription("description");
1046 job.AddInstance("hello");
1047 job.AddInstance("nope");
1048 job.AddInstance("world");
1049 job.SetPermissive(true);
1050 ASSERT_THROW(job.Step("jobId"), OrthancException); // Not started yet
1051 ASSERT_FALSE(job.HasTrailingStep());
1052 ASSERT_FALSE(job.IsTrailingStepDone());
1053 job.Start();
1054 ASSERT_EQ(JobStepCode_Continue, job.Step("jobId").GetCode());
1055 ASSERT_EQ(JobStepCode_Continue, job.Step("jobId").GetCode());
1056
1057 {
1058 DummyUnserializer unserializer;
1059 ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job));
1060 }
1061
1062 ASSERT_TRUE(job.Serialize(s));
1063 }
1064
1065 {
1066 DummyUnserializer unserializer;
1067 ASSERT_THROW(unserializer.UnserializeValue(s), OrthancException);
1068 ASSERT_THROW(unserializer.UnserializeOperation(s), OrthancException);
1069
1070 std::unique_ptr<IJob> job;
1071 job.reset(unserializer.UnserializeJob(s));
1072
1073 const DummyInstancesJob& tmp = dynamic_cast<const DummyInstancesJob&>(*job);
1074 ASSERT_FALSE(tmp.IsStarted());
1075 ASSERT_TRUE(tmp.IsPermissive());
1076 ASSERT_EQ("description", tmp.GetDescription());
1077 ASSERT_EQ(3u, tmp.GetInstancesCount());
1078 ASSERT_EQ(2u, tmp.GetPosition());
1079 ASSERT_EQ(1u, tmp.GetFailedInstances().size());
1080 ASSERT_EQ("hello", tmp.GetInstance(0));
1081 ASSERT_EQ("nope", tmp.GetInstance(1));
1082 ASSERT_EQ("world", tmp.GetInstance(2));
1083 ASSERT_TRUE(tmp.IsFailedInstance("nope"));
1084 }
1085
1086 // SequenceOfOperationsJob
1087
1088 {
1089 SequenceOfOperationsJob job;
1090 job.SetDescription("hello");
1091
1092 {
1093 SequenceOfOperationsJob::Lock lock(job);
1094 size_t a = lock.AddOperation(new LogJobOperation);
1095 size_t b = lock.AddOperation(new LogJobOperation);
1096 lock.Connect(a, b);
1097
1098 StringOperationValue s1("hello");
1099 StringOperationValue s2("world");
1100 lock.AddInput(a, s1);
1101 lock.AddInput(a, s2);
1102 lock.SetTrailingOperationTimeout(300);
1103 }
1104
1105 ASSERT_EQ(JobStepCode_Continue, job.Step("jobId").GetCode());
1106
1107 {
1108 GenericJobUnserializer unserializer;
1109 ASSERT_TRUE(CheckIdempotentSerialization(unserializer, job));
1110 }
1111
1112 ASSERT_TRUE(job.Serialize(s));
1113 }
1114
1115 {
1116 GenericJobUnserializer unserializer;
1117 ASSERT_THROW(unserializer.UnserializeValue(s), OrthancException);
1118 ASSERT_THROW(unserializer.UnserializeOperation(s), OrthancException);
1119
1120 std::unique_ptr<IJob> job;
1121 job.reset(unserializer.UnserializeJob(s));
1122
1123 std::string tmp;
1124 dynamic_cast<SequenceOfOperationsJob&>(*job).GetDescription(tmp);
1125 ASSERT_EQ("hello", tmp);
1126 }
1127 }
1128
1129
1130 static bool IsSameTagValue(ParsedDicomFile& dicom1,
1131 ParsedDicomFile& dicom2,
1132 DicomTag tag)
1133 {
1134 std::string a, b;
1135 return (dicom1.GetTagValue(a, tag) &&
1136 dicom2.GetTagValue(b, tag) &&
1137 (a == b));
1138 }
1139
1140
1141
1142 TEST(JobsSerialization, DicomModification)
1143 {
1144 Json::Value s;
1145
1146 ParsedDicomFile source(true);
1147 source.Insert(DICOM_TAG_STUDY_DESCRIPTION, "Test 1", false, "");
1148 source.Insert(DICOM_TAG_SERIES_DESCRIPTION, "Test 2", false, "");
1149 source.Insert(DICOM_TAG_PATIENT_NAME, "Test 3", false, "");
1150
1151 std::unique_ptr<ParsedDicomFile> modified(source.Clone(true));
1152
1153 {
1154 DicomModification modification;
1155 modification.SetLevel(ResourceType_Series);
1156 modification.Clear(DICOM_TAG_STUDY_DESCRIPTION);
1157 modification.Remove(DICOM_TAG_SERIES_DESCRIPTION);
1158 modification.Replace(DICOM_TAG_PATIENT_NAME, "Test 4", true);
1159
1160 modification.Apply(*modified);
1161
1162 s = 42;
1163 modification.Serialize(s);
1164 }
1165
1166 {
1167 DicomModification modification(s);
1168 ASSERT_EQ(ResourceType_Series, modification.GetLevel());
1169
1170 std::unique_ptr<ParsedDicomFile> second(source.Clone(true));
1171 modification.Apply(*second);
1172
1173 std::string s;
1174 ASSERT_TRUE(second->GetTagValue(s, DICOM_TAG_STUDY_DESCRIPTION));
1175 ASSERT_TRUE(s.empty());
1176 ASSERT_FALSE(second->GetTagValue(s, DICOM_TAG_SERIES_DESCRIPTION));
1177 ASSERT_TRUE(second->GetTagValue(s, DICOM_TAG_PATIENT_NAME));
1178 ASSERT_EQ("Test 4", s);
1179
1180 ASSERT_TRUE(IsSameTagValue(source, *modified, DICOM_TAG_STUDY_INSTANCE_UID));
1181 ASSERT_TRUE(IsSameTagValue(source, *second, DICOM_TAG_STUDY_INSTANCE_UID));
1182
1183 ASSERT_FALSE(IsSameTagValue(source, *second, DICOM_TAG_SERIES_INSTANCE_UID));
1184 ASSERT_TRUE(IsSameTagValue(*modified, *second, DICOM_TAG_SERIES_INSTANCE_UID));
1185 }
1186 }
1187
1188
1189 TEST(JobsSerialization, DicomInstanceOrigin)
1190 {
1191 Json::Value s;
1192 std::string t;
1193
1194 {
1195 DicomInstanceOrigin origin;
1196
1197 s = 42;
1198 origin.Serialize(s);
1199 }
1200
1201 {
1202 DicomInstanceOrigin origin(s);
1203 ASSERT_EQ(RequestOrigin_Unknown, origin.GetRequestOrigin());
1204 ASSERT_EQ("", std::string(origin.GetRemoteAetC()));
1205 ASSERT_FALSE(origin.LookupRemoteIp(t));
1206 ASSERT_FALSE(origin.LookupRemoteAet(t));
1207 ASSERT_FALSE(origin.LookupCalledAet(t));
1208 ASSERT_FALSE(origin.LookupHttpUsername(t));
1209 }
1210
1211 {
1212 DicomInstanceOrigin origin(DicomInstanceOrigin::FromDicomProtocol("host", "aet", "called"));
1213
1214 s = 42;
1215 origin.Serialize(s);
1216 }
1217
1218 {
1219 DicomInstanceOrigin origin(s);
1220 ASSERT_EQ(RequestOrigin_DicomProtocol, origin.GetRequestOrigin());
1221 ASSERT_EQ("aet", std::string(origin.GetRemoteAetC()));
1222 ASSERT_TRUE(origin.LookupRemoteIp(t)); ASSERT_EQ("host", t);
1223 ASSERT_TRUE(origin.LookupRemoteAet(t)); ASSERT_EQ("aet", t);
1224 ASSERT_TRUE(origin.LookupCalledAet(t)); ASSERT_EQ("called", t);
1225 ASSERT_FALSE(origin.LookupHttpUsername(t));
1226 }
1227
1228 {
1229 DicomInstanceOrigin origin(DicomInstanceOrigin::FromHttp("host", "username"));
1230
1231 s = 42;
1232 origin.Serialize(s);
1233 }
1234
1235 {
1236 DicomInstanceOrigin origin(s);
1237 ASSERT_EQ(RequestOrigin_RestApi, origin.GetRequestOrigin());
1238 ASSERT_EQ("", std::string(origin.GetRemoteAetC()));
1239 ASSERT_TRUE(origin.LookupRemoteIp(t)); ASSERT_EQ("host", t);
1240 ASSERT_FALSE(origin.LookupRemoteAet(t));
1241 ASSERT_FALSE(origin.LookupCalledAet(t));
1242 ASSERT_TRUE(origin.LookupHttpUsername(t)); ASSERT_EQ("username", t);
1243 }
1244
1245 {
1246 DicomInstanceOrigin origin(DicomInstanceOrigin::FromLua());
1247
1248 s = 42;
1249 origin.Serialize(s);
1250 }
1251
1252 {
1253 DicomInstanceOrigin origin(s);
1254 ASSERT_EQ(RequestOrigin_Lua, origin.GetRequestOrigin());
1255 ASSERT_FALSE(origin.LookupRemoteIp(t));
1256 ASSERT_FALSE(origin.LookupRemoteAet(t));
1257 ASSERT_FALSE(origin.LookupCalledAet(t));
1258 ASSERT_FALSE(origin.LookupHttpUsername(t));
1259 }
1260
1261 {
1262 DicomInstanceOrigin origin(DicomInstanceOrigin::FromPlugins());
1263
1264 s = 42;
1265 origin.Serialize(s);
1266 }
1267
1268 {
1269 DicomInstanceOrigin origin(s);
1270 ASSERT_EQ(RequestOrigin_Plugins, origin.GetRequestOrigin());
1271 ASSERT_FALSE(origin.LookupRemoteIp(t));
1272 ASSERT_FALSE(origin.LookupRemoteAet(t));
1273 ASSERT_FALSE(origin.LookupCalledAet(t));
1274 ASSERT_FALSE(origin.LookupHttpUsername(t));
1275 }
1276 }
1277
1278
1279 namespace
1280 {
1281 class OrthancJobsSerialization : public testing::Test
1282 {
1283 private:
1284 MemoryStorageArea storage_;
1285 SQLiteDatabaseWrapper db_; // The SQLite DB is in memory
1286 std::unique_ptr<ServerContext> context_;
1287
1288 public:
1289 OrthancJobsSerialization()
1290 {
1291 db_.Open();
1292 context_.reset(new ServerContext(db_, storage_, true /* running unit tests */, 10));
1293 context_->SetupJobsEngine(true, false);
1294 }
1295
1296 virtual ~OrthancJobsSerialization() ORTHANC_OVERRIDE
1297 {
1298 context_->Stop();
1299 context_.reset(NULL);
1300 db_.Close();
1301 }
1302
1303 ServerContext& GetContext()
1304 {
1305 return *context_;
1306 }
1307
1308 bool CreateInstance(std::string& id)
1309 {
1310 // Create a sample DICOM file
1311 ParsedDicomFile dicom(true);
1312 dicom.Replace(DICOM_TAG_PATIENT_NAME, std::string("JODOGNE"),
1313 false, DicomReplaceMode_InsertIfAbsent, "");
1314
1315 DicomInstanceToStore toStore;
1316 toStore.SetParsedDicomFile(dicom);
1317
1318 return (context_->Store(id, toStore, StoreInstanceMode_Default) == StoreStatus_Success);
1319 }
1320 };
1321 }
1322
1323
1324 TEST_F(OrthancJobsSerialization, Values)
1325 {
1326 std::string id;
1327 ASSERT_TRUE(CreateInstance(id));
1328
1329 Json::Value s;
1330 OrthancJobUnserializer unserializer(GetContext());
1331
1332 {
1333 DicomInstanceOperationValue instance(GetContext(), id);
1334
1335 ASSERT_TRUE(CheckIdempotentSerialization(unserializer, instance));
1336 instance.Serialize(s);
1337 }
1338
1339 std::unique_ptr<JobOperationValue> value;
1340 value.reset(unserializer.UnserializeValue(s));
1341 ASSERT_EQ(JobOperationValue::Type_DicomInstance, value->GetType());
1342 ASSERT_EQ(id, dynamic_cast<DicomInstanceOperationValue&>(*value).GetId());
1343
1344 {
1345 std::string content;
1346 dynamic_cast<DicomInstanceOperationValue&>(*value).ReadDicom(content);
1347
1348 ParsedDicomFile dicom(content);
1349 ASSERT_TRUE(dicom.GetTagValue(content, DICOM_TAG_PATIENT_NAME));
1350 ASSERT_EQ("JODOGNE", content);
1351 }
1352 }
1353
1354
1355 TEST_F(OrthancJobsSerialization, Operations)
1356 {
1357 std::string id;
1358 ASSERT_TRUE(CreateInstance(id));
1359
1360 Json::Value s;
1361 OrthancJobUnserializer unserializer(GetContext());
1362
1363 // DeleteResourceOperation
1364
1365 {
1366 DeleteResourceOperation operation(GetContext());
1367
1368 ASSERT_TRUE(CheckIdempotentSerialization(unserializer, operation));
1369 operation.Serialize(s);
1370 }
1371
1372 std::unique_ptr<IJobOperation> operation;
1373
1374 {
1375 operation.reset(unserializer.UnserializeOperation(s));
1376
1377 Json::Value dummy;
1378 ASSERT_THROW(dynamic_cast<LogJobOperation&>(*operation).Serialize(dummy), std::bad_cast);
1379 dynamic_cast<DeleteResourceOperation&>(*operation).Serialize(dummy);
1380 }
1381
1382 // StorePeerOperation
1383
1384 {
1385 WebServiceParameters peer;
1386 peer.SetUrl("http://localhost/");
1387 peer.SetCredentials("username", "password");
1388 peer.SetPkcs11Enabled(true);
1389
1390 StorePeerOperation operation(peer);
1391
1392 ASSERT_TRUE(CheckIdempotentSerialization(unserializer, operation));
1393 operation.Serialize(s);
1394 }
1395
1396 {
1397 operation.reset(unserializer.UnserializeOperation(s));
1398
1399 const StorePeerOperation& tmp = dynamic_cast<StorePeerOperation&>(*operation);
1400 ASSERT_EQ("http://localhost/", tmp.GetPeer().GetUrl());
1401 ASSERT_EQ("username", tmp.GetPeer().GetUsername());
1402 ASSERT_EQ("password", tmp.GetPeer().GetPassword());
1403 ASSERT_TRUE(tmp.GetPeer().IsPkcs11Enabled());
1404 }
1405
1406 // StoreScuOperation
1407
1408 {
1409 TimeoutDicomConnectionManager luaManager;
1410
1411 {
1412 RemoteModalityParameters modality;
1413 modality.SetApplicationEntityTitle("REMOTE");
1414 modality.SetHost("192.168.1.1");
1415 modality.SetPortNumber(1000);
1416 modality.SetManufacturer(ModalityManufacturer_StoreScp);
1417
1418 StoreScuOperation operation(GetContext(), luaManager, "TEST", modality);
1419
1420 ASSERT_TRUE(CheckIdempotentSerialization(unserializer, operation));
1421 operation.Serialize(s);
1422 }
1423
1424 {
1425 operation.reset(unserializer.UnserializeOperation(s));
1426
1427 const StoreScuOperation& tmp = dynamic_cast<StoreScuOperation&>(*operation);
1428 ASSERT_EQ("REMOTE", tmp.GetRemoteModality().GetApplicationEntityTitle());
1429 ASSERT_EQ("192.168.1.1", tmp.GetRemoteModality().GetHost());
1430 ASSERT_EQ(1000, tmp.GetRemoteModality().GetPortNumber());
1431 ASSERT_EQ(ModalityManufacturer_StoreScp, tmp.GetRemoteModality().GetManufacturer());
1432 ASSERT_EQ("TEST", tmp.GetLocalAet());
1433 }
1434 }
1435
1436 // SystemCallOperation
1437
1438 {
1439 SystemCallOperation operation(std::string("echo"));
1440 operation.AddPreArgument("a");
1441 operation.AddPreArgument("b");
1442 operation.AddPostArgument("c");
1443
1444 ASSERT_TRUE(CheckIdempotentSerialization(unserializer, operation));
1445 operation.Serialize(s);
1446 }
1447
1448 {
1449 operation.reset(unserializer.UnserializeOperation(s));
1450
1451 const SystemCallOperation& tmp = dynamic_cast<SystemCallOperation&>(*operation);
1452 ASSERT_EQ("echo", tmp.GetCommand());
1453 ASSERT_EQ(2u, tmp.GetPreArgumentsCount());
1454 ASSERT_EQ(1u, tmp.GetPostArgumentsCount());
1455 ASSERT_EQ("a", tmp.GetPreArgument(0));
1456 ASSERT_EQ("b", tmp.GetPreArgument(1));
1457 ASSERT_EQ("c", tmp.GetPostArgument(0));
1458 }
1459
1460 // ModifyInstanceOperation
1461
1462 {
1463 std::unique_ptr<DicomModification> modification(new DicomModification);
1464 modification->SetupAnonymization(DicomVersion_2008);
1465
1466 ModifyInstanceOperation operation(GetContext(), RequestOrigin_Lua, modification.release());
1467
1468 ASSERT_TRUE(CheckIdempotentSerialization(unserializer, operation));
1469 operation.Serialize(s);
1470 }
1471
1472 {
1473 operation.reset(unserializer.UnserializeOperation(s));
1474
1475 const ModifyInstanceOperation& tmp = dynamic_cast<ModifyInstanceOperation&>(*operation);
1476 ASSERT_EQ(RequestOrigin_Lua, tmp.GetRequestOrigin());
1477 ASSERT_TRUE(tmp.GetModification().IsRemoved(DICOM_TAG_STUDY_DESCRIPTION));
1478 }
1479 }
1480
1481
1482 TEST_F(OrthancJobsSerialization, Jobs)
1483 {
1484 Json::Value s;
1485
1486 // ArchiveJob
1487
1488 {
1489 ArchiveJob job(GetContext(), false, false);
1490 ASSERT_FALSE(job.Serialize(s)); // Cannot serialize this
1491 }
1492
1493 // DicomModalityStoreJob
1494
1495 OrthancJobUnserializer unserializer(GetContext());
1496
1497 {
1498 RemoteModalityParameters modality;
1499 modality.SetApplicationEntityTitle("REMOTE");
1500 modality.SetHost("192.168.1.1");
1501 modality.SetPortNumber(1000);
1502 modality.SetManufacturer(ModalityManufacturer_StoreScp);
1503
1504 DicomModalityStoreJob job(GetContext());
1505 job.SetLocalAet("LOCAL");
1506 job.SetRemoteModality(modality);
1507 job.SetMoveOriginator("MOVESCU", 42);
1508
1509 ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job));
1510 ASSERT_TRUE(job.Serialize(s));
1511 }
1512
1513 {
1514 std::unique_ptr<IJob> job;
1515 job.reset(unserializer.UnserializeJob(s));
1516
1517 DicomModalityStoreJob& tmp = dynamic_cast<DicomModalityStoreJob&>(*job);
1518 ASSERT_EQ("LOCAL", tmp.GetParameters().GetLocalApplicationEntityTitle());
1519 ASSERT_EQ("REMOTE", tmp.GetParameters().GetRemoteModality().GetApplicationEntityTitle());
1520 ASSERT_EQ("192.168.1.1", tmp.GetParameters().GetRemoteModality().GetHost());
1521 ASSERT_EQ(1000, tmp.GetParameters().GetRemoteModality().GetPortNumber());
1522 ASSERT_EQ(ModalityManufacturer_StoreScp, tmp.GetParameters().GetRemoteModality().GetManufacturer());
1523 ASSERT_TRUE(tmp.HasMoveOriginator());
1524 ASSERT_EQ("MOVESCU", tmp.GetMoveOriginatorAet());
1525 ASSERT_EQ(42, tmp.GetMoveOriginatorId());
1526 }
1527
1528 // OrthancPeerStoreJob
1529
1530 {
1531 WebServiceParameters peer;
1532 peer.SetUrl("http://localhost/");
1533 peer.SetCredentials("username", "password");
1534 peer.SetPkcs11Enabled(true);
1535
1536 OrthancPeerStoreJob job(GetContext());
1537 job.SetPeer(peer);
1538
1539 ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job));
1540 ASSERT_TRUE(job.Serialize(s));
1541 }
1542
1543 {
1544 std::unique_ptr<IJob> job;
1545 job.reset(unserializer.UnserializeJob(s));
1546
1547 OrthancPeerStoreJob& tmp = dynamic_cast<OrthancPeerStoreJob&>(*job);
1548 ASSERT_EQ("http://localhost/", tmp.GetPeer().GetUrl());
1549 ASSERT_EQ("username", tmp.GetPeer().GetUsername());
1550 ASSERT_EQ("password", tmp.GetPeer().GetPassword());
1551 ASSERT_TRUE(tmp.GetPeer().IsPkcs11Enabled());
1552 ASSERT_FALSE(tmp.IsTranscode());
1553 ASSERT_THROW(tmp.GetTransferSyntax(), OrthancException);
1554 }
1555
1556 {
1557 OrthancPeerStoreJob job(GetContext());
1558 ASSERT_THROW(job.SetTranscode("nope"), OrthancException);
1559 job.SetTranscode("1.2.840.10008.1.2.4.50");
1560
1561 ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job));
1562 ASSERT_TRUE(job.Serialize(s));
1563 }
1564
1565 {
1566 std::unique_ptr<IJob> job;
1567 job.reset(unserializer.UnserializeJob(s));
1568
1569 OrthancPeerStoreJob& tmp = dynamic_cast<OrthancPeerStoreJob&>(*job);
1570 ASSERT_EQ("http://127.0.0.1:8042/", tmp.GetPeer().GetUrl());
1571 ASSERT_EQ("", tmp.GetPeer().GetUsername());
1572 ASSERT_EQ("", tmp.GetPeer().GetPassword());
1573 ASSERT_FALSE(tmp.GetPeer().IsPkcs11Enabled());
1574 ASSERT_TRUE(tmp.IsTranscode());
1575 ASSERT_EQ(DicomTransferSyntax_JPEGProcess1, tmp.GetTransferSyntax());
1576 }
1577
1578 // ResourceModificationJob
1579
1580 {
1581 std::unique_ptr<DicomModification> modification(new DicomModification);
1582 modification->SetupAnonymization(DicomVersion_2008);
1583
1584 ResourceModificationJob job(GetContext());
1585 job.SetModification(modification.release(), ResourceType_Patient, true);
1586 job.SetOrigin(DicomInstanceOrigin::FromLua());
1587
1588 job.AddTrailingStep(); // Necessary since 1.7.0
1589 ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job));
1590 ASSERT_TRUE(job.Serialize(s));
1591 }
1592
1593 {
1594 std::unique_ptr<IJob> job;
1595 job.reset(unserializer.UnserializeJob(s));
1596
1597 ResourceModificationJob& tmp = dynamic_cast<ResourceModificationJob&>(*job);
1598 ASSERT_TRUE(tmp.IsAnonymization());
1599 ASSERT_FALSE(tmp.IsTranscode());
1600 ASSERT_THROW(tmp.GetTransferSyntax(), OrthancException);
1601 ASSERT_EQ(RequestOrigin_Lua, tmp.GetOrigin().GetRequestOrigin());
1602 ASSERT_TRUE(tmp.GetModification().IsRemoved(DICOM_TAG_STUDY_DESCRIPTION));
1603 }
1604
1605 {
1606 ResourceModificationJob job(GetContext());
1607 ASSERT_THROW(job.SetTranscode("nope"), OrthancException);
1608 job.SetTranscode(DicomTransferSyntax_JPEGProcess1);
1609
1610 job.AddTrailingStep(); // Necessary since 1.7.0
1611 ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job));
1612 ASSERT_TRUE(job.Serialize(s));
1613 }
1614
1615 {
1616 std::unique_ptr<IJob> job;
1617 job.reset(unserializer.UnserializeJob(s));
1618
1619 ResourceModificationJob& tmp = dynamic_cast<ResourceModificationJob&>(*job);
1620 ASSERT_FALSE(tmp.IsAnonymization());
1621 ASSERT_TRUE(tmp.IsTranscode());
1622 ASSERT_EQ(DicomTransferSyntax_JPEGProcess1, tmp.GetTransferSyntax());
1623 ASSERT_EQ(RequestOrigin_Unknown, tmp.GetOrigin().GetRequestOrigin());
1624 }
1625
1626 // SplitStudyJob
1627
1628 std::string instance;
1629 ASSERT_TRUE(CreateInstance(instance));
1630
1631 std::string study, series;
1632
1633 {
1634 ServerContext::DicomCacheLocker lock(GetContext(), instance);
1635 study = lock.GetDicom().GetHasher().HashStudy();
1636 series = lock.GetDicom().GetHasher().HashSeries();
1637 }
1638
1639 {
1640 std::list<std::string> tmp;
1641 GetContext().GetIndex().GetAllUuids(tmp, ResourceType_Study);
1642 ASSERT_EQ(1u, tmp.size());
1643 ASSERT_EQ(study, tmp.front());
1644 GetContext().GetIndex().GetAllUuids(tmp, ResourceType_Series);
1645 ASSERT_EQ(1u, tmp.size());
1646 ASSERT_EQ(series, tmp.front());
1647 }
1648
1649 std::string study2;
1650
1651 {
1652 std::string a, b;
1653
1654 {
1655 ASSERT_THROW(SplitStudyJob(GetContext(), std::string("nope")), OrthancException);
1656
1657 SplitStudyJob job(GetContext(), study);
1658 job.SetKeepSource(true);
1659 job.AddSourceSeries(series);
1660 ASSERT_THROW(job.AddSourceSeries("nope"), OrthancException);
1661 job.SetOrigin(DicomInstanceOrigin::FromLua());
1662 job.Replace(DICOM_TAG_PATIENT_NAME, "hello");
1663 job.Remove(DICOM_TAG_PATIENT_BIRTH_DATE);
1664 ASSERT_THROW(job.Replace(DICOM_TAG_SERIES_DESCRIPTION, "nope"), OrthancException);
1665 ASSERT_THROW(job.Remove(DICOM_TAG_SERIES_DESCRIPTION), OrthancException);
1666
1667 ASSERT_TRUE(job.GetTargetStudy().empty());
1668 a = job.GetTargetStudyUid();
1669 ASSERT_TRUE(job.LookupTargetSeriesUid(b, series));
1670
1671 job.AddTrailingStep();
1672 job.Start();
1673 ASSERT_EQ(JobStepCode_Continue, job.Step("jobId").GetCode());
1674 ASSERT_EQ(JobStepCode_Success, job.Step("jobId").GetCode());
1675
1676 study2 = job.GetTargetStudy();
1677 ASSERT_FALSE(study2.empty());
1678
1679 ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job));
1680 ASSERT_TRUE(job.Serialize(s));
1681 }
1682
1683 {
1684 std::unique_ptr<IJob> job;
1685 job.reset(unserializer.UnserializeJob(s));
1686
1687 SplitStudyJob& tmp = dynamic_cast<SplitStudyJob&>(*job);
1688 ASSERT_TRUE(tmp.IsKeepSource());
1689 ASSERT_EQ(study, tmp.GetSourceStudy());
1690 ASSERT_EQ(a, tmp.GetTargetStudyUid());
1691 ASSERT_EQ(RequestOrigin_Lua, tmp.GetOrigin().GetRequestOrigin());
1692
1693 std::string s;
1694 ASSERT_EQ(study2, tmp.GetTargetStudy());
1695 ASSERT_FALSE(tmp.LookupTargetSeriesUid(s, "nope"));
1696 ASSERT_TRUE(tmp.LookupTargetSeriesUid(s, series));
1697 ASSERT_EQ(b, s);
1698
1699 ASSERT_FALSE(tmp.LookupReplacement(s, DICOM_TAG_STUDY_DESCRIPTION));
1700 ASSERT_TRUE(tmp.LookupReplacement(s, DICOM_TAG_PATIENT_NAME));
1701 ASSERT_EQ("hello", s);
1702 ASSERT_FALSE(tmp.IsRemoved(DICOM_TAG_PATIENT_NAME));
1703 ASSERT_TRUE(tmp.IsRemoved(DICOM_TAG_PATIENT_BIRTH_DATE));
1704 }
1705 }
1706
1707 {
1708 std::list<std::string> tmp;
1709 GetContext().GetIndex().GetAllUuids(tmp, ResourceType_Study);
1710 ASSERT_EQ(2u, tmp.size());
1711 GetContext().GetIndex().GetAllUuids(tmp, ResourceType_Series);
1712 ASSERT_EQ(2u, tmp.size());
1713 }
1714
1715 // MergeStudyJob
1716
1717 {
1718 ASSERT_THROW(SplitStudyJob(GetContext(), std::string("nope")), OrthancException);
1719
1720 MergeStudyJob job(GetContext(), study);
1721 job.SetKeepSource(true);
1722 job.AddSource(study2);
1723 ASSERT_THROW(job.AddSourceSeries("nope"), OrthancException);
1724 ASSERT_THROW(job.AddSourceStudy("nope"), OrthancException);
1725 ASSERT_THROW(job.AddSource("nope"), OrthancException);
1726 job.SetOrigin(DicomInstanceOrigin::FromLua());
1727
1728 ASSERT_EQ(job.GetTargetStudy(), study);
1729
1730 job.AddTrailingStep();
1731 job.Start();
1732 ASSERT_EQ(JobStepCode_Continue, job.Step("jobId").GetCode());
1733 ASSERT_EQ(JobStepCode_Success, job.Step("jobId").GetCode());
1734
1735 ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job));
1736 ASSERT_TRUE(job.Serialize(s));
1737 }
1738
1739 {
1740 std::list<std::string> tmp;
1741 GetContext().GetIndex().GetAllUuids(tmp, ResourceType_Study);
1742 ASSERT_EQ(2u, tmp.size());
1743 GetContext().GetIndex().GetAllUuids(tmp, ResourceType_Series);
1744 ASSERT_EQ(3u, tmp.size());
1745 }
1746
1747 {
1748 std::unique_ptr<IJob> job;
1749 job.reset(unserializer.UnserializeJob(s));
1750
1751 MergeStudyJob& tmp = dynamic_cast<MergeStudyJob&>(*job);
1752 ASSERT_TRUE(tmp.IsKeepSource());
1753 ASSERT_EQ(study, tmp.GetTargetStudy());
1754 ASSERT_EQ(RequestOrigin_Lua, tmp.GetOrigin().GetRequestOrigin());
1755 }
1756 }
1757
1758
1759 TEST(JobsSerialization, Registry)
1760 {
1761 Json::Value s;
1762 std::string i1, i2;
1763
1764 {
1765 JobsRegistry registry(10);
1766 registry.Submit(i1, new DummyJob(), 10);
1767 registry.Submit(i2, new SequenceOfOperationsJob(), 30);
1768 registry.Serialize(s);
1769 }
1770
1771 {
1772 DummyUnserializer unserializer;
1773 JobsRegistry registry(unserializer, s, 10);
1774
1775 Json::Value t;
1776 registry.Serialize(t);
1777 ASSERT_TRUE(CheckSameJson(s, t));
1778 }
1779 }
1780
1781
1782 TEST(JobsSerialization, TrailingStep)
1783 {
1784 {
1785 Json::Value s;
1786
1787 DummyInstancesJob job;
1788 ASSERT_EQ(0u, job.GetCommandsCount());
1789 ASSERT_EQ(0u, job.GetInstancesCount());
1790
1791 job.Start();
1792 ASSERT_EQ(0u, job.GetPosition());
1793 ASSERT_FALSE(job.HasTrailingStep());
1794 ASSERT_FALSE(job.IsTrailingStepDone());
1795
1796 {
1797 DummyUnserializer unserializer;
1798 ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job));
1799 }
1800
1801 ASSERT_EQ(JobStepCode_Success, job.Step("jobId").GetCode());
1802 ASSERT_EQ(1u, job.GetPosition());
1803 ASSERT_FALSE(job.IsTrailingStepDone());
1804
1805 {
1806 DummyUnserializer unserializer;
1807 ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job));
1808 }
1809
1810 ASSERT_THROW(job.Step("jobId"), OrthancException);
1811 }
1812
1813 {
1814 Json::Value s;
1815
1816 DummyInstancesJob job;
1817 job.AddInstance("hello");
1818 job.AddInstance("world");
1819 ASSERT_EQ(2u, job.GetCommandsCount());
1820 ASSERT_EQ(2u, job.GetInstancesCount());
1821
1822 job.Start();
1823 ASSERT_EQ(0u, job.GetPosition());
1824 ASSERT_FALSE(job.HasTrailingStep());
1825 ASSERT_FALSE(job.IsTrailingStepDone());
1826
1827 {
1828 DummyUnserializer unserializer;
1829 ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job));
1830 }
1831
1832 ASSERT_EQ(JobStepCode_Continue, job.Step("jobId").GetCode());
1833 ASSERT_EQ(1u, job.GetPosition());
1834 ASSERT_FALSE(job.IsTrailingStepDone());
1835
1836 {
1837 DummyUnserializer unserializer;
1838 ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job));
1839 }
1840
1841 ASSERT_EQ(JobStepCode_Success, job.Step("jobId").GetCode());
1842 ASSERT_EQ(2u, job.GetPosition());
1843 ASSERT_FALSE(job.IsTrailingStepDone());
1844
1845 {
1846 DummyUnserializer unserializer;
1847 ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job));
1848 }
1849
1850 ASSERT_THROW(job.Step("jobId"), OrthancException);
1851 }
1852
1853 {
1854 Json::Value s;
1855
1856 DummyInstancesJob job;
1857 ASSERT_EQ(0u, job.GetInstancesCount());
1858 ASSERT_EQ(0u, job.GetCommandsCount());
1859 job.AddTrailingStep();
1860 ASSERT_EQ(0u, job.GetInstancesCount());
1861 ASSERT_EQ(1u, job.GetCommandsCount());
1862
1863 job.Start(); // This adds the trailing step
1864 ASSERT_EQ(0u, job.GetPosition());
1865 ASSERT_TRUE(job.HasTrailingStep());
1866 ASSERT_FALSE(job.IsTrailingStepDone());
1867
1868 {
1869 DummyUnserializer unserializer;
1870 ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job));
1871 }
1872
1873 ASSERT_EQ(JobStepCode_Success, job.Step("jobId").GetCode());
1874 ASSERT_EQ(1u, job.GetPosition());
1875 ASSERT_TRUE(job.IsTrailingStepDone());
1876
1877 {
1878 DummyUnserializer unserializer;
1879 ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job));
1880 }
1881
1882 ASSERT_THROW(job.Step("jobId"), OrthancException);
1883 }
1884
1885 {
1886 Json::Value s;
1887
1888 DummyInstancesJob job;
1889 job.AddInstance("hello");
1890 ASSERT_EQ(1u, job.GetInstancesCount());
1891 ASSERT_EQ(1u, job.GetCommandsCount());
1892 job.AddTrailingStep();
1893 ASSERT_EQ(1u, job.GetInstancesCount());
1894 ASSERT_EQ(2u, job.GetCommandsCount());
1895
1896 job.Start();
1897 ASSERT_EQ(2u, job.GetCommandsCount());
1898 ASSERT_EQ(0u, job.GetPosition());
1899 ASSERT_TRUE(job.HasTrailingStep());
1900 ASSERT_FALSE(job.IsTrailingStepDone());
1901
1902 {
1903 DummyUnserializer unserializer;
1904 ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job));
1905 }
1906
1907 ASSERT_EQ(JobStepCode_Continue, job.Step("jobId").GetCode());
1908 ASSERT_EQ(1u, job.GetPosition());
1909 ASSERT_FALSE(job.IsTrailingStepDone());
1910
1911 {
1912 DummyUnserializer unserializer;
1913 ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job));
1914 }
1915
1916 ASSERT_EQ(JobStepCode_Success, job.Step("jobId").GetCode());
1917 ASSERT_EQ(2u, job.GetPosition());
1918 ASSERT_TRUE(job.IsTrailingStepDone());
1919
1920 {
1921 DummyUnserializer unserializer;
1922 ASSERT_TRUE(CheckIdempotentSetOfInstances(unserializer, job));
1923 }
1924
1925 ASSERT_THROW(job.Step("jobId"), OrthancException);
1926 }
1927 }
1928
1929
1930 TEST(JobsSerialization, RemoteModalityParameters)
1931 {
1932 Json::Value s;
1933
1934 {
1935 RemoteModalityParameters modality;
1936 ASSERT_FALSE(modality.IsAdvancedFormatNeeded());
1937 modality.Serialize(s, false);
1938 ASSERT_EQ(Json::arrayValue, s.type());
1939 }
1940
1941 {
1942 RemoteModalityParameters modality(s);
1943 ASSERT_EQ("ORTHANC", modality.GetApplicationEntityTitle());
1944 ASSERT_EQ("127.0.0.1", modality.GetHost());
1945 ASSERT_EQ(104u, modality.GetPortNumber());
1946 ASSERT_EQ(ModalityManufacturer_Generic, modality.GetManufacturer());
1947 ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_Echo));
1948 ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_Find));
1949 ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_Get));
1950 ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_Store));
1951 ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_Move));
1952 ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_NAction));
1953 ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_NEventReport));
1954 ASSERT_TRUE(modality.IsTranscodingAllowed());
1955 }
1956
1957 s = Json::nullValue;
1958
1959 {
1960 RemoteModalityParameters modality;
1961 ASSERT_FALSE(modality.IsAdvancedFormatNeeded());
1962 ASSERT_THROW(modality.SetPortNumber(0), OrthancException);
1963 ASSERT_THROW(modality.SetPortNumber(65535), OrthancException);
1964 modality.SetApplicationEntityTitle("HELLO");
1965 modality.SetHost("world");
1966 modality.SetPortNumber(45);
1967 modality.SetManufacturer(ModalityManufacturer_GenericNoWildcardInDates);
1968 modality.Serialize(s, true);
1969 ASSERT_EQ(Json::objectValue, s.type());
1970 }
1971
1972 {
1973 RemoteModalityParameters modality(s);
1974 ASSERT_EQ("HELLO", modality.GetApplicationEntityTitle());
1975 ASSERT_EQ("world", modality.GetHost());
1976 ASSERT_EQ(45u, modality.GetPortNumber());
1977 ASSERT_EQ(ModalityManufacturer_GenericNoWildcardInDates, modality.GetManufacturer());
1978 ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_Echo));
1979 ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_Find));
1980 ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_Get));
1981 ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_Store));
1982 ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_Move));
1983 ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_NAction));
1984 ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_NEventReport));
1985 ASSERT_TRUE(modality.IsTranscodingAllowed());
1986 }
1987
1988 s["Port"] = "46";
1989
1990 {
1991 RemoteModalityParameters modality(s);
1992 ASSERT_EQ(46u, modality.GetPortNumber());
1993 }
1994
1995 s["Port"] = -1; ASSERT_THROW(RemoteModalityParameters m(s), OrthancException);
1996 s["Port"] = 65535; ASSERT_THROW(RemoteModalityParameters m(s), OrthancException);
1997 s["Port"] = "nope"; ASSERT_THROW(RemoteModalityParameters m(s), OrthancException);
1998
1999 std::set<DicomRequestType> operations;
2000 operations.insert(DicomRequestType_Echo);
2001 operations.insert(DicomRequestType_Find);
2002 operations.insert(DicomRequestType_Get);
2003 operations.insert(DicomRequestType_Move);
2004 operations.insert(DicomRequestType_Store);
2005 operations.insert(DicomRequestType_NAction);
2006 operations.insert(DicomRequestType_NEventReport);
2007
2008 ASSERT_EQ(7u, operations.size());
2009
2010 for (std::set<DicomRequestType>::const_iterator
2011 it = operations.begin(); it != operations.end(); ++it)
2012 {
2013 {
2014 RemoteModalityParameters modality;
2015 modality.SetRequestAllowed(*it, false);
2016 ASSERT_TRUE(modality.IsAdvancedFormatNeeded());
2017
2018 modality.Serialize(s, false);
2019 ASSERT_EQ(Json::objectValue, s.type());
2020 }
2021
2022 {
2023 RemoteModalityParameters modality(s);
2024
2025 ASSERT_FALSE(modality.IsRequestAllowed(*it));
2026
2027 for (std::set<DicomRequestType>::const_iterator
2028 it2 = operations.begin(); it2 != operations.end(); ++it2)
2029 {
2030 if (*it2 != *it)
2031 {
2032 ASSERT_TRUE(modality.IsRequestAllowed(*it2));
2033 }
2034 }
2035 }
2036 }
2037
2038 {
2039 Json::Value s;
2040 s["AllowStorageCommitment"] = false;
2041 s["AET"] = "AET";
2042 s["Host"] = "host";
2043 s["Port"] = "104";
2044
2045 RemoteModalityParameters modality(s);
2046 ASSERT_TRUE(modality.IsAdvancedFormatNeeded());
2047 ASSERT_EQ("AET", modality.GetApplicationEntityTitle());
2048 ASSERT_EQ("host", modality.GetHost());
2049 ASSERT_EQ(104u, modality.GetPortNumber());
2050 ASSERT_FALSE(modality.IsRequestAllowed(DicomRequestType_NAction));
2051 ASSERT_FALSE(modality.IsRequestAllowed(DicomRequestType_NEventReport));
2052 ASSERT_TRUE(modality.IsTranscodingAllowed());
2053 }
2054
2055 {
2056 Json::Value s;
2057 s["AllowNAction"] = false;
2058 s["AllowNEventReport"] = true;
2059 s["AET"] = "AET";
2060 s["Host"] = "host";
2061 s["Port"] = "104";
2062 s["AllowTranscoding"] = false;
2063
2064 RemoteModalityParameters modality(s);
2065 ASSERT_TRUE(modality.IsAdvancedFormatNeeded());
2066 ASSERT_EQ("AET", modality.GetApplicationEntityTitle());
2067 ASSERT_EQ("host", modality.GetHost());
2068 ASSERT_EQ(104u, modality.GetPortNumber());
2069 ASSERT_FALSE(modality.IsRequestAllowed(DicomRequestType_NAction));
2070 ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_NEventReport));
2071 ASSERT_FALSE(modality.IsTranscodingAllowed());
2072 }
2073
2074 {
2075 Json::Value s;
2076 s["AllowNAction"] = true;
2077 s["AllowNEventReport"] = true;
2078 s["AET"] = "AET";
2079 s["Host"] = "host";
2080 s["Port"] = "104";
2081
2082 RemoteModalityParameters modality(s);
2083 ASSERT_FALSE(modality.IsAdvancedFormatNeeded());
2084 ASSERT_EQ("AET", modality.GetApplicationEntityTitle());
2085 ASSERT_EQ("host", modality.GetHost());
2086 ASSERT_EQ(104u, modality.GetPortNumber());
2087 ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_NAction));
2088 ASSERT_TRUE(modality.IsRequestAllowed(DicomRequestType_NEventReport));
2089 ASSERT_TRUE(modality.IsTranscodingAllowed());
2090 }
2091 }
2092
2093
2094 TEST_F(OrthancJobsSerialization, DicomAssociationParameters)
2095 {
2096 Json::Value v;
2097
2098 {
2099 v = Json::objectValue;
2100 DicomAssociationParameters p;
2101 p.SerializeJob(v);
2102 }
2103
2104 {
2105 DicomAssociationParameters p = DicomAssociationParameters::UnserializeJob(v);
2106 ASSERT_EQ("ORTHANC", p.GetLocalApplicationEntityTitle());
2107 ASSERT_EQ("ANY-SCP", p.GetRemoteModality().GetApplicationEntityTitle());
2108 ASSERT_EQ(104u, p.GetRemoteModality().GetPortNumber());
2109 ASSERT_EQ(ModalityManufacturer_Generic, p.GetRemoteModality().GetManufacturer());
2110 ASSERT_EQ("127.0.0.1", p.GetRemoteModality().GetHost());
2111 ASSERT_EQ(DicomAssociationParameters::GetDefaultTimeout(), p.GetTimeout());
2112 }
2113
2114 {
2115 v = Json::objectValue;
2116 DicomAssociationParameters p;
2117 p.SetLocalApplicationEntityTitle("HELLO");
2118 p.SetRemoteApplicationEntityTitle("WORLD");
2119 p.SetRemotePort(42);
2120 p.SetRemoteHost("MY_HOST");
2121 p.SetTimeout(43);
2122 p.SerializeJob(v);
2123 }
2124
2125 {
2126 DicomAssociationParameters p = DicomAssociationParameters::UnserializeJob(v);
2127 ASSERT_EQ("HELLO", p.GetLocalApplicationEntityTitle());
2128 ASSERT_EQ("WORLD", p.GetRemoteModality().GetApplicationEntityTitle());
2129 ASSERT_EQ(42u, p.GetRemoteModality().GetPortNumber());
2130 ASSERT_EQ(ModalityManufacturer_Generic, p.GetRemoteModality().GetManufacturer());
2131 ASSERT_EQ("MY_HOST", p.GetRemoteModality().GetHost());
2132 ASSERT_EQ(43u, p.GetTimeout());
2133 }
2134
2135 {
2136 DicomModalityStoreJob job(GetContext());
2137 job.Serialize(v);
2138 }
2139
2140 {
2141 OrthancJobUnserializer unserializer(GetContext());
2142 std::unique_ptr<DicomModalityStoreJob> job(
2143 dynamic_cast<DicomModalityStoreJob*>(unserializer.UnserializeJob(v)));
2144 ASSERT_EQ("ORTHANC", job->GetParameters().GetLocalApplicationEntityTitle());
2145 ASSERT_EQ("ANY-SCP", job->GetParameters().GetRemoteModality().GetApplicationEntityTitle());
2146 ASSERT_EQ("127.0.0.1", job->GetParameters().GetRemoteModality().GetHost());
2147 ASSERT_EQ(104u, job->GetParameters().GetRemoteModality().GetPortNumber());
2148 ASSERT_EQ(ModalityManufacturer_Generic, job->GetParameters().GetRemoteModality().GetManufacturer());
2149 ASSERT_EQ(DicomAssociationParameters::GetDefaultTimeout(), job->GetParameters().GetTimeout());
2150 ASSERT_FALSE(job->HasMoveOriginator());
2151 ASSERT_THROW(job->GetMoveOriginatorAet(), OrthancException);
2152 ASSERT_THROW(job->GetMoveOriginatorId(), OrthancException);
2153 ASSERT_FALSE(job->HasStorageCommitment());
2154 }
2155
2156 {
2157 RemoteModalityParameters r;
2158 r.SetApplicationEntityTitle("HELLO");
2159 r.SetPortNumber(42);
2160 r.SetHost("MY_HOST");
2161
2162 DicomModalityStoreJob job(GetContext());
2163 job.SetLocalAet("WORLD");
2164 job.SetRemoteModality(r);
2165 job.SetTimeout(43);
2166 job.SetMoveOriginator("ORIGINATOR", 100);
2167 job.EnableStorageCommitment(true);
2168 job.Serialize(v);
2169 }
2170
2171 {
2172 OrthancJobUnserializer unserializer(GetContext());
2173 std::unique_ptr<DicomModalityStoreJob> job(
2174 dynamic_cast<DicomModalityStoreJob*>(unserializer.UnserializeJob(v)));
2175 ASSERT_EQ("WORLD", job->GetParameters().GetLocalApplicationEntityTitle());
2176 ASSERT_EQ("HELLO", job->GetParameters().GetRemoteModality().GetApplicationEntityTitle());
2177 ASSERT_EQ("MY_HOST", job->GetParameters().GetRemoteModality().GetHost());
2178 ASSERT_EQ(42u, job->GetParameters().GetRemoteModality().GetPortNumber());
2179 ASSERT_EQ(ModalityManufacturer_Generic, job->GetParameters().GetRemoteModality().GetManufacturer());
2180 ASSERT_EQ(43u, job->GetParameters().GetTimeout());
2181 ASSERT_TRUE(job->HasMoveOriginator());
2182 ASSERT_EQ("ORIGINATOR", job->GetMoveOriginatorAet());
2183 ASSERT_EQ(100, job->GetMoveOriginatorId());
2184 ASSERT_TRUE(job->HasStorageCommitment());
2185 }
2186
2187 {
2188 DicomMoveScuJob job(GetContext());
2189 job.Serialize(v);
2190 }
2191
2192 {
2193 OrthancJobUnserializer unserializer(GetContext());
2194 std::unique_ptr<DicomMoveScuJob> job(
2195 dynamic_cast<DicomMoveScuJob*>(unserializer.UnserializeJob(v)));
2196 ASSERT_EQ("ORTHANC", job->GetParameters().GetLocalApplicationEntityTitle());
2197 ASSERT_EQ("ANY-SCP", job->GetParameters().GetRemoteModality().GetApplicationEntityTitle());
2198 ASSERT_EQ("127.0.0.1", job->GetParameters().GetRemoteModality().GetHost());
2199 ASSERT_EQ(104u, job->GetParameters().GetRemoteModality().GetPortNumber());
2200 ASSERT_EQ(ModalityManufacturer_Generic, job->GetParameters().GetRemoteModality().GetManufacturer());
2201 ASSERT_EQ(DicomAssociationParameters::GetDefaultTimeout(), job->GetParameters().GetTimeout());
2202 }
2203
2204 {
2205 RemoteModalityParameters r;
2206 r.SetApplicationEntityTitle("HELLO");
2207 r.SetPortNumber(42);
2208 r.SetHost("MY_HOST");
2209
2210 DicomMoveScuJob job(GetContext());
2211 job.SetLocalAet("WORLD");
2212 job.SetRemoteModality(r);
2213 job.SetTimeout(43);
2214 job.Serialize(v);
2215 }
2216
2217 {
2218 OrthancJobUnserializer unserializer(GetContext());
2219 std::unique_ptr<DicomMoveScuJob> job(
2220 dynamic_cast<DicomMoveScuJob*>(unserializer.UnserializeJob(v)));
2221 ASSERT_EQ("WORLD", job->GetParameters().GetLocalApplicationEntityTitle());
2222 ASSERT_EQ("HELLO", job->GetParameters().GetRemoteModality().GetApplicationEntityTitle());
2223 ASSERT_EQ("MY_HOST", job->GetParameters().GetRemoteModality().GetHost());
2224 ASSERT_EQ(42u, job->GetParameters().GetRemoteModality().GetPortNumber());
2225 ASSERT_EQ(ModalityManufacturer_Generic, job->GetParameters().GetRemoteModality().GetManufacturer());
2226 ASSERT_EQ(43u, job->GetParameters().GetTimeout());
2227 }
2228 }