Bug 489481 - Get C++ tests for our C++ helper classes
authorShawn Wilsher <sdwilsh@shawnwilsher.com>
Wed, 13 May 2009 12:53:57 -0400
changeset 28291 9c62b2169f76be6c08ae0190652948b84d22f3f6
parent 28290 aaaa9883af9dbd1978448cf3af309b9e15e4fa94
child 28292 42832a65a57952529b2aaf89d86859b54d4081a8
child 28418 68d3b1809943343da23578a44e58c3b1436db227
push idunknown
push userunknown
push dateunknown
bugs489481
milestone1.9.2a1pre
Bug 489481 - Get C++ tests for our C++ helper classes This gets rid of the old, unused native code test file for storage. It adds two more test files which test our C++ helper classes to ensure that they behave as advertised. Also fixes a bug found in mozStorageTransaction that was uncovered while writing these tests. r=asuth
storage/public/mozStorageHelper.h
storage/test/Makefile.in
storage/test/storage1.cpp
storage/test/storage_test_harness.h
storage/test/storage_test_harness_tail.h
storage/test/test_statement_scoper.cpp
storage/test/test_transaction_helper.cpp
--- a/storage/public/mozStorageHelper.h
+++ b/storage/public/mozStorageHelper.h
@@ -93,17 +93,21 @@ public:
    */
   nsresult Commit()
   {
     if (!mConnection || mCompleted)
       return NS_OK; // no connection, or already done
     mCompleted = PR_TRUE;
     if (! mHasTransaction)
       return NS_OK; // transaction not ours, ignore
-    return mConnection->CommitTransaction();
+    nsresult rv = mConnection->CommitTransaction();
+    if (NS_SUCCEEDED(rv))
+      mHasTransaction = PR_FALSE;
+
+    return rv;
   }
 
   /**
    * Rolls back the transaction in progress. You should only call this function
    * if this object has a real transaction (HasTransaction() = true) because
    * otherwise, there is no transaction to roll back.
    */
   nsresult Rollback()
@@ -117,16 +121,19 @@ public:
     // It is possible that a rollback will return busy, so we busy wait...
     nsresult rv = NS_OK;
     do {
       rv = mConnection->RollbackTransaction();
       if (rv == NS_ERROR_STORAGE_BUSY)
         (void)PR_Sleep(PR_INTERVAL_NO_WAIT);
     } while (rv == NS_ERROR_STORAGE_BUSY);
 
+    if (NS_SUCCEEDED(rv))
+      mHasTransaction = PR_FALSE;
+
     return rv;
   }
 
   /**
    * Returns whether this object wraps a real transaction. False means that
    * this object doesn't do anything because there was already a transaction in
    * progress when it was created.
    */
--- a/storage/test/Makefile.in
+++ b/storage/test/Makefile.in
@@ -39,23 +39,24 @@
 
 DEPTH		= ../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
-PROGRAM = teststorage1$(BIN_SUFFIX)
-
 MODULE = test_storage
 
 XPCSHELL_TESTS = unit
 
-CPPSRCS = storage1.cpp
+CPP_UNIT_TESTS = \
+  test_transaction_helper.cpp \
+  test_statement_scoper.cpp \
+  $(NULL)
 
 REQUIRES = \
 	xpcom \
 	string \
 	storage \
 	$(NULL)
 
 LIBS = \
