////////////////////////////////////////////////////////////////////// // Copyright (c) 2010, Oliver 'kfs1' Smith // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // // - Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // - Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // - Neither the name of KingFisher Software nor the names of its contributors // may be used to endorse or promote products derived from this software // without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED // TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // ////////////////////////////////////////////////////////////////////// // #include #include "dbaConn.h" #include // Controls verbose output. static bool g_verbose = false ; using namespace std; #define ATTEMPT( _statement ) \ progress << " . " << __LINE__ << ":" << #_statement << ": " ; \ _statement ; \ progress << "OK." << endl #define CHECK( _statement ) \ progress << " ? " << __LINE__ << ":" << #_statement << ": " ; \ if ( !(_statement) ) throw std::logic_error("Not true") ; \ progress << "OK." << endl #define ANNOTATE( _statement ) \ if ( g_verbose ) progress << " # " << _statement << "." << endl template bool testDatabase(const char* name, const DBA::Credentials& creds) { stringstream progress ; try { cout << "- Testing database type '" << name << "':" ; // Create an instance of the database connector. DBAType conn ; // Attempt to connect. ATTEMPT( conn.Connect(creds) ) ; ANNOTATE( "Connected to database '" << creds.database << "'" ) ; // Attempt a simple query. const char statement[] = "SELECT 40 + 2" ; ATTEMPT( DBA::ResultSet rs(conn, sizeof(statement) - 1, statement) ) ; // We know the query should have returned a row. CHECK( rs.HasRows() ) ; // Can we retrieve it? CHECK( rs.FetchRow() ) ; const char* indexedValue = NULL ; ATTEMPT( indexedValue = rs[0] ) ; ANNOTATE( "rs[0] gave '" << indexedValue << "'" ) ; // Make sure what we got back looks sensible. CHECK( indexedValue != NULL ) ; CHECK( strcmp(indexedValue, "42") == 0 ) ; // Verify that an illegal array index throws an exception. progress << " . logic_error throw on illegal operator[] index: " ; try { indexedValue = rs[999] ; throw std::logic_error("No exception.") ; } catch ( std::logic_error& e ) { progress << "OK." << endl ; } // Now try with operator >>. const char* cursoredValue = NULL ; ATTEMPT( rs >> cursoredValue ) ; ANNOTATE( "rs >> cursoredValue gave '" << cursoredValue << "'" ) ; // And does that also look good? CHECK( cursoredValue != NULL ) ; CHECK( strcmp(cursoredValue, "42") == 0 ) ; // Ensure that a further use of >> throws an exception. progress << " . " << __LINE__ << ":logic_error throw at end of row: " ; try { rs >> cursoredValue ; throw std::logic_error("No exception.") ; } catch ( std::logic_error& e ) { progress << "OK." << endl ; } // Ensure that FetchRow() returns false attempting to fetch a second row. CHECK( !rs.FetchRow() ) ; // Test 'Do' and then use the result to test multiple columns. ATTEMPT( rs.Do("SELECT '%s', '%s', 'A string of some length', %u + %u, %f * %f, NULL", 200, "Hello", "world", 40, 2, 3.1, 4.2) ) ; // Retrieve the results. CHECK( rs.HasRows() && rs.FetchRow() ) ; CHECK( rs[0] != NULL && rs[1] != NULL && rs[2] != NULL && rs[3] != NULL && rs[4] != NULL ) ; CHECK( rs[5] == NULL ) ; CHECK( strcmp(rs[0], "Hello") == 0 ) ; CHECK( strcmp(rs[1], "world") == 0 ) ; CHECK( strcmp(rs[2], "A string of some length") == 0 ) ; CHECK( strcmp(rs[3], "42") == 0 ) ; CHECK( (float)atof(rs[4]) == (float)13.02 ) ; // Now attempt to access them via cursor. const char* hello = NULL ; const char* world = NULL ; const char* longstring = NULL ; unsigned int life = 0 ; float danger = 0.0 ; ATTEMPT( rs >> hello >> world >> longstring >> life >> danger ) ; CHECK( hello != NULL && strcmp(hello, "Hello") == 0 ) ; CHECK( world != NULL && strcmp(world, "world") == 0 ) ; CHECK( longstring != NULL && strcmp(longstring, "A string of some length") == 0 ) ; CHECK( life == 42 ) ; CHECK( danger == 13.02f ) ; // And finally, some more advanced functionality. const char* strings[3] = { "hello", "world", "farewell" } ; static const char createTable[] = "CREATE TEMPORARY TABLE t_dba_test ( `an_int` int not null, `a_string` varchar(30) )" ; ATTEMPT( rs.Do(sizeof(createTable) - 1, createTable) ) ; ATTEMPT( rs.Do("INSERT INTO t_dba_test (`an_int`, `a_string`) VALUES (0, '%s')", 200, strings[0]) ) ; ATTEMPT( rs.Do("INSERT INTO t_dba_test (`an_int`, `a_string`) VALUES (1, '%s')", 200, strings[1]) ) ; ATTEMPT( rs.Do("INSERT INTO t_dba_test (`an_int`, `a_string`) VALUES (2, '%s')", 200, strings[2]) ) ; static const char selectRows[] = "SELECT `an_int`, `a_string`, NULL, 1 FROM `t_dba_test` ORDER BY `an_int`" ; ATTEMPT( rs.Do(sizeof(selectRows) - 1, selectRows) ) ; CHECK( rs.HasRows() ) ; if ( conn.HasAccurateRowCount() ) { CHECK( rs.Rows() == 3 ) ; } else { CHECK( rs.Rows() >= 1 ) ; } int row = 0 ; do { ANNOTATE( "Row " << row ) ; int theInt = ~0 ; // So it won't match the 0 in the first row. const char* theString = NULL ; const char* null = NULL ; CHECK( rs.FetchRow() ) ; CHECK( rs[1] != NULL && strcmp(rs[1], strings[row]) == 0 ) ; CHECK( rs[2] == NULL || rs[2][0] == 0 ) ; ATTEMPT( rs >> theInt ) ; CHECK( theInt == row ) ; ATTEMPT( rs >> theString ) ; CHECK( theString != NULL && strcmp(theString, strings[row]) == 0 ) ; ATTEMPT( rs >> null ) ; CHECK( null == NULL || *null == 0 ) ; ++row ; } while ( row < 3 ) ; // We should get false now if we try for another row. CHECK( !rs.FetchRow() ) ; ATTEMPT( conn.Disconnect() ) ; } catch ( std::exception& e ) { progress << "FAIL: " << e.what() << endl ; cout << "FAILED." << endl << progress.rdbuf()->str().c_str() ; return false ; } cout << "OK." << endl ; if ( g_verbose ) cout << progress.rdbuf()->str().c_str() ; return true ; } int main(int argc, const char* argv[]) { // If called with "-v" or "/v", enable verbose output. if ( argc > 1 && (strcmp(argv[1], "-v") == 0 || strcmp(argv[1], "/v") == 0) ) g_verbose = true ; bool success = true ; #ifdef DBA_ENABLE_MYSQL // MySQL database credentials, I have a local database called 'test' // my username is 'osmith' with password 'database'. DBA::Credentials myCredentials("localhost", 0, "test", "osmith", "database") ; if ( !testDatabase("MySQL", myCredentials) ) success = false ; #endif #ifdef DBA_ENABLE_SQLITE DBA::Credentials sqCredentials("test") ; if ( !testDatabase("SQLite", sqCredentials) ) success = false ; #endif if ( success ) cout << "SUCCESS. All tests passed." << endl ; else cout << "FAILED. Some tests failed." << endl ; return 0 ; }