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 General Public License as
9  * published by the Free Software Foundation, either version 3 of the
10  * License, or (at your option) any later version.
11  *
12  * In addition, as a special exception, the copyright holders of this
13  * program give permission to link the code of its release with the
14  * OpenSSL project's "OpenSSL" library (or with modified versions of it
15  * that use the same license as the "OpenSSL" library), and distribute
16  * the linked executables. You must obey the GNU General Public License
17  * in all respects for all of the code used other than "OpenSSL". If you
18  * modify file(s) with this exception, you may extend this exception to
19  * your version of the file(s), but you are not obligated to do so. If
20  * you do not wish to do so, delete this exception statement from your
21  * version. If you delete this exception statement from all source files
22  * in the program, then also delete it here.
23  *
24  * This program is distributed in the hope that it will be useful, but
25  * WITHOUT ANY WARRANTY; without even the implied warranty of
26  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27  * General Public License for more details.
28  *
29  * You should have received a copy of the GNU General Public License
30  * along with this program. If not, see <http://www.gnu.org/licenses/>.
31  **/
32 
33 
34 #include "PrecompiledHeadersUnitTests.h"
35 #include <gtest/gtest.h>
36 
37 #include "../../OrthancFramework/Sources/DicomFormat/DicomArray.h"
38 #include "../../OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.h"
39 #include "../../OrthancFramework/Sources/DicomParsing/ParsedDicomFile.h"
40 #include "../../OrthancFramework/Sources/DicomParsing/ToDcmtkBridge.h"
41 #include "../../OrthancFramework/Sources/EnumerationDictionary.h"
42 #include "../../OrthancFramework/Sources/Images/Image.h"
43 #include "../../OrthancFramework/Sources/Images/PngWriter.h"
44 #include "../../OrthancFramework/Sources/Logging.h"
45 #include "../../OrthancFramework/Sources/OrthancException.h"
46 #include "../../OrthancFramework/Sources/Toolbox.h"
47 
48 #include "../Plugins/Engine/PluginsEnumerations.h"
49 #include "../Sources/DicomInstanceToStore.h"
50 #include "../Sources/OrthancConfiguration.h"  // For the FontRegistry
51 #include "../Sources/OrthancInitialization.h"
52 #include "../Sources/ServerEnumerations.h"
53 #include "../Sources/ServerToolbox.h"
54 #include "../Sources/StorageCommitmentReports.h"
55 
56 #include <OrthancServerResources.h>
57 
58 #include <dcmtk/dcmdata/dcdeftag.h>
59 
60 
61 using namespace Orthanc;
62 
63 
TEST(EnumerationDictionary,Simple)64 TEST(EnumerationDictionary, Simple)
65 {
66   EnumerationDictionary<MetadataType>  d;
67 
68   ASSERT_THROW(d.Translate("ReceptionDate"), OrthancException);
69   ASSERT_EQ(MetadataType_ModifiedFrom, d.Translate("5"));
70   ASSERT_EQ(256, d.Translate("256"));
71 
72   d.Add(MetadataType_Instance_ReceptionDate, "ReceptionDate");
73 
74   ASSERT_EQ(MetadataType_Instance_ReceptionDate, d.Translate("ReceptionDate"));
75   ASSERT_EQ(MetadataType_Instance_ReceptionDate, d.Translate("2"));
76   ASSERT_EQ("ReceptionDate", d.Translate(MetadataType_Instance_ReceptionDate));
77 
78   ASSERT_THROW(d.Add(MetadataType_Instance_ReceptionDate, "Hello"), OrthancException);
79   ASSERT_THROW(d.Add(MetadataType_ModifiedFrom, "ReceptionDate"), OrthancException); // already used
80   ASSERT_THROW(d.Add(MetadataType_ModifiedFrom, "1024"), OrthancException); // cannot register numbers
81   d.Add(MetadataType_ModifiedFrom, "ModifiedFrom");  // ok
82 }
83 
84 
TEST(EnumerationDictionary,ServerEnumerations)85 TEST(EnumerationDictionary, ServerEnumerations)
86 {
87   ASSERT_STREQ("Patient", EnumerationToString(ResourceType_Patient));
88   ASSERT_STREQ("Study", EnumerationToString(ResourceType_Study));
89   ASSERT_STREQ("Series", EnumerationToString(ResourceType_Series));
90   ASSERT_STREQ("Instance", EnumerationToString(ResourceType_Instance));
91 
92   ASSERT_STREQ("ModifiedSeries", EnumerationToString(ChangeType_ModifiedSeries));
93 
94   ASSERT_STREQ("Failure", EnumerationToString(StoreStatus_Failure));
95   ASSERT_STREQ("Success", EnumerationToString(StoreStatus_Success));
96 
97   ASSERT_STREQ("CompletedSeries", EnumerationToString(ChangeType_CompletedSeries));
98 
99   ASSERT_EQ("IndexInSeries", EnumerationToString(MetadataType_Instance_IndexInSeries));
100   ASSERT_EQ("LastUpdate", EnumerationToString(MetadataType_LastUpdate));
101 
102   ASSERT_EQ(ResourceType_Patient, StringToResourceType("PATienT"));
103   ASSERT_EQ(ResourceType_Study, StringToResourceType("STudy"));
104   ASSERT_EQ(ResourceType_Series, StringToResourceType("SeRiEs"));
105   ASSERT_EQ(ResourceType_Instance, StringToResourceType("INStance"));
106   ASSERT_EQ(ResourceType_Instance, StringToResourceType("IMagE"));
107   ASSERT_THROW(StringToResourceType("heLLo"), OrthancException);
108 
109   ASSERT_EQ(2047, StringToMetadata("2047"));
110   ASSERT_THROW(StringToMetadata("Ceci est un test"), OrthancException);
111   ASSERT_THROW(RegisterUserMetadata(128, ""), OrthancException); // too low (< 1024)
112   ASSERT_THROW(RegisterUserMetadata(128000, ""), OrthancException); // too high (> 65535)
113   RegisterUserMetadata(2047, "Ceci est un test");
114   ASSERT_EQ(2047, StringToMetadata("2047"));
115   ASSERT_EQ(2047, StringToMetadata("Ceci est un test"));
116 
117   ASSERT_STREQ("Generic", EnumerationToString(StringToModalityManufacturer("Generic")));
118   ASSERT_STREQ("GenericNoWildcardInDates", EnumerationToString(StringToModalityManufacturer("GenericNoWildcardInDates")));
119   ASSERT_STREQ("GenericNoUniversalWildcard", EnumerationToString(StringToModalityManufacturer("GenericNoUniversalWildcard")));
120   ASSERT_STREQ("Vitrea", EnumerationToString(StringToModalityManufacturer("Vitrea")));
121   ASSERT_STREQ("GE", EnumerationToString(StringToModalityManufacturer("GE")));
122   // backward compatibility tests (to remove once we make these manufacturer really obsolete)
123   ASSERT_STREQ("Generic", EnumerationToString(StringToModalityManufacturer("MedInria")));
124   ASSERT_STREQ("Generic", EnumerationToString(StringToModalityManufacturer("EFilm2")));
125   ASSERT_STREQ("Generic", EnumerationToString(StringToModalityManufacturer("ClearCanvas")));
126   ASSERT_STREQ("Generic", EnumerationToString(StringToModalityManufacturer("Dcm4Chee")));
127   ASSERT_STREQ("GenericNoWildcardInDates", EnumerationToString(StringToModalityManufacturer("SyngoVia")));
128   ASSERT_STREQ("GenericNoWildcardInDates", EnumerationToString(StringToModalityManufacturer("AgfaImpax")));
129 
130   ASSERT_STREQ("default", EnumerationToString(StringToVerbosity("default")));
131   ASSERT_STREQ("verbose", EnumerationToString(StringToVerbosity("verbose")));
132   ASSERT_STREQ("trace", EnumerationToString(StringToVerbosity("trace")));
133   ASSERT_THROW(StringToVerbosity("nope"), OrthancException);
134 }
135 
136 
137 
TEST(FontRegistry,Basic)138 TEST(FontRegistry, Basic)
139 {
140   Orthanc::Image s(Orthanc::PixelFormat_RGB24, 640, 480, false);
141   memset(s.GetBuffer(), 0, s.GetPitch() * s.GetHeight());
142 
143   {
144     Orthanc::OrthancConfiguration::ReaderLock lock;
145     ASSERT_GE(1u, lock.GetConfiguration().GetFontRegistry().GetSize());
146     lock.GetConfiguration().GetFontRegistry().GetFont(0).Draw
147       (s, "Hello world É\n\rComment ça va ?\nq", 50, 60, 255, 0, 0);
148   }
149 
150   Orthanc::PngWriter w;
151   Orthanc::IImageWriter::WriteToFile(w, "UnitTestsResults/font.png", s);
152 }
153 
154 
TEST(FromDcmtkBridge,ValueRepresentationConversions)155 TEST(FromDcmtkBridge, ValueRepresentationConversions)
156 {
157 #if ORTHANC_ENABLE_PLUGINS == 1
158   ASSERT_EQ(1, ValueRepresentation_ApplicationEntity);
159   ASSERT_EQ(1, OrthancPluginValueRepresentation_AE);
160 
161   for (int i = ValueRepresentation_ApplicationEntity;
162        i <= ValueRepresentation_NotSupported; i++)
163   {
164     ValueRepresentation vr = static_cast<ValueRepresentation>(i);
165 
166     if (vr == ValueRepresentation_NotSupported)
167     {
168       ASSERT_THROW(ToDcmtkBridge::Convert(vr), OrthancException);
169       ASSERT_THROW(Plugins::Convert(vr), OrthancException);
170     }
171     else if (vr == ValueRepresentation_OtherDouble ||
172              vr == ValueRepresentation_OtherLong ||
173              vr == ValueRepresentation_UniversalResource ||
174              vr == ValueRepresentation_UnlimitedCharacters)
175     {
176       // These VR are not supported as of DCMTK 3.6.0
177       ASSERT_THROW(ToDcmtkBridge::Convert(vr), OrthancException);
178       ASSERT_EQ(OrthancPluginValueRepresentation_UN, Plugins::Convert(vr));
179     }
180     else
181     {
182       ASSERT_EQ(vr, FromDcmtkBridge::Convert(ToDcmtkBridge::Convert(vr)));
183 
184       OrthancPluginValueRepresentation plugins = Plugins::Convert(vr);
185       ASSERT_EQ(vr, Plugins::Convert(plugins));
186     }
187   }
188 
189   for (int i = OrthancPluginValueRepresentation_AE;
190        i <= OrthancPluginValueRepresentation_UT; i++)
191   {
192     OrthancPluginValueRepresentation plugins = static_cast<OrthancPluginValueRepresentation>(i);
193     ValueRepresentation orthanc = Plugins::Convert(plugins);
194     ASSERT_EQ(plugins, Plugins::Convert(orthanc));
195   }
196 #endif
197 }
198 
199 
200 
TEST(DicomMap,DicomAsJson)201 TEST(DicomMap, DicomAsJson)
202 {
203   // This is a Latin-1 test string: "crane" with a circumflex accent
204   const unsigned char raw[] = { 0x63, 0x72, 0xe2, 0x6e, 0x65 };
205   std::string latin1((char*) &raw[0], sizeof(raw) / sizeof(char));
206 
207   std::string utf8 = Toolbox::ConvertToUtf8(latin1, Encoding_Latin1, false);
208 
209   ParsedDicomFile dicom(false);
210   dicom.SetEncoding(Encoding_Latin1);
211   dicom.ReplacePlainString(DICOM_TAG_PATIENT_NAME, "Hello");
212   dicom.ReplacePlainString(DICOM_TAG_STUDY_DESCRIPTION, utf8);
213   dicom.ReplacePlainString(DICOM_TAG_SERIES_DESCRIPTION, std::string(ORTHANC_MAXIMUM_TAG_LENGTH, 'a'));
214   dicom.ReplacePlainString(DICOM_TAG_MANUFACTURER, std::string(ORTHANC_MAXIMUM_TAG_LENGTH + 1, 'a'));
215   dicom.ReplacePlainString(DICOM_TAG_PIXEL_DATA, "binary");
216   dicom.ReplacePlainString(DICOM_TAG_ROWS, "512");
217 
218   DcmDataset& dataset = *dicom.GetDcmtkObject().getDataset();
219   dataset.insertEmptyElement(DCM_StudyID, OFFalse);
220 
221   {
222     std::unique_ptr<DcmSequenceOfItems> sequence(new DcmSequenceOfItems(DCM_ReferencedSeriesSequence));
223 
224     {
225       std::unique_ptr<DcmItem> item(new DcmItem);
226       item->putAndInsertString(DCM_ReferencedSOPInstanceUID, "nope", OFFalse);
227       ASSERT_TRUE(sequence->insert(item.release(), false, false).good());
228     }
229 
230     ASSERT_TRUE(dataset.insert(sequence.release(), false, false).good());
231   }
232 
233 
234   // Check re-encoding
235   DcmElement* element = NULL;
236   ASSERT_TRUE(dataset.findAndGetElement(DCM_StudyDescription, element).good() &&
237               element != NULL);
238 
239   char* c = NULL;
240   ASSERT_TRUE(element != NULL &&
241               element->isLeaf() &&
242               element->isaString() &&
243               element->getString(c).good());
244   ASSERT_EQ(0, memcmp(c, raw, latin1.length()));
245 
246   ASSERT_TRUE(dataset.findAndGetElement(DCM_Rows, element).good() &&
247               element != NULL &&
248               element->getTag().getEVR() == EVR_US);
249 
250   std::unique_ptr<DicomInstanceToStore> toStore(DicomInstanceToStore::CreateFromParsedDicomFile(dicom));
251 
252   Json::Value dicomAsJson;
253   OrthancConfiguration::DefaultDicomDatasetToJson(dicomAsJson, toStore->GetParsedDicomFile());
254 
255   DicomMap m;
256   m.FromDicomAsJson(dicomAsJson);
257 
258   ASSERT_EQ("ISO_IR 100", m.GetValue(DICOM_TAG_SPECIFIC_CHARACTER_SET).GetContent());
259 
260   ASSERT_FALSE(m.GetValue(DICOM_TAG_PATIENT_NAME).IsBinary());
261   ASSERT_EQ("Hello", m.GetValue(DICOM_TAG_PATIENT_NAME).GetContent());
262 
263   ASSERT_FALSE(m.GetValue(DICOM_TAG_STUDY_DESCRIPTION).IsBinary());
264   ASSERT_EQ(utf8, m.GetValue(DICOM_TAG_STUDY_DESCRIPTION).GetContent());
265 
266   ASSERT_FALSE(m.HasTag(DICOM_TAG_MANUFACTURER));                // Too long
267   ASSERT_FALSE(m.HasTag(DICOM_TAG_PIXEL_DATA));                  // Pixel data
268   ASSERT_FALSE(m.HasTag(DICOM_TAG_REFERENCED_SERIES_SEQUENCE));  // Sequence
269   ASSERT_EQ(DICOM_TAG_REFERENCED_SERIES_SEQUENCE.GetGroup(), DCM_ReferencedSeriesSequence.getGroup());
270   ASSERT_EQ(DICOM_TAG_REFERENCED_SERIES_SEQUENCE.GetElement(), DCM_ReferencedSeriesSequence.getElement());
271 
272   ASSERT_TRUE(m.HasTag(DICOM_TAG_SERIES_DESCRIPTION));  // Maximum length
273   ASSERT_FALSE(m.GetValue(DICOM_TAG_SERIES_DESCRIPTION).IsBinary());
274   ASSERT_EQ(ORTHANC_MAXIMUM_TAG_LENGTH,
275             static_cast<int>(m.GetValue(DICOM_TAG_SERIES_DESCRIPTION).GetContent().length()));
276 
277   ASSERT_FALSE(m.GetValue(DICOM_TAG_ROWS).IsBinary());
278   ASSERT_EQ("512", m.GetValue(DICOM_TAG_ROWS).GetContent());
279 
280   ASSERT_FALSE(m.GetValue(DICOM_TAG_STUDY_ID).IsNull());
281   ASSERT_FALSE(m.GetValue(DICOM_TAG_STUDY_ID).IsBinary());
282   ASSERT_EQ("", m.GetValue(DICOM_TAG_STUDY_ID).GetContent());
283 
284   DicomArray a(m);
285   ASSERT_EQ(6u, a.GetSize());
286 
287 
288   //dicom.SaveToFile("/tmp/test.dcm");
289   //std::cout << toStore.GetJson() << std::endl;
290   //a.Print(stdout);
291 }
292 
293 
294 
295 namespace Orthanc
296 {
297   // Namespace for the "FRIEND_TEST()" directive in "FromDcmtkBridge" to apply:
298   // https://github.com/google/googletest/blob/master/googletest/docs/AdvancedGuide.md#private-class-members
299 
300   static const DicomTag REFERENCED_STUDY_SEQUENCE(0x0008, 0x1110);
301   static const DicomTag REFERENCED_PATIENT_SEQUENCE(0x0008, 0x1120);
302 
CreateSampleJson(Json::Value & a)303   static void CreateSampleJson(Json::Value& a)
304   {
305     {
306       Json::Value b = Json::objectValue;
307       b["PatientName"] = "Hello";
308       b["PatientID"] = "World";
309       b["StudyDescription"] = "Toto";
310       a.append(b);
311     }
312 
313     {
314       Json::Value b = Json::objectValue;
315       b["PatientName"] = "data:application/octet-stream;base64,SGVsbG8y";  // echo -n "Hello2" | base64
316       b["PatientID"] = "World2";
317       a.append(b);
318     }
319   }
320 
TEST(FromDcmtkBridge,FromJson)321   TEST(FromDcmtkBridge, FromJson)
322   {
323     std::unique_ptr<DcmElement> element;
324 
325     {
326       Json::Value a;
327       a = "Hello";
328       element.reset(FromDcmtkBridge::FromJson(DICOM_TAG_PATIENT_NAME, a, false, Encoding_Utf8, ""));
329 
330       Json::Value b;
331       std::set<DicomTag> ignoreTagLength;
332       ignoreTagLength.insert(DICOM_TAG_PATIENT_ID);
333 
334       FromDcmtkBridge::ElementToJson(b, *element, DicomToJsonFormat_Short,
335                                      DicomToJsonFlags_Default, 0, Encoding_Ascii, false, ignoreTagLength, 0);
336       ASSERT_TRUE(b.isMember("0010,0010"));
337       ASSERT_EQ("Hello", b["0010,0010"].asString());
338 
339       FromDcmtkBridge::ElementToJson(b, *element, DicomToJsonFormat_Short,
340                                      DicomToJsonFlags_Default, 3, Encoding_Ascii, false, ignoreTagLength, 0);
341       ASSERT_TRUE(b["0010,0010"].isNull()); // "Hello" has more than 3 characters
342 
343       FromDcmtkBridge::ElementToJson(b, *element, DicomToJsonFormat_Full,
344                                      DicomToJsonFlags_Default, 3, Encoding_Ascii, false, ignoreTagLength, 0);
345       ASSERT_TRUE(b["0010,0010"].isObject());
346       ASSERT_EQ("PatientName", b["0010,0010"]["Name"].asString());
347       ASSERT_EQ("TooLong", b["0010,0010"]["Type"].asString());
348       ASSERT_TRUE(b["0010,0010"]["Value"].isNull());
349 
350       ignoreTagLength.insert(DICOM_TAG_PATIENT_NAME);
351       FromDcmtkBridge::ElementToJson(b, *element, DicomToJsonFormat_Short,
352                                      DicomToJsonFlags_Default, 3, Encoding_Ascii, false, ignoreTagLength, 0);
353       ASSERT_EQ("Hello", b["0010,0010"].asString());
354     }
355 
356     {
357       Json::Value a;
358       a = "Hello";
359       // Cannot assign a string to a sequence
360       ASSERT_THROW(element.reset(FromDcmtkBridge::FromJson(REFERENCED_STUDY_SEQUENCE, a, false, Encoding_Utf8, "")), OrthancException);
361     }
362 
363     {
364       Json::Value a = Json::arrayValue;
365       a.append("Hello");
366       // Cannot assign an array to a string
367       ASSERT_THROW(element.reset(FromDcmtkBridge::FromJson(DICOM_TAG_PATIENT_NAME, a, false, Encoding_Utf8, "")), OrthancException);
368     }
369 
370     {
371       Json::Value a;
372       a = "data:application/octet-stream;base64,SGVsbG8=";  // echo -n "Hello" | base64
373       element.reset(FromDcmtkBridge::FromJson(DICOM_TAG_PATIENT_NAME, a, true, Encoding_Utf8, ""));
374 
375       Json::Value b;
376       std::set<DicomTag> ignoreTagLength;
377       FromDcmtkBridge::ElementToJson(b, *element, DicomToJsonFormat_Short,
378                                      DicomToJsonFlags_Default, 0, Encoding_Ascii, false, ignoreTagLength, 0);
379       ASSERT_EQ("Hello", b["0010,0010"].asString());
380     }
381 
382     {
383       Json::Value a = Json::arrayValue;
384       CreateSampleJson(a);
385       element.reset(FromDcmtkBridge::FromJson(REFERENCED_STUDY_SEQUENCE, a, true, Encoding_Utf8, ""));
386 
387       {
388         Json::Value b;
389         std::set<DicomTag> ignoreTagLength;
390         FromDcmtkBridge::ElementToJson(b, *element, DicomToJsonFormat_Short,
391                                        DicomToJsonFlags_Default, 0, Encoding_Ascii, false, ignoreTagLength, 0);
392         ASSERT_EQ(Json::arrayValue, b["0008,1110"].type());
393         ASSERT_EQ(2u, b["0008,1110"].size());
394 
395         Json::Value::ArrayIndex i = (b["0008,1110"][0]["0010,0010"].asString() == "Hello") ? 0 : 1;
396 
397         ASSERT_EQ(3u, b["0008,1110"][i].size());
398         ASSERT_EQ(2u, b["0008,1110"][1 - i].size());
399         ASSERT_EQ(b["0008,1110"][i]["0010,0010"].asString(), "Hello");
400         ASSERT_EQ(b["0008,1110"][i]["0010,0020"].asString(), "World");
401         ASSERT_EQ(b["0008,1110"][i]["0008,1030"].asString(), "Toto");
402         ASSERT_EQ(b["0008,1110"][1 - i]["0010,0010"].asString(), "Hello2");
403         ASSERT_EQ(b["0008,1110"][1 - i]["0010,0020"].asString(), "World2");
404       }
405 
406       {
407         Json::Value b;
408         std::set<DicomTag> ignoreTagLength;
409         FromDcmtkBridge::ElementToJson(b, *element, DicomToJsonFormat_Full,
410                                        DicomToJsonFlags_Default, 0, Encoding_Ascii, false, ignoreTagLength, 0);
411 
412         Json::Value c;
413         Toolbox::SimplifyDicomAsJson(c, b, DicomToJsonFormat_Human);
414 
415         a[1]["PatientName"] = "Hello2";  // To remove the Data URI Scheme encoding
416         ASSERT_EQ(0, c["ReferencedStudySequence"].compare(a));
417       }
418     }
419   }
420 }
421 
422 
TEST(StorageCommitmentReports,Basic)423 TEST(StorageCommitmentReports, Basic)
424 {
425   Orthanc::StorageCommitmentReports reports(2);
426   ASSERT_EQ(2u, reports.GetMaxSize());
427 
428   {
429     Orthanc::StorageCommitmentReports::Accessor accessor(reports, "nope");
430     ASSERT_EQ("nope", accessor.GetTransactionUid());
431     ASSERT_FALSE(accessor.IsValid());
432     ASSERT_THROW(accessor.GetReport(), Orthanc::OrthancException);
433   }
434 
435   reports.Store("a", new Orthanc::StorageCommitmentReports::Report("aet_a"));
436   reports.Store("b", new Orthanc::StorageCommitmentReports::Report("aet_b"));
437   reports.Store("c", new Orthanc::StorageCommitmentReports::Report("aet_c"));
438 
439   {
440     Orthanc::StorageCommitmentReports::Accessor accessor(reports, "a");
441     ASSERT_FALSE(accessor.IsValid());
442   }
443 
444   {
445     Orthanc::StorageCommitmentReports::Accessor accessor(reports, "b");
446     ASSERT_TRUE(accessor.IsValid());
447     ASSERT_EQ("aet_b", accessor.GetReport().GetRemoteAet());
448     ASSERT_EQ(Orthanc::StorageCommitmentReports::Report::Status_Pending,
449               accessor.GetReport().GetStatus());
450   }
451 
452   {
453     Orthanc::StorageCommitmentReports::Accessor accessor(reports, "c");
454     ASSERT_EQ("aet_c", accessor.GetReport().GetRemoteAet());
455     ASSERT_TRUE(accessor.IsValid());
456   }
457 
458   {
459     std::unique_ptr<Orthanc::StorageCommitmentReports::Report> report
460       (new Orthanc::StorageCommitmentReports::Report("aet"));
461     report->AddSuccess("class1", "instance1");
462     report->AddFailure("class2", "instance2",
463                        Orthanc::StorageCommitmentFailureReason_ReferencedSOPClassNotSupported);
464     report->MarkAsComplete();
465     reports.Store("a", report.release());
466   }
467 
468   {
469     Orthanc::StorageCommitmentReports::Accessor accessor(reports, "a");
470     ASSERT_TRUE(accessor.IsValid());
471     ASSERT_EQ("aet", accessor.GetReport().GetRemoteAet());
472     ASSERT_EQ(Orthanc::StorageCommitmentReports::Report::Status_Failure,
473               accessor.GetReport().GetStatus());
474   }
475 
476   {
477     Orthanc::StorageCommitmentReports::Accessor accessor(reports, "b");
478     ASSERT_FALSE(accessor.IsValid());
479   }
480 
481   {
482     Orthanc::StorageCommitmentReports::Accessor accessor(reports, "c");
483     ASSERT_TRUE(accessor.IsValid());
484   }
485 
486   {
487     std::unique_ptr<Orthanc::StorageCommitmentReports::Report> report
488       (new Orthanc::StorageCommitmentReports::Report("aet"));
489     report->AddSuccess("class1", "instance1");
490     report->MarkAsComplete();
491     reports.Store("a", report.release());
492   }
493 
494   {
495     Orthanc::StorageCommitmentReports::Accessor accessor(reports, "a");
496     ASSERT_TRUE(accessor.IsValid());
497     ASSERT_EQ("aet", accessor.GetReport().GetRemoteAet());
498     ASSERT_EQ(Orthanc::StorageCommitmentReports::Report::Status_Success,
499               accessor.GetReport().GetStatus());
500   }
501 
502   {
503     Orthanc::StorageCommitmentReports::Accessor accessor(reports, "b");
504     ASSERT_FALSE(accessor.IsValid());
505   }
506 
507   {
508     Orthanc::StorageCommitmentReports::Accessor accessor(reports, "c");
509     ASSERT_TRUE(accessor.IsValid());
510   }
511 }
512 
513 
514 
main(int argc,char ** argv)515 int main(int argc, char **argv)
516 {
517   Logging::Initialize();
518   Toolbox::InitializeGlobalLocale(NULL);
519   SetGlobalVerbosity(Verbosity_Verbose);
520   Toolbox::DetectEndianness();
521   SystemToolbox::MakeDirectory("UnitTestsResults");
522   OrthancInitialize();
523 
524   ::testing::InitGoogleTest(&argc, argv);
525   int result = RUN_ALL_TESTS();
526 
527   OrthancFinalize();
528   Logging::Finalize();
529 
530   return result;
531 }
532