Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
mitkDataStorageCompare.cpp
Go to the documentation of this file.
1 /*===================================================================
2 
3 The Medical Imaging Interaction Toolkit (MITK)
4 
5 Copyright (c) German Cancer Research Center,
6 Division of Medical and Biological Informatics.
7 All rights reserved.
8 
9 This software is distributed WITHOUT ANY WARRANTY; without
10 even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 A PARTICULAR PURPOSE.
12 
13 See LICENSE.txt or http://www.mitk.org for details.
14 
15 ===================================================================*/
16 
17 #include "mitkDataStorageCompare.h"
18 #include "mitkBaseDataCompare.h"
19 
20 #include "mitkBaseRenderer.h"
21 #include "mitkMapper.h"
22 
23 #include "usGetModuleContext.h"
24 #include "usLDAPFilter.h"
25 #include "usModuleContext.h"
26 
28  const mitk::DataStorage *test,
29  Tests flags,
30  double eps)
31  : m_Eps(eps),
32  m_TestAspects(flags),
33  m_ReferenceDS(reference),
34  m_TestDS(test),
35  m_HierarchyPassed(true),
36  m_DataPassed(true),
37  m_PropertiesPassed(true),
38  m_MappersPassed(true),
39  m_InteractorsPassed(true),
40  m_AspectsFailed(0)
41 {
43 }
44 
46 {
47  return Compare(true);
48 }
49 
51 {
52  DescribeHierarchyOfNodes(m_ReferenceDS, m_RefNodesByHierarchy);
53  DescribeHierarchyOfNodes(m_TestDS, m_TestNodesByHierarchy);
54 
55  m_HierarchyPassed = true;
56  m_DataPassed = true;
57  m_PropertiesPassed = true;
58  m_MappersPassed = true;
59  m_InteractorsPassed = true;
60  m_AspectsFailed = 0;
61 
62  if (m_TestAspects & CMP_Hierarchy)
63  m_HierarchyPassed = CompareHierarchy(verbose);
64 
65  if (m_TestAspects != CMP_Nothing)
66  CompareDataNodes(verbose);
67 
68  if ((m_TestAspects & CMP_Data) && !m_DataPassed)
69  ++m_AspectsFailed;
70  if ((m_TestAspects & CMP_Properties) && !m_PropertiesPassed)
71  ++m_AspectsFailed;
72  if ((m_TestAspects & CMP_Mappers) && !m_MappersPassed)
73  ++m_AspectsFailed;
74  if ((m_TestAspects & CMP_Interactors) && !m_InteractorsPassed)
75  ++m_AspectsFailed;
76 
77  if (verbose)
78  Report();
79 
80  return m_AspectsFailed == 0;
81 }
82 
84 {
85  MITK_INFO << "Comparison results:";
86  MITK_INFO << " Hierarchy comparison: "
87  << (m_TestAspects & CMP_Hierarchy ? (m_HierarchyPassed ? "pass" : "fail") : "skipped");
88  MITK_INFO << " Data comparison: "
89  << (m_TestAspects & CMP_Data ? (m_DataPassed ? "pass" : "fail") : "skipped");
90  MITK_INFO << " Properties comparison: "
91  << (m_TestAspects & CMP_Properties ? (m_PropertiesPassed ? "pass" : "fail") : "skipped");
92  MITK_INFO << " Mappers comparison: "
93  << (m_TestAspects & CMP_Mappers ? (m_MappersPassed ? "pass" : "fail") : "skipped");
94  MITK_INFO << " Interactors comparison: "
95  << (m_TestAspects & CMP_Interactors ? (m_InteractorsPassed ? "pass" : "fail") : "skipped");
96 
97  if (m_AspectsFailed == 0)
98  MITK_INFO << " Summary: ALL PASSED";
99  else
100  MITK_INFO << " Summary: " << m_AspectsFailed << " failures";
101 }
102 
103 void mitk::DataStorageCompare::DescribeHierarchyOfNodes(DataStorage::ConstPointer storage,
104  HierarchyDescriptorMap &result)
105 {
106  result.clear();
107  if (storage.IsNull())
108  return;
109 
110  mitk::DataStorage::SetOfObjects::ConstPointer allNodes = storage->GetAll();
111  for (auto node : *allNodes)
112  {
113  std::string descriptor = GenerateHierarchyDescriptor(node, storage);
114  result.insert(std::make_pair(descriptor, node));
115  }
116 }
117 
118 std::string mitk::DataStorageCompare::GenerateNodeDescriptor(mitk::DataNode::Pointer node)
119 {
120  if (node.IsNull())
121  return "nullptr";
122 
123  std::string thisDataDescriptor = "nullptr";
124  mitk::BaseData *data = node->GetData();
125  if (data != nullptr)
126  thisDataDescriptor = data->GetNameOfClass();
127 
128  std::string thisNodeName = node->GetName();
129 
130  std::string thisNodesDescriptor = std::string("_") + thisDataDescriptor + "_(" + thisNodeName + ")";
131  return thisNodesDescriptor;
132 }
133 
134 std::string mitk::DataStorageCompare::GenerateHierarchyDescriptor(mitk::DataNode::Pointer node,
136 {
137  std::string thisNodesDescriptor = GenerateNodeDescriptor(node);
139  storage->GetSources(node, nullptr, true); // direct sources without filter
140 
141  // construct descriptors for parents
142  std::vector<std::string> parentDescriptors;
143 
144  for (auto parent : *parents)
145  parentDescriptors.push_back(GenerateHierarchyDescriptor(parent, storage));
146 
147  // sort descriptors (we don't want to rely on potentially random order of parents)
148  std::sort(parentDescriptors.begin(), parentDescriptors.end());
149 
150  // construct a string from all sorted parent descriptors
151  if (!parentDescriptors.empty())
152  {
153  thisNodesDescriptor += " <(";
154  for (auto descriptor : parentDescriptors)
155  {
156  if (descriptor != parentDescriptors.front()) // join by '+'
157  {
158  thisNodesDescriptor += " + ";
159  }
160  thisNodesDescriptor += descriptor;
161  }
162  thisNodesDescriptor += ")";
163  }
164 
165  return thisNodesDescriptor;
166 }
167 
168 bool mitk::DataStorageCompare::CompareHierarchy(bool verbose)
169 {
170  int numberOfMisMatches = 0;
171 
172  // check for each reference storage entry
173  // if it can be found in test storage with
174  // an identical hierarchy descriptor.
175  // Compare just counts because there might be
176  // multiple nodes that have the same name / type / etc.
177  for (auto entry : m_RefNodesByHierarchy)
178  {
179  const std::string &key = entry.first;
180  const mitk::DataNode::Pointer &node = entry.second;
181 
182  unsigned int timesInReference = m_RefNodesByHierarchy.count(key);
183  unsigned int timesInTest = m_TestNodesByHierarchy.count(key);
184 
185  if (timesInTest != timesInReference)
186  {
187  ++numberOfMisMatches;
188  if (verbose)
189  {
190  MITK_WARN << "### Hierarchy mismatch problem";
191  MITK_WARN << " Reference storage has " << timesInReference << " node(s), test storage " << timesInTest;
192  MITK_WARN << " Node name '" << node->GetName() << "'";
193  MITK_WARN << " Reference hierarchy descriptor: " << key;
194  }
195  }
196  }
197 
198  // test also keys that are _only_ in test!
199  for (auto entry : m_TestNodesByHierarchy)
200  {
201  const std::string &key = entry.first;
202  const mitk::DataNode::Pointer &node = entry.second;
203 
204  unsigned int timesInReference = m_RefNodesByHierarchy.count(key);
205  unsigned int timesInTest = m_TestNodesByHierarchy.count(key);
206 
207  // we already tested all items in reference storage.
208  // Here we want to test additional items in test storage.
209  if (timesInTest > timesInReference)
210  {
211  ++numberOfMisMatches;
212 
213  if (verbose)
214  {
215  MITK_WARN << "### Hierarchy mismatch problem";
216  MITK_WARN << " Test storage has more nodes (" << timesInReference << ") than reference storage ("
217  << timesInTest << ")";
218  MITK_WARN << " Node name '" << node->GetName() << "'";
219  MITK_WARN << " Reference hierarchy descriptor: " << key;
220  }
221  }
222  }
223 
224  // for debug purposes we provide a dump of the test storage
225  // in error cases. This can be compared to the test case
226  // by a programmer.
227  if (verbose && numberOfMisMatches > 0)
228  {
229  MITK_WARN << "Dumping test storage because there were errors:";
230  for (auto entry : m_TestNodesByHierarchy)
231  {
232  const std::string &key = entry.first;
233  const mitk::DataNode::Pointer &node = entry.second;
234  MITK_WARN << " Test node '" << node->GetName() << "', hierarchy : " << key;
235  }
236  }
237 
238  return numberOfMisMatches == 0;
239 }
240 
241 bool mitk::DataStorageCompare::AreNodesEqual(const mitk::DataNode *reference, const mitk::DataNode *test, bool verbose)
242 {
243  if (reference == nullptr && test == nullptr)
244  return true;
245 
246  if (reference == nullptr && test != nullptr)
247  {
248  if (verbose)
249  MITK_WARN << " Reference node is nullptr, test node is not (type " << test->GetNameOfClass() << ")";
250  return false;
251  }
252 
253  if (reference != nullptr && test == nullptr)
254  {
255  if (verbose)
256  MITK_WARN << " Test node is nullptr, reference node is not (type " << reference->GetNameOfClass() << ")";
257  return false;
258  }
259 
260  if (m_TestAspects & CMP_Data)
261  m_DataPassed &= IsDataEqual(reference->GetData(), test->GetData(), verbose);
262 
263  if (m_TestAspects & CMP_Properties)
264  m_PropertiesPassed &= ArePropertyListsEqual(*reference, *test, verbose);
265 
266  if (m_TestAspects & CMP_Mappers)
267  m_MappersPassed &= AreMappersEqual(*reference, *test, verbose);
268 
269  // .. add interactors/mappers
270 
271  // two real nodes, need to really compare
272  return m_AspectsFailed == 0;
273 }
274 
275 bool mitk::DataStorageCompare::IsDataEqual(const mitk::BaseData *reference, const mitk::BaseData *test, bool verbose)
276 {
277  // early-out for nullptrs
278  if (reference == nullptr && test == nullptr)
279  return true;
280 
281  if (reference == nullptr && test != nullptr)
282  {
283  if (verbose)
284  MITK_WARN << " Reference data is nullptr, test data is not (type " << test->GetNameOfClass() << ")";
285  return false;
286  }
287 
288  if (reference != nullptr && test == nullptr)
289  {
290  if (verbose)
291  MITK_WARN << " Test data is nullptr, reference data is not (type " << reference->GetNameOfClass() << ")";
292  return false;
293  }
294 
295  // two real BaseData objects, need to really compare
296  if (reference->GetNameOfClass() != test->GetNameOfClass())
297  {
298  if (verbose)
299  MITK_WARN << " Mismatch: Reference data is '" << reference->GetNameOfClass() << "', "
300  << "test data is '" << test->GetNameOfClass() << "'";
301  return false;
302  }
303  try
304  {
305  std::string ldapFilter = std::string("(basedata=") + reference->GetNameOfClass() + "*)";
306  std::vector<us::ServiceReference<BaseDataCompare>> comparators =
307  us::GetModuleContext()->GetServiceReferences<BaseDataCompare>(ldapFilter);
308  if (comparators.empty())
309  {
310  // bad, no comparator found, cannot compare
311  MITK_ERROR << "Comparison error: no comparator for objects of type '" << reference->GetNameOfClass() << "'";
312  return false;
313  }
314  else if (comparators.size() > 1)
315  {
316  MITK_WARN << "Comparison warning: multiple comparisons possible for objects of type '"
317  << reference->GetNameOfClass() << "'. Using just one.";
318  // bad, multiple comparators, need to add ranking or something
319  }
320 
321  BaseDataCompare *comparator = us::GetModuleContext()->GetService<BaseDataCompare>(comparators.front());
322  if (!comparator)
323  {
324  MITK_ERROR << "Service lookup error, cannot get comparator for class " << reference->GetNameOfClass();
325  }
326 
327  return comparator->AreEqual(reference, test, m_Eps, verbose);
328  }
329  catch (std::exception &e)
330  {
331  MITK_ERROR << "Exception during comparison: " << e.what();
332  return false;
333  }
334 }
335 
336 bool mitk::DataStorageCompare::ArePropertyListsEqual(const mitk::DataNode &reference,
337  const mitk::DataNode &test,
338  bool verbose)
339 {
340  DataNode::PropertyListKeyNames refListNames = reference.GetPropertyListNames();
342  // add the empty names to treat all lists equally
343  refListNames.push_back("");
344  testListNames.push_back("");
345 
346  // verify that list names are identical
347  bool error = false;
348  if (refListNames.size() != testListNames.size())
349  {
350  for (auto name : refListNames)
351  if (std::find(testListNames.begin(), testListNames.end(), name) == testListNames.end())
352  {
353  MITK_WARN << "Propertylist '" << name << "' from reference node (" << reference.GetName()
354  << ") not found in test node.";
355  error = true;
356  }
357 
358  for (auto name : testListNames)
359  if (std::find(refListNames.begin(), refListNames.end(), name) == refListNames.end())
360  {
361  MITK_WARN << "Propertylist '" << name << "' did not exist in reference node (" << reference.GetName()
362  << "), but is present in test node.";
363  error = true;
364  }
365 
366  if (error)
367  return false;
368  }
369 
370  // compare each list
371  for (auto name : refListNames)
372  {
373  if (!ArePropertyListsEqual(*(reference.GetPropertyList(name)), *(test.GetPropertyList(name)), verbose))
374  {
375  MITK_WARN << "Property mismatch while comparing propertylist '" << name << "'. See messages above.";
376  error = true;
377  }
378  }
379 
380  return !error;
381 }
382 
383 bool mitk::DataStorageCompare::ArePropertyListsEqual(const mitk::PropertyList &reference,
384  const mitk::PropertyList &test,
385  bool verbose)
386 {
387  const mitk::PropertyList::PropertyMap *refMap = reference.GetMap();
388 
389  bool error = false;
390 
391  for (auto refEntry : *refMap)
392  {
393  std::string propertyKey = refEntry.first;
394  BaseProperty::Pointer refProperty = refEntry.second;
395  BaseProperty::Pointer testProperty = test.GetProperty(propertyKey);
396 
397  if (testProperty.IsNull())
398  {
399  if (verbose)
400  MITK_WARN << "Property '" << propertyKey << "' not found in test, only in reference.";
401  error = true;
402  }
403  else
404  {
405  if (!(*refProperty == *testProperty))
406  {
407  if (verbose)
408  {
409  MITK_WARN << "Property '" << propertyKey << "' does not match original.";
410  MITK_WARN << "Reference was: " << refProperty->GetValueAsString();
411  MITK_WARN << "Test was:" << testProperty->GetValueAsString();
412  }
413  error = true;
414  }
415  }
416  }
417 
418  return !error;
419 }
420 
421 bool mitk::DataStorageCompare::AreMappersEqual(const mitk::DataNode &reference,
422  const mitk::DataNode &test,
423  bool verbose)
424 {
425  bool error = false;
426 
427  mitk::Mapper *refMapper2D = reference.GetMapper(mitk::BaseRenderer::Standard2D);
429 
430  if (refMapper2D == nullptr && testMapper2D == nullptr)
431  {
432  ; // ok
433  }
434  else if (refMapper2D != nullptr && testMapper2D == nullptr)
435  {
436  if (verbose)
437  {
438  MITK_WARN << "Mapper for 2D was '" << refMapper2D->GetNameOfClass() << "' in reference, is 'nullptr"
439  << "' in test (DataNode '" << reference.GetName() << "')";
440  }
441  error = true;
442  }
443  else if (refMapper2D == nullptr && testMapper2D != nullptr)
444  {
445  if (verbose)
446  {
447  MITK_WARN << "Mapper for 2D was 'nullptr"
448  << "' in reference, is '" << testMapper2D->GetNameOfClass() << "' in test (DataNode '"
449  << reference.GetName() << "')";
450  }
451  error = true;
452  } // else both are valid pointers, we just compare the type
453  else if (refMapper2D->GetNameOfClass() != testMapper2D->GetNameOfClass())
454  {
455  if (verbose)
456  {
457  MITK_WARN << "Mapper for 2D was '" << refMapper2D->GetNameOfClass() << "' in reference, is '"
458  << testMapper2D->GetNameOfClass() << "' in test (DataNode '" << reference.GetName() << "')";
459  }
460  error = true;
461  }
462 
463  mitk::Mapper *refMapper3D = reference.GetMapper(mitk::BaseRenderer::Standard3D);
465 
466  if (refMapper3D == nullptr && testMapper3D == nullptr)
467  {
468  ; // ok
469  }
470  else if (refMapper3D != nullptr && testMapper3D == nullptr)
471  {
472  if (verbose)
473  {
474  MITK_WARN << "Mapper for 3D was '" << refMapper3D->GetNameOfClass() << "' in reference, is 'nullptr"
475  << "' in test (DataNode '" << reference.GetName() << "')";
476  }
477  error = true;
478  }
479  else if (refMapper3D == nullptr && testMapper3D != nullptr)
480  {
481  if (verbose)
482  {
483  MITK_WARN << "Mapper for 3D was 'nullptr"
484  << "' in reference, is '" << testMapper3D->GetNameOfClass() << "' in test (DataNode '"
485  << reference.GetName() << "')";
486  }
487  error = true;
488  } // else both are valid pointers, we just compare the type
489  else if (refMapper3D->GetNameOfClass() != testMapper3D->GetNameOfClass())
490  {
491  if (verbose)
492  {
493  MITK_WARN << "Mapper for 3D was '" << refMapper3D->GetNameOfClass() << "' in reference, is '"
494  << testMapper3D->GetNameOfClass() << "' in test (DataNode '" << reference.GetName() << "')";
495  }
496  error = true;
497  }
498 
499  return !error;
500 }
501 
502 bool mitk::DataStorageCompare::CompareDataNodes(bool verbose)
503 {
504  int numberOfMisMatches = 0;
505 
506  for (auto entry : m_RefNodesByHierarchy)
507  {
508  const std::string &key = entry.first;
509  const mitk::DataNode::Pointer &refNode = entry.second;
510 
511  unsigned int timesInReference = m_RefNodesByHierarchy.count(key);
512  unsigned int timesInTest = m_TestNodesByHierarchy.count(key);
513 
514  if (timesInReference == 1 && timesInTest == 1)
515  {
516  // go on an compare those two
517  auto testEntry = m_TestNodesByHierarchy.find(key);
518  mitk::DataNode::Pointer testNode = testEntry->second;
519  if (!AreNodesEqual(refNode, testNode, verbose))
520  {
521  ++numberOfMisMatches;
522  if (verbose)
523  {
524  MITK_WARN << "### DataNode mismatch problem";
525  MITK_WARN << " Node '" << key << "' did not compare to equal (see warnings above).";
526  }
527  }
528  }
529  else
530  {
531  ++numberOfMisMatches;
532  if (verbose)
533  {
534  MITK_WARN << "### DataNode mismatch problem";
535  MITK_WARN << " Reference storage has " << timesInReference << " node(s), test storage " << timesInTest;
536  MITK_WARN << " This does not match or we don't know how to figure out comparison partners";
537  }
538  }
539  }
540 
541  return numberOfMisMatches == 0;
542 }
mitk::PropertyList * GetPropertyList(const mitk::BaseRenderer *renderer=nullptr) const
Get the PropertyList of the renderer. If renderer is NULL, the BaseRenderer-independent PropertyList ...
mitk::BaseProperty * GetProperty(const std::string &propertyKey) const
Get a property by its name.
Data management class that handles 'was created by' relations.
#define MITK_INFO
Definition: mitkLogMacros.h:22
std::vector< MapOfPropertyLists::key_type > PropertyListKeyNames
Definition: mitkDataNode.h:72
Base of all data objects.
Definition: mitkBaseData.h:39
#define MITK_ERROR
Definition: mitkLogMacros.h:24
bool CompareVerbose()
Shorthand for Compare(true).
Follow Up Storage - Class to facilitate loading/accessing structured follow-up data.
Definition: testcase.h:32
bool GetName(std::string &nodeName, const mitk::BaseRenderer *renderer=nullptr, const char *propertyKey="name") const
Convenience access method for accessing the name of an object (instance of StringProperty with proper...
Definition: mitkDataNode.h:366
BaseData * GetData() const
Get the data object (instance of BaseData, e.g., an Image) managed by this DataNode.
itk::SmartPointer< Self > Pointer
Key-value list holding instances of BaseProperty.
DataStorageCompare(const DataStorage *reference, const DataStorage *test, Tests flags=CMP_All, double eps=mitk::eps)
Constructor taking reference and test DataStorage.
Base class of all mappers, Vtk as well as OpenGL mappers.
Definition: mitkMapper.h:54
void * GetService(const ServiceReferenceBase &reference)
itk::SmartPointer< const Self > ConstPointer
PropertyListKeyNames GetPropertyListNames() const
The "names" used for (renderer-specific) PropertyLists in GetPropertyList(string).
#define MITK_WARN
Definition: mitkLogMacros.h:23
std::map< std::string, BaseProperty::Pointer > PropertyMap
bool Compare(bool verbose=false)
Execute the comparison.
std::vector< ServiceReferenceU > GetServiceReferences(const std::string &clazz, const std::string &filter=std::string())
static void RegisterCoreEquals()
Register core type comparators that come with mitk::Equal() functions.
mitk::Mapper * GetMapper(MapperSlotId id) const
Tests
Flag describing the aspects of comparing two DataStorages.
MITKCORE_EXPORT const ScalarType eps
static ModuleContext * GetModuleContext()
Returns the module context of the calling module.
void Report()
Prints a small summary of what tests have been executed and which ones failed or passed.
Class for nodes of the DataTree.
Definition: mitkDataNode.h:66
const PropertyMap * GetMap() const