michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "TestHarness.h" michael@0: #include "nsMemory.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsDirectoryServiceDefs.h" michael@0: #include "mozIStorageService.h" michael@0: #include "mozIStorageConnection.h" michael@0: #include "mozIStorageStatementCallback.h" michael@0: #include "mozIStorageCompletionCallback.h" michael@0: #include "mozIStorageBindingParamsArray.h" michael@0: #include "mozIStorageBindingParams.h" michael@0: #include "mozIStorageAsyncStatement.h" michael@0: #include "mozIStorageStatement.h" michael@0: #include "mozIStoragePendingStatement.h" michael@0: #include "mozIStorageError.h" michael@0: #include "nsThreadUtils.h" michael@0: michael@0: static int gTotalTests = 0; michael@0: static int gPassedTests = 0; michael@0: michael@0: #define do_check_true(aCondition) \ michael@0: PR_BEGIN_MACRO \ michael@0: gTotalTests++; \ michael@0: if (aCondition) { \ michael@0: gPassedTests++; \ michael@0: } else { \ michael@0: fail("%s | Expected true, got false at line %d", __FILE__, __LINE__); \ michael@0: } \ michael@0: PR_END_MACRO michael@0: michael@0: #define do_check_false(aCondition) \ michael@0: PR_BEGIN_MACRO \ michael@0: gTotalTests++; \ michael@0: if (!aCondition) { \ michael@0: gPassedTests++; \ michael@0: } else { \ michael@0: fail("%s | Expected false, got true at line %d", __FILE__, __LINE__); \ michael@0: } \ michael@0: PR_END_MACRO michael@0: michael@0: #define do_check_success(aResult) \ michael@0: do_check_true(NS_SUCCEEDED(aResult)) michael@0: michael@0: #ifdef LINUX michael@0: // XXX Linux opt builds on tinderbox are orange due to linking with stdlib. michael@0: // This is sad and annoying, but it's a workaround that works. michael@0: #define do_check_eq(aExpected, aActual) \ michael@0: do_check_true(aExpected == aActual) michael@0: #else michael@0: #include michael@0: michael@0: // Print nsresult as uint32_t michael@0: std::ostream& operator<<(std::ostream& aStream, const nsresult aInput) michael@0: { michael@0: return aStream << static_cast(aInput); michael@0: } michael@0: michael@0: #define do_check_eq(aExpected, aActual) \ michael@0: PR_BEGIN_MACRO \ michael@0: gTotalTests++; \ michael@0: if (aExpected == aActual) { \ michael@0: gPassedTests++; \ michael@0: } else { \ michael@0: std::ostringstream temp; \ michael@0: temp << __FILE__ << " | Expected '" << aExpected << "', got '"; \ michael@0: temp << aActual <<"' at line " << __LINE__; \ michael@0: fail(temp.str().c_str()); \ michael@0: } \ michael@0: PR_END_MACRO michael@0: #endif michael@0: michael@0: already_AddRefed michael@0: getService() michael@0: { michael@0: nsCOMPtr ss = michael@0: do_GetService("@mozilla.org/storage/service;1"); michael@0: do_check_true(ss); michael@0: return ss.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: getMemoryDatabase() michael@0: { michael@0: nsCOMPtr ss = getService(); michael@0: nsCOMPtr conn; michael@0: nsresult rv = ss->OpenSpecialDatabase("memory", getter_AddRefs(conn)); michael@0: do_check_success(rv); michael@0: return conn.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: getDatabase() michael@0: { michael@0: nsCOMPtr dbFile; michael@0: (void)NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, michael@0: getter_AddRefs(dbFile)); michael@0: NS_ASSERTION(dbFile, "The directory doesn't exists?!"); michael@0: michael@0: nsresult rv = dbFile->Append(NS_LITERAL_STRING("storage_test_db.sqlite")); michael@0: do_check_success(rv); michael@0: michael@0: nsCOMPtr ss = getService(); michael@0: nsCOMPtr conn; michael@0: rv = ss->OpenDatabase(dbFile, getter_AddRefs(conn)); michael@0: do_check_success(rv); michael@0: return conn.forget(); michael@0: } michael@0: michael@0: michael@0: class AsyncStatementSpinner : public mozIStorageStatementCallback michael@0: , public mozIStorageCompletionCallback michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_MOZISTORAGESTATEMENTCALLBACK michael@0: NS_DECL_MOZISTORAGECOMPLETIONCALLBACK michael@0: michael@0: AsyncStatementSpinner(); michael@0: michael@0: void SpinUntilCompleted(); michael@0: michael@0: uint16_t completionReason; michael@0: michael@0: protected: michael@0: virtual ~AsyncStatementSpinner() {} michael@0: volatile bool mCompleted; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(AsyncStatementSpinner, michael@0: mozIStorageStatementCallback, michael@0: mozIStorageCompletionCallback) michael@0: michael@0: AsyncStatementSpinner::AsyncStatementSpinner() michael@0: : completionReason(0) michael@0: , mCompleted(false) michael@0: { michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: AsyncStatementSpinner::HandleResult(mozIStorageResultSet *aResultSet) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: AsyncStatementSpinner::HandleError(mozIStorageError *aError) michael@0: { michael@0: int32_t result; michael@0: nsresult rv = aError->GetResult(&result); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: nsAutoCString message; michael@0: rv = aError->GetMessage(message); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsAutoCString warnMsg; michael@0: warnMsg.Append("An error occurred while executing an async statement: "); michael@0: warnMsg.AppendInt(result); michael@0: warnMsg.Append(" "); michael@0: warnMsg.Append(message); michael@0: NS_WARNING(warnMsg.get()); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: AsyncStatementSpinner::HandleCompletion(uint16_t aReason) michael@0: { michael@0: completionReason = aReason; michael@0: mCompleted = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: AsyncStatementSpinner::Complete(nsresult, nsISupports*) michael@0: { michael@0: mCompleted = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: void AsyncStatementSpinner::SpinUntilCompleted() michael@0: { michael@0: nsCOMPtr thread(::do_GetCurrentThread()); michael@0: nsresult rv = NS_OK; michael@0: bool processed = true; michael@0: while (!mCompleted && NS_SUCCEEDED(rv)) { michael@0: rv = thread->ProcessNextEvent(true, &processed); michael@0: } michael@0: } michael@0: michael@0: #define NS_DECL_ASYNCSTATEMENTSPINNER \ michael@0: NS_IMETHOD HandleResult(mozIStorageResultSet *aResultSet); michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: //// Async Helpers michael@0: michael@0: /** michael@0: * Execute an async statement, blocking the main thread until we get the michael@0: * callback completion notification. michael@0: */ michael@0: void michael@0: blocking_async_execute(mozIStorageBaseStatement *stmt) michael@0: { michael@0: nsRefPtr spinner(new AsyncStatementSpinner()); michael@0: michael@0: nsCOMPtr pendy; michael@0: (void)stmt->ExecuteAsync(spinner, getter_AddRefs(pendy)); michael@0: spinner->SpinUntilCompleted(); michael@0: } michael@0: michael@0: /** michael@0: * Invoke AsyncClose on the given connection, blocking the main thread until we michael@0: * get the completion notification. michael@0: */ michael@0: void michael@0: blocking_async_close(mozIStorageConnection *db) michael@0: { michael@0: nsRefPtr spinner(new AsyncStatementSpinner()); michael@0: michael@0: db->AsyncClose(spinner); michael@0: spinner->SpinUntilCompleted(); michael@0: }