1 /*
2  *
3  *  Copyright (C) 2015-2017, J. Riesmeier, Oldenburg, Germany
4  *  All rights reserved.  See COPYRIGHT file for details.
5  *
6  *  This software and supporting documentation are maintained by
7  *
8  *    OFFIS e.V.
9  *    R&D Division Health
10  *    Escherweg 2
11  *    D-26121 Oldenburg, Germany
12  *
13  *
14  *  Module: dcmsr
15  *
16  *  Author: Joerg Riesmeier
17  *
18  *  Purpose:
19  *    test program for classes DSRRootTemplate and DSRSubTemplate
20  *
21  */
22 
23 
24 #include "dcmtk/config/osconfig.h"    /* make sure OS specific configuration is included first */
25 
26 #include "dcmtk/ofstd/oftest.h"
27 
28 #include "dcmtk/dcmsr/dsrdoc.h"
29 #include "dcmtk/dcmsr/dsrdocst.h"
30 #include "dcmtk/dcmsr/dsrrtpl.h"
31 #include "dcmtk/dcmsr/dsrstpl.h"
32 #include "dcmtk/dcmsr/dsrreftn.h"
33 
34 
35 /* minimal test class for root template (Basic Diagnostic Imaging Report) */
36 class SRTestTemplate2000
37   : public DSRRootTemplate
38 {
39 
40   public:
41 
SRTestTemplate2000()42     SRTestTemplate2000()
43       : DSRRootTemplate(DT_BasicTextSR, "2000", "DCMR", UID_DICOMContentMappingResource)
44     {
45         /* make sure that at least the root CONTAINER is there */
46         OFCHECK(addContentItem(RT_isRoot, VT_Container) > 0);
47     }
48 };
49 
50 /* minimal test class for sub-template (Person Observer Identifying Attributes) */
51 class SRTestTemplate1003
52   : public DSRSubTemplate
53 {
54 
55   public:
56 
SRTestTemplate1003()57     SRTestTemplate1003()
58       : DSRSubTemplate("1003", "DCMR")
59     {
60         setExtensible();
61         /* make sure that at least the PNAME content item is there */
62         OFCHECK(addContentItem(RT_unknown, VT_PName, DSRCodedEntryValue("121008", "DCM", "Person Observer Name")).good());
63     }
64 };
65 
66 /* minimal test class for sub-template (Planar ROI Measurements) */
67 class SRTestTemplate1410
68   : public DSRSubTemplate
69 {
70 
71   public:
72 
SRTestTemplate1410()73     SRTestTemplate1410()
74       : DSRSubTemplate("1410", "DCMR")
75     {
76         setExtensible();
77         setOrderSignificant();
78         /* make sure that at least the "root" CONTAINER is there */
79         OFCHECK(addContentItem(RT_unknown, VT_Container, DSRCodedEntryValue("125007", "DCM", "Measurement Group")).good());
80         /* ... and two mandatory child nodes */
81         OFCHECK(addChildContentItem(RT_hasObsContext, VT_Text, DSRCodedEntryValue("112039", "DCM", "Tracking Identifier")).good());
82         OFCHECK(addContentItem(RT_hasObsContext, VT_UIDRef, DSRCodedEntryValue("112040", "DCM", "Tracking Unique Identifier")).good());
83     }
84 };
85 
86 /* minimal test class for root template with included templates (Measurement Report) */
87 class SRTestTemplate1500
88   : public DSRRootTemplate
89 {
90 
91   public:
92 
SRTestTemplate1500()93     SRTestTemplate1500()
94       : DSRRootTemplate(DT_EnhancedSR, "1500", "DCMR", UID_DICOMContentMappingResource)
95     {
96         /* make sure that at least the root CONTAINER is there */
97         OFCHECK(addContentItem(RT_isRoot, VT_Container, DSRCodedEntryValue("126000", "DCM", "Imaging Measurement Report")).good());
98         /* ... and include two sub-templates */
99         OFCHECK(includeTemplate(DSRSharedSubTemplate(new SRTestTemplate1003()), AM_belowCurrent, RT_hasObsContext).good());
100         OFCHECK(includeTemplate(DSRSharedSubTemplate(new SRTestTemplate1410()), AM_afterCurrent, RT_contains).good());
101     }
102 };
103 
104 /* minimal test class for template with by-reference relationship (Measurement Group) */
105 class SRTestTemplate1501
106   : public DSRSubTemplate
107 {
108 
109   public:
110 
SRTestTemplate1501()111     SRTestTemplate1501()
112       : DSRSubTemplate("1501", "DCMR", UID_DICOMContentMappingResource)
113     {
114         setExtensible();
115         /* make sure that at least the top-level CONTAINER is there */
116         OFCHECK(addContentItem(RT_contains, VT_Container, DSRCodedEntryValue("125007,", "DCM", "Measurement Group")).good());
117         /* ... and add two measurements, one referring to the other */
118         OFCHECK(addChildContentItem(RT_contains, VT_Num, DSRCodedEntryValue("12345", "99TEST", "Some Measurement")).good());
119         const size_t nodeID = getNodeID();
120         OFCHECK(addContentItem(RT_contains, VT_Num, DSRCodedEntryValue("09876", "99TEST", "Some other Measurement")).good());
121         OFCHECK(addByReferenceRelationship(RT_inferredFrom, nodeID) > 0);
122         /* update by-reference relationships (prepare for cloning) */
123         OFCHECK(updateByReferenceRelationships().good());
124     }
125 };
126 
127 
128 /* minimal test class for included sub-template with contained by-reference relationship */
129 class SRTestTemplate1410with1501
130   : public DSRSubTemplate
131 {
132 
133   public:
134 
SRTestTemplate1410with1501()135     SRTestTemplate1410with1501()
136       : DSRSubTemplate("1410", "DCMR")
137     {
138         setExtensible();
139         /* insert sub-template with some content items */
140         OFCHECK(insertExtraTemplate(SRTestTemplate1410(), AM_belowCurrent, RT_contains).good());
141         /* include sub-template with by-reference relationship */
142         OFCHECK(includeTemplate(DSRSharedSubTemplate(new SRTestTemplate1501()), AM_belowCurrent, RT_contains).good());
143     }
144 };
145 
146 
147 /* minimal test class for root template with the mandatory CONTAINER */
148 class SRTestRootTemplate
149   : public DSRRootTemplate
150 {
151 
152   public:
153 
SRTestRootTemplate()154     SRTestRootTemplate()
155       : DSRRootTemplate(DT_ComprehensiveSR, "0815", "99TEST")
156     {
157         setExtensible();
158         /* make sure that at least the root CONTAINER is there */
159         OFCHECK(addContentItem(RT_isRoot, VT_Container, DSRCodedEntryValue("1234", "99TEST", "Some test code")).good());
160     }
161 };
162 
163 
OFTEST(dcmsr_rootTemplate)164 OFTEST(dcmsr_rootTemplate)
165 {
166     /* first, create an empty SR document */
167     DSRDocument doc(DSRTypes::DT_ComprehensiveSR);
168     /* then, create an almost empty "Basic Diagnostic Imaging Report" (TID 2000) */
169     SRTestTemplate2000 templ;
170     /* perform some basic checks */
171     OFCHECK_EQUAL(doc.getDocumentType(), DSRTypes::DT_ComprehensiveSR);
172     OFCHECK_EQUAL(doc.getTree().countNodes(), 0);
173     OFCHECK_EQUAL(templ.getDocumentType(), DSRTypes::DT_BasicTextSR);
174     OFCHECK_EQUAL(templ.countNodes(), 1);
175     OFCHECK_EQUAL(templ.getTemplateIdentifier(), "2000");
176     OFCHECK_EQUAL(templ.getMappingResource(), "DCMR");
177     OFCHECK_EQUAL(templ.getMappingResourceUID(), UID_DICOMContentMappingResource);
178     OFCHECK(templ.isRootTemplate());
179     OFCHECK(!templ.isExtensible());
180     OFCHECK(!templ.isOrderSignificant());
181     /* replace the document tree with the content of the template */
182     OFCHECK(doc.setTreeFromRootTemplate(templ, OFFalse /*expandTree*/).good());
183     /* and perform some further checks */
184     OFCHECK_EQUAL(doc.getDocumentType(), DSRTypes::DT_BasicTextSR);
185     OFCHECK_EQUAL(doc.getTree().countNodes(), 1);
186     OFCHECK(doc.getTree().compareTemplateIdentification("2000", "DCMR"));
187     OFCHECK(doc.getTree().compareTemplateIdentification("2000", "DCMR", UID_DICOMContentMappingResource));
188     OFCHECK(!doc.getTree().compareTemplateIdentification("200", "DCMR"));
189     OFCHECK(!doc.getTree().compareTemplateIdentification("2000", "DCM"));
190 }
191 
192 
OFTEST(dcmsr_subTemplate_1)193 OFTEST(dcmsr_subTemplate_1)
194 {
195     /* first, create an empty SR document */
196     DSRDocument doc(DSRTypes::DT_ComprehensiveSR);
197     /* then, create an almost empty "Planar ROI Measurements" (TID 1410) */
198     SRTestTemplate1410 templ;
199     /* and, an empty SR document tree */
200     DSRDocumentTree tree(DSRTypes::DT_EnhancedSR);
201     /* perform some basic checks */
202     OFCHECK_EQUAL(doc.getDocumentType(), DSRTypes::DT_ComprehensiveSR);
203     OFCHECK_EQUAL(doc.getTree().countNodes(), 0);
204     OFCHECK_EQUAL(tree.getDocumentType(), DSRTypes::DT_EnhancedSR);
205     OFCHECK_EQUAL(tree.countNodes(), 0);
206     OFCHECK_EQUAL(templ.countNodes(), 3);
207     OFCHECK_EQUAL(templ.getTemplateIdentifier(), "1410");
208     OFCHECK_EQUAL(templ.getMappingResource(), "DCMR");
209     OFCHECK_EQUAL(templ.getMappingResourceUID(), "");
210     OFCHECK(!templ.isRootTemplate());
211     OFCHECK(templ.isExtensible());
212     OFCHECK(templ.isOrderSignificant());
213     /* insert TID 1410 into the tree ... */
214     OFCHECK(tree.insertSubTree(templ.cloneTree(), DSRTypes::AM_belowCurrent, DSRTypes::RT_isRoot).good());
215     /* and replace the tree of the SR document with the content of the template */
216     OFCHECK(doc.setTree(tree).good());
217     /* finally, perform some further checks */
218     OFCHECK_EQUAL(doc.getDocumentType(), DSRTypes::DT_EnhancedSR);
219     OFCHECK_EQUAL(doc.getTree().countNodes(), 3);
220     OFCHECK(doc.getTree().compareTemplateIdentification("1410", "DCMR"));
221 }
222 
223 
OFTEST(dcmsr_subTemplate_2)224 OFTEST(dcmsr_subTemplate_2)
225 {
226     /* first, create an empty SR document */
227     DSRDocument doc(DSRTypes::DT_ComprehensiveSR);
228     /* then, create an almost empty "Basic Diagnostic Imaging Report" (TID 2000) */
229     SRTestTemplate2000 templ1;
230     /* and make it extensible (only needed for this test) */
231     templ1.setExtensible();
232     /* also create an almost empty "Person Observer Identifying Attributes" (TID 1003) */
233     SRTestTemplate1003 templ2;
234     /* perform some basic checks */
235     OFCHECK_EQUAL(doc.getDocumentType(), DSRTypes::DT_ComprehensiveSR);
236     OFCHECK_EQUAL(doc.getTree().countNodes(), 0);
237     OFCHECK_EQUAL(templ1.getDocumentType(), DSRTypes::DT_BasicTextSR);
238     OFCHECK_EQUAL(templ1.countNodes(), 1);
239     OFCHECK_EQUAL(templ2.countNodes(), 1);
240     /* insert TID 1003 into TID 2000 */
241     OFCHECK(templ1.insertExtraTemplate(templ2, DSRTypes::AM_belowCurrent, DSRTypes::RT_hasAcqContext).good());
242     OFCHECK_EQUAL(templ1.countNodes(), 2);
243     OFCHECK_EQUAL(templ2.countNodes(), 1);
244     /* replace the document tree with the content of the template */
245     OFCHECK(doc.setTreeFromRootTemplate(templ1, OFFalse /*expandTree*/).good());
246     /* and perform some further checks */
247     OFCHECK_EQUAL(doc.getDocumentType(), DSRTypes::DT_BasicTextSR);
248     OFCHECK_EQUAL(doc.getTree().countNodes(), 2);
249     OFCHECK(doc.getTree().compareTemplateIdentification("2000", "DCMR"));
250 }
251 
252 
OFTEST(dcmsr_subTemplate_3)253 OFTEST(dcmsr_subTemplate_3)
254 {
255     /* first, create an almost empty "Planar ROI Measurements" (TID 1410) */
256     SRTestTemplate1410 templ;
257     /* then, add additional content items (since the template is extensible) */
258     OFCHECK(templ.isExtensible());
259     OFCHECK(templ.addExtraContentItem(DSRTypes::RT_contains, DSRTypes::VT_Text).good());
260     OFCHECK(templ.getCurrentContentItem().setConceptName(DSRBasicCodedEntry("121106", "DCM", "Comment")).good());
261     OFCHECK(templ.getCurrentContentItem().setStringValue("Some comment").good());
262     OFCHECK(templ.addExtraContentItem(DSRTypes::RT_hasConceptMod, DSRTypes::VT_Text, DSRTypes::AM_belowCurrent).good());
263     OFCHECK(templ.getCurrentContentItem().setConceptName(DSRBasicCodedEntry("121051", "DCM", "Equivalent Meaning of Value")).good());
264     OFCHECK(templ.getCurrentContentItem().setStringValue("blabla").good());
265     OFCHECK(templ.gotoParent() > 0);
266     /* also try to add if template is non-extensible */
267     templ.setExtensible(OFFalse);
268     OFCHECK(templ.addExtraContentItem(DSRTypes::RT_contains, DSRTypes::VT_Container) == SR_EC_NonExtensibleTemplate);
269     /* finally, perform some further checks */
270     OFCHECK_EQUAL(templ.countNodes(), 5);
271     OFCHECK_EQUAL(templ.countChildNodes(), 1);
272 }
273 
274 
OFTEST(dcmsr_createExpandedTree)275 OFTEST(dcmsr_createExpandedTree)
276 {
277     /* first, create an empty SR document */
278     DSRDocument doc(DSRTypes::DT_ComprehensiveSR);
279     /* then, create an almost empty "Measurement Report" (TID 1500) */
280     SRTestTemplate1500 templ;
281     OFCHECK_EQUAL(templ.countNodes(), 3);
282     /* and set its content as the document tree (with expanded sub-templates) */
283     OFCHECK(doc.getTree().isEmpty());
284     OFCHECK(doc.setTreeFromRootTemplate(templ, OFTrue /*expandTree*/).good());
285     OFCHECK(doc.getTree().isExpandedDocumentTree());
286     OFCHECK_EQUAL(doc.getTree().countNodes(), 5);
287     /* do the same without expanding the (included) sub-templates */
288     OFCHECK(doc.setTreeFromRootTemplate(templ, OFFalse /*expandTree*/).good());
289     OFCHECK(!doc.getTree().isExpandedDocumentTree());
290     OFCHECK_EQUAL(doc.getTree().countNodes(), 3);
291     /* and perform some further checks */
292     OFCHECK_EQUAL(doc.getDocumentType(), DSRTypes::DT_EnhancedSR);
293     OFCHECK(doc.getTree().compareTemplateIdentification("1500", "DCMR"));
294 }
295 
296 
OFTEST(dcmsr_templateWithByReferenceRelationship_1)297 OFTEST(dcmsr_templateWithByReferenceRelationship_1)
298 {
299     /* first, create an almost empty "Planar ROI Measurements" (TID 1410) */
300     SRTestTemplate1410 templ;
301     /* insert sub-template with by-reference relationship */
302     OFCHECK(templ.isExtensible());
303     OFCHECK(templ.insertExtraTemplate(SRTestTemplate1501(), DSRTypes::AM_afterCurrent, DSRTypes::RT_contains).good());
304     /* then, go to the source content item of the by-reference relationship */
305     OFCHECK(templ.gotoNamedNode(DSRCodedEntryValue("09876", "99TEST", "Some other Measurement")) > 0);
306     /* check whether the correct content item has been found */
307     OFCHECK(templ.getCurrentContentItem().getValueType() == DSRTypes::VT_Num);
308     /* and, finally, check whether the by-reference relationship is still valid */
309     OFCHECK(templ.updateByReferenceRelationships().good());
310     OFCHECK(templ.gotoChild() > 0);
311     OFCHECK(templ.getCurrentContentItem().getValueType() == DSRTypes::VT_byReference);
312     OFCHECK(templ.getCurrentContentItem().getReferencedNodeID() > 0);
313     const DSRDocumentTreeNode *treeNode = templ.getTree().getCurrentNode();
314     if (treeNode != NULL)
315     {
316         if (treeNode->getValueType() == DSRTypes::VT_byReference)
317         {
318             const DSRByReferenceTreeNode *node = OFstatic_cast(const DSRByReferenceTreeNode *, treeNode);
319             OFCHECK_EQUAL(node->getReferencedContentItem(), "1.3.1");
320         }
321     } else
322         OFCHECK_FAIL("could not get read-only access to current node");
323 }
324 
325 
OFTEST(dcmsr_templateWithByReferenceRelationship_2)326 OFTEST(dcmsr_templateWithByReferenceRelationship_2)
327 {
328     DSRDocument doc;
329     /* first, create a sub-template with included sub-template */
330     SRTestTemplate1410with1501 subTempl;
331     OFCHECK_EQUAL(subTempl.countNodes(), 4);
332     OFCHECK_EQUAL(subTempl.countNodes(OFTrue /*searchIntoSubTemplates*/, OFFalse /*countIncludedTemplateNodes*/), 7);
333     /* then, create a root template with a CONTAINER content item */
334     SRTestRootTemplate rootTempl;
335     OFCHECK_EQUAL(rootTempl.countNodes(), 1);
336     /* insert the sub-template into it */
337     OFCHECK(rootTempl.isExtensible());
338     OFCHECK(rootTempl.insertExtraTemplate(subTempl).good());
339     OFCHECK_EQUAL(rootTempl.countNodes(), 5);
340     OFCHECK_EQUAL(rootTempl.countNodes(OFTrue /*searchIntoSubTemplates*/, OFFalse /*countIncludedTemplateNodes*/), 8);
341     /* check whether the by-reference relationship is still valid */
342     OFCHECK(rootTempl.updateByReferenceRelationships(OFTrue /*includedTemplates*/).good());
343     DSRIncludedTemplateNodeCursor cursor;
344     if (rootTempl.getTree().getCursorToRootNode(cursor))
345     {
346         do {
347             const DSRDocumentTreeNode *treeNode = cursor.getNode();
348             if (treeNode != NULL)
349             {
350                 if (treeNode->getValueType() == DSRTypes::VT_byReference)
351                 {
352                     const DSRByReferenceTreeNode *node = OFstatic_cast(const DSRByReferenceTreeNode *, treeNode);
353                     OFCHECK_EQUAL(node->getReferencedContentItem(), "1.1.3.1.1");
354                 }
355             }
356         } while (cursor.iterate());
357     }
358     /* and, finally, set its content as the document tree */
359     OFCHECK(doc.setTreeFromRootTemplate(rootTempl, OFTrue /*expandTree*/).good());
360     OFCHECK_EQUAL(doc.getTree().countNodes(), 8);
361 }
362