diff options
-rw-r--r-- | project2/Jamfile.jam | 10 | ||||
-rw-r--r-- | project2/common/dataSource.h | 1 | ||||
-rw-r--r-- | project2/sql/Jamfile.jam | 34 | ||||
-rw-r--r-- | project2/sql/mockDatasource.cpp | 15 | ||||
-rw-r--r-- | project2/sql/mockDatasource.h | 16 | ||||
-rw-r--r-- | project2/sql/rdbmsDataSource.cpp | 10 | ||||
-rw-r--r-- | project2/sql/rdbmsDataSource.h | 5 | ||||
-rw-r--r-- | project2/sql/sql-modPQ.cpp | 60 | ||||
-rw-r--r-- | project2/sql/sql-modPQ.h | 24 | ||||
-rw-r--r-- | project2/sql/unittests/Jamfile.jam | 38 | ||||
-rw-r--r-- | project2/sql/unittests/datasources/pqmock.xml | 4 | ||||
-rw-r--r-- | project2/sql/unittests/pqschema.sql | 8 | ||||
-rw-r--r-- | project2/sql/unittests/testCore.cpp | 11 | ||||
-rw-r--r-- | project2/sql/unittests/testCore.h | 12 | ||||
-rw-r--r-- | project2/sql/unittests/testpq.cpp | 60 | ||||
-rw-r--r-- | project2/ut/definedDirs.h | 1 |
16 files changed, 287 insertions, 22 deletions
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 : : : : <library>url//p2url @@ -19,6 +19,9 @@ alias p2parts : : : : <library>streams//p2streams <library>basics//p2basics <library>lib//p2lib + <odbc>yes:<library>sql//p2sqlmodODBC + <pq>yes:<library>sql//p2sqlmodPQ + <mysql>yes:<library>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 : : : : <cflags>"`pkg-config --cflags glibmm-2.4`" <linkflags>"`pkg-config --libs glibmm-2.4`" ; -explicit object sql-modMySQL ; -obj sql-modMySQL : + +build-project unittests ; + +explicit library p2sqlmodMySQL ; +lib p2sqlmodMySQL : sql-modMySQL.cpp : <library>../../libmysqlpp//mysqlpp <library>glibmm <include>../../libmisc - <library>../common + <library>../common//p2common + <library>p2sql : : <library>../../libmysqlpp//mysqlpp ; - -explicit object sql-modODBC ; -obj sql-modODBC : + +explicit library p2sqlmodODBC ; +lib p2sqlmodODBC : sql-modODBC.cpp : <library>../../libodbcpp//odbcpp <library>glibmm <include>../../libmisc - <library>../common + <library>../common//p2common + <library>p2sql : : <library>../../libodbcpp//odbcpp ; - -explicit object sql-modPQ ; -obj sql-modPQ : + +explicit library p2sqlmodPQ ; +lib p2sqlmodPQ : sql-modPQ.cpp : <library>../../libpqpp//pqpp <library>glibmm <include>../../libmisc - <library>../common + <library>../common//p2common + <library>p2sql : : <library>../../libpqpp//pqpp ; - + cpp-pch pch : pch.hpp : <include>../../libmisc <include>../../libdbpp <library>glibmm <library>../common//p2common ; + lib p2sql : pch [ glob *.cpp : sql-mod*.cpp ] ../../libdbpp//dbpp : <include>. - <odbc>yes:<library>sql-modODBC - <pq>yes:<library>sql-modPQ - <mysql>yes:<library>sql-modMySQL <library>glibmm <library>../common//p2common <library>../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 <connection.h> +#include <safeMapFind.h> + +DECLARE_CUSTOM_COMPONENT_LOADER("mock", T, MockConnectionLoader, ConnectionLoader); + +std::map<std::string, MockConnectionLoader::ConnectionCreator> MockConnectionLoader::mocks; +unsigned int MockConnectionLoader::mocked = 0; + +DB::Connection * +MockConnectionLoader::create(const std::string & n) const +{ + return safeMapFind<std::runtime_error>(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 <rdbmsDataSource.h> + +class MockConnectionLoader : public ConnectionLoader { + public: + DB::Connection * create(const std::string &) const; + + static unsigned int mocked; + typedef boost::function<DB::Connection *()> ConnectionCreator; + static std::map<std::string, ConnectionCreator> 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 @@ -116,14 +116,20 @@ RdbmsDataSource::getReadonly() const } void +RdbmsDataSource::close() +{ + LOCK(ilock); + LOCK(glock); + RdbmsDataSource::dbhosts.clear(); +} + +void RdbmsDataSource::commit() { LOCK(ilock); 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 <misc.h> +#include <scripts.h> +#include <logger.h> + typedef PQ::Connection PQConnection; DECLARE_GENERIC_LOADER("postgresql", ConnectionLoader, PQConnection) + +MockPqDatabase::MockPqDatabase(const std::string & masterdb, const std::string & name, const std::vector<boost::filesystem::path> & ss) : + master(InstanceMap<ConnectionLoader, std::string>::Get<std::invalid_argument>("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<ConnectionLoader, std::string>::Get<std::invalid_argument>("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 <boost/filesystem/path.hpp> + +class MockPqDatabase { + public: + MockPqDatabase(const std::string & master, const std::string & name, const std::vector<boost::filesystem::path> & 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 + : : + <library>../../ut//p2ut + <library>../../common//p2common + <library>boost_filesystem + <define>ROOT=\"$(me)\" + ; + +run + testpq.cpp + : : : + <define>BOOST_TEST_DYN_LINK + <library>sqlTestCore + <library>../../common//p2common + <library>../../basics//p2basics + <library>../../ut//p2ut + <library>../../lib//p2lib + <library>../../xml//p2xml + <library>..//p2sql + <library>..//p2sqlmodPQ + <library>boost_system + <library>boost_filesystem + <library>../../ut//boost_utf + <define>ROOT=\"$(me)\" + <dependency>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 @@ +<?xml version="1.0"?> +<project2:rdbmsdatasource xmlns:project2="http://project2.randomdan.homeip.net" name="pqmock"> + <masterdsn provider="mock" dsn="pqmock" /> +</project2:rdbmsdatasource> 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 <testOptionsSource.h> +#include <definedDirs.h> + +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 <commonObjects.h> + +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 <boost/test/unit_test.hpp> + +#include <mockDatasource.h> +#include <definedDirs.h> +#include <modifycommand.h> +#include <sql-modPQ.h> +#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<RdbmsDataSource>("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<RdbmsDataSource>("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 <boost/filesystem/path.hpp> +#include <boost/filesystem/convenience.hpp> #ifndef ROOT #error "ROOT needs to be defined at compilation time" |