IODA Bundle
test_codecs_end_to_end.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/io/Buffer.h"
12 #include "eckit/io/DataHandle.h"
13 #include "eckit/io/MemoryHandle.h"
14 #include "eckit/testing/Test.h"
15 
16 #include "odc/core/MetaData.h"
17 #include "odc/Reader.h"
18 #include "odc/Writer.h"
19 #include "odc/tools/MockReader.h"
20 #include "odc/codec/Integer.h"
21 #include "odc/codec/String.h"
22 
23 using namespace eckit::testing;
24 
25 // Constant codecs are a little different from the others, as they can store multiple
26 // different types within the same codec...
27 
28 
29 /// Encoding/decoding using codecs and the reader/writer are tested elsewhere.
30 /// This file is for miscelaneous tests, in case the edge cases elsewhere are insufficient.
31 ///
32 /// @note This is mainly SDS being paranoid about removing apparently duplicate tests when restructuring
33 
34 // ------------------------------------------------------------------------------------------------------
35 
36 namespace {
37 
38  // This looks-like a read-iterator. It isn't, but that doesn't matter!
39 
40  const int num_rows_to_write = 10;
41 
43  public:
45  columns_(1),
46  type_(type),
47  data_(data),
48  nRows_(num_rows_to_write),
49  refCount_(0),
50  noMore_(false) {
51 
52  columns_[0] = new odc::core::Column(columns_);
53  ASSERT(columns_[0]);
54 
55  columns_[0]->name("a-col");
56  columns_[0]->type<odc::core::SameByteOrder>(type_);
57  columns_[0]->hasMissing(false);
58  }
59 
60  odc::core::MetaData& columns() { return columns_; }
61  bool isNewDataset() { return false; }
62  double* data() { return &data_; }
63 
64  bool next() {
65  if (nRows_ == 0) return false;
66  nRows_--;
67  if (nRows_ == 0) noMore_ = true;
68  return true;
69  }
70 
71  protected:
74  double data_;
75  int nRows_;
76 
77  public: // Required for IteratorProxy
78  int refCount_;
79  bool noMore_;
80  };
81 
82  // n.b. Cannot use local classes as template arguments until c++11, so declare it here.
83 
84  // A constant integer value ...
85 
86  const long the_const_value = 20090624;
87 
90  columns_[0]->coder(std::unique_ptr<odc::core::Codec>(new odc::codec::CodecInt32<odc::core::SameByteOrder, double>(odc::api::INTEGER)));
91  }
92  };
93 
94  // A constant string value (the full 8 bytes)
95 
96  const char* const_string_1 = "a-string";
97 
99  MockReadIteratorConstString1() : MockReadIterator(odc::api::STRING, *reinterpret_cast<const double*>(const_string_1)) {
100  columns_[0]->coder(std::unique_ptr<odc::core::Codec>(new odc::codec::CodecChars<odc::core::SameByteOrder>(odc::api::STRING)));
101  }
102  };
103 }
104 
105 
106 CASE("The constant integer codec stores a constant integer") {
107 
108  // Construct the encoded stuff
109 
110  eckit::Buffer buf(4096);
111 
112  eckit::MemoryHandle writeDH(buf);
113 
114  {
115  odc::Writer<> oda(writeDH);
116  odc::Writer<>::iterator outit = oda.begin();
117 
119  outit->pass1(reader.begin(), reader.end());
120  }
121 
122  // And test that this decodes correctly
123 
124  {
125  eckit::MemoryHandle dh(buf.data(), static_cast<size_t>(writeDH.position()));
126  dh.openForRead();
127  odc::Reader oda(dh);
128 
129  odc::Reader::iterator it = oda.begin();
130  odc::Reader::iterator end = oda.end();
131 
132  EXPECT(it->columns()[0]->name() == "a-col");
133 
134  size_t count = 0;
135  for ( ; it != end; ++it) {
136  EXPECT(static_cast<long>((*it)[0]) == the_const_value);
137  EXPECT((*it)[0] == static_cast<double>(the_const_value));
138  count++;
139  }
140 
141  EXPECT(count == num_rows_to_write);
142 
143  // Check that this has used the constant codec.
144  EXPECT(it->columns()[0]->coder().name() == "constant");
145  EXPECT(it->columns()[0]->type() == odc::api::INTEGER);
146  }
147 }
148 
149 
150 CASE("The constant codec can also store strings") {
151 
152  // Construct the encoded stuff
153 
154  eckit::Buffer buf(4096);
155 
156  eckit::MemoryHandle writeDH(buf);
157 
158  {
159  odc::Writer<> oda(writeDH);
160  odc::Writer<>::iterator outit = oda.begin();
161 
163  outit->pass1(reader.begin(), reader.end());
164  }
165 
166  // And test that this decodes correctly
167 
168  {
169  eckit::MemoryHandle dh(buf.data(), static_cast<size_t>(writeDH.position()));
170  dh.openForRead();
171  odc::Reader oda(dh);
172 
173  odc::Reader::iterator it = oda.begin();
174  odc::Reader::iterator end = oda.end();
175 
176  EXPECT(it->columns()[0]->name() == "a-col");
177 
178  size_t count = 0;
179  for ( ; it != end; ++it) {
180  double val = (*it)[0];
181  EXPECT(::memcmp(const_string_1, &val, sizeof(val)) == 0);
182  count++;
183  }
184 
185  EXPECT(count == num_rows_to_write);
186 
187  // Check that this has used the constant codec.
188  EXPECT(it->columns()[0]->coder().name() == "constant_string");
189  EXPECT(it->columns()[0]->type() == odc::api::STRING);
190  }
191 }
192 
193 
194 CASE("The constant codec can also store doubles") {
195 
196  const double constant_value = -987654321.4321e-34;
197 
198  // Don't use the pass1 mechanism here. Build a writer explicitly to show that we can
199 
201 
202  for (size_t i = 0; i < 2; i++) {
203 
204  odc::api::ColumnType type = types[i];
205 
206  // Construct the encoded stuff
207 
208  eckit::Buffer buf(4096);
209 
210  eckit::MemoryHandle writeDH(buf);
211 
212  {
213  odc::Writer<> oda(writeDH);
214  odc::Writer<>::iterator outit = oda.begin();
215 
216  outit->setNumberOfColumns(1);
217  outit->setColumn(0, "abcdefg", type);
218  outit->writeHeader();
219 
220  for (size_t i = 0; i < num_rows_to_write; i++) {
221  (*outit)[0] = constant_value;
222  ++outit;
223  }
224  }
225 
226  // And test that this decodes correctly
227 
228  {
229  eckit::MemoryHandle dh(buf.data(), static_cast<size_t>(writeDH.position()));
230  dh.openForRead();
231  odc::Reader oda(dh);
232 
233  odc::Reader::iterator it = oda.begin();
234  odc::Reader::iterator end = oda.end();
235 
236  EXPECT(it->columns()[0]->name() == "abcdefg");
237 
238  size_t count = 0;
239  for ( ; it != end; ++it) {
240  double val = (*it)[0];
241  EXPECT(val == constant_value);
242  count++;
243  }
244 
245  EXPECT(count == num_rows_to_write);
246 
247  // Check that this has used the constant codec.
248  EXPECT(it->columns()[0]->coder().name() == "constant");
249  EXPECT(it->columns()[0]->type() == type);
250  }
251  }
252 }
253 
254 
255 CASE("Missing values are encoded and decoded correctly") {
256 
257  // Create a mapping between the codecs, their associated missing values, and encoded data sizes
258 
259  typedef std::map<std::string, std::pair<double, int> > MapType;
260  MapType codec_value_map;
261 
262  codec_value_map["short_real"] = std::make_pair(odc::MDI::realMDI(), sizeof(float));
263  codec_value_map["short_real2"] = std::make_pair(odc::MDI::realMDI(), sizeof(float));
264  codec_value_map["long_real"] = std::make_pair(odc::MDI::realMDI(), sizeof(double));
265  codec_value_map["int8_missing"] = std::make_pair(odc::MDI::integerMDI(), sizeof(int8_t));
266  codec_value_map["int16_missing"] = std::make_pair(odc::MDI::integerMDI(), sizeof(int16_t));
267 
268  for (MapType::const_iterator it = codec_value_map.begin(); it != codec_value_map.end(); ++it) {
269 
270  const std::string& codec_name(it->first);
271  double missing_value = it->second.first;
272  int encoded_size = it->second.second;
273 
274  // Get the appropriate codec
275 
276  std::unique_ptr<odc::core::Codec> c(odc::core::CodecFactory::instance().build<odc::core::SameByteOrder>(codec_name, odc::api::DOUBLE));
277 
278  EXPECT(c->name() == codec_name);
279 
280  // Write data into a buffer
281 
282  unsigned char buffer[256];
283 
284  unsigned char* next_pos = c->encode(buffer, missing_value);
285 
286  EXPECT((next_pos - buffer) == encoded_size);
287 
288  // And check that we can decode it again!
289 
290  odc::core::DataStream<odc::core::SameByteOrder> ds(buffer, sizeof(buffer));
291  c->setDataStream(ds);
292 
293  double decoded;
294  c->decode(&decoded);
295 
296  ASSERT(ds.position() == eckit::Offset(encoded_size));
297  ASSERT(decoded == missing_value);
298  }
299 }
300 
301 
302 // ------------------------------------------------------------------------------------------------------
303 
304 int main(int argc, char* argv[]) {
305  return run_tests(argc, argv);
306 }
307 
void oda
static void count(void *counter, const double *data, size_t n)
Definition: UnitTests.cc:531
static double realMDI()
Definition: MDI.h:21
static double integerMDI()
Definition: MDI.h:22
void writeHeader()
void setNumberOfColumns(size_t n)
Definition: IteratorProxy.h:95
unsigned long pass1(T b, const T e)
const core::MetaData & columns() const
Definition: IteratorProxy.h:94
int setColumn(size_t index, const std::string &name, api::ColumnType type)
static CodecFactory & instance()
Definition: CodecFactory.cc:25
eckit::Offset position() const
Definition: DataStream.h:198
const iterator end()
Definition: MockReader.h:27
Definition: ColumnInfo.h:23
int main(int argc, char *argv[])
CASE("The constant integer codec stores a constant integer")