From 8af06d99317d2fdc499eef86d9d5e9c157a052c2 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Tue, 28 Apr 2015 21:02:14 +0100 Subject: Move the mocked PQ db into the PQ module and start the basis of the SQL mod unit tests --- project2/Jamfile.jam | 10 +++-- project2/common/dataSource.h | 1 + project2/sql/Jamfile.jam | 34 ++++++++------- project2/sql/mockDatasource.cpp | 15 +++++++ project2/sql/mockDatasource.h | 16 +++++++ project2/sql/rdbmsDataSource.cpp | 10 ++++- project2/sql/rdbmsDataSource.h | 5 ++- project2/sql/sql-modPQ.cpp | 60 +++++++++++++++++++++++++++ project2/sql/sql-modPQ.h | 24 +++++++++++ project2/sql/unittests/Jamfile.jam | 38 +++++++++++++++++ project2/sql/unittests/datasources/pqmock.xml | 4 ++ project2/sql/unittests/pqschema.sql | 8 ++++ project2/sql/unittests/testCore.cpp | 11 +++++ project2/sql/unittests/testCore.h | 12 ++++++ project2/sql/unittests/testpq.cpp | 60 +++++++++++++++++++++++++++ project2/ut/definedDirs.h | 1 + 16 files changed, 287 insertions(+), 22 deletions(-) create mode 100644 project2/sql/mockDatasource.cpp create mode 100644 project2/sql/mockDatasource.h create mode 100644 project2/sql/sql-modPQ.h create mode 100644 project2/sql/unittests/Jamfile.jam create mode 100644 project2/sql/unittests/datasources/pqmock.xml create mode 100644 project2/sql/unittests/pqschema.sql create mode 100644 project2/sql/unittests/testCore.cpp create mode 100644 project2/sql/unittests/testCore.h create mode 100644 project2/sql/unittests/testpq.cpp diff --git a/project2/Jamfile.jam b/project2/Jamfile.jam index ca057b4..8e083e7 100644 --- a/project2/Jamfile.jam +++ b/project2/Jamfile.jam @@ -1,9 +1,9 @@ import package ; import feature : feature ; -feature odbc : yes no : propagated ; -feature pq : yes no : propagated ; -feature mysql : yes no : propagated ; +feature odbc : yes no ; +feature pq : yes no ; +feature mysql : yes no ; alias p2parts : : : : url//p2url @@ -19,6 +19,9 @@ alias p2parts : : : : streams//p2streams basics//p2basics lib//p2lib + yes:sql//p2sqlmodODBC + yes:sql//p2sqlmodPQ + yes:sql//p2sqlmodMySQL ; alias p2daemonparts : : : : @@ -33,6 +36,7 @@ build-project daemon ; build-project common//unittests ; build-project basics//unittests ; build-project ice//unittests ; +build-project sql//unittests ; explicit install installp2con installp2cgi installp2fcgi ; package.install install : : console//p2console cgi//p2cgi cgi//p2fcgi daemon//p2daemon ; diff --git a/project2/common/dataSource.h b/project2/common/dataSource.h index 1f557dd..063d33e 100644 --- a/project2/common/dataSource.h +++ b/project2/common/dataSource.h @@ -13,6 +13,7 @@ class DataSource : public SourceObject { DataSource(ScriptNodePtr p); virtual ~DataSource(); + virtual void close() { }; virtual void commit() { }; virtual void rollback() { }; }; diff --git a/project2/sql/Jamfile.jam b/project2/sql/Jamfile.jam index ea879fb..306c94a 100644 --- a/project2/sql/Jamfile.jam +++ b/project2/sql/Jamfile.jam @@ -2,54 +2,58 @@ alias glibmm : : : : "`pkg-config --cflags glibmm-2.4`" "`pkg-config --libs glibmm-2.4`" ; -explicit object sql-modMySQL ; -obj sql-modMySQL : + +build-project unittests ; + +explicit library p2sqlmodMySQL ; +lib p2sqlmodMySQL : sql-modMySQL.cpp : ../../libmysqlpp//mysqlpp glibmm ../../libmisc - ../common + ../common//p2common + p2sql : : ../../libmysqlpp//mysqlpp ; - -explicit object sql-modODBC ; -obj sql-modODBC : + +explicit library p2sqlmodODBC ; +lib p2sqlmodODBC : sql-modODBC.cpp : ../../libodbcpp//odbcpp glibmm ../../libmisc - ../common + ../common//p2common + p2sql : : ../../libodbcpp//odbcpp ; - -explicit object sql-modPQ ; -obj sql-modPQ : + +explicit library p2sqlmodPQ ; +lib p2sqlmodPQ : sql-modPQ.cpp : ../../libpqpp//pqpp glibmm ../../libmisc - ../common + ../common//p2common + p2sql : : ../../libpqpp//pqpp ; - + cpp-pch pch : pch.hpp : ../../libmisc ../../libdbpp glibmm ../common//p2common ; + lib p2sql : pch [ glob *.cpp : sql-mod*.cpp ] ../../libdbpp//dbpp : . - yes:sql-modODBC - yes:sql-modPQ - yes:sql-modMySQL glibmm ../common//p2common ../lib//p2lib diff --git a/project2/sql/mockDatasource.cpp b/project2/sql/mockDatasource.cpp new file mode 100644 index 0000000..06648ad --- /dev/null +++ b/project2/sql/mockDatasource.cpp @@ -0,0 +1,15 @@ +#include "mockDatasource.h" +#include +#include + +DECLARE_CUSTOM_COMPONENT_LOADER("mock", T, MockConnectionLoader, ConnectionLoader); + +std::map MockConnectionLoader::mocks; +unsigned int MockConnectionLoader::mocked = 0; + +DB::Connection * +MockConnectionLoader::create(const std::string & n) const +{ + return safeMapFind(mocks, n)->second(); +} + diff --git a/project2/sql/mockDatasource.h b/project2/sql/mockDatasource.h new file mode 100644 index 0000000..facc8a5 --- /dev/null +++ b/project2/sql/mockDatasource.h @@ -0,0 +1,16 @@ +#ifndef MOCKDATASOURCE_H +#define MOCKDATASOURCE_H + +#include + +class MockConnectionLoader : public ConnectionLoader { + public: + DB::Connection * create(const std::string &) const; + + static unsigned int mocked; + typedef boost::function ConnectionCreator; + static std::map mocks; +}; + +#endif + diff --git a/project2/sql/rdbmsDataSource.cpp b/project2/sql/rdbmsDataSource.cpp index b25e8dc..5f0d43a 100644 --- a/project2/sql/rdbmsDataSource.cpp +++ b/project2/sql/rdbmsDataSource.cpp @@ -115,6 +115,14 @@ RdbmsDataSource::getReadonly() const return connectTo(masterDsn).get(); } +void +RdbmsDataSource::close() +{ + LOCK(ilock); + LOCK(glock); + RdbmsDataSource::dbhosts.clear(); +} + void RdbmsDataSource::commit() { @@ -122,8 +130,6 @@ RdbmsDataSource::commit() LOCK(glock); auto masters = dbhosts.equal_range(masterDsn); for (auto m = masters.first; m != masters.second; m++) { - if (m->second->threadId) { - } if (m->second->txOpen && m->second->threadId && *m->second->threadId == std::this_thread::get_id()) { m->second->connection->commitTx(); m->second->txOpen = false; diff --git a/project2/sql/rdbmsDataSource.h b/project2/sql/rdbmsDataSource.h index 0a1ddfd..6d7e623 100644 --- a/project2/sql/rdbmsDataSource.h +++ b/project2/sql/rdbmsDataSource.h @@ -75,8 +75,9 @@ class RdbmsDataSource : public DataSource { ConnectionRef getReadonly() const; ConnectionRef getWritable() const; - virtual void commit(); - virtual void rollback(); + virtual void close() override; + virtual void commit() override; + virtual void rollback() override; const ConnectionInfo masterDsn; const bool preferLocal; diff --git a/project2/sql/sql-modPQ.cpp b/project2/sql/sql-modPQ.cpp index cb753e0..5267f28 100644 --- a/project2/sql/sql-modPQ.cpp +++ b/project2/sql/sql-modPQ.cpp @@ -1,4 +1,64 @@ #include "connectionLoader.h" #include "../libpqpp/connection.h" +#include "sql-modPQ.h" +#include +#include +#include + typedef PQ::Connection PQConnection; DECLARE_GENERIC_LOADER("postgresql", ConnectionLoader, PQConnection) + +MockPqDatabase::MockPqDatabase(const std::string & masterdb, const std::string & name, const std::vector & ss) : + master(InstanceMap::Get("postgresql")->create(masterdb)), + testDbName(stringbf("test_%d_%d", getpid(), ++MockConnectionLoader::mocked)), + mockName(name) +{ + Logger()->messagebf(LOG_DEBUG, "Setting up new mocked database %s", testDbName); + DropDatabase(); + CreateNewDatabase(); + try { + for (auto s : ss) { + Logger()->messagebf(LOG_DEBUG, "%s << %s", testDbName, s); + if (system(("psql -v ON_ERROR_STOP=1 -q -1 -U postgres " + testDbName + " -f " + s.string()).c_str())) { + throw std::runtime_error("Failed to execute " + s.string()); + } + } + Logger()->messagebf(LOG_DEBUG, "%s initialized", testDbName); + MockConnectionLoader::mocks[name] = boost::bind(MockPqDatabase::openConnection, stringbf("user=postgres dbname=%s", testDbName)); + } + catch (...) { + DropDatabase(); + throw; + } +} + +DB::Connection * +MockPqDatabase::openConnection(const std::string & connStr) +{ + return InstanceMap::Get("postgresql")->create(connStr); +} + +MockPqDatabase::~MockPqDatabase() +{ + Logger()->messagebf(LOG_DEBUG, "Tearing down mocked database %s", testDbName); + DropDatabase(); + delete master; + MockConnectionLoader::mocks.erase(mockName); + Logger()->messagebf(LOG_DEBUG, "%s torn down", testDbName); +} + +void MockPqDatabase::DropDatabase() const +{ + Logger()->messagebf(LOG_INFO, "Killing any active connections to database %s", testDbName); + master->execute("SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '" + testDbName + "'"); + Logger()->messagebf(LOG_INFO, "Dropping (if exists) old database %s", testDbName); + master->execute("DROP DATABASE IF EXISTS " + testDbName); +} + +void MockPqDatabase::CreateNewDatabase() const +{ + Logger()->messagebf(LOG_INFO, "Creating new database %s", testDbName); + master->execute("CREATE DATABASE " + testDbName); +} + + diff --git a/project2/sql/sql-modPQ.h b/project2/sql/sql-modPQ.h new file mode 100644 index 0000000..f71b5ab --- /dev/null +++ b/project2/sql/sql-modPQ.h @@ -0,0 +1,24 @@ +#ifndef MOCKPQDATASOURCE_H +#define MOCKPQDATASOURCE_H + +#include "mockDatasource.h" +#include + +class MockPqDatabase { + public: + MockPqDatabase(const std::string & master, const std::string & name, const std::vector & ss); + ~MockPqDatabase(); + + protected: + void DropDatabase() const; + void CreateNewDatabase() const; + + private: + static DB::Connection * openConnection(const std::string & connStr); + const DB::Connection * master; + const std::string testDbName; + const std::string mockName; +}; + +#endif + diff --git a/project2/sql/unittests/Jamfile.jam b/project2/sql/unittests/Jamfile.jam new file mode 100644 index 0000000..24db2e5 --- /dev/null +++ b/project2/sql/unittests/Jamfile.jam @@ -0,0 +1,38 @@ + +import testing ; + +lib boost_system ; +lib boost_filesystem ; +lib IceUtil ; +lib Ice ; + +path-constant me : . ; + +lib sqlTestCore : + testCore.cpp + : : + ../../ut//p2ut + ../../common//p2common + boost_filesystem + ROOT=\"$(me)\" + ; + +run + testpq.cpp + : : : + BOOST_TEST_DYN_LINK + sqlTestCore + ../../common//p2common + ../../basics//p2basics + ../../ut//p2ut + ../../lib//p2lib + ../../xml//p2xml + ..//p2sql + ..//p2sqlmodPQ + boost_system + boost_filesystem + ../../ut//boost_utf + ROOT=\"$(me)\" + pqschema.sql + : testpq ; + diff --git a/project2/sql/unittests/datasources/pqmock.xml b/project2/sql/unittests/datasources/pqmock.xml new file mode 100644 index 0000000..fb2ec1b --- /dev/null +++ b/project2/sql/unittests/datasources/pqmock.xml @@ -0,0 +1,4 @@ + + + + diff --git a/project2/sql/unittests/pqschema.sql b/project2/sql/unittests/pqschema.sql new file mode 100644 index 0000000..b51a279 --- /dev/null +++ b/project2/sql/unittests/pqschema.sql @@ -0,0 +1,8 @@ +CREATE TABLE test( + id int, + fl numeric(5,2), + string text, + dt timestamp without time zone, + tf boolean, + ts interval); +INSERT INTO test VALUES(4, 123.45, 'some text', '2015-04-27 23:06:03', true, '1 day 14:13:12'); diff --git a/project2/sql/unittests/testCore.cpp b/project2/sql/unittests/testCore.cpp new file mode 100644 index 0000000..8af0e43 --- /dev/null +++ b/project2/sql/unittests/testCore.cpp @@ -0,0 +1,11 @@ +#include "testCore.h" +#include +#include + +TestCore::TestCore() +{ + TestOptionsSource::LoadTestOptions({ + { "common.datasourceRoot", (RootDir / "datasources").string() }, + }); +} + diff --git a/project2/sql/unittests/testCore.h b/project2/sql/unittests/testCore.h new file mode 100644 index 0000000..e39e9c6 --- /dev/null +++ b/project2/sql/unittests/testCore.h @@ -0,0 +1,12 @@ +#ifndef PROJECT2_SQL_UNITTEST_CORE +#define PROJECT2_SQL_UNITTEST_CORE + +#include + +class TestCore : public CommonObjects { + public: + TestCore(); +}; + +#endif + diff --git a/project2/sql/unittests/testpq.cpp b/project2/sql/unittests/testpq.cpp new file mode 100644 index 0000000..434a184 --- /dev/null +++ b/project2/sql/unittests/testpq.cpp @@ -0,0 +1,60 @@ +#define BOOST_TEST_MODULE TestPQ +#include + +#include +#include +#include +#include +#include "testCore.h" + +class StandardMockDatabase : public MockPqDatabase { + public: + StandardMockDatabase() : MockPqDatabase("user=postgres dbname=postgres", "pqmock", { + RootDir / "pqschema.sql" }) + { + } +}; + +BOOST_GLOBAL_FIXTURE( StandardMockDatabase ); + +BOOST_FIXTURE_TEST_SUITE( Core, TestCore ); + +BOOST_AUTO_TEST_CASE( transactions ) +{ + RdbmsDataSource * ds = CommonObjects::dataSource("pqmock"); + auto ro = ds->getReadonly(); + + BOOST_REQUIRE_EQUAL(false, ro->inTx()); + ro->beginTx(); + BOOST_REQUIRE_EQUAL(true, ro->inTx()); + ro->rollbackTx(); + BOOST_REQUIRE_EQUAL(false, ro->inTx()); + + ro->beginTx(); + BOOST_REQUIRE_EQUAL(true, ro->inTx()); + ro->commitTx(); + BOOST_REQUIRE_EQUAL(false, ro->inTx()); + + ds->close(); +} + +BOOST_AUTO_TEST_CASE( bindAndSend ) +{ + RdbmsDataSource * ds = CommonObjects::dataSource("pqmock"); + auto rw = ds->getWritable(); + + auto mod = rw->newModifyCommand("INSERT INTO test VALUES(?, ?, ?, ?, ?, ?)"); + mod->bindParamI(0, 12); + mod->bindParamF(1, 3.14); + mod->bindParamS(2, "C String"); + mod->bindParamT(3, boost::posix_time::from_time_t(time(NULL))); + mod->bindNull(4); + mod->bindNull(5); + mod->execute(); + delete mod; + ds->commit(); + ds->close(); +} + +BOOST_AUTO_TEST_SUITE_END(); + diff --git a/project2/ut/definedDirs.h b/project2/ut/definedDirs.h index fff408a..cbed012 100644 --- a/project2/ut/definedDirs.h +++ b/project2/ut/definedDirs.h @@ -2,6 +2,7 @@ #define P2_UT_DEFINEDDIRS #include +#include #ifndef ROOT #error "ROOT needs to be defined at compilation time" -- cgit v1.2.3