From 5628a61652edd67912a691d4937329b25c02dca3 Mon Sep 17 00:00:00 2001 From: Marco Bonardo Date: Thu, 28 Apr 2011 19:55:09 +0200 Subject: [PATCH] Bug 638123 - Test for bug 637957. r=sdwilsh --- storage/test/Makefile.in | 1 + ..._async_callbacks_with_spun_event_loops.cpp | 184 ++++++++++++++++++ 2 files changed, 185 insertions(+) create mode 100644 storage/test/test_async_callbacks_with_spun_event_loops.cpp diff --git a/storage/test/Makefile.in b/storage/test/Makefile.in index 660057536a2..b99836ae673 100644 --- a/storage/test/Makefile.in +++ b/storage/test/Makefile.in @@ -59,6 +59,7 @@ CPP_UNIT_TESTS = \ test_service_init_background_thread.cpp \ test_AsXXX_helpers.cpp \ test_StatementCache.cpp \ + test_async_callbacks_with_spun_event_loops.cpp \ $(NULL) ifdef MOZ_DEBUG diff --git a/storage/test/test_async_callbacks_with_spun_event_loops.cpp b/storage/test/test_async_callbacks_with_spun_event_loops.cpp new file mode 100644 index 00000000000..433755a968d --- /dev/null +++ b/storage/test/test_async_callbacks_with_spun_event_loops.cpp @@ -0,0 +1,184 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +#include "storage_test_harness.h" +#include "prthread.h" +#include "nsIEventTarget.h" +#include "nsIInterfaceRequestorUtils.h" + +#include "sqlite3.h" + +//////////////////////////////////////////////////////////////////////////////// +//// Async Helpers + +/** + * Invoke AsyncClose on the given connection, blocking the main thread until we + * get the completion notification. + */ +void +blocking_async_close(mozIStorageConnection *db) +{ + nsRefPtr spinner(new AsyncStatementSpinner()); + + db->AsyncClose(spinner); + spinner->SpinUntilCompleted(); +} + +/** + * Spins the events loop for current thread until aCondition is true. + */ +void +spin_events_loop_until_true(const bool* const aCondition) +{ + nsCOMPtr thread(::do_GetCurrentThread()); + nsresult rv = NS_OK; + PRBool processed = PR_TRUE; + while (!(*aCondition) && NS_SUCCEEDED(rv)) { + rv = thread->ProcessNextEvent(true, &processed); + } +} + +//////////////////////////////////////////////////////////////////////////////// +//// mozIStorageStatementCallback implementation + +class UnownedCallback : public mozIStorageStatementCallback +{ +public: + NS_DECL_ISUPPORTS + + // Whether the object has been destroyed. + static bool sAlive; + // Whether the first result was received. + static bool sResult; + // Whether an error was received. + static bool sError; + + UnownedCallback(mozIStorageConnection* aDBConn) + : mDBConn(aDBConn) + , mCompleted(false) + { + sAlive = true; + sResult = false; + sError = false; + } + + ~UnownedCallback() + { + sAlive = false; + blocking_async_close(mDBConn); + } + + NS_IMETHOD HandleResult(mozIStorageResultSet* aResultSet) + { + sResult = true; + spin_events_loop_until_true(&mCompleted); + if (!sAlive) { + NS_RUNTIMEABORT("The statement callback was destroyed prematurely."); + } + return NS_OK; + } + + NS_IMETHOD HandleError(mozIStorageError* aError) + { + sError = true; + spin_events_loop_until_true(&mCompleted); + if (!sAlive) { + NS_RUNTIMEABORT("The statement callback was destroyed prematurely."); + } + return NS_OK; + } + + NS_IMETHOD HandleCompletion(PRUint16 aReason) + { + mCompleted = true; + return NS_OK; + } + +protected: + nsCOMPtr mDBConn; + bool mCompleted; +}; + +NS_IMPL_ISUPPORTS1(UnownedCallback, mozIStorageStatementCallback); + +bool UnownedCallback::sAlive = false; +bool UnownedCallback::sResult = false; +bool UnownedCallback::sError = false; + +//////////////////////////////////////////////////////////////////////////////// +//// Tests + +void +test_SpinEventsLoopInHandleResult() +{ + nsCOMPtr db(getMemoryDatabase()); + + // Create a test table and populate it. + nsCOMPtr stmt; + db->CreateStatement(NS_LITERAL_CSTRING( + "CREATE TABLE test (id INTEGER PRIMARY KEY)" + ), getter_AddRefs(stmt)); + stmt->Execute(); + stmt->Finalize(); + + db->CreateStatement(NS_LITERAL_CSTRING( + "INSERT INTO test (id) VALUES (?)" + ), getter_AddRefs(stmt)); + for (PRInt32 i = 0; i < 30; ++i) { + stmt->BindInt32ByIndex(0, i); + stmt->Execute(); + stmt->Reset(); + } + stmt->Finalize(); + + db->CreateStatement(NS_LITERAL_CSTRING( + "SELECT * FROM test" + ), getter_AddRefs(stmt)); + nsCOMPtr ps; + do_check_success(stmt->ExecuteAsync(new UnownedCallback(db), + getter_AddRefs(ps))); + stmt->Finalize(); + + spin_events_loop_until_true(&UnownedCallback::sResult); +} + +void +test_SpinEventsLoopInHandleError() +{ + nsCOMPtr db(getMemoryDatabase()); + + // Create a test table and populate it. + nsCOMPtr stmt; + db->CreateStatement(NS_LITERAL_CSTRING( + "CREATE TABLE test (id INTEGER PRIMARY KEY)" + ), getter_AddRefs(stmt)); + stmt->Execute(); + stmt->Finalize(); + + db->CreateStatement(NS_LITERAL_CSTRING( + "INSERT INTO test (id) VALUES (1)" + ), getter_AddRefs(stmt)); + stmt->Execute(); + stmt->Finalize(); + + // This will cause a constraint error. + db->CreateStatement(NS_LITERAL_CSTRING( + "INSERT INTO test (id) VALUES (1)" + ), getter_AddRefs(stmt)); + nsCOMPtr ps; + do_check_success(stmt->ExecuteAsync(new UnownedCallback(db), + getter_AddRefs(ps))); + stmt->Finalize(); + + spin_events_loop_until_true(&UnownedCallback::sError); +} + +void (*gTests[])(void) = { + test_SpinEventsLoopInHandleResult, + test_SpinEventsLoopInHandleError, +}; + +const char *file = __FILE__; +#define TEST_NAME "test async callbacks with spun event loops" +#define TEST_FILE file +#include "storage_test_harness_tail.h"