diff options
author | Dan Goodliffe <dan@randomdan.homeip.net> | 2015-05-02 02:50:20 +0100 |
---|---|---|
committer | Dan Goodliffe <dan@randomdan.homeip.net> | 2015-05-02 02:50:20 +0100 |
commit | 82c224a22abece418c0d2bf0267517c207fcf802 (patch) | |
tree | 2e5b1e3264828360549d8ecd6ae77f2e41ea7a0a | |
parent | Test against a fixed time as class is initialized once per test (diff) | |
download | project2-82c224a22abece418c0d2bf0267517c207fcf802.tar.bz2 project2-82c224a22abece418c0d2bf0267517c207fcf802.tar.xz project2-82c224a22abece418c0d2bf0267517c207fcf802.zip |
Add MySQL mocking and test cases
-rw-r--r-- | project2/sql/sql-modMySQL.cpp | 72 | ||||
-rw-r--r-- | project2/sql/sql-modMySQL.h | 24 | ||||
-rw-r--r-- | project2/sql/unittests/Jamfile.jam | 8 | ||||
-rw-r--r-- | project2/sql/unittests/datasources/mysqlmock.xml | 4 | ||||
-rw-r--r-- | project2/sql/unittests/mysqlschema.sql | 7 | ||||
-rw-r--r-- | project2/sql/unittests/testmysql.cpp | 115 |
6 files changed, 230 insertions, 0 deletions
diff --git a/project2/sql/sql-modMySQL.cpp b/project2/sql/sql-modMySQL.cpp index 9ca12c0..24172ce 100644 --- a/project2/sql/sql-modMySQL.cpp +++ b/project2/sql/sql-modMySQL.cpp @@ -1,4 +1,76 @@ #include "connectionLoader.h" #include "../libmysqlpp/connection.h" +#include "sql-modMySQL.h" +#include <misc.h> +#include <scripts.h> +#include <logger.h> +#include <fstream> + typedef MySQL::Connection MySQLConnection; DECLARE_GENERIC_LOADER("mysql", ConnectionLoader, MySQLConnection) + +MockMySQLDatabase::MockMySQLDatabase(const std::string & name, const std::vector<boost::filesystem::path> & ss) : + master(InstanceMap<ConnectionLoader, std::string>::Get<std::invalid_argument>("mysql")->create("options=p2testmysql")), + testDbName(stringbf("test_%d_%d", getpid(), ++MockConnectionLoader::mocked)), + mockName(name) +{ + Logger()->messagebf(LOG_DEBUG, "Setting up new mocked database %s", testDbName); + DropDatabase(); + CreateNewDatabase(); + DB::Connection * conn = openConnection(stringbf("options=p2testmysql;database=%s", testDbName)); + try { + for (auto s : ss) { + conn->beginTx(); + Logger()->messagebf(LOG_DEBUG, "%s << %s", testDbName, s); + std::ifstream f; + f.open(s.string()); + while (!f.eof()) { + char buf[BUFSIZ]; + f.getline(buf, BUFSIZ, ';'); + if (!f.eof()) + conn->execute(buf); + } + f.close(); + conn->commitTx(); + } + Logger()->messagebf(LOG_DEBUG, "%s initialized", testDbName); + MockConnectionLoader::mocks[name] = boost::bind(MockMySQLDatabase::openConnection, stringbf("options=p2testmysql;database=%s", testDbName)); + } + catch (...) { + if (conn->inTx()) { + conn->rollbackTx(); + } + delete conn; + DropDatabase(); + throw; + } +} + +DB::Connection * +MockMySQLDatabase::openConnection(const std::string & connStr) +{ + return InstanceMap<ConnectionLoader, std::string>::Get<std::invalid_argument>("mysql")->create(connStr); +} + +MockMySQLDatabase::~MockMySQLDatabase() +{ + 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 MockMySQLDatabase::DropDatabase() const +{ + Logger()->messagebf(LOG_INFO, "Dropping (if exists) old database %s", testDbName); + master->execute("DROP DATABASE IF EXISTS " + testDbName); +} + +void MockMySQLDatabase::CreateNewDatabase() const +{ + Logger()->messagebf(LOG_INFO, "Creating new database %s", testDbName); + master->execute("CREATE DATABASE " + testDbName); +} + + diff --git a/project2/sql/sql-modMySQL.h b/project2/sql/sql-modMySQL.h new file mode 100644 index 0000000..628bfdb --- /dev/null +++ b/project2/sql/sql-modMySQL.h @@ -0,0 +1,24 @@ +#ifndef MOCKMYSQLDATASOURCE_H +#define MOCKMYSQLDATASOURCE_H + +#include "mockDatasource.h" +#include <boost/filesystem/path.hpp> + +class MockMySQLDatabase { + public: + MockMySQLDatabase(const std::string & name, const std::vector<boost::filesystem::path> & ss); + ~MockMySQLDatabase(); + + 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 index d9874ce..d14aea4 100644 --- a/project2/sql/unittests/Jamfile.jam +++ b/project2/sql/unittests/Jamfile.jam @@ -44,3 +44,11 @@ run <dependency>sqliteschema.sql : testsqlite ; +run + testmysql.cpp + : : : + <library>sqlTestCore + <library>..//p2sqlmodMySQL + <dependency>mysqlschema.sql + : testmysql ; + diff --git a/project2/sql/unittests/datasources/mysqlmock.xml b/project2/sql/unittests/datasources/mysqlmock.xml new file mode 100644 index 0000000..9641bd1 --- /dev/null +++ b/project2/sql/unittests/datasources/mysqlmock.xml @@ -0,0 +1,4 @@ +<?xml version="1.0"?> +<project2:rdbmsdatasource xmlns:project2="http://project2.randomdan.homeip.net" name="mysqlmock"> + <masterdsn provider="mock" dsn="mysqlmock" /> +</project2:rdbmsdatasource> diff --git a/project2/sql/unittests/mysqlschema.sql b/project2/sql/unittests/mysqlschema.sql new file mode 100644 index 0000000..39e63dc --- /dev/null +++ b/project2/sql/unittests/mysqlschema.sql @@ -0,0 +1,7 @@ +CREATE TABLE test( + id int, + fl numeric(5,2), + string varchar(30), + dt timestamp, + ts time); +INSERT INTO test VALUES(4, 123.45, 'some text', '2015-04-27 23:06:03', '38:13:12'); diff --git a/project2/sql/unittests/testmysql.cpp b/project2/sql/unittests/testmysql.cpp new file mode 100644 index 0000000..4debd35 --- /dev/null +++ b/project2/sql/unittests/testmysql.cpp @@ -0,0 +1,115 @@ +#define BOOST_TEST_MODULE TestMySQL +#include <boost/test/unit_test.hpp> + +#include <mockDatasource.h> +#include <definedDirs.h> +#include <modifycommand.h> +#include <selectcommand.h> +#include <column.h> +#include <sql-modMySQL.h> +#include "testCore.h" +#include <sqlHandleAsVariableType.h> + +class StandardMockDatabase : public MockMySQLDatabase { + public: + StandardMockDatabase() : MockMySQLDatabase("mysqlmock", { + RootDir / "mysqlschema.sql" }) + { + } +}; + +BOOST_GLOBAL_FIXTURE( StandardMockDatabase ); + +BOOST_FIXTURE_TEST_SUITE( Core, TestCore ); + +BOOST_AUTO_TEST_CASE( transactions ) +{ + RdbmsDataSource * ds = CommonObjects::dataSource<RdbmsDataSource>("mysqlmock"); + 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>("mysqlmock"); + auto rw = ds->getWritable(); + + auto mod = rw->newModifyCommand("INSERT INTO test VALUES(?, ?, ?, ?, ?)"); + mod->bindParamI(0, testInt); + mod->bindParamF(1, testDouble); + mod->bindParamS(2, testString); + mod->bindParamT(3, testDateTime); + mod->bindParamT(4, testInterval); + mod->execute(); + delete mod; + ds->commit(); + ds->close(); +} + +template<typename T> +void +assertColumnValueHelper(DB::SelectCommand & sel, unsigned int col, const T & t) +{ + HandleAsVariableType h; + sel[col].apply(h); + BOOST_REQUIRE_EQUAL(t, h.variable.as<T>()); +} + +BOOST_AUTO_TEST_CASE( bindAndSelect ) +{ + RdbmsDataSource * ds = CommonObjects::dataSource<RdbmsDataSource>("mysqlmock"); + auto ro = ds->getReadonly(); + + auto select = ro->newSelectCommand("SELECT * FROM test WHERE id = ?"); + select->bindParamI(0, testInt); + select->execute(); + int rows = 0; + while (select->fetch()) { + assertColumnValueHelper(*select, 0, testInt); + assertColumnValueHelper(*select, 1, testDouble); + assertColumnValueHelper(*select, 2, testString); + assertColumnValueHelper(*select, 3, testDateTime); + assertColumnValueHelper(*select, 4, testInterval); + rows += 1; + } + delete select; + BOOST_REQUIRE_EQUAL(1, rows); + ds->close(); +} + +BOOST_AUTO_TEST_CASE( bindAndSelectOther ) +{ + RdbmsDataSource * ds = CommonObjects::dataSource<RdbmsDataSource>("mysqlmock"); + auto ro = ds->getReadonly(); + + auto select = ro->newSelectCommand("SELECT * FROM test WHERE id != ?"); + select->bindParamI(0, testInt); + select->execute(); + int rows = 0; + while (select->fetch()) { + assertColumnValueHelper(*select, 0, 4); + assertColumnValueHelper(*select, 1, 123.45); + assertColumnValueHelper(*select, 2, std::string("some text")); + assertColumnValueHelper(*select, 3, boost::posix_time::ptime_from_tm({ 3, 6, 23, 27, 3, 115, 0, 0, 0, 0, 0})); + assertColumnValueHelper(*select, 4, boost::posix_time::time_duration(38, 13, 12)); + rows += 1; + } + delete select; + BOOST_REQUIRE_EQUAL(1, rows); + ds->close(); +} + +BOOST_AUTO_TEST_SUITE_END(); + |