summaryrefslogtreecommitdiff
path: root/src/tests
diff options
context:
space:
mode:
Diffstat (limited to 'src/tests')
-rw-r--r--src/tests/testaccount.cpp207
-rw-r--r--src/tests/testaccount.h116
-rw-r--r--src/tests/testcipher.cpp154
-rw-r--r--src/tests/testcipher.h115
-rw-r--r--src/tests/testdataprovider.cpp334
-rw-r--r--src/tests/testdataprovider.h156
-rw-r--r--src/tests/testrecordset.cpp285
-rw-r--r--src/tests/testrecordset.h215
-rw-r--r--src/tests/testsmain.cpp65
-rw-r--r--src/tests/teststorage.cpp310
-rw-r--r--src/tests/teststorage.h132
11 files changed, 2089 insertions, 0 deletions
diff --git a/src/tests/testaccount.cpp b/src/tests/testaccount.cpp
new file mode 100644
index 00000000..a4061164
--- /dev/null
+++ b/src/tests/testaccount.cpp
@@ -0,0 +1,207 @@
+/*
+ * The Mana World Server
+ * Copyright 2004 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or any later version.
+ *
+ * The Mana World is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with The Mana World; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * $Id$
+ */
+
+
+#include "testaccount.h"
+
+
+// register the fixture into the 'registry'
+CPPUNIT_TEST_SUITE_REGISTRATION(AccountTest);
+
+
+using namespace tmwserv;
+
+
+/**
+ * Set up fixtures.
+ */
+void
+AccountTest::setUp(void)
+{
+ Being* sam = new Being("sam", 1, 1, 1, 1, 1, 1, 1, 1);
+ Being* merry = new Being("merry", 1, 1, 1, 1, 1, 1, 1, 1);
+ Being* pippin = new Being("pippin", 1, 1, 1, 1, 1, 1, 1, 1);
+ mCharacters.push_back(sam);
+ mCharacters.push_back(merry);
+ mCharacters.push_back(pippin);
+
+ mAccount = new Account("frodo",
+ "baggins",
+ "frodo@theshire.com",
+ mCharacters);
+}
+
+
+/**
+ * Tear down fixtures.
+ */
+void
+AccountTest::tearDown(void)
+{
+ for (Beings::iterator it = mCharacters.begin();
+ it != mCharacters.end();
+ ++it)
+ {
+ delete (*it);
+ }
+
+ delete mAccount;
+ mAccount = 0;
+}
+
+
+/**
+ * Test creating an Account using the default constructor
+ * and setting the account info using the mutators.
+ */
+void
+AccountTest::testCreate1(void)
+{
+ const std::string name("frodo");
+ const std::string password("baggins");
+ const std::string encrypted("d70d266f4c276b5706881a46f43a88b0");
+ const std::string email("frodo@theshire.com");
+
+ Account account;
+ account.setName(name);
+ account.setPassword(password);
+ account.setEmail(email);
+ account.setCharacters(mCharacters);
+
+ CPPUNIT_ASSERT_EQUAL(account.getName(), name);
+ CPPUNIT_ASSERT_EQUAL(account.getPassword(), encrypted);
+ CPPUNIT_ASSERT_EQUAL(account.getEmail(), email);
+
+ CPPUNIT_ASSERT_EQUAL(mAccount->getCharacters().size(),
+ mCharacters.size());
+
+ Beings& characters = account.getCharacters();
+
+ for (int i = 0; i < mCharacters.size(); ++i) {
+ CPPUNIT_ASSERT_EQUAL(characters[i]->getName(),
+ mCharacters[i]->getName());
+ }
+}
+
+
+/**
+ * Test creating an Account passing the initial account info
+ * to the constructor.
+ */
+void
+AccountTest::testCreate2(void)
+{
+ const std::string name("frodo");
+ const std::string password("baggins");
+ const std::string encrypted("d70d266f4c276b5706881a46f43a88b0");
+ const std::string email("frodo@theshire.com");
+
+ Account account(name, password, email, mCharacters);
+
+ CPPUNIT_ASSERT_EQUAL(account.getName(), name);
+ CPPUNIT_ASSERT_EQUAL(account.getPassword(), encrypted);
+ CPPUNIT_ASSERT_EQUAL(account.getEmail(), email);
+
+ CPPUNIT_ASSERT_EQUAL(mAccount->getCharacters().size(),
+ mCharacters.size());
+
+ Beings& characters = account.getCharacters();
+
+ for (int i = 0; i < mCharacters.size(); ++i) {
+ CPPUNIT_ASSERT_EQUAL(characters[i]->getName(),
+ mCharacters[i]->getName());
+ }
+}
+
+
+/**
+ * Test adding a new character.
+ */
+void
+AccountTest::testAddCharacter1(void)
+{
+ Being* bilbo = new Being("bilbo", 1, 1, 1, 1, 1, 1, 1, 1);
+
+ mAccount->addCharacter(bilbo);
+
+ CPPUNIT_ASSERT_EQUAL(mAccount->getCharacters().size(), (size_t) 4);
+
+ std::vector<std::string> names;
+ names.push_back("sam");
+ names.push_back("merry");
+ names.push_back("pippin");
+ names.push_back("bilbo");
+
+ for (int i = 0; i < mCharacters.size(); ++i) {
+ CPPUNIT_ASSERT_EQUAL(mCharacters[i]->getName(), names[i]);
+ }
+
+ delete bilbo;
+}
+
+
+/**
+ * Test passing a NULL pointer to addCharacter().
+ */
+void
+AccountTest::testAddCharacter2(void)
+{
+ mAccount->addCharacter(NULL);
+
+ CPPUNIT_ASSERT_EQUAL(mAccount->getCharacters().size(), (size_t) 3);
+
+ std::vector<std::string> names;
+ names.push_back("sam");
+ names.push_back("merry");
+ names.push_back("pippin");
+
+ for (int i = 0; i < mCharacters.size(); ++i) {
+ CPPUNIT_ASSERT_EQUAL(mCharacters[i]->getName(), names[i]);
+ }
+}
+
+
+/**
+ * Test the accessor getCharacter() with a valid name.
+ */
+void
+AccountTest::testGetCharacter1(void)
+{
+ const std::string name("merry");
+
+ Being* merry = mAccount->getCharacter(name);
+
+ CPPUNIT_ASSERT(merry != 0);
+ CPPUNIT_ASSERT_EQUAL(merry->getName(), name);
+}
+
+
+/**
+ * Test the accessor getCharacter() with an unexisting name.
+ */
+void
+AccountTest::testGetCharacter2(void)
+{
+ Being* nobody = mAccount->getCharacter("johndoe");
+
+ CPPUNIT_ASSERT(nobody == 0);
+}
diff --git a/src/tests/testaccount.h b/src/tests/testaccount.h
new file mode 100644
index 00000000..8806e766
--- /dev/null
+++ b/src/tests/testaccount.h
@@ -0,0 +1,116 @@
+/*
+ * The Mana World Server
+ * Copyright 2004 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or any later version.
+ *
+ * The Mana World is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with The Mana World; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * $Id$
+ */
+
+
+#ifndef _TMWSERV_TEST_ACCOUNT_H_
+#define _TMWSERV_TEST_ACCOUNT_H_
+
+
+#include <cppunit/extensions/HelperMacros.h>
+
+#include "../account.h"
+
+
+/**
+ * Unit test for the Account class.
+ */
+class AccountTest: public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(AccountTest);
+
+ // add tests to the test suite.
+ CPPUNIT_TEST(testCreate1);
+ CPPUNIT_TEST(testCreate2);
+ CPPUNIT_TEST(testAddCharacter1);
+ CPPUNIT_TEST(testAddCharacter2);
+ CPPUNIT_TEST(testGetCharacter1);
+ CPPUNIT_TEST(testGetCharacter2);
+
+ CPPUNIT_TEST_SUITE_END();
+
+
+ public:
+ /**
+ * Set up fixtures.
+ */
+ void
+ setUp(void);
+
+
+ /**
+ * Tear down fixtures.
+ */
+ void
+ tearDown(void);
+
+
+ /**
+ * Test creating an Account using the default constructor
+ * and setting the account info using the mutators.
+ */
+ void
+ testCreate1(void);
+
+
+ /**
+ * Test creating an Account passing the initial account info
+ * to the constructor.
+ */
+ void
+ testCreate2(void);
+
+
+ /**
+ * Test adding a new character.
+ */
+ void
+ testAddCharacter1(void);
+
+
+ /**
+ * Test passing a NULL pointer to addCharacter().
+ */
+ void
+ testAddCharacter2(void);
+
+
+ /**
+ * Test the accessor getCharacter() with a valid name.
+ */
+ void
+ testGetCharacter1(void);
+
+
+ /**
+ * Test the accessor getCharacter() with an unexisting name.
+ */
+ void
+ testGetCharacter2(void);
+
+
+ private:
+ tmwserv::Account* mAccount; /**< the default account */
+ Beings mCharacters; /**< a list of beings */
+};
+
+
+#endif // _TMWSERV_TEST_ACCOUNT_H_
diff --git a/src/tests/testcipher.cpp b/src/tests/testcipher.cpp
new file mode 100644
index 00000000..0c40fdf9
--- /dev/null
+++ b/src/tests/testcipher.cpp
@@ -0,0 +1,154 @@
+/*
+ * The Mana World Server
+ * Copyright 2004 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or any later version.
+ *
+ * The Mana World is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with The Mana World; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * $Id$
+ */
+
+
+#include <string>
+
+#include "../utils/cipher.h"
+#include "testcipher.h"
+
+
+// register the fixture into the 'registry'
+CPPUNIT_TEST_SUITE_REGISTRATION(CipherTest);
+
+
+using namespace tmwserv::utils;
+
+
+/**
+ * Set up fixtures.
+ */
+void
+CipherTest::setUp(void)
+{
+ // NOOP
+}
+
+
+/**
+ * Tear down fixtures.
+ */
+void
+CipherTest::tearDown(void)
+{
+ // NOOP
+}
+
+
+/**
+ * Test encoding a string with the MD5 digest algorithm.
+ */
+void
+CipherTest::testMd5_1(void)
+{
+ const std::string expected("d41d8cd98f00b204e9800998ecf8427e");
+ std::string actual(Cipher::instance().md5(""));
+
+ CPPUNIT_ASSERT_EQUAL(actual, expected);
+}
+
+
+/**
+ * Test encoding a string with the MD5 digest algorithm.
+ */
+void
+CipherTest::testMd5_2(void)
+{
+ const std::string expected("0cc175b9c0f1b6a831c399e269772661");
+ std::string actual(Cipher::instance().md5("a"));
+
+ CPPUNIT_ASSERT_EQUAL(actual, expected);
+}
+
+
+/**
+ * Test encoding a string with the MD5 digest algorithm.
+ */
+void
+CipherTest::testMd5_3(void)
+{
+ const std::string expected("900150983cd24fb0d6963f7d28e17f72");
+ std::string actual(Cipher::instance().md5("abc"));
+
+ CPPUNIT_ASSERT_EQUAL(actual, expected);
+}
+
+
+/**
+ * Test encoding a string with the MD5 digest algorithm.
+ */
+void
+CipherTest::testMd5_4(void)
+{
+ const std::string expected("f96b697d7cb7938d525a2f31aaf161d0");
+ std::string actual(Cipher::instance().md5("message digest"));
+
+ CPPUNIT_ASSERT_EQUAL(actual, expected);
+}
+
+
+/**
+ * Test encoding a string with the MD5 digest algorithm.
+ */
+void
+CipherTest::testMd5_5(void)
+{
+ const std::string expected("c3fcd3d76192e4007dfb496cca67e13b");
+ std::string actual(Cipher::instance().md5("abcdefghijklmnopqrstuvwxyz"));
+
+ CPPUNIT_ASSERT_EQUAL(actual, expected);
+}
+
+
+/**
+ * Test encoding a string with the MD5 digest algorithm.
+ */
+void
+CipherTest::testMd5_6(void)
+{
+ const std::string expected("d174ab98d277d9f5a5611c2c9f419d9f");
+
+ std::string s("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+ s += "abcdefghijklmnopqrstuvwxyz";
+ s += "0123456789";
+ std::string actual(Cipher::instance().md5(s));
+
+ CPPUNIT_ASSERT_EQUAL(actual, expected);
+}
+
+
+/**
+ * Test encoding a string with the MD5 digest algorithm.
+ */
+void
+CipherTest::testMd5_7(void)
+{
+ const std::string expected("57edf4a22be3c955ac49da2e2107b67a");
+
+ std::string s;
+ for (int i = 0; i < 8; ++i) {
+ s += "1234567890";
+ }
+ std::string actual(Cipher::instance().md5(s));
+
+ CPPUNIT_ASSERT_EQUAL(actual, expected);
+}
diff --git a/src/tests/testcipher.h b/src/tests/testcipher.h
new file mode 100644
index 00000000..7db81803
--- /dev/null
+++ b/src/tests/testcipher.h
@@ -0,0 +1,115 @@
+/*
+ * The Mana World Server
+ * Copyright 2004 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or any later version.
+ *
+ * The Mana World is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with The Mana World; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * $Id$
+ */
+
+
+#ifndef _TMWSERV_TEST_CIPHER_H_
+#define _TMWSERV_TEST_CIPHER_H_
+
+
+#include <cppunit/extensions/HelperMacros.h>
+
+
+/**
+ * Unit test for the Cipher class.
+ */
+class CipherTest: public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(CipherTest);
+
+ // add tests to the test suite.
+ CPPUNIT_TEST(testMd5_1);
+ CPPUNIT_TEST(testMd5_2);
+ CPPUNIT_TEST(testMd5_3);
+ CPPUNIT_TEST(testMd5_4);
+ CPPUNIT_TEST(testMd5_5);
+ CPPUNIT_TEST(testMd5_6);
+ CPPUNIT_TEST(testMd5_7);
+
+ CPPUNIT_TEST_SUITE_END();
+
+
+ public:
+ /**
+ * Set up fixtures.
+ */
+ void
+ setUp(void);
+
+
+ /**
+ * Tear down fixtures.
+ */
+ void
+ tearDown(void);
+
+
+ /**
+ * Test encoding a string with the MD5 digest algorithm.
+ */
+ void
+ testMd5_1(void);
+
+
+ /**
+ * Test encoding a string with the MD5 digest algorithm.
+ */
+ void
+ testMd5_2(void);
+
+
+ /**
+ * Test encoding a string with the MD5 digest algorithm.
+ */
+ void
+ testMd5_3(void);
+
+
+ /**
+ * Test encoding a string with the MD5 digest algorithm.
+ */
+ void
+ testMd5_4(void);
+
+
+ /**
+ * Test encoding a string with the MD5 digest algorithm.
+ */
+ void
+ testMd5_5(void);
+
+
+ /**
+ * Test encoding a string with the MD5 digest algorithm.
+ */
+ void
+ testMd5_6(void);
+
+
+ /**
+ * Test encoding a string with the MD5 digest algorithm.
+ */
+ void
+ testMd5_7(void);
+};
+
+
+#endif // _TMWSERV_TEST_CIPHER_H_
diff --git a/src/tests/testdataprovider.cpp b/src/tests/testdataprovider.cpp
new file mode 100644
index 00000000..06a2b0ee
--- /dev/null
+++ b/src/tests/testdataprovider.cpp
@@ -0,0 +1,334 @@
+/*
+ * The Mana World Server
+ * Copyright 2004 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or any later version.
+ *
+ * The Mana World is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with The Mana World; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * $Id$
+ */
+
+
+#include <physfs.h>
+
+#if defined (MYSQL_SUPPORT)
+#include <mysql/mysql.h>
+#elif defined (POSTGRE_SUPPORT)
+#include <libpq-fe.h>
+#endif
+
+#include "../dal/dataproviderfactory.h"
+
+#include "testdataprovider.h"
+
+
+// register the fixture into the 'registry'
+CPPUNIT_TEST_SUITE_REGISTRATION(DataProviderTest);
+
+
+// initialize the static variables.
+std::string DataProviderTest::mDbName("tmwteststorage");
+std::string DataProviderTest::mDbUser("guest");
+std::string DataProviderTest::mDbPassword("guest");
+std::string DataProviderTest::mSqlCreateTable(
+ "create table employees ("
+ " id integer primary key, "
+ " name varchar(32) not null);"
+);
+std::string DataProviderTest::mSqlInsertRow(
+ "insert into employees values (1, 'john');"
+);
+std::string DataProviderTest::mSqlFetchRow("select * from employees;");
+
+
+using namespace tmwserv::dal;
+
+
+/**
+ * Set up fixtures.
+ */
+void
+DataProviderTest::setUp(void)
+{
+ // reinitialize the database before each test.
+ reinitDb();
+
+ // obtain a data provider.
+ try {
+ mDb = DataProviderFactory::createDataProvider();
+ }
+ catch (const std::runtime_error& e) {
+ CPPUNIT_FAIL(e.what());
+ }
+}
+
+
+/**
+ * Tear down fixtures.
+ */
+void
+DataProviderTest::tearDown(void)
+{
+ mDb->disconnect();
+
+ delete mDb;
+ mDb = 0;
+
+ // clean the database after each test.
+ reinitDb();
+}
+
+
+/**
+ * Connection to an existing database.
+ */
+void
+DataProviderTest::testConnection1(void)
+{
+#ifdef SQLITE_SUPPORT
+ std::string dbFile(mDbName);
+ dbFile += ".db";
+
+ mDb->connect(dbFile, mDbUser, mDbPassword);
+#else
+ mDb->connect(mDbName, mDbUser, mDbPassword);
+#endif
+
+ CPPUNIT_ASSERT(mDb->isConnected());
+}
+
+
+/**
+ * Create a new table in the database.
+ */
+void
+DataProviderTest::testCreateTable1(void)
+{
+#ifdef SQLITE_SUPPORT
+ std::string dbFile(mDbName);
+ dbFile += ".db";
+
+ mDb->connect(dbFile, mDbUser, mDbPassword);
+#else
+ mDb->connect(mDbName, mDbUser, mDbPassword);
+#endif
+
+ CPPUNIT_ASSERT(mDb->isConnected());
+
+ const RecordSet& rs = mDb->execSql(mSqlCreateTable);
+ CPPUNIT_ASSERT(rs.isEmpty());
+
+ mDb->disconnect();
+ CPPUNIT_ASSERT(!mDb->isConnected());
+}
+
+
+/**
+ * Create the same table one more time in the database.
+ */
+void
+DataProviderTest::testCreateTable2(void)
+{
+#ifdef SQLITE_SUPPORT
+ std::string dbFile(mDbName);
+ dbFile += ".db";
+
+ mDb->connect(dbFile, mDbUser, mDbPassword);
+#else
+ mDb->connect(mDbName, mDbUser, mDbPassword);
+#endif
+
+ CPPUNIT_ASSERT(mDb->isConnected());
+
+ mDb->execSql(mSqlCreateTable);
+ // this should throw tmwserv::dal::DbSqlQueryExecFailure.
+ mDb->execSql(mSqlCreateTable);
+}
+
+
+/**
+ * Insert a new row to the table.
+ */
+void
+DataProviderTest::testInsert1(void)
+{
+#ifdef SQLITE_SUPPORT
+ std::string dbFile(mDbName);
+ dbFile += ".db";
+
+ mDb->connect(dbFile, mDbUser, mDbPassword);
+#else
+ mDb->connect(mDbName, mDbUser, mDbPassword);
+#endif
+
+ CPPUNIT_ASSERT(mDb->isConnected());
+
+ mDb->execSql(mSqlCreateTable);
+
+ const RecordSet& rs = mDb->execSql(mSqlInsertRow);
+ // an insert query does not return any records
+ // so the recordset remains empty.
+ CPPUNIT_ASSERT(rs.isEmpty());
+
+ mDb->disconnect();
+ CPPUNIT_ASSERT(!mDb->isConnected());
+}
+
+
+/**
+ * Insert the same record again.
+ */
+void
+DataProviderTest::testInsert2(void)
+{
+#ifdef SQLITE_SUPPORT
+ std::string dbFile(mDbName);
+ dbFile += ".db";
+
+ mDb->connect(dbFile, mDbUser, mDbPassword);
+#else
+ mDb->connect(mDbName, mDbUser, mDbPassword);
+#endif
+
+ CPPUNIT_ASSERT(mDb->isConnected());
+
+ // this should throw tmwserv::dal::DbSqlQueryExecFailure
+ // as we are violating the primary key uniqueness.
+ mDb->execSql(mSqlInsertRow);
+}
+
+
+/**
+ * Fetch data from the table.
+ */
+void
+DataProviderTest::testFetch1(void)
+{
+#ifdef SQLITE_SUPPORT
+ std::string dbFile(mDbName);
+ dbFile += ".db";
+
+ mDb->connect(dbFile, mDbUser, mDbPassword);
+#else
+ mDb->connect(mDbName, mDbUser, mDbPassword);
+#endif
+
+ CPPUNIT_ASSERT(mDb->isConnected());
+
+ mDb->execSql(mSqlCreateTable);
+ mDb->execSql(mSqlInsertRow);
+
+ const RecordSet& rs = mDb->execSql(mSqlFetchRow);
+ CPPUNIT_ASSERT(!rs.isEmpty());
+
+ std::string id("1");
+ std::string name("john");
+ CPPUNIT_ASSERT_EQUAL(id, rs(0, "id"));
+ CPPUNIT_ASSERT_EQUAL(name, rs(0, "name"));
+
+ mDb->disconnect();
+ CPPUNIT_ASSERT(!mDb->isConnected());
+}
+
+
+/**
+ * Disconnection from an open database.
+ */
+void
+DataProviderTest::testDisconnection1(void)
+{
+#ifdef SQLITE_SUPPORT
+ std::string dbFile(mDbName);
+ dbFile += ".db";
+
+ mDb->connect(dbFile, mDbUser, mDbPassword);
+#else
+ mDb->connect(mDbName, mDbUser, mDbPassword);
+#endif
+
+ CPPUNIT_ASSERT(mDb->isConnected());
+
+ mDb->disconnect();
+ CPPUNIT_ASSERT(!mDb->isConnected());
+}
+
+
+/**
+ * Disconnection from a closed database.
+ */
+void
+DataProviderTest::testDisconnection2(void)
+{
+ mDb->disconnect();
+ CPPUNIT_ASSERT(!mDb->isConnected());
+}
+
+
+/**
+ * Reinitialize the database.
+ */
+void
+DataProviderTest::reinitDb(void)
+{
+ // we are not using DataProvider::execSql() to execute the SQL queries
+ // here as the class is the purpose of these tests.
+
+#if defined (MYSQL_SUPPORT)
+ {
+ // initialize the MySQL library.
+ MYSQL* db = mysql_init(NULL);
+
+ if (!db) {
+ CPPUNIT_FAIL("unable to initialize the MySQL library: no memory");
+ }
+
+ // connect to the database.
+ if (!mysql_real_connect(db,
+ NULL,
+ mDbUser.c_str(),
+ mDbPassword.c_str(),
+ mDbName.c_str(),
+ 0,
+ NULL,
+ 0))
+ {
+ CPPUNIT_FAIL(mysql_error(db));
+ }
+
+ // drop the table.
+ std::string sql("drop table employees;");
+ if (mysql_query(db, sql.c_str()) != 0) {
+ // ignore, the table may not exist.
+ }
+
+ // close the connection and free memory.
+ mysql_close(db);
+ mysql_library_end();
+ db = 0;
+ }
+#elif defined (POSTGRE_SUPPORT)
+ // TODO: drop tables.
+#else // SQLITE_SUPPORT
+ std::string dbFile(mDbName);
+ dbFile += ".db";
+
+ // ensure that the database file does not exist.
+ if (PHYSFS_exists(dbFile.c_str())) {
+ if (PHYSFS_delete(dbFile.c_str()) == 0) {
+ CPPUNIT_FAIL(PHYSFS_getLastError());
+ }
+ }
+#endif
+}
diff --git a/src/tests/testdataprovider.h b/src/tests/testdataprovider.h
new file mode 100644
index 00000000..1f0a082c
--- /dev/null
+++ b/src/tests/testdataprovider.h
@@ -0,0 +1,156 @@
+/*
+ * The Mana World Server
+ * Copyright 2004 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or any later version.
+ *
+ * The Mana World is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with The Mana World; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * $Id$
+ */
+
+
+#ifndef _TMWSERV_TEST_DATA_PROVIDER_H_
+#define _TMWSERV_TEST_DATA_PROVIDER_H_
+
+
+#include <cppunit/extensions/HelperMacros.h>
+
+#include "../dal/dalexcept.h"
+#include "../dal/dataprovider.h"
+
+
+/**
+ * Requirements:
+ * - if using MySQL or PostgreSQL as backends, then make sure that a user
+ * named 'guest' with the password 'guest' has enough privileges to
+ * create tables and populate them in a database named 'tmwteststorage'
+ * prior to running the teststorage unit tests.
+ */
+
+
+using namespace tmwserv::dal;
+
+
+/**
+ * Unit test for the DataProvider class.
+ */
+class DataProviderTest: public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(DataProviderTest);
+
+ // add tests to the test suite.
+ CPPUNIT_TEST(testConnection1);
+ CPPUNIT_TEST(testCreateTable1);
+ CPPUNIT_TEST_EXCEPTION(testCreateTable2, DbSqlQueryExecFailure);
+ CPPUNIT_TEST(testInsert1);
+ CPPUNIT_TEST_EXCEPTION(testInsert2, DbSqlQueryExecFailure);
+ CPPUNIT_TEST(testFetch1);
+ CPPUNIT_TEST(testDisconnection1);
+ CPPUNIT_TEST(testDisconnection2);
+
+ CPPUNIT_TEST_SUITE_END();
+
+
+ public:
+ /**
+ * Set up fixtures.
+ */
+ void
+ setUp(void);
+
+
+ /**
+ * Tear down fixtures.
+ */
+ void
+ tearDown(void);
+
+
+ /**
+ * Connection to an existing database.
+ */
+ void
+ testConnection1(void);
+
+
+ /**
+ * Create a new table in the database.
+ */
+ void
+ testCreateTable1(void);
+
+
+ /**
+ * Create the same table one more time in the database.
+ */
+ void
+ testCreateTable2(void);
+
+
+ /**
+ * Insert a new record into the table.
+ */
+ void
+ testInsert1(void);
+
+
+ /**
+ * Insert the same record again.
+ */
+ void
+ testInsert2(void);
+
+
+ /**
+ * Fetch data from the table.
+ */
+ void
+ testFetch1(void);
+
+
+ /**
+ * Disconnection from an open database.
+ */
+ void
+ testDisconnection1(void);
+
+
+ /**
+ * Disconnection from a closed database.
+ */
+ void
+ testDisconnection2(void);
+
+
+ private:
+ /**
+ * Reinitialize the database.
+ */
+ void
+ reinitDb(void);
+
+
+ private:
+ DataProvider* mDb; /**< the data provider */
+ static std::string mDbName; /**< the database name */
+ static std::string mDbUser; /**< the database user */
+ static std::string mDbPassword; /**< the database password */
+ static std::string mSqlCreateTable; /**< SQL query to create table */
+ static std::string mSqlInsertRow; /**< SQL query to delete table */
+ static std::string mSqlFetchRow; /**< SQL query to fetch data */
+};
+
+
+#endif // _TMWSERV_TEST_DATA_PROVIDER_H_
diff --git a/src/tests/testrecordset.cpp b/src/tests/testrecordset.cpp
new file mode 100644
index 00000000..26a9e016
--- /dev/null
+++ b/src/tests/testrecordset.cpp
@@ -0,0 +1,285 @@
+/*
+ * The Mana World Server
+ * Copyright 2004 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or any later version.
+ *
+ * The Mana World is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with The Mana World; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * $Id$
+ */
+
+
+#include <sstream>
+
+#include "testrecordset.h"
+
+
+// register the fixture into the 'registry'
+CPPUNIT_TEST_SUITE_REGISTRATION(RecordSetTest);
+
+
+using namespace tmwserv::dal;
+
+
+/**
+ * Set up fixtures.
+ */
+void
+RecordSetTest::setUp(void)
+{
+ // populate mNonEmptyRs.
+ Row headers;
+ headers.push_back("id");
+ headers.push_back("name");
+ mNonEmptyRs.setColumnHeaders(headers);
+
+ Row r1;
+ r1.push_back("1");
+ r1.push_back("john");
+ mNonEmptyRs.add(r1);
+
+ Row r2;
+ r2.push_back("2");
+ r2.push_back("mike");
+ mNonEmptyRs.add(r2);
+}
+
+
+/**
+ * Tear down fixtures.
+ */
+void
+RecordSetTest::tearDown(void)
+{
+ // NOOP
+}
+
+
+/**
+ * Test RecordSet::rows() on an empty RecordSet.
+ */
+void
+RecordSetTest::testRows1(void)
+{
+ CPPUNIT_ASSERT_EQUAL((unsigned int) 0, mEmptyRs.rows());
+}
+
+
+/**
+ * Test RecordSet::rows() on a non-empty RecordSet.
+ */
+void
+RecordSetTest::testRows2(void)
+{
+ CPPUNIT_ASSERT_EQUAL((unsigned int) 2, mNonEmptyRs.rows());
+}
+
+
+/**
+ * Test RecordSet::cols() on an empty RecordSet.
+ */
+void
+RecordSetTest::testCols1(void)
+{
+ CPPUNIT_ASSERT_EQUAL((unsigned int) 0, mEmptyRs.cols());
+}
+
+
+/**
+ * Test RecordSet::cols() on a non-empty RecordSet.
+ */
+void
+RecordSetTest::testCols2(void)
+{
+ CPPUNIT_ASSERT_EQUAL((unsigned int) 2, mNonEmptyRs.cols());
+}
+
+
+/**
+ * Call RecordSet::isEmpty() from an empty RecordSet.
+ */
+void
+RecordSetTest::testIsEmpty1(void)
+{
+ CPPUNIT_ASSERT(mEmptyRs.isEmpty());
+}
+
+
+/**
+ * Call RecordSet::isEmpty() from a non-empty RecordSet.
+ */
+void
+RecordSetTest::testIsEmpty2(void)
+{
+ CPPUNIT_ASSERT(!mNonEmptyRs.isEmpty());
+}
+
+
+/**
+ * Call RecordSet::operator() from an empty RecordSet.
+ */
+void
+RecordSetTest::testOperator1(void)
+{
+ // this should throw std::invalid_argument.
+ mEmptyRs(0, 0);
+}
+
+
+/**
+ * Call RecordSet::operator() from a non-empty RecordSet with
+ * a row index and a column index as parameters.
+ */
+void
+RecordSetTest::testOperator2(void)
+{
+ std::string value("mike");
+
+ CPPUNIT_ASSERT_EQUAL(value, mNonEmptyRs(1, 1));
+}
+
+
+/**
+ * Call RecordSet::operator() from a non-empty RecordSet with
+ * a row index and a column index that are out of range as
+ * parameters.
+ */
+void
+RecordSetTest::testOperator3(void)
+{
+ // this should throw std::out_of_range.
+ mNonEmptyRs(2, 2);
+}
+
+
+/**
+ * Call RecordSet::operator() from a non-empty RecordSet with
+ * a row index and a field name as parameters.
+ */
+void
+RecordSetTest::testOperator4(void)
+{
+ std::string value("1");
+
+ CPPUNIT_ASSERT_EQUAL(value, mNonEmptyRs(0, "id"));
+}
+
+
+/**
+ * Call RecordSet::operator() from a non-empty RecordSet with
+ * a row index that is out of range and a field name as parameters.
+ */
+void
+RecordSetTest::testOperator5(void)
+{
+ // this should throw std::out_of_range.
+ mNonEmptyRs(3, "id");
+}
+
+
+/**
+ * Call RecordSet::operator() from a non-empty RecordSet with
+ * a row index and a field name that does not exist as parameters.
+ */
+void
+RecordSetTest::testOperator6(void)
+{
+ // this should throw std::invalid_argument.
+ mNonEmptyRs(1, "noname");
+}
+
+
+/**
+ * Test writing an empty RecordSet to an output stream.
+ */
+void
+RecordSetTest::testOutputStream1(void)
+{
+ std::string emptyStr;
+
+ std::ostringstream os;
+ os << mEmptyRs;
+
+ CPPUNIT_ASSERT_EQUAL(emptyStr, os.str());
+}
+
+
+/**
+ * Test writing a non-empty RecordSet to an output stream.
+ */
+void
+RecordSetTest::testOutputStream2(void)
+{
+ std::ostringstream os1;
+ os1 << "|id|name|" << std::endl << std::endl
+ << "|1|john|" << std::endl
+ << "|2|mike|" << std::endl;
+
+ std::ostringstream os2;
+ os2 << mNonEmptyRs;
+
+ CPPUNIT_ASSERT_EQUAL(os1.str(), os2.str());
+}
+
+
+/**
+ * Test RecordSet::add() to add a new now.
+ */
+void
+RecordSetTest::testAdd1(void)
+{
+ std::string id("3");
+ std::string name("elena");
+
+ Row r;
+ r.push_back(id);
+ r.push_back(name);
+ mNonEmptyRs.add(r);
+
+ CPPUNIT_ASSERT_EQUAL((unsigned int) 3, mNonEmptyRs.rows());
+ CPPUNIT_ASSERT_EQUAL((unsigned int) 2, mNonEmptyRs.cols());
+ CPPUNIT_ASSERT_EQUAL(id, mNonEmptyRs(2, 0));
+ CPPUNIT_ASSERT_EQUAL(name, mNonEmptyRs(2, 1));
+}
+
+
+/**
+ * Test RecordSet::add() to add a new now with a different number
+ * of fields.
+ */
+void
+RecordSetTest::testAdd2(void)
+{
+ Row r;
+ r.push_back("4");
+
+ // this should throw std::invalid_argument.
+ mNonEmptyRs.add(r);
+}
+
+
+/**
+ * Test RecordSet::add() to add a new now to a RecordSet that does
+ * not have column headers.
+ */
+void
+RecordSetTest::testAdd3(void)
+{
+ Row r;
+ r.push_back("5");
+
+ // this should throw tmw::dal::RsColumnHeadersNotSet.
+ mEmptyRs.add(r);
+}
diff --git a/src/tests/testrecordset.h b/src/tests/testrecordset.h
new file mode 100644
index 00000000..66dc7117
--- /dev/null
+++ b/src/tests/testrecordset.h
@@ -0,0 +1,215 @@
+/*
+ * The Mana World Server
+ * Copyright 2004 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or any later version.
+ *
+ * The Mana World is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with The Mana World; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * $Id$
+ */
+
+
+#ifndef _TMWSERV_TEST_RECORDSET_H_
+#define _TMWSERV_TEST_RECORDSET_H_
+
+
+#include <cppunit/extensions/HelperMacros.h>
+
+#include <stdexcept>
+
+#include "../dal/dalexcept.h"
+#include "../dal/recordset.h"
+
+
+using namespace tmwserv::dal;
+
+
+/**
+ * Unit test for the RecordSet class.
+ */
+class RecordSetTest: public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(RecordSetTest);
+
+ // add tests to the test suite.
+ CPPUNIT_TEST(testRows1);
+ CPPUNIT_TEST(testRows2);
+ CPPUNIT_TEST(testCols1);
+ CPPUNIT_TEST(testCols2);
+ CPPUNIT_TEST(testIsEmpty1);
+ CPPUNIT_TEST(testIsEmpty2);
+ CPPUNIT_TEST_EXCEPTION(testOperator1, std::invalid_argument);
+ CPPUNIT_TEST(testOperator2);
+ CPPUNIT_TEST_EXCEPTION(testOperator3, std::out_of_range);
+ CPPUNIT_TEST(testOperator4);
+ CPPUNIT_TEST_EXCEPTION(testOperator5, std::out_of_range);
+ CPPUNIT_TEST_EXCEPTION(testOperator6, std::invalid_argument);
+ CPPUNIT_TEST(testOutputStream1);
+ CPPUNIT_TEST(testOutputStream2);
+ CPPUNIT_TEST(testAdd1);
+ CPPUNIT_TEST_EXCEPTION(testAdd2, std::invalid_argument);
+ CPPUNIT_TEST_EXCEPTION(testAdd3, RsColumnHeadersNotSet);
+
+ CPPUNIT_TEST_SUITE_END();
+
+
+ public:
+ /**
+ * Set up fixtures.
+ */
+ void
+ setUp(void);
+
+
+ /**
+ * Tear down fixtures.
+ */
+ void
+ tearDown(void);
+
+
+ /**
+ * Call RecordSet::rows() from an empty RecordSet.
+ */
+ void
+ testRows1(void);
+
+
+ /**
+ * Call RecordSet::rows() from a non-empty RecordSet.
+ */
+ void
+ testRows2(void);
+
+
+ /**
+ * Call RecordSet::cols() from an empty RecordSet.
+ */
+ void
+ testCols1(void);
+
+
+ /**
+ * Call RecordSet::cols() from a non-empty RecordSet.
+ */
+ void
+ testCols2(void);
+
+
+ /**
+ * Call RecordSet::isEmpty() from an empty RecordSet.
+ */
+ void
+ testIsEmpty1(void);
+
+
+ /**
+ * Call RecordSet::isEmpty() from a non-empty RecordSet.
+ */
+ void
+ testIsEmpty2(void);
+
+
+ /**
+ * Call RecordSet::operator() from an empty RecordSet.
+ */
+ void
+ testOperator1(void);
+
+
+ /**
+ * Call RecordSet::operator() from a non-empty RecordSet with
+ * a row index and a column index as parameters.
+ */
+ void
+ testOperator2(void);
+
+ /**
+ * Call RecordSet::operator() from a non-empty RecordSet with
+ * a row index and a column index that are out of range as
+ * parameters.
+ */
+ void
+ testOperator3(void);
+
+
+ /**
+ * Call RecordSet::operator() from a non-empty RecordSet with
+ * a row index and a field name as parameters.
+ */
+ void
+ testOperator4(void);
+
+
+ /**
+ * Call RecordSet::operator() from a non-empty RecordSet with
+ * a row index that is out of range and a field name as parameters.
+ */
+ void
+ testOperator5(void);
+
+
+ /**
+ * Call RecordSet::operator() from a non-empty RecordSet with
+ * a row index and a field name that does not exist as parameters.
+ */
+ void
+ testOperator6(void);
+
+
+ /**
+ * Test writing an empty RecordSet to an output stream.
+ */
+ void
+ testOutputStream1(void);
+
+
+ /**
+ * Test writing a non-empty RecordSet to an output stream.
+ */
+ void
+ testOutputStream2(void);
+
+
+ /**
+ * Call RecordSet::add() to add a new now.
+ */
+ void
+ testAdd1(void);
+
+
+ /**
+ * Call RecordSet::add() to add a new now with a different number
+ * of fields.
+ */
+ void
+ testAdd2(void);
+
+
+ /**
+ * Call RecordSet::add() to add a new now to a RecordSet that does
+ * not have column headers.
+ */
+ void
+ testAdd3(void);
+
+
+ private:
+ RecordSet mEmptyRs; /**< empty recordset */
+ RecordSet mNonEmptyRs; /**< recordset with some data */
+};
+
+
+#endif // _TMWSERV_TEST_RECORDSET_H_
diff --git a/src/tests/testsmain.cpp b/src/tests/testsmain.cpp
new file mode 100644
index 00000000..70cdc185
--- /dev/null
+++ b/src/tests/testsmain.cpp
@@ -0,0 +1,65 @@
+/*
+ * The Mana World Server
+ * Copyright 2004 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or any later version.
+ *
+ * The Mana World is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with The Mana World; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * $Id$
+ */
+
+
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <cppunit/ui/text/TextTestRunner.h>
+
+#include <physfs.h>
+
+
+/**
+ * Notes:
+ * - if the unit test application is linked to libsqlite3, there will
+ * be a memory leak (8 bytes) while no leaks are detected when linked
+ * to libmysqlclient. the leak was detected using Valgrind, an
+ * excellent memory debugger.
+ *
+ * TODO: check memory leak when linked to libpq (PostgreSQL).
+ */
+
+
+int main(int argc, char* argv[])
+{
+ // initialize the PhysicsFS library.
+ PHYSFS_init(argv[0]);
+ PHYSFS_addToSearchPath(".", 1);
+ PHYSFS_setWriteDir(".");
+
+ using namespace CppUnit;
+
+ // get the top level suite from the registry.
+ Test* suite = TestFactoryRegistry::getRegistry().makeTest();
+
+ // add the test to the list of test to run.
+ TextTestRunner runner;
+ runner.addTest(suite);
+
+ // run the tests.
+ bool wasSuccessful = runner.run();
+
+ // denitialize the PhysicsFS library.
+ PHYSFS_deinit();
+
+ // return error code 1 if the one of test failed.
+ return wasSuccessful ? 0 : 1;
+}
diff --git a/src/tests/teststorage.cpp b/src/tests/teststorage.cpp
new file mode 100644
index 00000000..9757fdf3
--- /dev/null
+++ b/src/tests/teststorage.cpp
@@ -0,0 +1,310 @@
+/*
+ * The Mana World Server
+ * Copyright 2004 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or any later version.
+ *
+ * The Mana World is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with The Mana World; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * $Id$
+ */
+
+
+#include <sstream>
+
+#include <physfs.h>
+
+#include "../utils/cipher.h"
+
+#include "../dalstoragesql.h"
+#include "../storage.h"
+#include "teststorage.h"
+
+
+// register the fixture into the 'registry'
+CPPUNIT_TEST_SUITE_REGISTRATION(StorageTest);
+
+
+// initialization of static attributes.
+std::string StorageTest::mStorageName("tmwteststorage");
+std::string StorageTest::mStorageUser("guest");
+std::string StorageTest::mStorageUserPassword("guest");
+
+
+using namespace tmwserv;
+
+
+/**
+ * Set up fixtures.
+ */
+void
+StorageTest::setUp(void)
+{
+ // reinitialize the storage before each test.
+ initStorage();
+
+ // create a storage.
+ Storage& myStorage = Storage::instance(mStorageName);
+ myStorage.setUser(mStorageUser);
+ myStorage.setPassword(mStorageUserPassword);
+
+ // open the storage.
+ myStorage.open();
+}
+
+
+/**
+ * Tear down fixtures.
+ */
+void
+StorageTest::tearDown(void)
+{
+ // close the storage.
+ Storage& myStorage = Storage::instance(mStorageName);
+ myStorage.close();
+
+ // delete the storage.
+ Storage::destroy();
+
+ // clean the storage after each test.
+ cleanStorage();
+}
+
+
+/**
+ * Fetch an existing account from the database.
+ */
+void
+StorageTest::testGetAccount1(void)
+{
+ Storage& myStorage = Storage::instance(mStorageName);
+
+ CPPUNIT_ASSERT(myStorage.isOpen());
+
+ Account* account = myStorage.getAccount("kindjal");
+
+ using namespace tmwserv::utils;
+
+ std::string name("kindjal");
+ std::string password(Cipher::instance().md5("kindjal"));
+ std::string email("kindjal@domain");
+
+ CPPUNIT_ASSERT(account != 0);
+ CPPUNIT_ASSERT_EQUAL(account->getName(), name);
+ CPPUNIT_ASSERT_EQUAL(account->getPassword(), password);
+ CPPUNIT_ASSERT_EQUAL(account->getEmail(), email);
+}
+
+
+/**
+ * Fetch an unexisting account from the database.
+ */
+void
+StorageTest::testGetAccount2(void)
+{
+ Storage& myStorage = Storage::instance(mStorageName);
+
+ if (!myStorage.isOpen()) {
+ CPPUNIT_FAIL("the storage is not opened.");
+ }
+
+ Account* account = myStorage.getAccount("xxx");
+
+ CPPUNIT_ASSERT(account == 0);
+}
+
+
+/**
+ * Initialize the storage.
+ */
+void
+StorageTest::initStorage(void)
+{
+#if defined (MYSQL_SUPPORT) || defined (POSTGRE_SUPPORT) || \
+ defined (SQLITE_SUPPORT)
+
+ // we are using a database to persist the data from the storage.
+
+ using namespace tmwserv::dal;
+
+ // insert initial data using the data provider directly.
+ // we must avoid using the APIs from Storage here as it's the purpose
+ // of these tests.
+ std::auto_ptr<DataProvider> db(DataProviderFactory::createDataProvider());
+
+ try {
+#ifdef SQLITE_SUPPORT
+ std::string dbFile(mStorageName);
+ dbFile += ".db";
+
+ // ensure that the file does not exist before the tests begin.
+ if (PHYSFS_exists(dbFile.c_str())) {
+ if (PHYSFS_delete(dbFile.c_str()) == 0) {
+ CPPUNIT_FAIL(PHYSFS_getLastError());
+ }
+ }
+
+ db->connect(dbFile, mStorageUser, mStorageUserPassword);
+#else
+ db->connect(mStorageName, mStorageUser, mStorageUserPassword);
+#endif
+
+ // drop the tables.
+ dropTable(db, MAPS_TBL_NAME);
+ dropTable(db, ACCOUNTS_TBL_NAME);
+ dropTable(db, CHARACTERS_TBL_NAME);
+ dropTable(db, ITEMS_TBL_NAME);
+ dropTable(db, WORLD_ITEMS_TBL_NAME);
+ dropTable(db, INVENTORIES_TBL_NAME);
+
+ // recreate the tables.
+ db->execSql(SQL_MAPS_TABLE);
+ db->execSql(SQL_ACCOUNTS_TABLE);
+ db->execSql(SQL_CHARACTERS_TABLE);
+ db->execSql(SQL_ITEMS_TABLE);
+ db->execSql(SQL_WORLD_ITEMS_TABLE);
+ db->execSql(SQL_INVENTORIES_TABLE);
+
+ // populate the tables.
+ insertAccount(db, "kindjal");
+
+ db->disconnect();
+ }
+ catch (const DbConnectionFailure& e) {
+ CPPUNIT_FAIL(e.what());
+ }
+ catch (const DbSqlQueryExecFailure& e) {
+ CPPUNIT_FAIL(e.what());
+ }
+ catch (const DbDisconnectionFailure& e) {
+ CPPUNIT_FAIL(e.what());
+ }
+ catch (const std::exception& e) {
+ CPPUNIT_FAIL(e.what());
+ }
+ catch (...) {
+ CPPUNIT_FAIL("unexpected exception");
+ }
+#else
+ // if we are in this block it means that we are not using a database
+ // to persist the data from the storage.
+ // at the moment, Storage assume that the data are persisted in a database
+ // so let's raise a preprocessing error.
+#error "no database backend defined"
+#endif
+}
+
+
+/**
+ * Clean the storage.
+ */
+void
+StorageTest::cleanStorage(void)
+{
+#if defined (MYSQL_SUPPORT) || defined (POSTGRE_SUPPORT) || \
+ defined (SQLITE_SUPPORT)
+
+ // we are using a database to persist the data from the storage.
+
+ using namespace tmwserv::dal;
+
+ std::auto_ptr<DataProvider> db(DataProviderFactory::createDataProvider());
+
+ try {
+#ifdef SQLITE_SUPPORT
+ std::string dbFile(mStorageName);
+ dbFile += ".db";
+
+ // ensure that the file does not exist before the tests begin.
+ if (PHYSFS_exists(dbFile.c_str())) {
+ if (PHYSFS_delete(dbFile.c_str()) == 0) {
+ CPPUNIT_FAIL(PHYSFS_getLastError());
+ }
+ }
+#else
+ db->connect(mStorageName, mStorageUser, mStorageUserPassword);
+
+ // drop the tables.
+ dropTable(db, MAPS_TBL_NAME);
+ dropTable(db, ACCOUNTS_TBL_NAME);
+ dropTable(db, CHARACTERS_TBL_NAME);
+ dropTable(db, ITEMS_TBL_NAME);
+ dropTable(db, WORLD_ITEMS_TBL_NAME);
+ dropTable(db, INVENTORIES_TBL_NAME);
+
+ db->disconnect();
+#endif
+ }
+ catch (const DbConnectionFailure& e) {
+ CPPUNIT_FAIL(e.what());
+ }
+ catch (const DbSqlQueryExecFailure& e) {
+ CPPUNIT_FAIL(e.what());
+ }
+ catch (const DbDisconnectionFailure& e) {
+ CPPUNIT_FAIL(e.what());
+ }
+ catch (const std::exception& e) {
+ CPPUNIT_FAIL(e.what());
+ }
+ catch (...) {
+ CPPUNIT_FAIL("unexpected exception");
+ }
+#else
+ // if we are in this block it means that we are not using a database
+ // to persist the data from the storage.
+ // at the moment, Storage assume that the data are persisted in a database
+ // so let's raise a preprocessing error.
+#error "no database backend defined"
+#endif
+}
+
+
+/**
+ * Drop a table.
+ */
+void
+StorageTest::dropTable(std::auto_ptr<DataProvider>& db,
+ const std::string& name)
+{
+ try {
+ std::string sql("drop table ");
+ sql += name;
+ sql += ";";
+ db->execSql(sql);
+ }
+ catch (const DbSqlQueryExecFailure& e) {
+ // ignore, the table may not exist.
+ }
+}
+
+
+/**
+ * Insert a new account.
+ */
+void
+StorageTest::insertAccount(std::auto_ptr<DataProvider>& db,
+ const std::string& name)
+{
+ std::ostringstream sql;
+
+ // the password will be identical to the name.
+
+ sql << "insert into " << ACCOUNTS_TBL_NAME
+ << "(username, password, email, level, banned) values "
+ << "('" << name << "', '" << name << "', '"
+ << name << "@domain', 1, 0);";
+
+ db->execSql(sql.str());
+}
diff --git a/src/tests/teststorage.h b/src/tests/teststorage.h
new file mode 100644
index 00000000..f8230735
--- /dev/null
+++ b/src/tests/teststorage.h
@@ -0,0 +1,132 @@
+/*
+ * The Mana World Server
+ * Copyright 2004 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or any later version.
+ *
+ * The Mana World is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with The Mana World; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * $Id$
+ */
+
+
+#ifndef _TMWSERV_TEST_STORAGE_H_
+#define _TMWSERV_TEST_STORAGE_H_
+
+
+#include <cppunit/extensions/HelperMacros.h>
+
+#include "../dal/dataproviderfactory.h"
+
+
+/**
+ * Requirements:
+ * - if using MySQL or PostgreSQL as backends, then make sure that a user
+ * named 'guest' with the password 'guest' has enough privileges to
+ * create tables and populate them in a database named 'tmwteststorage'
+ * prior to running the teststorage unit tests.
+ */
+
+
+using namespace tmwserv::dal;
+
+
+/**
+ * Unit test for the Storage class.
+ */
+class StorageTest: public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(StorageTest);
+
+ // add tests to the test suite.
+ CPPUNIT_TEST(testGetAccount1);
+ CPPUNIT_TEST(testGetAccount2);
+
+ CPPUNIT_TEST_SUITE_END();
+
+
+ public:
+ /**
+ * Set up fixtures.
+ */
+ void
+ setUp(void);
+
+
+ /**
+ * Tear down fixtures.
+ */
+ void
+ tearDown(void);
+
+
+ /**
+ * Fetch an existing account from the database.
+ */
+ void
+ testGetAccount1(void);
+
+
+ /**
+ * Fetch an unexisting account from the database.
+ */
+ void
+ testGetAccount2(void);
+
+
+ private:
+ /**
+ * Initialize the storage.
+ */
+ void
+ initStorage(void);
+
+
+ /**
+ * Clean the storage.
+ */
+ void
+ cleanStorage(void);
+
+
+ /**
+ * Drop a table.
+ *
+ * @param db the database.
+ * @param name the table name.
+ */
+ void
+ dropTable(std::auto_ptr<DataProvider>& db,
+ const std::string& name);
+
+
+ /**
+ * Insert a new account.
+ *
+ * @param db the database.
+ * @param name the user name.
+ */
+ void
+ insertAccount(std::auto_ptr<DataProvider>&,
+ const std::string& name);
+
+
+ private:
+ static std::string mStorageName;
+ static std::string mStorageUser;
+ static std::string mStorageUserPassword;
+};
+
+
+#endif // _TMWSERV_TEST_STORAGE_H_