michael@0: /* Any copyright is dedicated to the Public Domain. michael@0: http://creativecommons.org/publicdomain/zero/1.0/ */ michael@0: michael@0: #include "storage_test_harness.h" michael@0: #include "prthread.h" michael@0: #include "nsIEventTarget.h" michael@0: #include "nsIInterfaceRequestorUtils.h" michael@0: #include "mozilla/Attributes.h" michael@0: michael@0: #include "sqlite3.h" michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: //// Async Helpers michael@0: michael@0: /** michael@0: * Spins the events loop for current thread until aCondition is true. michael@0: */ michael@0: void michael@0: spin_events_loop_until_true(const bool* const aCondition) michael@0: { michael@0: nsCOMPtr thread(::do_GetCurrentThread()); michael@0: nsresult rv = NS_OK; michael@0: bool processed = true; michael@0: while (!(*aCondition) && NS_SUCCEEDED(rv)) { michael@0: rv = thread->ProcessNextEvent(true, &processed); michael@0: } michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: //// mozIStorageStatementCallback implementation michael@0: michael@0: class UnownedCallback MOZ_FINAL : public mozIStorageStatementCallback michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: michael@0: // Whether the object has been destroyed. michael@0: static bool sAlive; michael@0: // Whether the first result was received. michael@0: static bool sResult; michael@0: // Whether an error was received. michael@0: static bool sError; michael@0: michael@0: UnownedCallback(mozIStorageConnection* aDBConn) michael@0: : mDBConn(aDBConn) michael@0: , mCompleted(false) michael@0: { michael@0: sAlive = true; michael@0: sResult = false; michael@0: sError = false; michael@0: } michael@0: michael@0: ~UnownedCallback() michael@0: { michael@0: sAlive = false; michael@0: blocking_async_close(mDBConn); michael@0: } michael@0: michael@0: NS_IMETHOD HandleResult(mozIStorageResultSet* aResultSet) michael@0: { michael@0: sResult = true; michael@0: spin_events_loop_until_true(&mCompleted); michael@0: if (!sAlive) { michael@0: NS_RUNTIMEABORT("The statement callback was destroyed prematurely."); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHOD HandleError(mozIStorageError* aError) michael@0: { michael@0: sError = true; michael@0: spin_events_loop_until_true(&mCompleted); michael@0: if (!sAlive) { michael@0: NS_RUNTIMEABORT("The statement callback was destroyed prematurely."); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHOD HandleCompletion(uint16_t aReason) michael@0: { michael@0: mCompleted = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: protected: michael@0: nsCOMPtr mDBConn; michael@0: bool mCompleted; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(UnownedCallback, mozIStorageStatementCallback) michael@0: michael@0: bool UnownedCallback::sAlive = false; michael@0: bool UnownedCallback::sResult = false; michael@0: bool UnownedCallback::sError = false; michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: //// Tests michael@0: michael@0: void michael@0: test_SpinEventsLoopInHandleResult() michael@0: { michael@0: nsCOMPtr db(getMemoryDatabase()); michael@0: michael@0: // Create a test table and populate it. michael@0: nsCOMPtr stmt; michael@0: db->CreateStatement(NS_LITERAL_CSTRING( michael@0: "CREATE TABLE test (id INTEGER PRIMARY KEY)" michael@0: ), getter_AddRefs(stmt)); michael@0: stmt->Execute(); michael@0: stmt->Finalize(); michael@0: michael@0: db->CreateStatement(NS_LITERAL_CSTRING( michael@0: "INSERT INTO test (id) VALUES (?)" michael@0: ), getter_AddRefs(stmt)); michael@0: for (int32_t i = 0; i < 30; ++i) { michael@0: stmt->BindInt32ByIndex(0, i); michael@0: stmt->Execute(); michael@0: stmt->Reset(); michael@0: } michael@0: stmt->Finalize(); michael@0: michael@0: db->CreateStatement(NS_LITERAL_CSTRING( michael@0: "SELECT * FROM test" michael@0: ), getter_AddRefs(stmt)); michael@0: nsCOMPtr ps; michael@0: do_check_success(stmt->ExecuteAsync(new UnownedCallback(db), michael@0: getter_AddRefs(ps))); michael@0: stmt->Finalize(); michael@0: michael@0: spin_events_loop_until_true(&UnownedCallback::sResult); michael@0: } michael@0: michael@0: void michael@0: test_SpinEventsLoopInHandleError() michael@0: { michael@0: nsCOMPtr db(getMemoryDatabase()); michael@0: michael@0: // Create a test table and populate it. michael@0: nsCOMPtr stmt; michael@0: db->CreateStatement(NS_LITERAL_CSTRING( michael@0: "CREATE TABLE test (id INTEGER PRIMARY KEY)" michael@0: ), getter_AddRefs(stmt)); michael@0: stmt->Execute(); michael@0: stmt->Finalize(); michael@0: michael@0: db->CreateStatement(NS_LITERAL_CSTRING( michael@0: "INSERT INTO test (id) VALUES (1)" michael@0: ), getter_AddRefs(stmt)); michael@0: stmt->Execute(); michael@0: stmt->Finalize(); michael@0: michael@0: // This will cause a constraint error. michael@0: db->CreateStatement(NS_LITERAL_CSTRING( michael@0: "INSERT INTO test (id) VALUES (1)" michael@0: ), getter_AddRefs(stmt)); michael@0: nsCOMPtr ps; michael@0: do_check_success(stmt->ExecuteAsync(new UnownedCallback(db), michael@0: getter_AddRefs(ps))); michael@0: stmt->Finalize(); michael@0: michael@0: spin_events_loop_until_true(&UnownedCallback::sError); michael@0: } michael@0: michael@0: void (*gTests[])(void) = { michael@0: test_SpinEventsLoopInHandleResult, michael@0: test_SpinEventsLoopInHandleError, michael@0: }; michael@0: michael@0: const char *file = __FILE__; michael@0: #define TEST_NAME "test async callbacks with spun event loops" michael@0: #define TEST_FILE file michael@0: #include "storage_test_harness_tail.h"