IODA Bundle
Comparator.cc
Go to the documentation of this file.
1 /*
2  * (C) Copyright 1996-2012 ECMWF.
3  *
4  * This software is licensed under the terms of the Apache Licence Version 2.0
5  * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
6  * In applying this licence, ECMWF does not waive the privileges and immunities
7  * granted to it by virtue of its status as an intergovernmental organisation nor
8  * does it submit to any jurisdiction.
9  */
10 
11 #include "eckit/config/Resource.h"
12 #include "eckit/exception/Exceptions.h"
13 #include "eckit/filesystem/PathName.h"
14 
15 #include "odc/Comparator.h"
16 #include "odc/core/Column.h"
17 #include "odc/core/MetaData.h"
18 #include "odc/Reader.h"
19 #include "odc/StringTool.h"
20 #include "odc/utility/Tracer.h"
21 
22 #include <string.h>
23 
24 #if __cplusplus >= 199711L
25 #define isnan(x) std::isnan(x)
26 #endif
27 
28 using namespace std;
29 using namespace eckit;
30 using namespace odc::api;
31 using namespace odc::utility;
32 using namespace odc::core;
33 
34 class ValuesDifferent : public Exception {
35 public:
36  ValuesDifferent(const std::string& what) : Exception(what) {}
37 };
38 
39 namespace odc {
40 
41 Comparator::Comparator(bool checkMissingFlag)
42 : nRow_(0),
43  checkMissingFlag_(checkMissingFlag),
44  NaN_isOK_(Resource<bool>("$odc_NAN_IS_OK", false))
45 {}
46 
47 
48 void Comparator::compare(const PathName& p1, const PathName& p2)
49 {
50  std::vector<std::string> noExcludedColumnTypes;
51  std::vector<std::string> noExcludedColumns;
52  compare(p1, p2, noExcludedColumnTypes, noExcludedColumns);
53 }
54 
55 void Comparator::compare(eckit::DataHandle& l, eckit::DataHandle& r)
56 {
57  std::vector<std::string> noExcludedColumnTypes;
58  std::vector<std::string> noExcludedColumns;
59  odc::Reader oda1(l);
60  odc::Reader oda2(r);
61 
62  odc::Reader::iterator it1(oda1.begin());
63  odc::Reader::iterator end1(oda1.end());
64  odc::Reader::iterator it2(oda2.begin());
65  odc::Reader::iterator end2(oda2.end());
66 
67  compare(it1, end1, it2, end2, "left", "right", noExcludedColumnTypes, noExcludedColumns);
68 }
69 
70 void Comparator::compare(const PathName& p1, const PathName& p2,
71  const std::vector<std::string>& excludedColumnsTypes,
72  const std::vector<std::string>& excludedColumns)
73 {
74  Tracer t(Log::debug(), std::string("Comparator::compare: ") + p1 + ", " + p2);
75 
76  odc::Reader oda1(p1);
77  odc::Reader oda2(p2);
78 
79  odc::Reader::iterator it1(oda1.begin());
80  odc::Reader::iterator end1(oda1.end());
81  odc::Reader::iterator it2(oda2.begin());
82  odc::Reader::iterator end2(oda2.end());
83 
84  compare(it1, end1, it2, end2, p1, p2, excludedColumnsTypes, excludedColumns);
85 }
86 
87 void Comparator::raiseNotEqual(const Column& column, double d1, double d2) {
88  ColumnType type(column.type());
89  stringstream ss;
90  ss << "Values different in column " << column.name() << ": "
91  << StringTool::valueAsString(d1, type) << " is not equal " << StringTool::valueAsString(d2, type) << endl;
92  throw ValuesDifferent(ss.str());
93 }
94 
95 void Comparator::compare(int nCols,
96  const double *data1,
97  const double *data2,
98  const MetaData& md1,
99  const MetaData& md2) {
100 
101  std::vector<int> skipColsUnused;
102  compare(nCols, data1, data2, md1, md2, skipColsUnused);
103 }
104 
105 void Comparator::compare(int nCols,
106  const double *data1,
107  const double *data2,
108  const MetaData& md1,
109  const MetaData& md2,
110  const std::vector<int>& skipCols) {
111 
112  std::vector<int>::const_iterator nextSkipCol = skipCols.begin();
113 
114  const double* pdata1 = data1;
115  const double* pdata2 = data2;
116 
117  unsigned long long numberOfDifferences (0);
118  for (int i=0; i < nCols; i++) {
119 
120  // Skip the specified columns
121  if (nextSkipCol != skipCols.end() && (*nextSkipCol) == i) {
122  ++nextSkipCol;
123  pdata1 += md1[i]->dataSizeDoubles();
124  pdata2 += md2[i]->dataSizeDoubles();
125  continue;
126  }
127 
128  try {
129  const Column& column(*md1[i]);
130  const Column& column2(*md2[i]);
131  ColumnType type(column.type());
132 
133  switch (type)
134  {
135  case STRING: {
136 
137  size_t width1 = column.dataSizeDoubles() * sizeof(double);
138  size_t width2 = column2.dataSizeDoubles() * sizeof(double);
139 
140  size_t len1 = ::strnlen(reinterpret_cast<const char*>(pdata1), width1);
141  size_t len2 = ::strnlen(reinterpret_cast<const char*>(pdata2), width2);
142  if (len1 != len2 ||
143  ::strncmp(reinterpret_cast<const char*>(pdata1), reinterpret_cast<const char*>(pdata2), len1)) {
144 
145  std::ostringstream ss;
146  ss << "String values differ in column " << column.name() << ": "
147  << std::string(reinterpret_cast<const char*>(pdata1), len1) << " is not equal to "
148  << std::string(reinterpret_cast<const char*>(pdata2), len2) << std::endl;
149  throw ValuesDifferent(ss.str());
150  }
151  break;
152  }
153  case INTEGER:
154  case BITFIELD:
155  case DOUBLE:
156  if (! (same(*pdata1, *pdata2) || (NaN_isOK_ && (::isnan(*pdata1) && ::isnan(*pdata2)))))
157  raiseNotEqual(column, *pdata1, *pdata2);
158  break;
159  case REAL:
160  if (! (same(float(*pdata1), float(*pdata2)) || (NaN_isOK_ && (::isnan(*pdata1) && ::isnan(*pdata2)))))
161  raiseNotEqual(column, *pdata1, *pdata2);
162  break;
163  case IGNORE:
164  default:
165  ASSERT(!"Unknown type");
166  break;
167  }
168  } catch (Exception &e) {
169  ++numberOfDifferences;
170  Log::info() << "While comparing rows number " << nRow_ << ", columns " << i
171  << " found different." << std::endl;
172  Log::info() << " " << e.what() << std::endl;
173 
174  Log::info() << " data1[" << i << "] = " << std::scientific << *pdata1 << std::endl;
175  Log::info() << " data2[" << i << "] = " << std::scientific << *pdata2 << std::endl;
176 
177  Log::info() << " md1[" << i << "] = " << *md1[i] << std::endl;
178  Log::info() << " md2[" << i << "] = " << *md2[i] << std::endl;
179 
180  //TODO: make it an option to stop when an error found
181  //throw;
182  }
183 
184  pdata1 += md1[i]->dataSizeDoubles();
185  pdata2 += md2[i]->dataSizeDoubles();
186  }
187 
188  if (numberOfDifferences)
189  {
190  stringstream ss;
191  ss << "Files differ. "; // << numberOfDifferences << " difference" << ((numberOfDifferences == 1) ? "" : "s") << " found.";
192  throw Exception(ss.str());
193  }
194 }
195 
196 
197 void Comparator::compare(const MetaData& metaData1, const MetaData& metaData2,
198  const std::set<std::string>& excludedColumnsTypes,
199  const std::set<std::string>& excludedColumns,
200  std::vector<int>& skipCols) {
201 
202  ASSERT("Number of columns must be the same" && (metaData1.size() == metaData2.size()));
203 
204  // We keep track of which columns are skipped in this routine.
205  skipCols.clear();
206 
207  size_t size = metaData1.size();
208  for (size_t i = 0; i < size; i++)
209  {
210  Column &column1 = *metaData1[i];
211  Column &column2 = *metaData2[i];
212 
213  try {
214  ASSERT(column1.name() == column2.name());
215 
216  // If we are skipping a column, then we should check nothing for it.
217  if (excludedColumns.find(column1.name()) != excludedColumns.end()) {
218  skipCols.push_back(i);
219  continue;
220  }
221 
222  if (excludedColumnsTypes.find(column1.name()) == excludedColumnsTypes.end())
223  {
224  ASSERT(column1.type() == column2.type());
225 
226  if (column1.type() == BITFIELD)
227  if (! (column1.bitfieldDef() == column2.bitfieldDef()))
228  {
229  Log::error() << "Comparator::compare: bitfield definitions for column "
230  << i << " '" << column1.name() << "' differ." << std::endl;
231  ASSERT(column1.bitfieldDef() == column2.bitfieldDef());
232  }
233  }
234 
235  if (checkMissingFlag_)
236  {
237  if (column1.missingValue() != column2.missingValue())
238  {
239  Log::warning() << column1.name() << " : "
240  << "column1.missingValue()=" << column1.missingValue() << ", "
241  << "column2.missingValue()=" << column2.missingValue() << ", "
242  << std::endl;
243  ASSERT(column1.missingValue() == column2.missingValue());
244  }
245  }
246  else
247  {
248  if (column1.missingValue() != column2.missingValue())
249  {
250  Log::warning() << column1.name() << " : "
251  << "column1.missingValue()=" << column1.missingValue() << ", "
252  << "column2.missingValue()=" << column2.missingValue() << ", "
253  << std::endl;
254  }
255  }
256 
257  if (column1.hasMissing() && column2.hasMissing())
258  ASSERT(column1.missingValue() == column2.missingValue());
259  } catch (...) {
260  Log::info() << "While comparing column " << i << ": "
261  << column1.name() << std::endl;
262  throw;
263  }
264  }
265 }
266 
267 } // namespace odc
268 
ValuesDifferent(const std::string &what)
Definition: Comparator.cc:36
bool compare(T1 &it1, const T1 &end1, T2 &it2, const T2 &end2, const std::string &desc1, const std::string &desc2)
Definition: Comparator.h:108
void raiseNotEqual(const core::Column &, double, double)
Definition: Comparator.cc:87
static int same(double A, double B)
Definition: Comparator.h:97
bool checkMissingFlag_
Definition: Comparator.h:103
const iterator end() const
Definition: Reader.cc:81
iterator begin()
Definition: Reader.cc:74
static std::string valueAsString(double, api::ColumnType)
Definition: StringTool.cc:163
void bitfieldDef(const eckit::sql::BitfieldDef &b)
Definition: Column.h:85
void hasMissing(bool h)
Delegations to Codec:
Definition: Column.h:71
size_t dataSizeDoubles() const
Definition: Column.h:54
void missingValue(double v)
Definition: Column.h:80
void name(const std::string name)
Definition: Column.h:57
static api::ColumnType type(const std::string &)
Definition: Column.cc:74
@ BITFIELD
Definition: ColumnType.h:27
Definition: ColumnInfo.h:23
Definition: encode.cc:30