1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
|
//
// Copyright (C) 2024 Vadim Zeitlin
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
//
#ifndef SOCI_TEST_CONTEXT_H_INCLUDED
#define SOCI_TEST_CONTEXT_H_INCLUDED
#include "soci/soci.h"
#include <cassert>
#include <clocale>
#include <cstdlib>
#include <memory>
#include <string>
// These variables are defined in the existing tests code and could be removed
// later.
extern std::string connectString;
extern soci::backend_factory const &backEnd;
namespace soci
{
namespace tests
{
// TODO: improve cleanup capabilities by subtypes, soci_test name may be omitted --mloskot
// i.e. optional ctor param accepting custom table name
class table_creator_base
{
public:
table_creator_base(session& sql)
: msession(sql) { drop(); }
virtual ~table_creator_base() { drop();}
private:
void drop()
{
try
{
msession << "drop table soci_test";
}
catch (soci_error const& e)
{
//std::cerr << e.what() << std::endl;
e.what();
}
}
session& msession;
SOCI_NOT_COPYABLE(table_creator_base)
};
using auto_table_creator = std::unique_ptr<table_creator_base>;
// This is a singleton class, at any given time there is at most one test
// context alive and common_tests fixture class uses it.
class test_context_base
{
public:
test_context_base()
: backEndFactory_(backEnd)
{
// This can't be a CHECK() because the test context is constructed
// outside of any test.
assert(!the_test_context_);
the_test_context_ = this;
}
static test_context_base const& get_instance()
{
assert(the_test_context_);
return *the_test_context_;
}
// This accessor is used by main() to allow it to initialize the test
// context. It shouldn't be used anywhere else.
static test_context_base* get_instance_pointer()
{
return the_test_context_;
}
// This function is called from main() with the value passed on the command
// line, if any.
//
// If it returns false, an error message about the connection string being
// required is printed and the tests exits immediately with an error, so
// the backends that don't require a connection string should override it.
virtual bool initialize_connect_string(std::string argFromCommandLine)
{
assert(connectString_.empty()); // Shouldn't be called more than once.
if (argFromCommandLine.empty())
return false;
connectString_ = argFromCommandLine;
// For compatibility with the existings tests, also set the global
// variable which they sometimes use instead of the one here.
::connectString = argFromCommandLine;
return true;
}
// This is a purely informational function returning an example of a DSN
// which can be used with this backend.
//
// It should be overridden if initialize_connect_string() is _not_
// overridden to improve the error message given when the connection string
// is not specified on the command line.
virtual std::string get_example_connection_string() const { return {}; }
// This function is called before starting to run the test suite.
//
// If it returns false, the process exits with error code 1 immediately,
// without running any tests. The function should print the reason for the
// error in this case.
virtual bool start_testing()
{
// To allow running tests in non-default ("C") locale, the following
// environment variable can be set and then the current default locale
// (which can itself be changed by setting LC_ALL environment variable)
// will then be used.
if (std::getenv("SOCI_TEST_USE_LC_ALL"))
std::setlocale(LC_ALL, "");
return true;
}
backend_factory const & get_backend_factory() const
{
return backEndFactory_;
}
std::string get_connect_string() const
{
return connectString_;
}
// Return the name of the backend as used in the dynamic library name.
virtual std::string get_backend_name() const = 0;
virtual std::string to_date_time(std::string const &dateTime) const = 0;
virtual table_creator_base* table_creator_1(session&) const = 0;
virtual table_creator_base* table_creator_2(session&) const = 0;
virtual table_creator_base* table_creator_3(session&) const = 0;
virtual table_creator_base* table_creator_4(session&) const = 0;
// Override this to return the table creator for a simple table containing
// an integer "id" column and CLOB "s" one.
//
// Returns null by default to indicate that CLOB is not supported.
virtual table_creator_base* table_creator_clob(session&) const { return NULL; }
// Override this to return the table creator for a simple table containing
// an integer "id" column and BLOB "b" one.
//
// Returns null by default to indicate that BLOB is not supported.
virtual table_creator_base* table_creator_blob(session&) const { return NULL; }
// Override this to return the table creator for a simple table containing
// an integer "id" column and XML "x" one.
//
// Returns null by default to indicate that XML is not supported.
virtual table_creator_base* table_creator_xml(session&) const { return NULL; }
// Override this to return the table creator for a simple table containing
// an identity integer "id" and a simple integer "val" columns.
//
// Returns null by default to indicate that identity is not supported.
virtual table_creator_base* table_creator_get_last_insert_id(session&) const { return NULL; }
// Return the casts that must be used to convert the between the database
// XML type and the query parameters.
//
// By default no special casts are done.
virtual std::string to_xml(std::string const& x) const { return x; }
virtual std::string from_xml(std::string const& x) const { return x; }
// Override this if the backend not only supports working with XML values
// (and so returns a non-null value from table_creator_xml()), but the
// database itself has real XML support instead of just allowing to store
// and retrieve XML as text. "Real" support means at least preventing the
// application from storing malformed XML in the database.
virtual bool has_real_xml_support() const { return false; }
// Override this if the backend doesn't handle floating point values
// correctly, i.e. writing a value and reading it back doesn't return
// *exactly* the same value.
virtual bool has_fp_bug() const { return false; }
// Override this if the backend doesn't handle partial success for
// operations using array parameters correctly.
virtual bool has_partial_update_bug() const { return false; }
// Override this if the backend wrongly returns CR LF when reading a string
// with just LFs from the database to strip the unwanted CRs.
virtual std::string fix_crlf_if_necessary(std::string const& s) const { return s; }
// Override this if the backend doesn't handle multiple active select
// statements at the same time, i.e. a result set must be entirely consumed
// before creating a new one (this is the case of MS SQL without MARS).
virtual bool has_multiple_select_bug() const { return false; }
// Override this if the backend may not have transactions support.
virtual bool has_transactions_support(session&) const { return true; }
// Override this if the backend silently truncates string values too long
// to fit by default.
virtual bool has_silent_truncate_bug(session&) const { return false; }
// Override this if the backend doesn't distinguish between empty and null
// strings (Oracle does this).
virtual bool treats_empty_strings_as_null() const { return false; }
// Override this if the backend does not store values bigger than INT64_MAX
// correctly. This can lead to an unexpected ordering of values as larger
// values might be stored as overflown and therefore negative integer.
virtual bool has_uint64_storage_bug() const { return false; }
// Override this if the backend truncates integer values bigger than INT64_MAX.
virtual bool truncates_uint64_to_int64() const { return false; }
// Override this to call commit() if it's necessary for the DDL statements
// to be taken into account (currently this is only the case for Firebird).
virtual void on_after_ddl(session&) const { }
// Put the database in SQL-complient mode for CHAR(N) values, return false
// if it's impossible, i.e. if the database doesn't behave correctly
// whatever we do.
virtual bool enable_std_char_padding(session&) const { return true; }
// Return the SQL expression giving the length of the specified string,
// i.e. "char_length(s)" in standard SQL but often "len(s)" or "length(s)"
// in practice and sometimes even worse (thanks Oracle).
virtual std::string sql_length(std::string const& s) const = 0;
virtual ~test_context_base()
{
the_test_context_ = NULL;
}
private:
backend_factory const &backEndFactory_;
std::string connectString_;
static test_context_base* the_test_context_;
SOCI_NOT_COPYABLE(test_context_base)
};
// Base class for all "normal" tests, i.e. those that run common tests.
class test_context_common : public test_context_base
{
public:
// This is implemented in test-common.cpp.
test_context_common();
};
// Fixture class for tests that need to use the database.
class common_tests
{
public:
common_tests()
: tc_(test_context_base::get_instance()),
backEndFactory_(tc_.get_backend_factory()),
connectString_(tc_.get_connect_string())
{}
protected:
test_context_base const & tc_;
backend_factory const &backEndFactory_;
std::string const connectString_;
SOCI_NOT_COPYABLE(common_tests)
};
} // namespace tests
} // namespace soci
#endif // SOCI_TEST_CONTEXT_H_INCLUDED
|