HorizonDatabasePlugin  0.2.0
SOCI wrapper for UE4(beta)
common-tests.h
Go to the documentation of this file.
1 //
2 // Copyright (C) 2004-2008 Maciej Sobczak, Stephen Hutton
3 // Distributed under the Boost Software License, Version 1.0.
4 // (See accompanying file LICENSE_1_0.txt or copy at
5 // http://www.boost.org/LICENSE_1_0.txt)
6 //
7 
8 #ifndef SOCI_COMMON_TESTS_H_INCLUDED
9 #define SOCI_COMMON_TESTS_H_INCLUDED
10 
11 #include "soci/soci.h"
12 
13 #ifdef HAVE_BOOST
14 // explicitly pull conversions for Boost's optional, tuple and fusion:
15 #include <boost/version.hpp>
16 #include "soci/boost-optional.h"
17 #include "soci/boost-tuple.h"
18 #include "soci/boost-gregorian-date.h"
19 #if defined(BOOST_VERSION) && BOOST_VERSION >= 103500
20 #include "soci/boost-fusion.h"
21 #endif // BOOST_VERSION
22 #endif // HAVE_BOOST
23 
24 //#include "soci-compiler.h"
25 
26 #define CATCH_CONFIG_RUNNER
27 //#include <catch.hpp>
28 
29 #include <algorithm>
30 #include <cassert>
31 #include <clocale>
32 #include <cstdlib>
33 #include <cmath>
34 #include <iomanip>
35 #include <iostream>
36 #include <limits>
37 #include <string>
38 #include <typeinfo>
39 
40 // Although SQL standard mandates right padding CHAR(N) values to their length
41 // with spaces, some backends don't confirm to it:
42 //
43 // - Firebird does pad the string but to the byte-size (not character size) of
44 // the column (i.e. CHAR(10) NONE is padded to 10 bytes but CHAR(10) UTF8 --
45 // to 40).
46 // - For MySql PAD_CHAR_TO_FULL_LENGTH option must be set, otherwise the value
47 // is trimmed.
48 // - SQLite never behaves correctly at all.
49 //
50 // This method will check result string from column defined as fixed char It
51 // will check only bytes up to the original string size. If padded string is
52 // bigger than expected string then all remaining chars must be spaces so if
53 // any non-space character is found it will fail.
54 void
55 checkEqualPadded(const std::string& padded_str, const std::string& expected_str)
56 {
57  size_t const len = expected_str.length();
58  std::string const start_str(padded_str, 0, len);
59 
60  if (start_str != expected_str)
61  {
62  throw soci::soci_error(
63  "Expected string \"" + expected_str + "\" "
64  "is different from the padded string \"" + padded_str + "\""
65  );
66  }
67 
68  if (padded_str.length() > len)
69  {
70  std::string const end_str(padded_str, len);
71  if (end_str != std::string(padded_str.length() - len, ' '))
72  {
73  throw soci::soci_error(
74  "\"" + padded_str + "\" starts with \"" + padded_str +
75  "\" but non-space characater(s) are found aftewards"
76  );
77  }
78  }
79 }
80 
81 #define CHECK_EQUAL_PADDED(padded_str, expected_str) \
82  CHECK_NOTHROW(checkEqualPadded(padded_str, expected_str));
83 
84 // Objects used later in tests 14,15
86 {
87  std::string name;
88  std::string phone;
89 };
90 
92 {
93 };
94 
96 {
97 public:
98  void setName(std::string const & n) { name_ = n; }
99  std::string getName() const { return name_; }
100 
101  void setPhone(std::string const & p) { phone_ = p; }
102  std::string getPhone() const { return phone_; }
103 
104 public:
105  std::string name_;
106  std::string phone_;
107 };
108 
109 // user-defined object for test26 and test28
110 class MyInt
111 {
112 public:
113  MyInt() : i_() {}
114  MyInt(int i) : i_(i) {}
115  void set(int i) { i_ = i; }
116  int get() const { return i_; }
117 private:
118  int i_;
119 };
120 
121 namespace soci
122 {
123 
124 // basic type conversion for user-defined type with single base value
125 template<> struct type_conversion<MyInt>
126 {
127  typedef int base_type;
128 
129  static void from_base(int i, indicator ind, MyInt &mi)
130  {
131  if (ind == i_ok)
132  {
133  mi.set(i);
134  }
135  }
136 
137  static void to_base(MyInt const &mi, int &i, indicator &ind)
138  {
139  i = mi.get();
140  ind = i_ok;
141  }
142 };
143 
144 // basic type conversion on many values (ORM)
145 template<> struct type_conversion<PhonebookEntry>
146 {
147  typedef soci::values base_type;
148 
149  static void from_base(values const &v, indicator /* ind */, PhonebookEntry &pe)
150  {
151  // here we ignore the possibility the the whole object might be NULL
152  pe.name = v.get<std::string>("NAME");
153  pe.phone = v.get<std::string>("PHONE", "<NULL>");
154  }
155 
156  static void to_base(PhonebookEntry const &pe, values &v, indicator &ind)
157  {
158  v.set("NAME", pe.name);
159  v.set("PHONE", pe.phone, pe.phone.empty() ? i_null : i_ok);
160  ind = i_ok;
161  }
162 };
163 
164 // type conversion which directly calls values::get_indicator()
165 template<> struct type_conversion<PhonebookEntry2>
166 {
167  typedef soci::values base_type;
168 
169  static void from_base(values const &v, indicator /* ind */, PhonebookEntry2 &pe)
170  {
171  // here we ignore the possibility the the whole object might be NULL
172 
173  pe.name = v.get<std::string>("NAME");
174  indicator ind = v.get_indicator("PHONE"); //another way to test for null
175  pe.phone = ind == i_null ? "<NULL>" : v.get<std::string>("PHONE");
176  }
177 
178  static void to_base(PhonebookEntry2 const &pe, values &v, indicator &ind)
179  {
180  v.set("NAME", pe.name);
181  v.set("PHONE", pe.phone, pe.phone.empty() ? i_null : i_ok);
182  ind = i_ok;
183  }
184 };
185 
186 template<> struct type_conversion<PhonebookEntry3>
187 {
188  typedef soci::values base_type;
189 
190  static void from_base(values const &v, indicator /* ind */, PhonebookEntry3 &pe)
191  {
192  // here we ignore the possibility the the whole object might be NULL
193 
194  pe.setName(v.get<std::string>("NAME"));
195  pe.setPhone(v.get<std::string>("PHONE", "<NULL>"));
196  }
197 
198  static void to_base(PhonebookEntry3 const &pe, values &v, indicator &ind)
199  {
200  v.set("NAME", pe.getName());
201  v.set("PHONE", pe.getPhone(), pe.getPhone().empty() ? i_null : i_ok);
202  ind = i_ok;
203  }
204 };
205 
206 } // namespace soci
207 
208 namespace soci
209 {
210 namespace tests
211 {
212 
213 // TODO: improve cleanup capabilities by subtypes, soci_test name may be omitted --mloskot
214 // i.e. optional ctor param accepting custom table name
216 {
217 public:
218  table_creator_base(session& sql)
219  : msession(sql) { drop(); }
220 
221  virtual ~table_creator_base() { drop();}
222 private:
223  void drop()
224  {
225  try
226  {
227  msession << "drop table if exists soci_test";
228  }
229  catch (soci_error const& e)
230  {
231  //std::cerr << e.what() << std::endl;
232  e.what();
233  }
234  }
235  session& msession;
236 
237  SOCI_NOT_COPYABLE(table_creator_base)
238 };
239 
241 {
242 public:
244  : msession(sql) { drop(); }
245 
246  virtual ~procedure_creator_base() { drop();}
247 private:
248  void drop()
249  {
250  try { msession << "drop procedure soci_test"; } catch (soci_error&) {}
251  }
252  session& msession;
253 
254  SOCI_NOT_COPYABLE(procedure_creator_base)
255 };
256 
258 {
259 public:
260  function_creator_base(session& sql)
261  : msession(sql) { drop(); }
262 
263  virtual ~function_creator_base() { drop();}
264 
265 protected:
266  virtual std::string dropstatement()
267  {
268  return "drop function soci_test";
269  }
270 
271 private:
272  void drop()
273  {
274  try { msession << dropstatement(); } catch (soci_error&) {}
275  }
276  session& msession;
277 
278  SOCI_NOT_COPYABLE(function_creator_base)
279 };
280 
281 // This is a singleton class, at any given time there is at most one test
282 // context alive and common_tests fixture class uses it.
284 {
285 public:
286  test_context_base(backend_factory const &backEnd,
287  std::string const &connectString)
288  : backEndFactory_(backEnd),
289  connectString_(connectString)
290  {
291  // This can't be a CHECK() because the test context is constructed
292  // outside of any test.
293  assert(!the_test_context_);
294 
295  the_test_context_ = this;
296 
297  // To allow running tests in non-default ("C") locale, the following
298  // environment variable can be set and then the current default locale
299  // (which can itself be changed by setting LC_ALL environment variable)
300  // will then be used.
301  if (std::getenv("SOCI_TEST_USE_LC_ALL"))
302  std::setlocale(LC_ALL, "");
303  }
304 
306  {
307  // REQUIRE(the_test_context_);
308 
309  return *the_test_context_;
310  }
311 
312  backend_factory const & get_backend_factory() const
313  {
314  return backEndFactory_;
315  }
316 
317  std::string get_connect_string() const
318  {
319  return connectString_;
320  }
321 
322  virtual std::string to_date_time(std::string const &dateTime) const = 0;
323 
324  virtual table_creator_base* table_creator_1(session&) const = 0;
325  virtual table_creator_base* table_creator_2(session&) const = 0;
326  virtual table_creator_base* table_creator_3(session&) const = 0;
327  virtual table_creator_base* table_creator_4(session&) const = 0;
328 
329  // Override this if the backend doesn't handle floating point values
330  // correctly, i.e. writing a value and reading it back doesn't return
331  // *exactly* the same value.
332  virtual bool has_fp_bug() const { return false; }
333 
334  // Override this if the backend doesn't handle multiple active select
335  // statements at the same time, i.e. a result set must be entirely consumed
336  // before creating a new one (this is the case of MS SQL without MARS).
337  virtual bool has_multiple_select_bug() const { return false; }
338 
339  // Override this if the backend may not have transactions support.
340  virtual bool has_transactions_support(session&) const { return true; }
341 
342  // Override this if the backend silently truncates string values too long
343  // to fit by default.
344  virtual bool has_silent_truncate_bug(session&) const { return false; }
345 
346  // Override this to call commit() if it's necessary for the DDL statements
347  // to be taken into account (currently this is only the case for Firebird).
348  virtual void on_after_ddl(session&) const { }
349 
350  // Put the database in SQL-complient mode for CHAR(N) values, return false
351  // if it's impossible, i.e. if the database doesn't behave correctly
352  // whatever we do.
353  virtual bool enable_std_char_padding(session&) const { return true; }
354 
356  {
357  the_test_context_ = NULL;
358  }
359 
360 private:
361  backend_factory const &backEndFactory_;
362  std::string const connectString_;
363 
364  static test_context_base* the_test_context_;
365 
366  SOCI_NOT_COPYABLE(test_context_base)
367 };
368 
369 // Currently all tests consist of just a single source file, so we can define
370 // this member here because this header is included exactly once.
371 tests::test_context_base* tests::test_context_base::the_test_context_ = NULL;
372 
373 
374 // Compare doubles for approximate equality. This has to be used everywhere
375 // where we write "3.14" (or "6.28") to the database as a string and then
376 // compare the value read back with the literal 3.14 floating point constant
377 // because they are not the same.
378 //
379 // It is also used for the backends which currently don't handle doubles
380 // correctly.
381 //
382 // Notice that this function is normally not used directly but rather from the
383 // macro below.
384 inline bool are_doubles_approx_equal(double const a, double const b)
385 {
386  // The formula taken from CATCH test framework
387  // https://github.com/philsquared/Catch/
388  // Thanks to Richard Harris for his help refining this formula
389  double const epsilon(std::numeric_limits<float>::epsilon() * 100);
390  double const scale(1.0);
391  return std::fabs(a - b) < epsilon * (scale + (std::max)(std::fabs(a), std::fabs(b)));
392 }
393 
394 // This is a macro to ensure we use the correct line numbers. The weird
395 // do/while construction is used to make this a statement and the even weirder
396 // condition in while ensures that the loop is executed exactly once without
397 // triggering warnings from MSVC about the condition being always false.
398 #define ASSERT_EQUAL_APPROX(a, b) \
399  do { \
400  if (!are_doubles_approx_equal((a), (b))) { \
401  FAIL( "Approximate equality check failed: " \
402  << std::fixed \
403  << std::setprecision(std::numeric_limits<double>::digits10 + 1) \
404  << (a) << " != " << (b) ); \
405  } \
406  } while ( (void)0, 0 )
407 
408 
409 // Exact double comparison function. We need one, instead of writing "a == b",
410 // only in order to have some place to put the pragmas disabling gcc warnings.
411 inline bool
412 are_doubles_exactly_equal(double a, double b)
413 {
414  // Avoid g++ warnings: we do really want the exact equality here.
415  // GCC_WARNING_SUPPRESS(float-equal)
416 
417  return a == b;
418 
419  // GCC_WARNING_RESTORE(float-equal)
420 }
421 
422 #define ASSERT_EQUAL_EXACT(a, b) \
423  do { \
424  if (!are_doubles_exactly_equal((a), (b))) { \
425  FAIL( "Exact equality check failed: " \
426  << std::fixed \
427  << std::setprecision(std::numeric_limits<double>::digits10 + 1) \
428  << (a) << " != " << (b) ); \
429  } \
430  } while ( (void)0, 0 )
431 
432 
433 // Compare two floating point numbers either exactly or approximately depending
434 // on test_context::has_fp_bug() return value.
435 inline bool
436 are_doubles_equal(test_context_base const& tc, double a, double b)
437 {
438  return tc.has_fp_bug()
441 }
442 
443 // This macro should be used when where we don't have any problems with string
444 // literals vs floating point literals mismatches described above and would
445 // ideally compare the numbers exactly but, unfortunately, currently can't do
446 // this unconditionally because at least some backends are currently buggy and
447 // don't handle the floating point values correctly.
448 //
449 // This can be only used from inside the common_tests class as it relies on
450 // having an accessible "tc_" variable to determine whether exact or
451 // approximate comparison should be used.
452 #define ASSERT_EQUAL(a, b) \
453  do { \
454  if (!are_doubles_equal(tc_, (a), (b))) { \
455  FAIL( "Equality check failed: " \
456  << std::fixed \
457  << std::setprecision(std::numeric_limits<double>::digits10 + 1) \
458  << (a) << " != " << (b) ); \
459  } \
460  } while ( (void)0, 0 )
461 
462 
464 {
465 public:
467  : tc_(test_context_base::get_instance()),
468  backEndFactory_(tc_.get_backend_factory()),
469  connectString_(tc_.get_connect_string())
470  {}
471 
472 protected:
474  backend_factory const &backEndFactory_;
475  std::string const connectString_;
476 
477  SOCI_NOT_COPYABLE(common_tests)
478 };
479 
480 typedef std::auto_ptr<table_creator_base> auto_table_creator;
481 
482 
483 
484 } // namespace tests
485 
486 } // namespace soci
487 
488 #endif // SOCI_COMMON_TESTS_H_INCLUDED
virtual bool has_silent_truncate_bug(session &) const
Definition: common-tests.h:344
Definition: common-tests.h:215
std::string name_
Definition: common-tests.h:105
static void to_base(PhonebookEntry2 const &pe, values &v, indicator &ind)
Definition: common-tests.h:178
Definition: common-tests.h:283
static void from_base(int i, indicator ind, MyInt &mi)
Definition: common-tests.h:129
static void from_base(values const &v, indicator, PhonebookEntry3 &pe)
Definition: common-tests.h:190
test_context_base const & tc_
Definition: common-tests.h:473
Definition: common-tests.h:91
static void to_base(PhonebookEntry const &pe, values &v, indicator &ind)
Definition: common-tests.h:156
void setPhone(std::string const &p)
Definition: common-tests.h:101
void setName(std::string const &n)
Definition: common-tests.h:98
virtual void on_after_ddl(session &) const
Definition: common-tests.h:348
soci::values base_type
Definition: common-tests.h:147
virtual bool has_multiple_select_bug() const
Definition: common-tests.h:337
virtual ~function_creator_base()
Definition: common-tests.h:263
table_creator_base(session &sql)
Definition: common-tests.h:218
soci::values base_type
Definition: common-tests.h:167
soci::values base_type
Definition: common-tests.h:188
int base_type
Definition: common-tests.h:127
virtual ~procedure_creator_base()
Definition: common-tests.h:246
int get() const
Definition: common-tests.h:116
Definition: common-tests.h:240
virtual bool has_fp_bug() const
Definition: common-tests.h:332
std::string const connectString_
Definition: common-tests.h:475
Definition: common-tests.h:85
virtual bool has_transactions_support(session &) const
Definition: common-tests.h:340
static void to_base(MyInt const &mi, int &i, indicator &ind)
Definition: common-tests.h:137
MyInt(int i)
Definition: common-tests.h:114
MyInt()
Definition: common-tests.h:113
Definition: common-tests.h:95
std::string get_connect_string() const
Definition: common-tests.h:317
static test_context_base const & get_instance()
Definition: common-tests.h:305
std::string getPhone() const
Definition: common-tests.h:102
Definition: common-tests.h:463
Definition: common-tests.h:110
backend_factory const & get_backend_factory() const
Definition: common-tests.h:312
Definition: common-tests.h:257
virtual ~table_creator_base()
Definition: common-tests.h:221
common_tests()
Definition: common-tests.h:466
bool are_doubles_equal(test_context_base const &tc, double a, double b)
Definition: common-tests.h:436
virtual ~test_context_base()
Definition: common-tests.h:355
static void to_base(PhonebookEntry3 const &pe, values &v, indicator &ind)
Definition: common-tests.h:198
void set(int i)
Definition: common-tests.h:115
bool are_doubles_approx_equal(double const a, double const b)
Definition: common-tests.h:384
static void from_base(values const &v, indicator, PhonebookEntry2 &pe)
Definition: common-tests.h:169
virtual bool enable_std_char_padding(session &) const
Definition: common-tests.h:353
std::auto_ptr< table_creator_base > auto_table_creator
Definition: common-tests.h:480
std::string phone_
Definition: common-tests.h:106
std::string name
Definition: common-tests.h:87
procedure_creator_base(session &sql)
Definition: common-tests.h:243
bool are_doubles_exactly_equal(double a, double b)
Definition: common-tests.h:412
test_context_base(backend_factory const &backEnd, std::string const &connectString)
Definition: common-tests.h:286
void checkEqualPadded(const std::string &padded_str, const std::string &expected_str)
Definition: common-tests.h:55
function_creator_base(session &sql)
Definition: common-tests.h:260
std::string phone
Definition: common-tests.h:88
Definition: HorizonTestDBTable1.h:104
static void from_base(values const &v, indicator, PhonebookEntry &pe)
Definition: common-tests.h:149
std::string getName() const
Definition: common-tests.h:99
backend_factory const & backEndFactory_
Definition: common-tests.h:474
virtual std::string dropstatement()
Definition: common-tests.h:266