summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--project2/Jamfile.jam10
-rw-r--r--project2/common/dataSource.h1
-rw-r--r--project2/sql/Jamfile.jam34
-rw-r--r--project2/sql/mockDatasource.cpp15
-rw-r--r--project2/sql/mockDatasource.h16
-rw-r--r--project2/sql/rdbmsDataSource.cpp10
-rw-r--r--project2/sql/rdbmsDataSource.h5
-rw-r--r--project2/sql/sql-modPQ.cpp60
-rw-r--r--project2/sql/sql-modPQ.h24
-rw-r--r--project2/sql/unittests/Jamfile.jam38
-rw-r--r--project2/sql/unittests/datasources/pqmock.xml4
-rw-r--r--project2/sql/unittests/pqschema.sql8
-rw-r--r--project2/sql/unittests/testCore.cpp11
-rw-r--r--project2/sql/unittests/testCore.h12
-rw-r--r--project2/sql/unittests/testpq.cpp60
-rw-r--r--project2/ut/definedDirs.h1
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"