deleted file mode 100644
--- a/storage/test/storage1.cpp
+++ /dev/null
@@ -1,244 +0,0 @@
-
-#include <stdlib.h>
-#include <stdio.h>
-
-#include "nsIComponentManager.h"
-#include "nsISimpleEnumerator.h"
-#include "nsIServiceManager.h"
-#include "nsILocalFile.h"
-#include "nsIVariant.h"
-#include "nsComponentManagerUtils.h"
-#include "nsServiceManagerUtils.h"
-#include "nsDirectoryServiceDefs.h"
-#include "nsDirectoryServiceUtils.h"
-#include "nsCOMPtr.h"
-#include "nsStringAPI.h"
-
-#include "mozIStorageService.h"
-#include "mozIStorageConnection.h"
-#include "mozIStorageValueArray.h"
-#include "mozIStorageStatement.h"
-#include "mozIStorageFunction.h"
-#include "mozIStorageAggregateFunction.h"
-#include "mozIStorageProgressHandler.h"
-
-#include "mozStorageCID.h"
-
-#include "nsXPCOMCID.h"
-
-#define TEST_CHECK_ERROR(rv) \
-        do { if (NS_FAILED(rv)) {              \
-            dbConn->GetLastError(&gerr); \
-            dbConn->GetLastErrorString(gerrstr); \
-            fprintf (stderr, "Error: %d 0x%08x %s\n", gerr, gerr, gerrstr.get()); \
-            return 0; \
-            } } while (0)
-
-#define TEST_DB NS_LITERAL_CSTRING("foo.sdb")
-
-int gerr;
-nsCString gerrstr;
-
-class TestFunc : public mozIStorageFunction {
-public:
-    TestFunc() { }
-    NS_DECL_ISUPPORTS
-    NS_DECL_MOZISTORAGEFUNCTION
-};
-
-NS_IMPL_ISUPPORTS1(TestFunc, mozIStorageFunction)
-
-NS_IMETHODIMP
-TestFunc::OnFunctionCall (mozIStorageValueArray *sva, nsIVariant **_retval)
-{
-    nsCOMPtr<nsIWritableVariant> outVar;
-
-    fprintf (stderr, "* function call!\n");
-
-    outVar = do_CreateInstance(NS_VARIANT_CONTRACTID);
-    if(!outVar)
-        return NS_ERROR_FAILURE;
-    outVar->SetAsInt32(0xDEADBEEF);
-    NS_ADDREF(*_retval = outVar);
-    return NS_OK;
-}
-
-class TestAggregateFunc : public mozIStorageAggregateFunction {
-private:
-    PRInt32 mCalls;
-public:
-    TestAggregateFunc() : mCalls(0) { }
-    NS_DECL_ISUPPORTS
-    NS_DECL_MOZISTORAGEAGGREGATEFUNCTION
-};
-
-NS_IMPL_ISUPPORTS1(TestAggregateFunc, mozIStorageAggregateFunction)
-
-NS_IMETHODIMP
-TestAggregateFunc::OnStep (mozIStorageValueArray *sva)
-{
-    ++mCalls;
-    fprintf (stderr, "* aggregate step %d!\n", mCalls);
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-TestAggregateFunc::OnFinal (nsIVariant **_retval)
-{
-    nsCOMPtr<nsIWritableVariant> outVar;
-
-    fprintf (stderr, "* aggregate result %d!\n", - mCalls * mCalls);
-
-    outVar = do_CreateInstance(NS_VARIANT_CONTRACTID);
-    if(!outVar)
-        return NS_ERROR_FAILURE;
-    outVar->SetAsInt32(-mCalls * mCalls);
-    NS_ADDREF(*_retval = outVar);
-    return NS_OK;
-}
-
-class TestProgressHandler : public mozIStorageProgressHandler {
-private:
-    PRUint32 mTicks;
-public:
-    TestProgressHandler() : mTicks(0) { }
-    NS_DECL_ISUPPORTS
-    NS_DECL_MOZISTORAGEPROGRESSHANDLER
-    PRUint32 getTicks() const { return mTicks; }
-};
-
-NS_IMPL_ISUPPORTS1(TestProgressHandler, mozIStorageProgressHandler)
-
-NS_IMETHODIMP
-TestProgressHandler::OnProgress (mozIStorageConnection *aConnection, PRBool *_retval)
-{
-    ++mTicks;
-    *_retval = PR_FALSE;
-    return NS_OK;
-}
-
-int
-main (int argc, char **argv)
-{
-    nsresult rv;
-    TestFunc *tf;
-    TestAggregateFunc *taf;
-
-    NS_InitXPCOM2(nsnull, nsnull, nsnull);
-
-    nsCOMPtr<mozIStorageService> dbSrv;
-    dbSrv = do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    nsCOMPtr<nsIFile> tmpDir;
-    rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tmpDir));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = tmpDir->AppendNative(TEST_DB);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    nsCAutoString tmpPath;
-    rv = tmpDir->GetNativePath(tmpPath);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    nsCOMPtr<nsILocalFile> f;
-    rv = NS_NewNativeLocalFile (tmpPath, PR_FALSE, getter_AddRefs(f));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    nsCOMPtr<mozIStorageConnection> dbConn;
-    rv = dbSrv->OpenDatabase(f, getter_AddRefs(dbConn));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    tf = new TestFunc();
-    rv = dbConn->CreateFunction(NS_LITERAL_CSTRING("x_test"), -1, tf);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    // Must be error: function exists
-    rv = dbConn->CreateFunction(NS_LITERAL_CSTRING("x_test"), -1, tf);
-    NS_ENSURE_TRUE((rv == NS_ERROR_FAILURE), NS_ERROR_FAILURE);
-
-    rv = dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("SELECT x_test(1)"));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    taf = new TestAggregateFunc();
-    rv = dbConn->CreateAggregateFunction(NS_LITERAL_CSTRING("x_aggr"), -1, taf);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    // Must be error: function exists with this name
-    rv = dbConn->CreateAggregateFunction(NS_LITERAL_CSTRING("x_aggr"), -1, taf);
-    NS_ENSURE_TRUE((rv == NS_ERROR_FAILURE), NS_ERROR_FAILURE);
-
-    // Must be error: function exists with this implementation
-    rv = dbConn->CreateAggregateFunction(NS_LITERAL_CSTRING("x_aggr2"), -1, taf);
-    NS_ENSURE_TRUE((rv == NS_ERROR_FAILURE), NS_ERROR_FAILURE);
-
-    rv = dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DROP TABLE foo"));
-    // TEST_CHECK_ERROR(rv);
-
-    rv = dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("CREATE TABLE foo (i INTEGER)"));
-    TEST_CHECK_ERROR(rv);
-
-    nsCOMPtr<mozIStorageStatement> dbFooInsertStatement;
-    rv = dbConn->CreateStatement (NS_LITERAL_CSTRING("INSERT INTO foo VALUES ( ?1 )"), getter_AddRefs(dbFooInsertStatement));
-    TEST_CHECK_ERROR(rv);
-
-    nsCOMPtr<mozIStorageStatement> dbFooSelectStatement;
-    rv = dbConn->CreateStatement (NS_LITERAL_CSTRING("SELECT i FROM foo"), getter_AddRefs(dbFooSelectStatement));
-    TEST_CHECK_ERROR(rv);
-
-    nsCOMPtr<mozIStorageStatement> dbAggrSelectStatement;
-    rv = dbConn->CreateStatement (NS_LITERAL_CSTRING("SELECT x_aggr(i) FROM foo"), getter_AddRefs(dbAggrSelectStatement));
-    TEST_CHECK_ERROR(rv);
-
-    for (int i = 0; i < 10; i++) {
-        rv = dbFooInsertStatement->BindInt32Parameter (0, i);
-        TEST_CHECK_ERROR(rv);
-
-        rv = dbFooInsertStatement->Execute ();
-        TEST_CHECK_ERROR(rv);
-    }
-
-    fprintf (stderr, "10 values written to foo...\n");
-
-    nsCOMPtr<mozIStorageValueArray> dbRow = do_QueryInterface(dbFooSelectStatement);
-    PRBool hasMore = PR_FALSE;
-
-    while ((dbFooSelectStatement->ExecuteStep(&hasMore) == NS_OK) && hasMore)
-    {
-        PRUint32 len;
-
-        dbRow->GetNumEntries (&len);
-        fprintf (stderr, "Row[length %d]: %d '%s'\n", len, dbRow->AsInt32(0), dbRow->AsSharedUTF8String(0, 0));
-    }
-
-    TEST_CHECK_ERROR(rv);
-    fprintf (stderr, "Done. %d 0x%08x %p\n", rv, rv, dbRow.get());
-
-    dbRow = do_QueryInterface(dbAggrSelectStatement);
-    hasMore = PR_FALSE;
-
-    while ((dbAggrSelectStatement->ExecuteStep(&hasMore) == NS_OK) && hasMore)
-    {
-        PRUint32 len;
-        dbRow->GetNumEntries (&len);
-        fprintf (stderr, "Row[length %d]: %d '%s'\n", len, dbRow->AsInt32(0), dbRow->AsSharedUTF8String(0, 0));
-    }
-
-    TEST_CHECK_ERROR(rv);
-    fprintf (stderr, "Done. %d 0x%08x %p\n", rv, rv, dbRow.get());
-
-    TestProgressHandler *tph = new TestProgressHandler();
-    nsCOMPtr<mozIStorageProgressHandler> oldHandler;
-    rv = dbConn->SetProgressHandler(1, tph, getter_AddRefs(oldHandler));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    nsCOMPtr<mozIStorageStatement> dbSortSelectStatement;
-    rv = dbConn->CreateStatement (NS_LITERAL_CSTRING("SELECT i FROM foo ORDER BY i DESC"), getter_AddRefs(dbSortSelectStatement));
-    TEST_CHECK_ERROR(rv);
-
-    rv = dbSortSelectStatement->ExecuteStep(&hasMore);
-    TEST_CHECK_ERROR(rv);
-    fprintf (stderr, "Statement execution takes %d ticks\n", tph->getTicks());
-
-    return 0;
-}
new file mode 100644
--- /dev/null
+++ b/storage/test/storage_test_harness.h
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is storage test code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Shawn Wilsher <me@shawnwilsher.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "TestHarness.h"
+#include "nsMemory.h"
+#include "mozIStorageService.h"
+#include "mozIStorageConnection.h"
+
+static size_t gTotalTests = 0;
+static size_t gPassedTests = 0;
+
+#define do_check_true(aCondition) \
+  PR_BEGIN_MACRO \
+    gTotalTests++; \
+    if (aCondition) \
+      gPassedTests++; \
+    else \
+      fail("Expected true, got false on line %d!", __LINE__); \
+  PR_END_MACRO
+
+#define do_check_false(aCondition) \
+  PR_BEGIN_MACRO \
+    gTotalTests++; \
+    if (!aCondition) \
+      gPassedTests++; \
+    else \
+      fail("Expected false, got true on line %d!", __LINE__); \
+  PR_END_MACRO
+
+already_AddRefed<mozIStorageConnection>
+getMemoryDatabase()
+{
+  nsCOMPtr<mozIStorageService> ss =
+    do_GetService("@mozilla.org/storage/service;1");
+  nsCOMPtr<mozIStorageConnection> conn;
+  (void)ss->OpenSpecialDatabase("memory", getter_AddRefs(conn));
+  return conn.forget();
+}
new file mode 100644
--- /dev/null
+++ b/storage/test/storage_test_harness_tail.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is storage test code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Shawn Wilsher <me@shawnwilsher.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef TEST_NAME
+#error "Must #define TEST_NAME before including storage_test_harness_tail.h"
+#endif
+
+#ifndef TEST_FILE
+#error "Must #define TEST_FILE before include storage_test_harness_tail.h"
+#endif
+
+int
+main(int aArgc,
+     char **aArgv)
+{
+  ScopedXPCOM xpcom(TEST_NAME);
+
+  for (size_t i = 0; i < NS_ARRAY_LENGTH(gTests); i++)
+    gTests[i]();
+
+  if (gPassedTests == gTotalTests)
+    passed(TEST_FILE);
+
+  (void)printf("%zu of %zu tests passed\n", gPassedTests, gTotalTests);
+}
new file mode 100644
--- /dev/null
+++ b/storage/test/test_statement_scoper.cpp
@@ -0,0 +1,137 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is storage test code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Shawn Wilsher <me@shawnwilsher.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "storage_test_harness.h"
+
+#include "mozStorageHelper.h"
+
+/**
+ * This file test our statement scoper in mozStorageHelper.h.
+ */
+
+void
+test_automatic_reset()
+{
+  nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
+
+  // Need to create a table to populate sqlite_master with an entry.
+  (void)db->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE TABLE test (id INTEGER PRIMARY KEY)"
+  ));
+
+  nsCOMPtr<mozIStorageStatement> stmt;
+  (void)db->CreateStatement(NS_LITERAL_CSTRING(
+    "SELECT * FROM sqlite_master"
+  ), getter_AddRefs(stmt));
+
+  // Reality check - make sure we start off in the right state.
+  PRInt32 state = -1;
+  (void)stmt->GetState(&state);
+  do_check_true(state == mozIStorageStatement::MOZ_STORAGE_STATEMENT_READY);
+
+  // Start executing the statement, which will put it into an executing state.
+  {
+    mozStorageStatementScoper scoper(stmt);
+    PRBool hasMore;
+    do_check_true(NS_SUCCEEDED(stmt->ExecuteStep(&hasMore)));
+
+    // Reality check that we are executing.
+    state = -1;
+    (void)stmt->GetState(&state);
+    do_check_true(state ==
+                  mozIStorageStatement::MOZ_STORAGE_STATEMENT_EXECUTING);
+  }
+
+  // And we should be ready again.
+  state = -1;
+  (void)stmt->GetState(&state);
+  do_check_true(state == mozIStorageStatement::MOZ_STORAGE_STATEMENT_READY);
+}
+
+void
+test_Abandon()
+{
+  nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
+
+  // Need to create a table to populate sqlite_master with an entry.
+  (void)db->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE TABLE test (id INTEGER PRIMARY KEY)"
+  ));
+
+  nsCOMPtr<mozIStorageStatement> stmt;
+  (void)db->CreateStatement(NS_LITERAL_CSTRING(
+    "SELECT * FROM sqlite_master"
+  ), getter_AddRefs(stmt));
+
+  // Reality check - make sure we start off in the right state.
+  PRInt32 state = -1;
+  (void)stmt->GetState(&state);
+  do_check_true(state == mozIStorageStatement::MOZ_STORAGE_STATEMENT_READY);
+
+  // Start executing the statement, which will put it into an executing state.
+  {
+    mozStorageStatementScoper scoper(stmt);
+    PRBool hasMore;
+    do_check_true(NS_SUCCEEDED(stmt->ExecuteStep(&hasMore)));
+
+    // Reality check that we are executing.
+    state = -1;
+    (void)stmt->GetState(&state);
+    do_check_true(state ==
+                  mozIStorageStatement::MOZ_STORAGE_STATEMENT_EXECUTING);
+
+    // And call Abandon.  We should not reset now when we fall out of scope.
+    scoper.Abandon();
+  }
+
+  // And we should still be executing.
+  state = -1;
+  (void)stmt->GetState(&state);
+  do_check_true(state == mozIStorageStatement::MOZ_STORAGE_STATEMENT_EXECUTING);
+}
+
+void (*gTests[])(void) = {
+  test_automatic_reset,
+  test_Abandon,
+};
+
+const char *file = __FILE__;
+#define TEST_NAME "statement scoper"
+#define TEST_FILE file
+#include "storage_test_harness_tail.h"
new file mode 100644
--- /dev/null
+++ b/storage/test/test_transaction_helper.cpp
@@ -0,0 +1,215 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is storage test code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Shawn Wilsher <me@shawnwilsher.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "storage_test_harness.h"
+
+#include "mozStorageHelper.h"
+
+/**
+ * This file test our Transaction helper in mozStorageHelper.h.
+ */
+
+void
+test_HasTransaction()
+{
+  nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
+
+  // First test that it holds the transaction after it should have gotten one.
+  {
+    mozStorageTransaction transaction(db, PR_FALSE);
+    do_check_true(transaction.HasTransaction());
+    (void)transaction.Commit();
+    // And that it does not have a transaction after we have committed.
+    do_check_false(transaction.HasTransaction());
+  }
+
+  // Check that no transaction is had after a rollback.
+  {
+    mozStorageTransaction transaction(db, PR_FALSE);
+    do_check_true(transaction.HasTransaction());
+    (void)transaction.Rollback();
+    do_check_false(transaction.HasTransaction());
+  }
+
+  // Check that we do not have a transaction if one is already obtained.
+  mozStorageTransaction outerTransaction(db, PR_FALSE);
+  do_check_true(outerTransaction.HasTransaction());
+  {
+    mozStorageTransaction innerTransaction(db, PR_FALSE);
+    do_check_false(innerTransaction.HasTransaction());
+  }
+}
+
+void
+test_Commit()
+{
+  nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
+
+  // Create a table in a transaction, call Commit, and make sure that it does
+  // exists after the transaction falls out of scope.
+  {
+    mozStorageTransaction transaction(db, PR_FALSE);
+    (void)db->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+      "CREATE TABLE test (id INTEGER PRIMARY KEY)"
+    ));
+    (void)transaction.Commit();
+  }
+
+  PRBool exists = PR_FALSE;
+  (void)db->TableExists(NS_LITERAL_CSTRING("test"), &exists);
+  do_check_true(exists);
+}
+
+void
+test_Rollback()
+{
+  nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
+
+  // Create a table in a transaction, call Rollback, and make sure that it does
+  // not exists after the transaction falls out of scope.
+  {
+    mozStorageTransaction transaction(db, PR_TRUE);
+    (void)db->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+      "CREATE TABLE test (id INTEGER PRIMARY KEY)"
+    ));
+    (void)transaction.Rollback();
+  }
+
+  PRBool exists = PR_TRUE;
+  (void)db->TableExists(NS_LITERAL_CSTRING("test"), &exists);
+  do_check_false(exists);
+}
+
+void
+test_AutoCommit()
+{
+  nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
+
+  // Create a table in a transaction, and make sure that it exists after the
+  // transaction falls out of scope.  This means the Commit was successful.
+  {
+    mozStorageTransaction transaction(db, PR_TRUE);
+    (void)db->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+      "CREATE TABLE test (id INTEGER PRIMARY KEY)"
+    ));
+  }
+
+  PRBool exists = PR_FALSE;
+  (void)db->TableExists(NS_LITERAL_CSTRING("test"), &exists);
+  do_check_true(exists);
+}
+
+void
+test_AutoRollback()
+{
+  nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
+
+  // Create a table in a transaction, and make sure that it does not exists
+  // after the transaction falls out of scope.  This means the Rollback was
+  // successful.
+  {
+    mozStorageTransaction transaction(db, PR_FALSE);
+    (void)db->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+      "CREATE TABLE test (id INTEGER PRIMARY KEY)"
+    ));
+  }
+
+  PRBool exists = PR_TRUE;
+  (void)db->TableExists(NS_LITERAL_CSTRING("test"), &exists);
+  do_check_false(exists);
+}
+
+void
+test_SetDefaultAction()
+{
+  nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
+
+  // First we test that rollback happens when we first set it to automatically
+  // commit.
+  {
+    mozStorageTransaction transaction(db, PR_TRUE);
+    (void)db->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+      "CREATE TABLE test1 (id INTEGER PRIMARY KEY)"
+    ));
+    transaction.SetDefaultAction(PR_FALSE);
+  }
+  PRBool exists = PR_TRUE;
+  (void)db->TableExists(NS_LITERAL_CSTRING("test1"), &exists);
+  do_check_false(exists);
+
+  // Now we do the opposite and test that a commit happens when we first set it
+  // to automatically rollback.
+  {
+    mozStorageTransaction transaction(db, PR_FALSE);
+    (void)db->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+      "CREATE TABLE test2 (id INTEGER PRIMARY KEY)"
+    ));
+    transaction.SetDefaultAction(PR_TRUE);
+  }
+  exists = PR_FALSE;
+  (void)db->TableExists(NS_LITERAL_CSTRING("test2"), &exists);
+  do_check_true(exists);
+}
+
+void
+test_null_database_connection()
+{
+  // We permit the use of the Transaction helper when passing a null database
+  // in, so we need to make sure this still works without crashing.
+  mozStorageTransaction transaction(nsnull, PR_FALSE);
+
+  do_check_false(transaction.HasTransaction());
+  do_check_true(NS_SUCCEEDED(transaction.Commit()));
+  do_check_true(NS_SUCCEEDED(transaction.Rollback()));
+}
+
+void (*gTests[])(void) = {
+  test_HasTransaction,
+  test_Commit,
+  test_Rollback,
+  test_AutoCommit,
+  test_AutoRollback,
+  test_SetDefaultAction,
+  test_null_database_connection,
+};
+
+const char *file = __FILE__;
+#define TEST_NAME "transaction helper"
+#define TEST_FILE file
+#include "storage_test_harness_tail.h"