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(); + | 
