mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-30 12:27:29 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
37
storage/test/moz.build
Normal file
37
storage/test/moz.build
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini']
|
||||
|
||||
GeckoCppUnitTests([
|
||||
'test_AsXXX_helpers',
|
||||
'test_async_callbacks_with_spun_event_loops',
|
||||
'test_asyncStatementExecution_transaction',
|
||||
'test_binding_params',
|
||||
'test_file_perms',
|
||||
'test_mutex',
|
||||
'test_service_init_background_thread',
|
||||
'test_statement_scoper',
|
||||
'test_StatementCache',
|
||||
'test_transaction_helper',
|
||||
'test_true_async',
|
||||
'test_unlock_notify',
|
||||
])
|
||||
|
||||
if CONFIG['MOZ_DEBUG'] and CONFIG['OS_ARCH'] not in ('WINNT', 'Darwin'):
|
||||
# FIXME bug 523392: test_deadlock_detector doesn't like Windows
|
||||
# FIXME bug 523378: also fails on OS X
|
||||
GeckoCppUnitTests([
|
||||
'test_deadlock_detector',
|
||||
])
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'..',
|
||||
]
|
||||
|
||||
USE_LIBS += [
|
||||
'sqlite',
|
||||
]
|
||||
389
storage/test/storage_test_harness.h
Normal file
389
storage/test/storage_test_harness.h
Normal file
|
|
@ -0,0 +1,389 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "TestHarness.h"
|
||||
|
||||
#include "nsMemory.h"
|
||||
#include "prthread.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsDirectoryServiceDefs.h"
|
||||
#include "mozilla/ReentrantMonitor.h"
|
||||
|
||||
#include "mozIStorageService.h"
|
||||
#include "mozIStorageConnection.h"
|
||||
#include "mozIStorageStatementCallback.h"
|
||||
#include "mozIStorageCompletionCallback.h"
|
||||
#include "mozIStorageBindingParamsArray.h"
|
||||
#include "mozIStorageBindingParams.h"
|
||||
#include "mozIStorageAsyncStatement.h"
|
||||
#include "mozIStorageStatement.h"
|
||||
#include "mozIStoragePendingStatement.h"
|
||||
#include "mozIStorageError.h"
|
||||
#include "nsIInterfaceRequestorUtils.h"
|
||||
#include "nsIEventTarget.h"
|
||||
|
||||
#include "sqlite3.h"
|
||||
|
||||
static int gTotalTests = 0;
|
||||
static int gPassedTests = 0;
|
||||
|
||||
#define do_check_true(aCondition) \
|
||||
PR_BEGIN_MACRO \
|
||||
gTotalTests++; \
|
||||
if (aCondition) { \
|
||||
gPassedTests++; \
|
||||
} else { \
|
||||
fail("%s | Expected true, got false at line %d", __FILE__, __LINE__); \
|
||||
} \
|
||||
PR_END_MACRO
|
||||
|
||||
#define do_check_false(aCondition) \
|
||||
PR_BEGIN_MACRO \
|
||||
gTotalTests++; \
|
||||
if (!aCondition) { \
|
||||
gPassedTests++; \
|
||||
} else { \
|
||||
fail("%s | Expected false, got true at line %d", __FILE__, __LINE__); \
|
||||
} \
|
||||
PR_END_MACRO
|
||||
|
||||
#define do_check_success(aResult) \
|
||||
do_check_true(NS_SUCCEEDED(aResult))
|
||||
|
||||
#ifdef LINUX
|
||||
// XXX Linux opt builds on tinderbox are orange due to linking with stdlib.
|
||||
// This is sad and annoying, but it's a workaround that works.
|
||||
#define do_check_eq(aExpected, aActual) \
|
||||
do_check_true(aExpected == aActual)
|
||||
#else
|
||||
#include <sstream>
|
||||
// Print nsresult as uint32_t
|
||||
std::ostream& operator<<(std::ostream& aStream, const nsresult aInput)
|
||||
{
|
||||
return aStream << static_cast<uint32_t>(aInput);
|
||||
}
|
||||
#define do_check_eq(aExpected, aActual) \
|
||||
PR_BEGIN_MACRO \
|
||||
gTotalTests++; \
|
||||
if (aExpected == aActual) { \
|
||||
gPassedTests++; \
|
||||
} else { \
|
||||
std::ostringstream temp; \
|
||||
temp << __FILE__ << " | Expected '" << aExpected << "', got '"; \
|
||||
temp << aActual <<"' at line " << __LINE__; \
|
||||
fail(temp.str().c_str()); \
|
||||
} \
|
||||
PR_END_MACRO
|
||||
#endif
|
||||
|
||||
#define do_check_ok(aInvoc) do_check_true((aInvoc) == SQLITE_OK)
|
||||
|
||||
already_AddRefed<mozIStorageService>
|
||||
getService()
|
||||
{
|
||||
nsCOMPtr<mozIStorageService> ss =
|
||||
do_GetService("@mozilla.org/storage/service;1");
|
||||
do_check_true(ss);
|
||||
return ss.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<mozIStorageConnection>
|
||||
getMemoryDatabase()
|
||||
{
|
||||
nsCOMPtr<mozIStorageService> ss = getService();
|
||||
nsCOMPtr<mozIStorageConnection> conn;
|
||||
nsresult rv = ss->OpenSpecialDatabase("memory", getter_AddRefs(conn));
|
||||
do_check_success(rv);
|
||||
return conn.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<mozIStorageConnection>
|
||||
getDatabase()
|
||||
{
|
||||
nsCOMPtr<nsIFile> dbFile;
|
||||
(void)NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
|
||||
getter_AddRefs(dbFile));
|
||||
NS_ASSERTION(dbFile, "The directory doesn't exists?!");
|
||||
|
||||
nsresult rv = dbFile->Append(NS_LITERAL_STRING("storage_test_db.sqlite"));
|
||||
do_check_success(rv);
|
||||
|
||||
nsCOMPtr<mozIStorageService> ss = getService();
|
||||
nsCOMPtr<mozIStorageConnection> conn;
|
||||
rv = ss->OpenDatabase(dbFile, getter_AddRefs(conn));
|
||||
do_check_success(rv);
|
||||
return conn.forget();
|
||||
}
|
||||
|
||||
|
||||
class AsyncStatementSpinner : public mozIStorageStatementCallback
|
||||
, public mozIStorageCompletionCallback
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_MOZISTORAGESTATEMENTCALLBACK
|
||||
NS_DECL_MOZISTORAGECOMPLETIONCALLBACK
|
||||
|
||||
AsyncStatementSpinner();
|
||||
|
||||
void SpinUntilCompleted();
|
||||
|
||||
uint16_t completionReason;
|
||||
|
||||
protected:
|
||||
virtual ~AsyncStatementSpinner() {}
|
||||
volatile bool mCompleted;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(AsyncStatementSpinner,
|
||||
mozIStorageStatementCallback,
|
||||
mozIStorageCompletionCallback)
|
||||
|
||||
AsyncStatementSpinner::AsyncStatementSpinner()
|
||||
: completionReason(0)
|
||||
, mCompleted(false)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AsyncStatementSpinner::HandleResult(mozIStorageResultSet *aResultSet)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AsyncStatementSpinner::HandleError(mozIStorageError *aError)
|
||||
{
|
||||
int32_t result;
|
||||
nsresult rv = aError->GetResult(&result);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsAutoCString message;
|
||||
rv = aError->GetMessage(message);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsAutoCString warnMsg;
|
||||
warnMsg.AppendLiteral("An error occurred while executing an async statement: ");
|
||||
warnMsg.AppendInt(result);
|
||||
warnMsg.Append(' ');
|
||||
warnMsg.Append(message);
|
||||
NS_WARNING(warnMsg.get());
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AsyncStatementSpinner::HandleCompletion(uint16_t aReason)
|
||||
{
|
||||
completionReason = aReason;
|
||||
mCompleted = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AsyncStatementSpinner::Complete(nsresult, nsISupports*)
|
||||
{
|
||||
mCompleted = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void AsyncStatementSpinner::SpinUntilCompleted()
|
||||
{
|
||||
nsCOMPtr<nsIThread> thread(::do_GetCurrentThread());
|
||||
nsresult rv = NS_OK;
|
||||
bool processed = true;
|
||||
while (!mCompleted && NS_SUCCEEDED(rv)) {
|
||||
rv = thread->ProcessNextEvent(true, &processed);
|
||||
}
|
||||
}
|
||||
|
||||
#define NS_DECL_ASYNCSTATEMENTSPINNER \
|
||||
NS_IMETHOD HandleResult(mozIStorageResultSet *aResultSet) override;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Async Helpers
|
||||
|
||||
/**
|
||||
* Execute an async statement, blocking the main thread until we get the
|
||||
* callback completion notification.
|
||||
*/
|
||||
void
|
||||
blocking_async_execute(mozIStorageBaseStatement *stmt)
|
||||
{
|
||||
RefPtr<AsyncStatementSpinner> spinner(new AsyncStatementSpinner());
|
||||
|
||||
nsCOMPtr<mozIStoragePendingStatement> pendy;
|
||||
(void)stmt->ExecuteAsync(spinner, getter_AddRefs(pendy));
|
||||
spinner->SpinUntilCompleted();
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke AsyncClose on the given connection, blocking the main thread until we
|
||||
* get the completion notification.
|
||||
*/
|
||||
void
|
||||
blocking_async_close(mozIStorageConnection *db)
|
||||
{
|
||||
RefPtr<AsyncStatementSpinner> spinner(new AsyncStatementSpinner());
|
||||
|
||||
db->AsyncClose(spinner);
|
||||
spinner->SpinUntilCompleted();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Mutex Watching
|
||||
|
||||
/**
|
||||
* Verify that mozIStorageAsyncStatement's life-cycle never triggers a mutex on
|
||||
* the caller (generally main) thread. We do this by decorating the sqlite
|
||||
* mutex logic with our own code that checks what thread it is being invoked on
|
||||
* and sets a flag if it is invoked on the main thread. We are able to easily
|
||||
* decorate the SQLite mutex logic because SQLite allows us to retrieve the
|
||||
* current function pointers being used and then provide a new set.
|
||||
*/
|
||||
|
||||
sqlite3_mutex_methods orig_mutex_methods;
|
||||
sqlite3_mutex_methods wrapped_mutex_methods;
|
||||
|
||||
bool mutex_used_on_watched_thread = false;
|
||||
PRThread *watched_thread = nullptr;
|
||||
/**
|
||||
* Ugly hack to let us figure out what a connection's async thread is. If we
|
||||
* were MOZILLA_INTERNAL_API and linked as such we could just include
|
||||
* mozStorageConnection.h and just ask Connection directly. But that turns out
|
||||
* poorly.
|
||||
*
|
||||
* When the thread a mutex is invoked on isn't watched_thread we save it to this
|
||||
* variable.
|
||||
*/
|
||||
PRThread *last_non_watched_thread = nullptr;
|
||||
|
||||
/**
|
||||
* Set a flag if the mutex is used on the thread we are watching, but always
|
||||
* call the real mutex function.
|
||||
*/
|
||||
extern "C" void wrapped_MutexEnter(sqlite3_mutex *mutex)
|
||||
{
|
||||
PRThread *curThread = ::PR_GetCurrentThread();
|
||||
if (curThread == watched_thread)
|
||||
mutex_used_on_watched_thread = true;
|
||||
else
|
||||
last_non_watched_thread = curThread;
|
||||
orig_mutex_methods.xMutexEnter(mutex);
|
||||
}
|
||||
|
||||
extern "C" int wrapped_MutexTry(sqlite3_mutex *mutex)
|
||||
{
|
||||
if (::PR_GetCurrentThread() == watched_thread)
|
||||
mutex_used_on_watched_thread = true;
|
||||
return orig_mutex_methods.xMutexTry(mutex);
|
||||
}
|
||||
|
||||
void hook_sqlite_mutex()
|
||||
{
|
||||
// We need to initialize and teardown SQLite to get it to set up the
|
||||
// default mutex handlers for us so we can steal them and wrap them.
|
||||
do_check_ok(sqlite3_initialize());
|
||||
do_check_ok(sqlite3_shutdown());
|
||||
do_check_ok(::sqlite3_config(SQLITE_CONFIG_GETMUTEX, &orig_mutex_methods));
|
||||
do_check_ok(::sqlite3_config(SQLITE_CONFIG_GETMUTEX, &wrapped_mutex_methods));
|
||||
wrapped_mutex_methods.xMutexEnter = wrapped_MutexEnter;
|
||||
wrapped_mutex_methods.xMutexTry = wrapped_MutexTry;
|
||||
do_check_ok(::sqlite3_config(SQLITE_CONFIG_MUTEX, &wrapped_mutex_methods));
|
||||
}
|
||||
|
||||
/**
|
||||
* Call to clear the watch state and to set the watching against this thread.
|
||||
*
|
||||
* Check |mutex_used_on_watched_thread| to see if the mutex has fired since
|
||||
* this method was last called. Since we're talking about the current thread,
|
||||
* there are no race issues to be concerned about
|
||||
*/
|
||||
void watch_for_mutex_use_on_this_thread()
|
||||
{
|
||||
watched_thread = ::PR_GetCurrentThread();
|
||||
mutex_used_on_watched_thread = false;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Thread Wedgers
|
||||
|
||||
/**
|
||||
* A runnable that blocks until code on another thread invokes its unwedge
|
||||
* method. By dispatching this to a thread you can ensure that no subsequent
|
||||
* runnables dispatched to the thread will execute until you invoke unwedge.
|
||||
*
|
||||
* The wedger is self-dispatching, just construct it with its target.
|
||||
*/
|
||||
class ThreadWedger : public mozilla::Runnable
|
||||
{
|
||||
public:
|
||||
explicit ThreadWedger(nsIEventTarget *aTarget)
|
||||
: mReentrantMonitor("thread wedger")
|
||||
, unwedged(false)
|
||||
{
|
||||
aTarget->Dispatch(this, aTarget->NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
mozilla::ReentrantMonitorAutoEnter automon(mReentrantMonitor);
|
||||
|
||||
if (!unwedged)
|
||||
automon.Wait();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void unwedge()
|
||||
{
|
||||
mozilla::ReentrantMonitorAutoEnter automon(mReentrantMonitor);
|
||||
unwedged = true;
|
||||
automon.Notify();
|
||||
}
|
||||
|
||||
private:
|
||||
mozilla::ReentrantMonitor mReentrantMonitor;
|
||||
bool unwedged;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Async Helpers
|
||||
|
||||
/**
|
||||
* A horrible hack to figure out what the connection's async thread is. By
|
||||
* creating a statement and async dispatching we can tell from the mutex who
|
||||
* is the async thread, PRThread style. Then we map that to an nsIThread.
|
||||
*/
|
||||
already_AddRefed<nsIThread>
|
||||
get_conn_async_thread(mozIStorageConnection *db)
|
||||
{
|
||||
// Make sure we are tracking the current thread as the watched thread
|
||||
watch_for_mutex_use_on_this_thread();
|
||||
|
||||
// - statement with nothing to bind
|
||||
nsCOMPtr<mozIStorageAsyncStatement> stmt;
|
||||
db->CreateAsyncStatement(
|
||||
NS_LITERAL_CSTRING("SELECT 1"),
|
||||
getter_AddRefs(stmt));
|
||||
blocking_async_execute(stmt);
|
||||
stmt->Finalize();
|
||||
|
||||
nsCOMPtr<nsIThreadManager> threadMan =
|
||||
do_GetService("@mozilla.org/thread-manager;1");
|
||||
nsCOMPtr<nsIThread> asyncThread;
|
||||
threadMan->GetThreadFromPRThread(last_non_watched_thread,
|
||||
getter_AddRefs(asyncThread));
|
||||
|
||||
// Additionally, check that the thread we get as the background thread is the
|
||||
// same one as the one we report from getInterface.
|
||||
nsCOMPtr<nsIEventTarget> target = do_GetInterface(db);
|
||||
nsCOMPtr<nsIThread> allegedAsyncThread = do_QueryInterface(target);
|
||||
PRThread *allegedPRThread;
|
||||
(void)allegedAsyncThread->GetPRThread(&allegedPRThread);
|
||||
do_check_eq(allegedPRThread, last_non_watched_thread);
|
||||
return asyncThread.forget();
|
||||
}
|
||||
28
storage/test/storage_test_harness_tail.h
Normal file
28
storage/test/storage_test_harness_tail.h
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#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 < mozilla::ArrayLength(gTests); i++)
|
||||
gTests[i]();
|
||||
|
||||
if (gPassedTests == gTotalTests)
|
||||
passed(TEST_FILE);
|
||||
|
||||
(void)printf("%i of %i tests passed\n", gPassedTests, gTotalTests);
|
||||
}
|
||||
127
storage/test/test_AsXXX_helpers.cpp
Normal file
127
storage/test/test_AsXXX_helpers.cpp
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
*Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
#include "storage_test_harness.h"
|
||||
#include "mozIStorageRow.h"
|
||||
#include "mozIStorageResultSet.h"
|
||||
|
||||
/**
|
||||
* This file tests AsXXX (AsInt32, AsInt64, ...) helpers.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Event Loop Spinning
|
||||
|
||||
class Spinner : public AsyncStatementSpinner
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_ASYNCSTATEMENTSPINNER
|
||||
Spinner() {}
|
||||
protected:
|
||||
virtual ~Spinner() {}
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED0(Spinner,
|
||||
AsyncStatementSpinner)
|
||||
|
||||
NS_IMETHODIMP
|
||||
Spinner::HandleResult(mozIStorageResultSet *aResultSet)
|
||||
{
|
||||
nsCOMPtr<mozIStorageRow> row;
|
||||
do_check_true(NS_SUCCEEDED(aResultSet->GetNextRow(getter_AddRefs(row))) && row);
|
||||
|
||||
do_check_eq(row->AsInt32(0), 0);
|
||||
do_check_eq(row->AsInt64(0), 0);
|
||||
do_check_eq(row->AsDouble(0), 0.0);
|
||||
|
||||
uint32_t len = 100;
|
||||
do_check_eq(row->AsSharedUTF8String(0, &len), (const char*)nullptr);
|
||||
do_check_eq(len, 0);
|
||||
len = 100;
|
||||
do_check_eq(row->AsSharedWString(0, &len), (const char16_t*)nullptr);
|
||||
do_check_eq(len, 0);
|
||||
len = 100;
|
||||
do_check_eq(row->AsSharedBlob(0, &len), (const uint8_t*)nullptr);
|
||||
do_check_eq(len, 0);
|
||||
|
||||
do_check_eq(row->IsNull(0), true);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
test_NULLFallback()
|
||||
{
|
||||
nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
|
||||
|
||||
nsCOMPtr<mozIStorageStatement> stmt;
|
||||
(void)db->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"SELECT NULL"
|
||||
), getter_AddRefs(stmt));
|
||||
|
||||
nsCOMPtr<mozIStorageValueArray> valueArray = do_QueryInterface(stmt);
|
||||
do_check_true(valueArray);
|
||||
|
||||
bool hasMore;
|
||||
do_check_true(NS_SUCCEEDED(stmt->ExecuteStep(&hasMore)) && hasMore);
|
||||
|
||||
do_check_eq(stmt->AsInt32(0), 0);
|
||||
do_check_eq(stmt->AsInt64(0), 0);
|
||||
do_check_eq(stmt->AsDouble(0), 0.0);
|
||||
uint32_t len = 100;
|
||||
do_check_eq(stmt->AsSharedUTF8String(0, &len), (const char*)nullptr);
|
||||
do_check_eq(len, 0);
|
||||
len = 100;
|
||||
do_check_eq(stmt->AsSharedWString(0, &len), (const char16_t*)nullptr);
|
||||
do_check_eq(len, 0);
|
||||
len = 100;
|
||||
do_check_eq(stmt->AsSharedBlob(0, &len), (const uint8_t*)nullptr);
|
||||
do_check_eq(len, 0);
|
||||
do_check_eq(stmt->IsNull(0), true);
|
||||
|
||||
do_check_eq(valueArray->AsInt32(0), 0);
|
||||
do_check_eq(valueArray->AsInt64(0), 0);
|
||||
do_check_eq(valueArray->AsDouble(0), 0.0);
|
||||
len = 100;
|
||||
do_check_eq(valueArray->AsSharedUTF8String(0, &len), (const char*)nullptr);
|
||||
do_check_eq(len, 0);
|
||||
len = 100;
|
||||
do_check_eq(valueArray->AsSharedWString(0, &len), (const char16_t*)nullptr);
|
||||
do_check_eq(len, 0);
|
||||
len = 100;
|
||||
do_check_eq(valueArray->AsSharedBlob(0, &len), (const uint8_t*)nullptr);
|
||||
do_check_eq(len, 0);
|
||||
do_check_eq(valueArray->IsNull(0), true);
|
||||
}
|
||||
|
||||
void
|
||||
test_asyncNULLFallback()
|
||||
{
|
||||
nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
|
||||
|
||||
nsCOMPtr<mozIStorageAsyncStatement> stmt;
|
||||
(void)db->CreateAsyncStatement(NS_LITERAL_CSTRING(
|
||||
"SELECT NULL"
|
||||
), getter_AddRefs(stmt));
|
||||
|
||||
nsCOMPtr<mozIStoragePendingStatement> pendingStmt;
|
||||
do_check_true(NS_SUCCEEDED(stmt->ExecuteAsync(nullptr, getter_AddRefs(pendingStmt))));
|
||||
do_check_true(pendingStmt);
|
||||
stmt->Finalize();
|
||||
RefPtr<Spinner> asyncSpin(new Spinner());
|
||||
db->AsyncClose(asyncSpin);
|
||||
asyncSpin->SpinUntilCompleted();
|
||||
|
||||
}
|
||||
|
||||
void (*gTests[])(void) = {
|
||||
test_NULLFallback
|
||||
, test_asyncNULLFallback
|
||||
};
|
||||
|
||||
const char *file = __FILE__;
|
||||
#define TEST_NAME "AsXXX helpers"
|
||||
#define TEST_FILE file
|
||||
#include "storage_test_harness_tail.h"
|
||||
163
storage/test/test_StatementCache.cpp
Normal file
163
storage/test/test_StatementCache.cpp
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "storage_test_harness.h"
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/storage/StatementCache.h"
|
||||
using namespace mozilla::storage;
|
||||
|
||||
/**
|
||||
* This file test our statement cache in StatementCache.h.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Helpers
|
||||
|
||||
class SyncCache : public StatementCache<mozIStorageStatement>
|
||||
{
|
||||
public:
|
||||
explicit SyncCache(nsCOMPtr<mozIStorageConnection>& aConnection)
|
||||
: StatementCache<mozIStorageStatement>(aConnection)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class AsyncCache : public StatementCache<mozIStorageAsyncStatement>
|
||||
{
|
||||
public:
|
||||
explicit AsyncCache(nsCOMPtr<mozIStorageConnection>& aConnection)
|
||||
: StatementCache<mozIStorageAsyncStatement>(aConnection)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Wraps nsCString so we can not implement the same functions twice for each
|
||||
* type.
|
||||
*/
|
||||
class StringWrapper : public nsCString
|
||||
{
|
||||
public:
|
||||
MOZ_IMPLICIT StringWrapper(const char* aOther)
|
||||
{
|
||||
this->Assign(aOther);
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Test Functions
|
||||
|
||||
template<typename StringType>
|
||||
void
|
||||
test_GetCachedStatement()
|
||||
{
|
||||
nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
|
||||
SyncCache cache(db);
|
||||
|
||||
StringType sql = "SELECT * FROM sqlite_master";
|
||||
|
||||
// Make sure we get a statement back with the right state.
|
||||
nsCOMPtr<mozIStorageStatement> stmt = cache.GetCachedStatement(sql);
|
||||
do_check_true(stmt);
|
||||
int32_t state;
|
||||
do_check_success(stmt->GetState(&state));
|
||||
do_check_eq(mozIStorageBaseStatement::MOZ_STORAGE_STATEMENT_READY, state);
|
||||
|
||||
// Check to make sure we get the same copy the second time we ask.
|
||||
nsCOMPtr<mozIStorageStatement> stmt2 = cache.GetCachedStatement(sql);
|
||||
do_check_true(stmt2);
|
||||
do_check_eq(stmt.get(), stmt2.get());
|
||||
}
|
||||
|
||||
template <typename StringType>
|
||||
void
|
||||
test_FinalizeStatements()
|
||||
{
|
||||
nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
|
||||
SyncCache cache(db);
|
||||
|
||||
StringType sql = "SELECT * FROM sqlite_master";
|
||||
|
||||
// Get a statement, and then tell the cache to finalize.
|
||||
nsCOMPtr<mozIStorageStatement> stmt = cache.GetCachedStatement(sql);
|
||||
do_check_true(stmt);
|
||||
|
||||
cache.FinalizeStatements();
|
||||
|
||||
// We should be in an invalid state at this point.
|
||||
int32_t state;
|
||||
do_check_success(stmt->GetState(&state));
|
||||
do_check_eq(mozIStorageBaseStatement::MOZ_STORAGE_STATEMENT_INVALID, state);
|
||||
|
||||
// Should be able to close the database now too.
|
||||
do_check_success(db->Close());
|
||||
}
|
||||
|
||||
template<typename StringType>
|
||||
void
|
||||
test_GetCachedAsyncStatement()
|
||||
{
|
||||
nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
|
||||
AsyncCache cache(db);
|
||||
|
||||
StringType sql = "SELECT * FROM sqlite_master";
|
||||
|
||||
// Make sure we get a statement back with the right state.
|
||||
nsCOMPtr<mozIStorageAsyncStatement> stmt = cache.GetCachedStatement(sql);
|
||||
do_check_true(stmt);
|
||||
int32_t state;
|
||||
do_check_success(stmt->GetState(&state));
|
||||
do_check_eq(mozIStorageBaseStatement::MOZ_STORAGE_STATEMENT_READY, state);
|
||||
|
||||
// Check to make sure we get the same copy the second time we ask.
|
||||
nsCOMPtr<mozIStorageAsyncStatement> stmt2 = cache.GetCachedStatement(sql);
|
||||
do_check_true(stmt2);
|
||||
do_check_eq(stmt.get(), stmt2.get());
|
||||
}
|
||||
|
||||
template <typename StringType>
|
||||
void
|
||||
test_FinalizeAsyncStatements()
|
||||
{
|
||||
nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
|
||||
AsyncCache cache(db);
|
||||
|
||||
StringType sql = "SELECT * FROM sqlite_master";
|
||||
|
||||
// Get a statement, and then tell the cache to finalize.
|
||||
nsCOMPtr<mozIStorageAsyncStatement> stmt = cache.GetCachedStatement(sql);
|
||||
do_check_true(stmt);
|
||||
|
||||
cache.FinalizeStatements();
|
||||
|
||||
// We should be in an invalid state at this point.
|
||||
int32_t state;
|
||||
do_check_success(stmt->GetState(&state));
|
||||
do_check_eq(mozIStorageBaseStatement::MOZ_STORAGE_STATEMENT_INVALID, state);
|
||||
|
||||
// Should be able to close the database now too.
|
||||
do_check_success(db->AsyncClose(nullptr));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Test Harness Stuff
|
||||
|
||||
void (*gTests[])(void) = {
|
||||
test_GetCachedStatement<const char []>,
|
||||
test_GetCachedStatement<StringWrapper>,
|
||||
test_FinalizeStatements<const char []>,
|
||||
test_FinalizeStatements<StringWrapper>,
|
||||
test_GetCachedAsyncStatement<const char []>,
|
||||
test_GetCachedAsyncStatement<StringWrapper>,
|
||||
test_FinalizeAsyncStatements<const char []>,
|
||||
test_FinalizeAsyncStatements<StringWrapper>,
|
||||
};
|
||||
|
||||
const char *file = __FILE__;
|
||||
#define TEST_NAME "StatementCache"
|
||||
#define TEST_FILE file
|
||||
#include "storage_test_harness_tail.h"
|
||||
509
storage/test/test_asyncStatementExecution_transaction.cpp
Normal file
509
storage/test/test_asyncStatementExecution_transaction.cpp
Normal file
|
|
@ -0,0 +1,509 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
#include "storage_test_harness.h"
|
||||
|
||||
#include "nsIEventTarget.h"
|
||||
#include "mozStorageConnection.h"
|
||||
|
||||
#include "sqlite3.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::storage;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Helpers
|
||||
|
||||
/**
|
||||
* Commit hook to detect transactions.
|
||||
*
|
||||
* @param aArg
|
||||
* An integer pointer that will be incremented for each commit.
|
||||
*/
|
||||
int commit_hook(void *aArg)
|
||||
{
|
||||
int *arg = static_cast<int *>(aArg);
|
||||
(*arg)++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the passed-in statements and checks if a transaction is created.
|
||||
* When done statements are finalized and database connection is closed.
|
||||
*
|
||||
* @param aDB
|
||||
* The database connection.
|
||||
* @param aStmts
|
||||
* Vector of statements.
|
||||
* @param aStmtsLen
|
||||
* Number of statements.
|
||||
* @param aTransactionExpected
|
||||
* Whether a transaction is expected or not.
|
||||
*/
|
||||
void
|
||||
check_transaction(mozIStorageConnection *aDB,
|
||||
mozIStorageBaseStatement **aStmts,
|
||||
uint32_t aStmtsLen,
|
||||
bool aTransactionExpected)
|
||||
{
|
||||
// -- install a transaction commit hook.
|
||||
int commit = 0;
|
||||
static_cast<Connection *>(aDB)->setCommitHook(commit_hook, &commit);
|
||||
|
||||
RefPtr<AsyncStatementSpinner> asyncSpin(new AsyncStatementSpinner());
|
||||
nsCOMPtr<mozIStoragePendingStatement> asyncPend;
|
||||
do_check_success(aDB->ExecuteAsync(aStmts, aStmtsLen, asyncSpin,
|
||||
getter_AddRefs(asyncPend)));
|
||||
do_check_true(asyncPend);
|
||||
|
||||
// -- complete the execution
|
||||
asyncSpin->SpinUntilCompleted();
|
||||
|
||||
// -- uninstall the transaction commit hook.
|
||||
static_cast<Connection *>(aDB)->setCommitHook(nullptr);
|
||||
|
||||
// -- check transaction
|
||||
do_check_eq(aTransactionExpected, !!commit);
|
||||
|
||||
// -- check that only one transaction was created.
|
||||
if (aTransactionExpected) {
|
||||
do_check_eq(1, commit);
|
||||
}
|
||||
|
||||
// -- cleanup
|
||||
for (uint32_t i = 0; i < aStmtsLen; ++i) {
|
||||
aStmts[i]->Finalize();
|
||||
}
|
||||
blocking_async_close(aDB);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Tests
|
||||
|
||||
/**
|
||||
* Test that executing multiple readonly AsyncStatements doesn't create a
|
||||
* transaction.
|
||||
*/
|
||||
void
|
||||
test_MultipleAsyncReadStatements()
|
||||
{
|
||||
nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
|
||||
|
||||
// -- create statements and execute them
|
||||
nsCOMPtr<mozIStorageAsyncStatement> stmt1;
|
||||
db->CreateAsyncStatement(NS_LITERAL_CSTRING(
|
||||
"SELECT * FROM sqlite_master"
|
||||
), getter_AddRefs(stmt1));
|
||||
|
||||
nsCOMPtr<mozIStorageAsyncStatement> stmt2;
|
||||
db->CreateAsyncStatement(NS_LITERAL_CSTRING(
|
||||
"SELECT * FROM sqlite_master"
|
||||
), getter_AddRefs(stmt2));
|
||||
|
||||
mozIStorageBaseStatement *stmts[] = {
|
||||
stmt1,
|
||||
stmt2,
|
||||
};
|
||||
|
||||
check_transaction(db, stmts, ArrayLength(stmts), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that executing multiple readonly Statements doesn't create a
|
||||
* transaction.
|
||||
*/
|
||||
void
|
||||
test_MultipleReadStatements()
|
||||
{
|
||||
nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
|
||||
|
||||
// -- create statements and execute them
|
||||
nsCOMPtr<mozIStorageStatement> stmt1;
|
||||
db->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"SELECT * FROM sqlite_master"
|
||||
), getter_AddRefs(stmt1));
|
||||
|
||||
nsCOMPtr<mozIStorageStatement> stmt2;
|
||||
db->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"SELECT * FROM sqlite_master"
|
||||
), getter_AddRefs(stmt2));
|
||||
|
||||
mozIStorageBaseStatement *stmts[] = {
|
||||
stmt1,
|
||||
stmt2,
|
||||
};
|
||||
|
||||
check_transaction(db, stmts, ArrayLength(stmts), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that executing multiple AsyncStatements causing writes creates a
|
||||
* transaction.
|
||||
*/
|
||||
void
|
||||
test_MultipleAsyncReadWriteStatements()
|
||||
{
|
||||
nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
|
||||
|
||||
// -- create statements and execute them
|
||||
nsCOMPtr<mozIStorageAsyncStatement> stmt1;
|
||||
db->CreateAsyncStatement(NS_LITERAL_CSTRING(
|
||||
"SELECT * FROM sqlite_master"
|
||||
), getter_AddRefs(stmt1));
|
||||
|
||||
nsCOMPtr<mozIStorageAsyncStatement> stmt2;
|
||||
db->CreateAsyncStatement(NS_LITERAL_CSTRING(
|
||||
"CREATE TABLE test (id INTEGER PRIMARY KEY)"
|
||||
), getter_AddRefs(stmt2));
|
||||
|
||||
mozIStorageBaseStatement *stmts[] = {
|
||||
stmt1,
|
||||
stmt2,
|
||||
};
|
||||
|
||||
check_transaction(db, stmts, ArrayLength(stmts), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that executing multiple Statements causing writes creates a transaction.
|
||||
*/
|
||||
void
|
||||
test_MultipleReadWriteStatements()
|
||||
{
|
||||
nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
|
||||
|
||||
// -- create statements and execute them
|
||||
nsCOMPtr<mozIStorageStatement> stmt1;
|
||||
db->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"SELECT * FROM sqlite_master"
|
||||
), getter_AddRefs(stmt1));
|
||||
|
||||
nsCOMPtr<mozIStorageStatement> stmt2;
|
||||
db->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"CREATE TABLE test (id INTEGER PRIMARY KEY)"
|
||||
), getter_AddRefs(stmt2));
|
||||
|
||||
mozIStorageBaseStatement *stmts[] = {
|
||||
stmt1,
|
||||
stmt2,
|
||||
};
|
||||
|
||||
check_transaction(db, stmts, ArrayLength(stmts), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that executing multiple AsyncStatements causing writes creates a
|
||||
* single transaction.
|
||||
*/
|
||||
void
|
||||
test_MultipleAsyncWriteStatements()
|
||||
{
|
||||
nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
|
||||
|
||||
// -- create statements and execute them
|
||||
nsCOMPtr<mozIStorageAsyncStatement> stmt1;
|
||||
db->CreateAsyncStatement(NS_LITERAL_CSTRING(
|
||||
"CREATE TABLE test1 (id INTEGER PRIMARY KEY)"
|
||||
), getter_AddRefs(stmt1));
|
||||
|
||||
nsCOMPtr<mozIStorageAsyncStatement> stmt2;
|
||||
db->CreateAsyncStatement(NS_LITERAL_CSTRING(
|
||||
"CREATE TABLE test2 (id INTEGER PRIMARY KEY)"
|
||||
), getter_AddRefs(stmt2));
|
||||
|
||||
mozIStorageBaseStatement *stmts[] = {
|
||||
stmt1,
|
||||
stmt2,
|
||||
};
|
||||
|
||||
check_transaction(db, stmts, ArrayLength(stmts), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that executing multiple Statements causing writes creates a
|
||||
* single transaction.
|
||||
*/
|
||||
void
|
||||
test_MultipleWriteStatements()
|
||||
{
|
||||
nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
|
||||
|
||||
// -- create statements and execute them
|
||||
nsCOMPtr<mozIStorageStatement> stmt1;
|
||||
db->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"CREATE TABLE test1 (id INTEGER PRIMARY KEY)"
|
||||
), getter_AddRefs(stmt1));
|
||||
|
||||
nsCOMPtr<mozIStorageStatement> stmt2;
|
||||
db->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"CREATE TABLE test2 (id INTEGER PRIMARY KEY)"
|
||||
), getter_AddRefs(stmt2));
|
||||
|
||||
mozIStorageBaseStatement *stmts[] = {
|
||||
stmt1,
|
||||
stmt2,
|
||||
};
|
||||
|
||||
check_transaction(db, stmts, ArrayLength(stmts), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that executing a single read-only AsyncStatement doesn't create a
|
||||
* transaction.
|
||||
*/
|
||||
void
|
||||
test_SingleAsyncReadStatement()
|
||||
{
|
||||
nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
|
||||
|
||||
// -- create statements and execute them
|
||||
nsCOMPtr<mozIStorageAsyncStatement> stmt;
|
||||
db->CreateAsyncStatement(NS_LITERAL_CSTRING(
|
||||
"SELECT * FROM sqlite_master"
|
||||
), getter_AddRefs(stmt));
|
||||
|
||||
mozIStorageBaseStatement *stmts[] = {
|
||||
stmt,
|
||||
};
|
||||
|
||||
check_transaction(db, stmts, ArrayLength(stmts), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that executing a single read-only Statement doesn't create a
|
||||
* transaction.
|
||||
*/
|
||||
void
|
||||
test_SingleReadStatement()
|
||||
{
|
||||
nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
|
||||
|
||||
// -- create statements and execute them
|
||||
nsCOMPtr<mozIStorageStatement> stmt;
|
||||
db->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"SELECT * FROM sqlite_master"
|
||||
), getter_AddRefs(stmt));
|
||||
|
||||
mozIStorageBaseStatement *stmts[] = {
|
||||
stmt,
|
||||
};
|
||||
|
||||
check_transaction(db, stmts, ArrayLength(stmts), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that executing a single AsyncStatement causing writes creates a
|
||||
* transaction.
|
||||
*/
|
||||
void
|
||||
test_SingleAsyncWriteStatement()
|
||||
{
|
||||
nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
|
||||
|
||||
// -- create statements and execute them
|
||||
nsCOMPtr<mozIStorageAsyncStatement> stmt;
|
||||
db->CreateAsyncStatement(NS_LITERAL_CSTRING(
|
||||
"CREATE TABLE test (id INTEGER PRIMARY KEY)"
|
||||
), getter_AddRefs(stmt));
|
||||
|
||||
mozIStorageBaseStatement *stmts[] = {
|
||||
stmt,
|
||||
};
|
||||
|
||||
check_transaction(db, stmts, ArrayLength(stmts), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that executing a single Statement causing writes creates a transaction.
|
||||
*/
|
||||
void
|
||||
test_SingleWriteStatement()
|
||||
{
|
||||
nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
|
||||
|
||||
// -- create statements and execute them
|
||||
nsCOMPtr<mozIStorageStatement> stmt;
|
||||
db->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"CREATE TABLE test (id INTEGER PRIMARY KEY)"
|
||||
), getter_AddRefs(stmt));
|
||||
|
||||
mozIStorageBaseStatement *stmts[] = {
|
||||
stmt,
|
||||
};
|
||||
|
||||
check_transaction(db, stmts, ArrayLength(stmts), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that executing a single read-only AsyncStatement with multiple params
|
||||
* doesn't create a transaction.
|
||||
*/
|
||||
void
|
||||
test_MultipleParamsAsyncReadStatement()
|
||||
{
|
||||
nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
|
||||
|
||||
// -- create statements and execute them
|
||||
nsCOMPtr<mozIStorageAsyncStatement> stmt;
|
||||
db->CreateAsyncStatement(NS_LITERAL_CSTRING(
|
||||
"SELECT :param FROM sqlite_master"
|
||||
), getter_AddRefs(stmt));
|
||||
|
||||
// -- bind multiple BindingParams
|
||||
nsCOMPtr<mozIStorageBindingParamsArray> paramsArray;
|
||||
stmt->NewBindingParamsArray(getter_AddRefs(paramsArray));
|
||||
for (int32_t i = 0; i < 2; i++) {
|
||||
nsCOMPtr<mozIStorageBindingParams> params;
|
||||
paramsArray->NewBindingParams(getter_AddRefs(params));
|
||||
params->BindInt32ByName(NS_LITERAL_CSTRING("param"), 1);
|
||||
paramsArray->AddParams(params);
|
||||
}
|
||||
stmt->BindParameters(paramsArray);
|
||||
paramsArray = nullptr;
|
||||
|
||||
mozIStorageBaseStatement *stmts[] = {
|
||||
stmt,
|
||||
};
|
||||
|
||||
check_transaction(db, stmts, ArrayLength(stmts), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that executing a single read-only Statement with multiple params
|
||||
* doesn't create a transaction.
|
||||
*/
|
||||
void
|
||||
test_MultipleParamsReadStatement()
|
||||
{
|
||||
nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
|
||||
|
||||
// -- create statements and execute them
|
||||
nsCOMPtr<mozIStorageStatement> stmt;
|
||||
db->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"SELECT :param FROM sqlite_master"
|
||||
), getter_AddRefs(stmt));
|
||||
|
||||
// -- bind multiple BindingParams
|
||||
nsCOMPtr<mozIStorageBindingParamsArray> paramsArray;
|
||||
stmt->NewBindingParamsArray(getter_AddRefs(paramsArray));
|
||||
for (int32_t i = 0; i < 2; i++) {
|
||||
nsCOMPtr<mozIStorageBindingParams> params;
|
||||
paramsArray->NewBindingParams(getter_AddRefs(params));
|
||||
params->BindInt32ByName(NS_LITERAL_CSTRING("param"), 1);
|
||||
paramsArray->AddParams(params);
|
||||
}
|
||||
stmt->BindParameters(paramsArray);
|
||||
paramsArray = nullptr;
|
||||
|
||||
mozIStorageBaseStatement *stmts[] = {
|
||||
stmt,
|
||||
};
|
||||
|
||||
check_transaction(db, stmts, ArrayLength(stmts), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that executing a single write AsyncStatement with multiple params
|
||||
* creates a transaction.
|
||||
*/
|
||||
void
|
||||
test_MultipleParamsAsyncWriteStatement()
|
||||
{
|
||||
nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
|
||||
|
||||
// -- create a table for writes
|
||||
nsCOMPtr<mozIStorageStatement> tableStmt;
|
||||
db->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"CREATE TABLE test (id INTEGER PRIMARY KEY)"
|
||||
), getter_AddRefs(tableStmt));
|
||||
tableStmt->Execute();
|
||||
tableStmt->Finalize();
|
||||
|
||||
// -- create statements and execute them
|
||||
nsCOMPtr<mozIStorageAsyncStatement> stmt;
|
||||
db->CreateAsyncStatement(NS_LITERAL_CSTRING(
|
||||
"DELETE FROM test WHERE id = :param"
|
||||
), getter_AddRefs(stmt));
|
||||
|
||||
// -- bind multiple BindingParams
|
||||
nsCOMPtr<mozIStorageBindingParamsArray> paramsArray;
|
||||
stmt->NewBindingParamsArray(getter_AddRefs(paramsArray));
|
||||
for (int32_t i = 0; i < 2; i++) {
|
||||
nsCOMPtr<mozIStorageBindingParams> params;
|
||||
paramsArray->NewBindingParams(getter_AddRefs(params));
|
||||
params->BindInt32ByName(NS_LITERAL_CSTRING("param"), 1);
|
||||
paramsArray->AddParams(params);
|
||||
}
|
||||
stmt->BindParameters(paramsArray);
|
||||
paramsArray = nullptr;
|
||||
|
||||
mozIStorageBaseStatement *stmts[] = {
|
||||
stmt,
|
||||
};
|
||||
|
||||
check_transaction(db, stmts, ArrayLength(stmts), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that executing a single write Statement with multiple params
|
||||
* creates a transaction.
|
||||
*/
|
||||
void
|
||||
test_MultipleParamsWriteStatement()
|
||||
{
|
||||
nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
|
||||
|
||||
// -- create a table for writes
|
||||
nsCOMPtr<mozIStorageStatement> tableStmt;
|
||||
db->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"CREATE TABLE test (id INTEGER PRIMARY KEY)"
|
||||
), getter_AddRefs(tableStmt));
|
||||
tableStmt->Execute();
|
||||
tableStmt->Finalize();
|
||||
|
||||
// -- create statements and execute them
|
||||
nsCOMPtr<mozIStorageStatement> stmt;
|
||||
db->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"DELETE FROM test WHERE id = :param"
|
||||
), getter_AddRefs(stmt));
|
||||
|
||||
// -- bind multiple BindingParams
|
||||
nsCOMPtr<mozIStorageBindingParamsArray> paramsArray;
|
||||
stmt->NewBindingParamsArray(getter_AddRefs(paramsArray));
|
||||
for (int32_t i = 0; i < 2; i++) {
|
||||
nsCOMPtr<mozIStorageBindingParams> params;
|
||||
paramsArray->NewBindingParams(getter_AddRefs(params));
|
||||
params->BindInt32ByName(NS_LITERAL_CSTRING("param"), 1);
|
||||
paramsArray->AddParams(params);
|
||||
}
|
||||
stmt->BindParameters(paramsArray);
|
||||
paramsArray = nullptr;
|
||||
|
||||
mozIStorageBaseStatement *stmts[] = {
|
||||
stmt,
|
||||
};
|
||||
|
||||
check_transaction(db, stmts, ArrayLength(stmts), true);
|
||||
}
|
||||
|
||||
void (*gTests[])(void) = {
|
||||
test_MultipleAsyncReadStatements,
|
||||
test_MultipleReadStatements,
|
||||
test_MultipleAsyncReadWriteStatements,
|
||||
test_MultipleReadWriteStatements,
|
||||
test_MultipleAsyncWriteStatements,
|
||||
test_MultipleWriteStatements,
|
||||
test_SingleAsyncReadStatement,
|
||||
test_SingleReadStatement,
|
||||
test_SingleAsyncWriteStatement,
|
||||
test_SingleWriteStatement,
|
||||
test_MultipleParamsAsyncReadStatement,
|
||||
test_MultipleParamsReadStatement,
|
||||
test_MultipleParamsAsyncWriteStatement,
|
||||
test_MultipleParamsWriteStatement,
|
||||
};
|
||||
|
||||
const char *file = __FILE__;
|
||||
#define TEST_NAME "async statement execution transaction"
|
||||
#define TEST_FILE file
|
||||
#include "storage_test_harness_tail.h"
|
||||
174
storage/test/test_async_callbacks_with_spun_event_loops.cpp
Normal file
174
storage/test/test_async_callbacks_with_spun_event_loops.cpp
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
/* 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 "mozilla/Attributes.h"
|
||||
|
||||
#include "sqlite3.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Async Helpers
|
||||
|
||||
/**
|
||||
* Spins the events loop for current thread until aCondition is true.
|
||||
*/
|
||||
void
|
||||
spin_events_loop_until_true(const bool* const aCondition)
|
||||
{
|
||||
nsCOMPtr<nsIThread> thread(::do_GetCurrentThread());
|
||||
nsresult rv = NS_OK;
|
||||
bool processed = true;
|
||||
while (!(*aCondition) && NS_SUCCEEDED(rv)) {
|
||||
rv = thread->ProcessNextEvent(true, &processed);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// mozIStorageStatementCallback implementation
|
||||
|
||||
class UnownedCallback final : 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;
|
||||
|
||||
explicit UnownedCallback(mozIStorageConnection* aDBConn)
|
||||
: mDBConn(aDBConn)
|
||||
, mCompleted(false)
|
||||
{
|
||||
sAlive = true;
|
||||
sResult = false;
|
||||
sError = false;
|
||||
}
|
||||
|
||||
private:
|
||||
~UnownedCallback()
|
||||
{
|
||||
sAlive = false;
|
||||
blocking_async_close(mDBConn);
|
||||
}
|
||||
|
||||
public:
|
||||
NS_IMETHOD HandleResult(mozIStorageResultSet* aResultSet) override
|
||||
{
|
||||
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) override
|
||||
{
|
||||
sError = true;
|
||||
spin_events_loop_until_true(&mCompleted);
|
||||
if (!sAlive) {
|
||||
NS_RUNTIMEABORT("The statement callback was destroyed prematurely.");
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD HandleCompletion(uint16_t aReason) override
|
||||
{
|
||||
mCompleted = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
protected:
|
||||
nsCOMPtr<mozIStorageConnection> mDBConn;
|
||||
bool mCompleted;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(UnownedCallback, mozIStorageStatementCallback)
|
||||
|
||||
bool UnownedCallback::sAlive = false;
|
||||
bool UnownedCallback::sResult = false;
|
||||
bool UnownedCallback::sError = false;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Tests
|
||||
|
||||
void
|
||||
test_SpinEventsLoopInHandleResult()
|
||||
{
|
||||
nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
|
||||
|
||||
// Create a test table and populate it.
|
||||
nsCOMPtr<mozIStorageStatement> 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 (int32_t 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<mozIStoragePendingStatement> 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<mozIStorageConnection> db(getMemoryDatabase());
|
||||
|
||||
// Create a test table and populate it.
|
||||
nsCOMPtr<mozIStorageStatement> 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<mozIStoragePendingStatement> 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"
|
||||
215
storage/test/test_binding_params.cpp
Normal file
215
storage/test/test_binding_params.cpp
Normal file
|
|
@ -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\:>~ :
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "storage_test_harness.h"
|
||||
|
||||
#include "mozStorageHelper.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
/**
|
||||
* This file tests binding and reading out string parameters through the
|
||||
* mozIStorageStatement API.
|
||||
*/
|
||||
|
||||
void
|
||||
test_ASCIIString()
|
||||
{
|
||||
nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
|
||||
|
||||
// Create table with a single string column.
|
||||
(void)db->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"CREATE TABLE test (str STRING)"
|
||||
));
|
||||
|
||||
// Create statements to INSERT and SELECT the string.
|
||||
nsCOMPtr<mozIStorageStatement> insert, select;
|
||||
(void)db->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"INSERT INTO test (str) VALUES (?1)"
|
||||
), getter_AddRefs(insert));
|
||||
(void)db->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"SELECT str FROM test"
|
||||
), getter_AddRefs(select));
|
||||
|
||||
// Roundtrip a string through the table, and ensure it comes out as expected.
|
||||
nsAutoCString inserted("I'm an ASCII string");
|
||||
{
|
||||
mozStorageStatementScoper scoper(insert);
|
||||
bool hasResult;
|
||||
do_check_true(NS_SUCCEEDED(insert->BindUTF8StringByIndex(0, inserted)));
|
||||
do_check_true(NS_SUCCEEDED(insert->ExecuteStep(&hasResult)));
|
||||
do_check_false(hasResult);
|
||||
}
|
||||
|
||||
nsAutoCString result;
|
||||
{
|
||||
mozStorageStatementScoper scoper(select);
|
||||
bool hasResult;
|
||||
do_check_true(NS_SUCCEEDED(select->ExecuteStep(&hasResult)));
|
||||
do_check_true(hasResult);
|
||||
do_check_true(NS_SUCCEEDED(select->GetUTF8String(0, result)));
|
||||
}
|
||||
|
||||
do_check_true(result == inserted);
|
||||
|
||||
(void)db->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DELETE FROM test"));
|
||||
}
|
||||
|
||||
void
|
||||
test_CString()
|
||||
{
|
||||
nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
|
||||
|
||||
// Create table with a single string column.
|
||||
(void)db->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"CREATE TABLE test (str STRING)"
|
||||
));
|
||||
|
||||
// Create statements to INSERT and SELECT the string.
|
||||
nsCOMPtr<mozIStorageStatement> insert, select;
|
||||
(void)db->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"INSERT INTO test (str) VALUES (?1)"
|
||||
), getter_AddRefs(insert));
|
||||
(void)db->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"SELECT str FROM test"
|
||||
), getter_AddRefs(select));
|
||||
|
||||
// Roundtrip a string through the table, and ensure it comes out as expected.
|
||||
static const char sCharArray[] =
|
||||
"I'm not a \xff\x00\xac\xde\xbb ASCII string!";
|
||||
nsAutoCString inserted(sCharArray, ArrayLength(sCharArray) - 1);
|
||||
do_check_true(inserted.Length() == ArrayLength(sCharArray) - 1);
|
||||
{
|
||||
mozStorageStatementScoper scoper(insert);
|
||||
bool hasResult;
|
||||
do_check_true(NS_SUCCEEDED(insert->BindUTF8StringByIndex(0, inserted)));
|
||||
do_check_true(NS_SUCCEEDED(insert->ExecuteStep(&hasResult)));
|
||||
do_check_false(hasResult);
|
||||
}
|
||||
|
||||
{
|
||||
nsAutoCString result;
|
||||
|
||||
mozStorageStatementScoper scoper(select);
|
||||
bool hasResult;
|
||||
do_check_true(NS_SUCCEEDED(select->ExecuteStep(&hasResult)));
|
||||
do_check_true(hasResult);
|
||||
do_check_true(NS_SUCCEEDED(select->GetUTF8String(0, result)));
|
||||
|
||||
do_check_true(result == inserted);
|
||||
}
|
||||
|
||||
(void)db->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DELETE FROM test"));
|
||||
}
|
||||
|
||||
void
|
||||
test_UTFStrings()
|
||||
{
|
||||
nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
|
||||
|
||||
// Create table with a single string column.
|
||||
(void)db->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"CREATE TABLE test (str STRING)"
|
||||
));
|
||||
|
||||
// Create statements to INSERT and SELECT the string.
|
||||
nsCOMPtr<mozIStorageStatement> insert, select;
|
||||
(void)db->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"INSERT INTO test (str) VALUES (?1)"
|
||||
), getter_AddRefs(insert));
|
||||
(void)db->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"SELECT str FROM test"
|
||||
), getter_AddRefs(select));
|
||||
|
||||
// Roundtrip a UTF8 string through the table, using UTF8 input and output.
|
||||
static const char sCharArray[] =
|
||||
"I'm a \xc3\xbb\xc3\xbc\xc3\xa2\xc3\xa4\xc3\xa7 UTF8 string!";
|
||||
nsAutoCString insertedUTF8(sCharArray, ArrayLength(sCharArray) - 1);
|
||||
do_check_true(insertedUTF8.Length() == ArrayLength(sCharArray) - 1);
|
||||
NS_ConvertUTF8toUTF16 insertedUTF16(insertedUTF8);
|
||||
do_check_true(insertedUTF8 == NS_ConvertUTF16toUTF8(insertedUTF16));
|
||||
{
|
||||
mozStorageStatementScoper scoper(insert);
|
||||
bool hasResult;
|
||||
do_check_true(NS_SUCCEEDED(insert->BindUTF8StringByIndex(0, insertedUTF8)));
|
||||
do_check_true(NS_SUCCEEDED(insert->ExecuteStep(&hasResult)));
|
||||
do_check_false(hasResult);
|
||||
}
|
||||
|
||||
{
|
||||
nsAutoCString result;
|
||||
|
||||
mozStorageStatementScoper scoper(select);
|
||||
bool hasResult;
|
||||
do_check_true(NS_SUCCEEDED(select->ExecuteStep(&hasResult)));
|
||||
do_check_true(hasResult);
|
||||
do_check_true(NS_SUCCEEDED(select->GetUTF8String(0, result)));
|
||||
|
||||
do_check_true(result == insertedUTF8);
|
||||
}
|
||||
|
||||
// Use UTF8 input and UTF16 output.
|
||||
{
|
||||
nsAutoString result;
|
||||
|
||||
mozStorageStatementScoper scoper(select);
|
||||
bool hasResult;
|
||||
do_check_true(NS_SUCCEEDED(select->ExecuteStep(&hasResult)));
|
||||
do_check_true(hasResult);
|
||||
do_check_true(NS_SUCCEEDED(select->GetString(0, result)));
|
||||
|
||||
do_check_true(result == insertedUTF16);
|
||||
}
|
||||
|
||||
(void)db->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DELETE FROM test"));
|
||||
|
||||
// Roundtrip the same string using UTF16 input and UTF8 output.
|
||||
{
|
||||
mozStorageStatementScoper scoper(insert);
|
||||
bool hasResult;
|
||||
do_check_true(NS_SUCCEEDED(insert->BindStringByIndex(0, insertedUTF16)));
|
||||
do_check_true(NS_SUCCEEDED(insert->ExecuteStep(&hasResult)));
|
||||
do_check_false(hasResult);
|
||||
}
|
||||
|
||||
{
|
||||
nsAutoCString result;
|
||||
|
||||
mozStorageStatementScoper scoper(select);
|
||||
bool hasResult;
|
||||
do_check_true(NS_SUCCEEDED(select->ExecuteStep(&hasResult)));
|
||||
do_check_true(hasResult);
|
||||
do_check_true(NS_SUCCEEDED(select->GetUTF8String(0, result)));
|
||||
|
||||
do_check_true(result == insertedUTF8);
|
||||
}
|
||||
|
||||
// Use UTF16 input and UTF16 output.
|
||||
{
|
||||
nsAutoString result;
|
||||
|
||||
mozStorageStatementScoper scoper(select);
|
||||
bool hasResult;
|
||||
do_check_true(NS_SUCCEEDED(select->ExecuteStep(&hasResult)));
|
||||
do_check_true(hasResult);
|
||||
do_check_true(NS_SUCCEEDED(select->GetString(0, result)));
|
||||
|
||||
do_check_true(result == insertedUTF16);
|
||||
}
|
||||
|
||||
(void)db->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DELETE FROM test"));
|
||||
}
|
||||
|
||||
void (*gTests[])(void) = {
|
||||
test_ASCIIString,
|
||||
test_CString,
|
||||
test_UTFStrings,
|
||||
};
|
||||
|
||||
const char *file = __FILE__;
|
||||
#define TEST_NAME "binding string params"
|
||||
#define TEST_FILE file
|
||||
#include "storage_test_harness_tail.h"
|
||||
602
storage/test/test_deadlock_detector.cpp
Normal file
602
storage/test/test_deadlock_detector.cpp
Normal file
|
|
@ -0,0 +1,602 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: sw=4 ts=4 et :
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* Note: This file is a copy of xpcom/tests/TestDeadlockDetector.cpp, but all
|
||||
* mutexes were turned into SQLiteMutexes.
|
||||
*/
|
||||
|
||||
#include "prenv.h"
|
||||
#include "prerror.h"
|
||||
#include "prio.h"
|
||||
#include "prproces.h"
|
||||
|
||||
#include "nsMemory.h"
|
||||
|
||||
#include "mozilla/CondVar.h"
|
||||
#include "mozilla/ReentrantMonitor.h"
|
||||
#include "SQLiteMutex.h"
|
||||
|
||||
#include "TestHarness.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
/**
|
||||
* Helper class to allocate a sqlite3_mutex for our SQLiteMutex. Also makes
|
||||
* keeping the test files in sync easier.
|
||||
*/
|
||||
class TestMutex : public mozilla::storage::SQLiteMutex
|
||||
{
|
||||
public:
|
||||
explicit TestMutex(const char* aName)
|
||||
: mozilla::storage::SQLiteMutex(aName)
|
||||
, mInner(sqlite3_mutex_alloc(SQLITE_MUTEX_FAST))
|
||||
{
|
||||
NS_ASSERTION(mInner, "could not allocate a sqlite3_mutex");
|
||||
initWithMutex(mInner);
|
||||
}
|
||||
|
||||
~TestMutex()
|
||||
{
|
||||
sqlite3_mutex_free(mInner);
|
||||
}
|
||||
|
||||
void Lock()
|
||||
{
|
||||
lock();
|
||||
}
|
||||
|
||||
void Unlock()
|
||||
{
|
||||
unlock();
|
||||
}
|
||||
private:
|
||||
sqlite3_mutex *mInner;
|
||||
};
|
||||
|
||||
static PRThread*
|
||||
spawn(void (*run)(void*), void* arg)
|
||||
{
|
||||
return PR_CreateThread(PR_SYSTEM_THREAD,
|
||||
run,
|
||||
arg,
|
||||
PR_PRIORITY_NORMAL,
|
||||
PR_GLOBAL_THREAD,
|
||||
PR_JOINABLE_THREAD,
|
||||
0);
|
||||
}
|
||||
|
||||
#define PASS() \
|
||||
do { \
|
||||
passed(__FUNCTION__); \
|
||||
return NS_OK; \
|
||||
} while (0)
|
||||
|
||||
#define FAIL(why) \
|
||||
do { \
|
||||
fail("%s | %s - %s", __FILE__, __FUNCTION__, why); \
|
||||
return NS_ERROR_FAILURE; \
|
||||
} while (0)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static const char* sPathToThisBinary;
|
||||
static const char* sAssertBehaviorEnv = "XPCOM_DEBUG_BREAK=abort";
|
||||
|
||||
class Subprocess
|
||||
{
|
||||
public:
|
||||
// not available until process finishes
|
||||
int32_t mExitCode;
|
||||
nsCString mStdout;
|
||||
nsCString mStderr;
|
||||
|
||||
explicit Subprocess(const char* aTestName) {
|
||||
// set up stdio redirection
|
||||
PRFileDesc* readStdin; PRFileDesc* writeStdin;
|
||||
PRFileDesc* readStdout; PRFileDesc* writeStdout;
|
||||
PRFileDesc* readStderr; PRFileDesc* writeStderr;
|
||||
PRProcessAttr* pattr = PR_NewProcessAttr();
|
||||
|
||||
NS_ASSERTION(pattr, "couldn't allocate process attrs");
|
||||
|
||||
NS_ASSERTION(PR_SUCCESS == PR_CreatePipe(&readStdin, &writeStdin),
|
||||
"couldn't create child stdin pipe");
|
||||
NS_ASSERTION(PR_SUCCESS == PR_SetFDInheritable(readStdin, true),
|
||||
"couldn't set child stdin inheritable");
|
||||
PR_ProcessAttrSetStdioRedirect(pattr, PR_StandardInput, readStdin);
|
||||
|
||||
NS_ASSERTION(PR_SUCCESS == PR_CreatePipe(&readStdout, &writeStdout),
|
||||
"couldn't create child stdout pipe");
|
||||
NS_ASSERTION(PR_SUCCESS == PR_SetFDInheritable(writeStdout, true),
|
||||
"couldn't set child stdout inheritable");
|
||||
PR_ProcessAttrSetStdioRedirect(pattr, PR_StandardOutput, writeStdout);
|
||||
|
||||
NS_ASSERTION(PR_SUCCESS == PR_CreatePipe(&readStderr, &writeStderr),
|
||||
"couldn't create child stderr pipe");
|
||||
NS_ASSERTION(PR_SUCCESS == PR_SetFDInheritable(writeStderr, true),
|
||||
"couldn't set child stderr inheritable");
|
||||
PR_ProcessAttrSetStdioRedirect(pattr, PR_StandardError, writeStderr);
|
||||
|
||||
// set up argv with test name to run
|
||||
char* const newArgv[3] = {
|
||||
strdup(sPathToThisBinary),
|
||||
strdup(aTestName),
|
||||
0
|
||||
};
|
||||
|
||||
// make sure the child will abort if an assertion fails
|
||||
NS_ASSERTION(PR_SUCCESS == PR_SetEnv(sAssertBehaviorEnv),
|
||||
"couldn't set XPCOM_DEBUG_BREAK env var");
|
||||
|
||||
PRProcess* proc;
|
||||
NS_ASSERTION(proc = PR_CreateProcess(sPathToThisBinary,
|
||||
newArgv,
|
||||
0, // inherit environment
|
||||
pattr),
|
||||
"couldn't create process");
|
||||
PR_Close(readStdin);
|
||||
PR_Close(writeStdout);
|
||||
PR_Close(writeStderr);
|
||||
|
||||
mProc = proc;
|
||||
mStdinfd = writeStdin;
|
||||
mStdoutfd = readStdout;
|
||||
mStderrfd = readStderr;
|
||||
|
||||
free(newArgv[0]);
|
||||
free(newArgv[1]);
|
||||
PR_DestroyProcessAttr(pattr);
|
||||
}
|
||||
|
||||
void RunToCompletion(uint32_t aWaitMs)
|
||||
{
|
||||
PR_Close(mStdinfd);
|
||||
|
||||
PRPollDesc pollfds[2];
|
||||
int32_t nfds;
|
||||
bool stdoutOpen = true, stderrOpen = true;
|
||||
char buf[4096];
|
||||
|
||||
PRIntervalTime now = PR_IntervalNow();
|
||||
PRIntervalTime deadline = now + PR_MillisecondsToInterval(aWaitMs);
|
||||
|
||||
while ((stdoutOpen || stderrOpen) && now < deadline) {
|
||||
nfds = 0;
|
||||
if (stdoutOpen) {
|
||||
pollfds[nfds].fd = mStdoutfd;
|
||||
pollfds[nfds].in_flags = PR_POLL_READ;
|
||||
pollfds[nfds].out_flags = 0;
|
||||
++nfds;
|
||||
}
|
||||
if (stderrOpen) {
|
||||
pollfds[nfds].fd = mStderrfd;
|
||||
pollfds[nfds].in_flags = PR_POLL_READ;
|
||||
pollfds[nfds].out_flags = 0;
|
||||
++nfds;
|
||||
}
|
||||
|
||||
int32_t rv = PR_Poll(pollfds, nfds, deadline - now);
|
||||
NS_ASSERTION(0 <= rv, PR_ErrorToName(PR_GetError()));
|
||||
|
||||
if (0 == rv) { // timeout
|
||||
fputs("(timed out!)\n", stderr);
|
||||
Finish(false); // abnormal
|
||||
return;
|
||||
}
|
||||
|
||||
for (int32_t i = 0; i < nfds; ++i) {
|
||||
if (!pollfds[i].out_flags)
|
||||
continue;
|
||||
|
||||
bool isStdout = mStdoutfd == pollfds[i].fd;
|
||||
int32_t len = 0;
|
||||
|
||||
if (PR_POLL_READ & pollfds[i].out_flags) {
|
||||
len = PR_Read(pollfds[i].fd, buf, sizeof(buf) - 1);
|
||||
NS_ASSERTION(0 <= len, PR_ErrorToName(PR_GetError()));
|
||||
}
|
||||
else if (!(PR_POLL_HUP & pollfds[i].out_flags)) {
|
||||
NS_ERROR(PR_ErrorToName(PR_GetError()));
|
||||
}
|
||||
|
||||
if (0 < len) {
|
||||
buf[len] = '\0';
|
||||
if (isStdout)
|
||||
mStdout += buf;
|
||||
else
|
||||
mStderr += buf;
|
||||
}
|
||||
else if (isStdout) {
|
||||
stdoutOpen = false;
|
||||
}
|
||||
else {
|
||||
stderrOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
now = PR_IntervalNow();
|
||||
}
|
||||
|
||||
if (stdoutOpen)
|
||||
fputs("(stdout still open!)\n", stderr);
|
||||
if (stderrOpen)
|
||||
fputs("(stderr still open!)\n", stderr);
|
||||
if (now > deadline)
|
||||
fputs("(timed out!)\n", stderr);
|
||||
|
||||
Finish(!stdoutOpen && !stderrOpen && now <= deadline);
|
||||
}
|
||||
|
||||
private:
|
||||
void Finish(bool normalExit) {
|
||||
if (!normalExit) {
|
||||
PR_KillProcess(mProc);
|
||||
mExitCode = -1;
|
||||
int32_t dummy;
|
||||
PR_WaitProcess(mProc, &dummy);
|
||||
}
|
||||
else {
|
||||
PR_WaitProcess(mProc, &mExitCode); // this had better not block ...
|
||||
}
|
||||
|
||||
PR_Close(mStdoutfd);
|
||||
PR_Close(mStderrfd);
|
||||
}
|
||||
|
||||
PRProcess* mProc;
|
||||
PRFileDesc* mStdinfd; // writeable
|
||||
PRFileDesc* mStdoutfd; // readable
|
||||
PRFileDesc* mStderrfd; // readable
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Harness for checking detector errors
|
||||
bool
|
||||
CheckForDeadlock(const char* test, const char* const* findTokens)
|
||||
{
|
||||
Subprocess proc(test);
|
||||
proc.RunToCompletion(5000);
|
||||
|
||||
if (0 == proc.mExitCode)
|
||||
return false;
|
||||
|
||||
int32_t idx = 0;
|
||||
for (const char* const* tp = findTokens; *tp; ++tp) {
|
||||
const char* const token = *tp;
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
idx = proc.mStderr.Find(token, false, idx);
|
||||
#else
|
||||
nsCString tokenCString(token);
|
||||
idx = proc.mStderr.Find(tokenCString, idx);
|
||||
#endif
|
||||
if (-1 == idx) {
|
||||
printf("(missed token '%s' in output)\n", token);
|
||||
puts("----------------------------------\n");
|
||||
puts(proc.mStderr.get());
|
||||
puts("----------------------------------\n");
|
||||
return false;
|
||||
}
|
||||
idx += strlen(token);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Single-threaded sanity tests
|
||||
|
||||
// Stupidest possible deadlock.
|
||||
int
|
||||
Sanity_Child()
|
||||
{
|
||||
TestMutex m1("dd.sanity.m1");
|
||||
m1.Lock();
|
||||
m1.Lock();
|
||||
return 0; // not reached
|
||||
}
|
||||
|
||||
nsresult
|
||||
Sanity()
|
||||
{
|
||||
const char* const tokens[] = {
|
||||
"###!!! ERROR: Potential deadlock detected",
|
||||
"=== Cyclical dependency starts at\n--- Mutex : dd.sanity.m1",
|
||||
"=== Cycle completed at\n--- Mutex : dd.sanity.m1",
|
||||
"###!!! Deadlock may happen NOW!", // better catch these easy cases...
|
||||
"###!!! ASSERTION: Potential deadlock detected",
|
||||
0
|
||||
};
|
||||
if (CheckForDeadlock("Sanity", tokens)) {
|
||||
PASS();
|
||||
} else {
|
||||
FAIL("deadlock not detected");
|
||||
}
|
||||
}
|
||||
|
||||
// Slightly less stupid deadlock.
|
||||
int
|
||||
Sanity2_Child()
|
||||
{
|
||||
TestMutex m1("dd.sanity2.m1");
|
||||
TestMutex m2("dd.sanity2.m2");
|
||||
m1.Lock();
|
||||
m2.Lock();
|
||||
m1.Lock();
|
||||
return 0; // not reached
|
||||
}
|
||||
|
||||
nsresult
|
||||
Sanity2()
|
||||
{
|
||||
const char* const tokens[] = {
|
||||
"###!!! ERROR: Potential deadlock detected",
|
||||
"=== Cyclical dependency starts at\n--- Mutex : dd.sanity2.m1",
|
||||
"--- Next dependency:\n--- Mutex : dd.sanity2.m2",
|
||||
"=== Cycle completed at\n--- Mutex : dd.sanity2.m1",
|
||||
"###!!! Deadlock may happen NOW!", // better catch these easy cases...
|
||||
"###!!! ASSERTION: Potential deadlock detected",
|
||||
0
|
||||
};
|
||||
if (CheckForDeadlock("Sanity2", tokens)) {
|
||||
PASS();
|
||||
} else {
|
||||
FAIL("deadlock not detected");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
Sanity3_Child()
|
||||
{
|
||||
TestMutex m1("dd.sanity3.m1");
|
||||
TestMutex m2("dd.sanity3.m2");
|
||||
TestMutex m3("dd.sanity3.m3");
|
||||
TestMutex m4("dd.sanity3.m4");
|
||||
|
||||
m1.Lock();
|
||||
m2.Lock();
|
||||
m3.Lock();
|
||||
m4.Lock();
|
||||
m4.Unlock();
|
||||
m3.Unlock();
|
||||
m2.Unlock();
|
||||
m1.Unlock();
|
||||
|
||||
m4.Lock();
|
||||
m1.Lock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Sanity3()
|
||||
{
|
||||
const char* const tokens[] = {
|
||||
"###!!! ERROR: Potential deadlock detected",
|
||||
"=== Cyclical dependency starts at\n--- Mutex : dd.sanity3.m1",
|
||||
"--- Next dependency:\n--- Mutex : dd.sanity3.m2",
|
||||
"--- Next dependency:\n--- Mutex : dd.sanity3.m3",
|
||||
"--- Next dependency:\n--- Mutex : dd.sanity3.m4",
|
||||
"=== Cycle completed at\n--- Mutex : dd.sanity3.m1",
|
||||
"###!!! ASSERTION: Potential deadlock detected",
|
||||
0
|
||||
};
|
||||
if (CheckForDeadlock("Sanity3", tokens)) {
|
||||
PASS();
|
||||
} else {
|
||||
FAIL("deadlock not detected");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
Sanity4_Child()
|
||||
{
|
||||
mozilla::ReentrantMonitor m1("dd.sanity4.m1");
|
||||
TestMutex m2("dd.sanity4.m2");
|
||||
m1.Enter();
|
||||
m2.Lock();
|
||||
m1.Enter();
|
||||
return 0;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Sanity4()
|
||||
{
|
||||
const char* const tokens[] = {
|
||||
"Re-entering ReentrantMonitor after acquiring other resources",
|
||||
"###!!! ERROR: Potential deadlock detected",
|
||||
"=== Cyclical dependency starts at\n--- ReentrantMonitor : dd.sanity4.m1",
|
||||
"--- Next dependency:\n--- Mutex : dd.sanity4.m2",
|
||||
"=== Cycle completed at\n--- ReentrantMonitor : dd.sanity4.m1",
|
||||
"###!!! ASSERTION: Potential deadlock detected",
|
||||
0
|
||||
};
|
||||
if (CheckForDeadlock("Sanity4", tokens)) {
|
||||
PASS();
|
||||
} else {
|
||||
FAIL("deadlock not detected");
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Multithreaded tests
|
||||
|
||||
TestMutex* ttM1;
|
||||
TestMutex* ttM2;
|
||||
|
||||
static void
|
||||
TwoThreads_thread(void* arg)
|
||||
{
|
||||
int32_t m1First = NS_PTR_TO_INT32(arg);
|
||||
if (m1First) {
|
||||
ttM1->Lock();
|
||||
ttM2->Lock();
|
||||
ttM2->Unlock();
|
||||
ttM1->Unlock();
|
||||
}
|
||||
else {
|
||||
ttM2->Lock();
|
||||
ttM1->Lock();
|
||||
ttM1->Unlock();
|
||||
ttM2->Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
TwoThreads_Child()
|
||||
{
|
||||
ttM1 = new TestMutex("dd.twothreads.m1");
|
||||
ttM2 = new TestMutex("dd.twothreads.m2");
|
||||
if (!ttM1 || !ttM2)
|
||||
NS_RUNTIMEABORT("couldn't allocate mutexes");
|
||||
|
||||
PRThread* t1 = spawn(TwoThreads_thread, (void*) 0);
|
||||
PR_JoinThread(t1);
|
||||
|
||||
PRThread* t2 = spawn(TwoThreads_thread, (void*) 1);
|
||||
PR_JoinThread(t2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
nsresult
|
||||
TwoThreads()
|
||||
{
|
||||
const char* const tokens[] = {
|
||||
"###!!! ERROR: Potential deadlock detected",
|
||||
"=== Cyclical dependency starts at\n--- Mutex : dd.twothreads.m2",
|
||||
"--- Next dependency:\n--- Mutex : dd.twothreads.m1",
|
||||
"=== Cycle completed at\n--- Mutex : dd.twothreads.m2",
|
||||
"###!!! ASSERTION: Potential deadlock detected",
|
||||
0
|
||||
};
|
||||
|
||||
if (CheckForDeadlock("TwoThreads", tokens)) {
|
||||
PASS();
|
||||
} else {
|
||||
FAIL("deadlock not detected");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TestMutex* cndMs[4];
|
||||
const uint32_t K = 100000;
|
||||
|
||||
static void
|
||||
ContentionNoDeadlock_thread(void* arg)
|
||||
{
|
||||
int32_t starti = NS_PTR_TO_INT32(arg);
|
||||
|
||||
for (uint32_t k = 0; k < K; ++k) {
|
||||
for (int32_t i = starti; i < (int32_t) ArrayLength(cndMs); ++i)
|
||||
cndMs[i]->Lock();
|
||||
// comment out the next two lines for deadlocking fun!
|
||||
for (int32_t i = ArrayLength(cndMs) - 1; i >= starti; --i)
|
||||
cndMs[i]->Unlock();
|
||||
|
||||
starti = (starti + 1) % 3;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
ContentionNoDeadlock_Child()
|
||||
{
|
||||
PRThread* threads[3];
|
||||
|
||||
for (uint32_t i = 0; i < ArrayLength(cndMs); ++i)
|
||||
cndMs[i] = new TestMutex("dd.cnd.ms");
|
||||
|
||||
for (int32_t i = 0; i < (int32_t) ArrayLength(threads); ++i)
|
||||
threads[i] = spawn(ContentionNoDeadlock_thread, NS_INT32_TO_PTR(i));
|
||||
|
||||
for (uint32_t i = 0; i < ArrayLength(threads); ++i)
|
||||
PR_JoinThread(threads[i]);
|
||||
|
||||
for (uint32_t i = 0; i < ArrayLength(cndMs); ++i)
|
||||
delete cndMs[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ContentionNoDeadlock()
|
||||
{
|
||||
const char * func = __func__;
|
||||
Subprocess proc(func);
|
||||
proc.RunToCompletion(60000);
|
||||
if (0 != proc.mExitCode) {
|
||||
printf("(expected 0 == return code, got %d)\n", proc.mExitCode);
|
||||
puts("(output)\n----------------------------------\n");
|
||||
puts(proc.mStdout.get());
|
||||
puts("----------------------------------\n");
|
||||
puts("(error output)\n----------------------------------\n");
|
||||
puts(proc.mStderr.get());
|
||||
puts("----------------------------------\n");
|
||||
|
||||
FAIL("deadlock");
|
||||
}
|
||||
PASS();
|
||||
}
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
if (1 < argc) {
|
||||
// XXX can we run w/o scoped XPCOM?
|
||||
const char* test = argv[1];
|
||||
ScopedXPCOM xpcom(test);
|
||||
if (xpcom.failed())
|
||||
return 1;
|
||||
|
||||
// running in a spawned process. call the specificed child function.
|
||||
if (!strcmp("Sanity", test))
|
||||
return Sanity_Child();
|
||||
if (!strcmp("Sanity2", test))
|
||||
return Sanity2_Child();
|
||||
if (!strcmp("Sanity3", test))
|
||||
return Sanity3_Child();
|
||||
if (!strcmp("Sanity4", test))
|
||||
return Sanity4_Child();
|
||||
|
||||
if (!strcmp("TwoThreads", test))
|
||||
return TwoThreads_Child();
|
||||
if (!strcmp("ContentionNoDeadlock", test))
|
||||
return ContentionNoDeadlock_Child();
|
||||
|
||||
fail("%s | %s - unknown child test", __FILE__, __FUNCTION__);
|
||||
return 1;
|
||||
}
|
||||
|
||||
ScopedXPCOM xpcom("Storage deadlock detector correctness (" __FILE__ ")");
|
||||
if (xpcom.failed())
|
||||
return 1;
|
||||
|
||||
// in the first invocation of this process. we will be the "driver".
|
||||
int rv = 0;
|
||||
|
||||
sPathToThisBinary = argv[0];
|
||||
|
||||
if (NS_FAILED(Sanity()))
|
||||
rv = 1;
|
||||
if (NS_FAILED(Sanity2()))
|
||||
rv = 1;
|
||||
if (NS_FAILED(Sanity3()))
|
||||
rv = 1;
|
||||
if (NS_FAILED(Sanity4()))
|
||||
rv = 1;
|
||||
|
||||
if (NS_FAILED(TwoThreads()))
|
||||
rv = 1;
|
||||
if (NS_FAILED(ContentionNoDeadlock()))
|
||||
rv = 1;
|
||||
|
||||
return rv;
|
||||
}
|
||||
44
storage/test/test_file_perms.cpp
Normal file
44
storage/test/test_file_perms.cpp
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "storage_test_harness.h"
|
||||
#include "nsIFile.h"
|
||||
#include "prio.h"
|
||||
|
||||
/**
|
||||
* This file tests that the file permissions of the sqlite files match what
|
||||
* we request they be
|
||||
*/
|
||||
|
||||
void
|
||||
test_file_perms()
|
||||
{
|
||||
nsCOMPtr<mozIStorageConnection> db(getDatabase());
|
||||
nsCOMPtr<nsIFile> dbFile;
|
||||
do_check_success(db->GetDatabaseFile(getter_AddRefs(dbFile)));
|
||||
|
||||
uint32_t perms = 0;
|
||||
do_check_success(dbFile->GetPermissions(&perms));
|
||||
|
||||
// This reflexts the permissions defined by SQLITE_DEFAULT_FILE_PERMISSIONS in
|
||||
// db/sqlite3/src/Makefile.in and must be kept in sync with that
|
||||
#ifdef ANDROID
|
||||
do_check_true(perms == (PR_IRUSR | PR_IWUSR));
|
||||
#elif defined(XP_WIN)
|
||||
do_check_true(perms == (PR_IRUSR | PR_IWUSR | PR_IRGRP | PR_IWGRP | PR_IROTH | PR_IWOTH));
|
||||
#else
|
||||
do_check_true(perms == (PR_IRUSR | PR_IWUSR | PR_IRGRP | PR_IROTH));
|
||||
#endif
|
||||
}
|
||||
|
||||
void (*gTests[])(void) = {
|
||||
test_file_perms,
|
||||
};
|
||||
|
||||
const char *file = __FILE__;
|
||||
#define TEST_NAME "file perms"
|
||||
#define TEST_FILE file
|
||||
#include "storage_test_harness_tail.h"
|
||||
85
storage/test/test_mutex.cpp
Normal file
85
storage/test/test_mutex.cpp
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "storage_test_harness.h"
|
||||
|
||||
#include "SQLiteMutex.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::storage;
|
||||
|
||||
/**
|
||||
* This file test our sqlite3_mutex wrapper in SQLiteMutex.h.
|
||||
*/
|
||||
|
||||
void
|
||||
test_AutoLock()
|
||||
{
|
||||
int lockTypes[] = {
|
||||
SQLITE_MUTEX_FAST,
|
||||
SQLITE_MUTEX_RECURSIVE,
|
||||
};
|
||||
for (size_t i = 0; i < ArrayLength(lockTypes); i++) {
|
||||
// Get our test mutex (we have to allocate a SQLite mutex of the right type
|
||||
// too!).
|
||||
SQLiteMutex mutex("TestMutex");
|
||||
sqlite3_mutex *inner = sqlite3_mutex_alloc(lockTypes[i]);
|
||||
do_check_true(inner);
|
||||
mutex.initWithMutex(inner);
|
||||
|
||||
// And test that our automatic locking wrapper works as expected.
|
||||
mutex.assertNotCurrentThreadOwns();
|
||||
{
|
||||
SQLiteMutexAutoLock lockedScope(mutex);
|
||||
mutex.assertCurrentThreadOwns();
|
||||
}
|
||||
mutex.assertNotCurrentThreadOwns();
|
||||
|
||||
// Free the wrapped mutex - we don't need it anymore.
|
||||
sqlite3_mutex_free(inner);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
test_AutoUnlock()
|
||||
{
|
||||
int lockTypes[] = {
|
||||
SQLITE_MUTEX_FAST,
|
||||
SQLITE_MUTEX_RECURSIVE,
|
||||
};
|
||||
for (size_t i = 0; i < ArrayLength(lockTypes); i++) {
|
||||
// Get our test mutex (we have to allocate a SQLite mutex of the right type
|
||||
// too!).
|
||||
SQLiteMutex mutex("TestMutex");
|
||||
sqlite3_mutex *inner = sqlite3_mutex_alloc(lockTypes[i]);
|
||||
do_check_true(inner);
|
||||
mutex.initWithMutex(inner);
|
||||
|
||||
// And test that our automatic unlocking wrapper works as expected.
|
||||
{
|
||||
SQLiteMutexAutoLock lockedScope(mutex);
|
||||
|
||||
{
|
||||
SQLiteMutexAutoUnlock unlockedScope(mutex);
|
||||
mutex.assertNotCurrentThreadOwns();
|
||||
}
|
||||
mutex.assertCurrentThreadOwns();
|
||||
}
|
||||
|
||||
// Free the wrapped mutex - we don't need it anymore.
|
||||
sqlite3_mutex_free(inner);
|
||||
}
|
||||
}
|
||||
|
||||
void (*gTests[])(void) = {
|
||||
test_AutoLock,
|
||||
test_AutoUnlock,
|
||||
};
|
||||
|
||||
const char *file = __FILE__;
|
||||
#define TEST_NAME "SQLiteMutex"
|
||||
#define TEST_FILE file
|
||||
#include "storage_test_harness_tail.h"
|
||||
58
storage/test/test_service_init_background_thread.cpp
Normal file
58
storage/test/test_service_init_background_thread.cpp
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim set:sw=2 ts=2 et lcs=trail\:.,tab\:>~ : */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "storage_test_harness.h"
|
||||
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
/**
|
||||
* This file tests that the storage service can be initialized off of the main
|
||||
* thread without issue.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Helpers
|
||||
|
||||
class ServiceInitializer : public mozilla::Runnable
|
||||
{
|
||||
public:
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
// Use an explicit do_GetService instead of getService so that the check in
|
||||
// getService doesn't blow up.
|
||||
nsCOMPtr<mozIStorageService> service = do_GetService("@mozilla.org/storage/service;1");
|
||||
do_check_false(service);
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Test Functions
|
||||
|
||||
void
|
||||
test_service_initialization_on_background_thread()
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> event = new ServiceInitializer();
|
||||
do_check_true(event);
|
||||
|
||||
nsCOMPtr<nsIThread> thread;
|
||||
do_check_success(NS_NewThread(getter_AddRefs(thread)));
|
||||
|
||||
do_check_success(thread->Dispatch(event, NS_DISPATCH_NORMAL));
|
||||
|
||||
// Shutting down the thread will spin the event loop until all work in its
|
||||
// event queue is completed. This will act as our thread synchronization.
|
||||
do_check_success(thread->Shutdown());
|
||||
}
|
||||
|
||||
void (*gTests[])(void) = {
|
||||
test_service_initialization_on_background_thread,
|
||||
};
|
||||
|
||||
const char *file = __FILE__;
|
||||
#define TEST_NAME "Background Thread Initialization"
|
||||
#define TEST_FILE file
|
||||
#include "storage_test_harness_tail.h"
|
||||
104
storage/test/test_statement_scoper.cpp
Normal file
104
storage/test/test_statement_scoper.cpp
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#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.
|
||||
int32_t 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);
|
||||
bool 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.
|
||||
int32_t 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);
|
||||
bool 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"
|
||||
175
storage/test/test_transaction_helper.cpp
Normal file
175
storage/test/test_transaction_helper.cpp
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "storage_test_harness.h"
|
||||
|
||||
#include "mozStorageHelper.h"
|
||||
#include "mozStorageConnection.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::storage;
|
||||
|
||||
bool has_transaction(mozIStorageConnection* aDB) {
|
||||
return !(static_cast<Connection *>(aDB)->getAutocommit());
|
||||
}
|
||||
|
||||
/**
|
||||
* This file test our Transaction helper in mozStorageHelper.h.
|
||||
*/
|
||||
|
||||
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, false);
|
||||
do_check_true(has_transaction(db));
|
||||
(void)db->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"CREATE TABLE test (id INTEGER PRIMARY KEY)"
|
||||
));
|
||||
(void)transaction.Commit();
|
||||
}
|
||||
do_check_false(has_transaction(db));
|
||||
|
||||
bool exists = 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, true);
|
||||
do_check_true(has_transaction(db));
|
||||
(void)db->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"CREATE TABLE test (id INTEGER PRIMARY KEY)"
|
||||
));
|
||||
(void)transaction.Rollback();
|
||||
}
|
||||
do_check_false(has_transaction(db));
|
||||
|
||||
bool exists = 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, true);
|
||||
do_check_true(has_transaction(db));
|
||||
(void)db->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"CREATE TABLE test (id INTEGER PRIMARY KEY)"
|
||||
));
|
||||
}
|
||||
do_check_false(has_transaction(db));
|
||||
|
||||
bool exists = 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, false);
|
||||
do_check_true(has_transaction(db));
|
||||
(void)db->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"CREATE TABLE test (id INTEGER PRIMARY KEY)"
|
||||
));
|
||||
}
|
||||
do_check_false(has_transaction(db));
|
||||
|
||||
bool exists = true;
|
||||
(void)db->TableExists(NS_LITERAL_CSTRING("test"), &exists);
|
||||
do_check_false(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(nullptr, false);
|
||||
do_check_true(NS_SUCCEEDED(transaction.Commit()));
|
||||
do_check_true(NS_SUCCEEDED(transaction.Rollback()));
|
||||
}
|
||||
|
||||
void
|
||||
test_async_Commit()
|
||||
{
|
||||
// note this will be active for any following test.
|
||||
hook_sqlite_mutex();
|
||||
|
||||
nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
|
||||
|
||||
// -- wedge the thread
|
||||
nsCOMPtr<nsIThread> target(get_conn_async_thread(db));
|
||||
do_check_true(target);
|
||||
RefPtr<ThreadWedger> wedger (new ThreadWedger(target));
|
||||
|
||||
{
|
||||
mozStorageTransaction transaction(db, false,
|
||||
mozIStorageConnection::TRANSACTION_DEFERRED,
|
||||
true);
|
||||
do_check_true(has_transaction(db));
|
||||
(void)db->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"CREATE TABLE test (id INTEGER PRIMARY KEY)"
|
||||
));
|
||||
(void)transaction.Commit();
|
||||
}
|
||||
do_check_true(has_transaction(db));
|
||||
|
||||
// -- unwedge the async thread
|
||||
wedger->unwedge();
|
||||
|
||||
// Ensure the transaction has done its job by enqueueing an async execution.
|
||||
nsCOMPtr<mozIStorageAsyncStatement> stmt;
|
||||
(void)db->CreateAsyncStatement(NS_LITERAL_CSTRING(
|
||||
"SELECT NULL"
|
||||
), getter_AddRefs(stmt));
|
||||
blocking_async_execute(stmt);
|
||||
stmt->Finalize();
|
||||
do_check_false(has_transaction(db));
|
||||
bool exists = false;
|
||||
(void)db->TableExists(NS_LITERAL_CSTRING("test"), &exists);
|
||||
do_check_true(exists);
|
||||
|
||||
blocking_async_close(db);
|
||||
}
|
||||
|
||||
void (*gTests[])(void) = {
|
||||
test_Commit,
|
||||
test_Rollback,
|
||||
test_AutoCommit,
|
||||
test_AutoRollback,
|
||||
test_null_database_connection,
|
||||
test_async_Commit,
|
||||
};
|
||||
|
||||
const char *file = __FILE__;
|
||||
#define TEST_NAME "transaction helper"
|
||||
#define TEST_FILE file
|
||||
#include "storage_test_harness_tail.h"
|
||||
186
storage/test/test_true_async.cpp
Normal file
186
storage/test/test_true_async.cpp
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "storage_test_harness.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Tests
|
||||
|
||||
void
|
||||
test_TrueAsyncStatement()
|
||||
{
|
||||
// (only the first test needs to call this)
|
||||
hook_sqlite_mutex();
|
||||
|
||||
nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
|
||||
|
||||
// Start watching for forbidden mutex usage.
|
||||
watch_for_mutex_use_on_this_thread();
|
||||
|
||||
// - statement with nothing to bind
|
||||
nsCOMPtr<mozIStorageAsyncStatement> stmt;
|
||||
db->CreateAsyncStatement(
|
||||
NS_LITERAL_CSTRING("CREATE TABLE test (id INTEGER PRIMARY KEY)"),
|
||||
getter_AddRefs(stmt)
|
||||
);
|
||||
blocking_async_execute(stmt);
|
||||
stmt->Finalize();
|
||||
do_check_false(mutex_used_on_watched_thread);
|
||||
|
||||
// - statement with something to bind ordinally
|
||||
db->CreateAsyncStatement(
|
||||
NS_LITERAL_CSTRING("INSERT INTO test (id) VALUES (?)"),
|
||||
getter_AddRefs(stmt)
|
||||
);
|
||||
stmt->BindInt32ByIndex(0, 1);
|
||||
blocking_async_execute(stmt);
|
||||
stmt->Finalize();
|
||||
do_check_false(mutex_used_on_watched_thread);
|
||||
|
||||
// - statement with something to bind by name
|
||||
db->CreateAsyncStatement(
|
||||
NS_LITERAL_CSTRING("INSERT INTO test (id) VALUES (:id)"),
|
||||
getter_AddRefs(stmt)
|
||||
);
|
||||
nsCOMPtr<mozIStorageBindingParamsArray> paramsArray;
|
||||
stmt->NewBindingParamsArray(getter_AddRefs(paramsArray));
|
||||
nsCOMPtr<mozIStorageBindingParams> params;
|
||||
paramsArray->NewBindingParams(getter_AddRefs(params));
|
||||
params->BindInt32ByName(NS_LITERAL_CSTRING("id"), 2);
|
||||
paramsArray->AddParams(params);
|
||||
params = nullptr;
|
||||
stmt->BindParameters(paramsArray);
|
||||
paramsArray = nullptr;
|
||||
blocking_async_execute(stmt);
|
||||
stmt->Finalize();
|
||||
do_check_false(mutex_used_on_watched_thread);
|
||||
|
||||
// - now, make sure creating a sync statement does trigger our guard.
|
||||
// (If this doesn't happen, our test is bunk and it's important to know that.)
|
||||
nsCOMPtr<mozIStorageStatement> syncStmt;
|
||||
db->CreateStatement(NS_LITERAL_CSTRING("SELECT * FROM test"),
|
||||
getter_AddRefs(syncStmt));
|
||||
syncStmt->Finalize();
|
||||
do_check_true(mutex_used_on_watched_thread);
|
||||
|
||||
blocking_async_close(db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that cancellation before a statement is run successfully stops the
|
||||
* statement from executing.
|
||||
*/
|
||||
void
|
||||
test_AsyncCancellation()
|
||||
{
|
||||
nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
|
||||
|
||||
// -- wedge the thread
|
||||
nsCOMPtr<nsIThread> target(get_conn_async_thread(db));
|
||||
do_check_true(target);
|
||||
RefPtr<ThreadWedger> wedger (new ThreadWedger(target));
|
||||
|
||||
// -- create statements and cancel them
|
||||
// - async
|
||||
nsCOMPtr<mozIStorageAsyncStatement> asyncStmt;
|
||||
db->CreateAsyncStatement(
|
||||
NS_LITERAL_CSTRING("CREATE TABLE asyncTable (id INTEGER PRIMARY KEY)"),
|
||||
getter_AddRefs(asyncStmt)
|
||||
);
|
||||
|
||||
RefPtr<AsyncStatementSpinner> asyncSpin(new AsyncStatementSpinner());
|
||||
nsCOMPtr<mozIStoragePendingStatement> asyncPend;
|
||||
(void)asyncStmt->ExecuteAsync(asyncSpin, getter_AddRefs(asyncPend));
|
||||
do_check_true(asyncPend);
|
||||
asyncPend->Cancel();
|
||||
|
||||
// - sync
|
||||
nsCOMPtr<mozIStorageStatement> syncStmt;
|
||||
db->CreateStatement(
|
||||
NS_LITERAL_CSTRING("CREATE TABLE syncTable (id INTEGER PRIMARY KEY)"),
|
||||
getter_AddRefs(syncStmt)
|
||||
);
|
||||
|
||||
RefPtr<AsyncStatementSpinner> syncSpin(new AsyncStatementSpinner());
|
||||
nsCOMPtr<mozIStoragePendingStatement> syncPend;
|
||||
(void)syncStmt->ExecuteAsync(syncSpin, getter_AddRefs(syncPend));
|
||||
do_check_true(syncPend);
|
||||
syncPend->Cancel();
|
||||
|
||||
// -- unwedge the async thread
|
||||
wedger->unwedge();
|
||||
|
||||
// -- verify that both statements report they were canceled
|
||||
asyncSpin->SpinUntilCompleted();
|
||||
do_check_true(asyncSpin->completionReason ==
|
||||
mozIStorageStatementCallback::REASON_CANCELED);
|
||||
|
||||
syncSpin->SpinUntilCompleted();
|
||||
do_check_true(syncSpin->completionReason ==
|
||||
mozIStorageStatementCallback::REASON_CANCELED);
|
||||
|
||||
// -- verify that neither statement constructed their tables
|
||||
nsresult rv;
|
||||
bool exists;
|
||||
rv = db->TableExists(NS_LITERAL_CSTRING("asyncTable"), &exists);
|
||||
do_check_true(rv == NS_OK);
|
||||
do_check_false(exists);
|
||||
rv = db->TableExists(NS_LITERAL_CSTRING("syncTable"), &exists);
|
||||
do_check_true(rv == NS_OK);
|
||||
do_check_false(exists);
|
||||
|
||||
// -- cleanup
|
||||
asyncStmt->Finalize();
|
||||
syncStmt->Finalize();
|
||||
blocking_async_close(db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the destructor for an asynchronous statement which has a
|
||||
* sqlite3_stmt will dispatch that statement to the async thread for
|
||||
* finalization rather than trying to finalize it on the main thread
|
||||
* (and thereby running afoul of our mutex use detector).
|
||||
*/
|
||||
void test_AsyncDestructorFinalizesOnAsyncThread()
|
||||
{
|
||||
// test_TrueAsyncStatement called hook_sqlite_mutex() for us
|
||||
|
||||
nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
|
||||
watch_for_mutex_use_on_this_thread();
|
||||
|
||||
// -- create an async statement
|
||||
nsCOMPtr<mozIStorageAsyncStatement> stmt;
|
||||
db->CreateAsyncStatement(
|
||||
NS_LITERAL_CSTRING("CREATE TABLE test (id INTEGER PRIMARY KEY)"),
|
||||
getter_AddRefs(stmt)
|
||||
);
|
||||
|
||||
// -- execute it so it gets a sqlite3_stmt that needs to be finalized
|
||||
blocking_async_execute(stmt);
|
||||
do_check_false(mutex_used_on_watched_thread);
|
||||
|
||||
// -- forget our reference
|
||||
stmt = nullptr;
|
||||
|
||||
// -- verify the mutex was not touched
|
||||
do_check_false(mutex_used_on_watched_thread);
|
||||
|
||||
// -- make sure the statement actually gets finalized / cleanup
|
||||
// the close will assert if we failed to finalize!
|
||||
blocking_async_close(db);
|
||||
}
|
||||
|
||||
void (*gTests[])(void) = {
|
||||
// this test must be first because it hooks the mutex mechanics
|
||||
test_TrueAsyncStatement,
|
||||
test_AsyncCancellation,
|
||||
test_AsyncDestructorFinalizesOnAsyncThread
|
||||
};
|
||||
|
||||
const char *file = __FILE__;
|
||||
#define TEST_NAME "true async statement"
|
||||
#define TEST_FILE file
|
||||
#include "storage_test_harness_tail.h"
|
||||
266
storage/test/test_unlock_notify.cpp
Normal file
266
storage/test/test_unlock_notify.cpp
Normal file
|
|
@ -0,0 +1,266 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim set:sw=2 ts=2 et lcs=trail\:.,tab\:>~ : */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "storage_test_harness.h"
|
||||
|
||||
#include "mozilla/ReentrantMonitor.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "mozIStorageStatement.h"
|
||||
|
||||
/**
|
||||
* This file tests that our implementation around sqlite3_unlock_notify works
|
||||
* as expected.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Helpers
|
||||
|
||||
enum State {
|
||||
STARTING,
|
||||
WRITE_LOCK,
|
||||
READ_LOCK,
|
||||
TEST_DONE
|
||||
};
|
||||
|
||||
class DatabaseLocker : public mozilla::Runnable
|
||||
{
|
||||
public:
|
||||
explicit DatabaseLocker(const char* aSQL)
|
||||
: monitor("DatabaseLocker::monitor")
|
||||
, mSQL(aSQL)
|
||||
, mState(STARTING)
|
||||
{
|
||||
}
|
||||
|
||||
void RunInBackground()
|
||||
{
|
||||
(void)NS_NewNamedThread("DatabaseLocker", getter_AddRefs(mThread));
|
||||
do_check_true(mThread);
|
||||
|
||||
do_check_success(mThread->Dispatch(this, NS_DISPATCH_NORMAL));
|
||||
}
|
||||
|
||||
void Shutdown()
|
||||
{
|
||||
if (mThread) {
|
||||
mThread->Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
mozilla::ReentrantMonitorAutoEnter lock(monitor);
|
||||
|
||||
nsCOMPtr<mozIStorageConnection> db(getDatabase());
|
||||
|
||||
nsCString sql(mSQL);
|
||||
nsCOMPtr<mozIStorageStatement> stmt;
|
||||
do_check_success(db->CreateStatement(sql, getter_AddRefs(stmt)));
|
||||
|
||||
bool hasResult;
|
||||
do_check_success(stmt->ExecuteStep(&hasResult));
|
||||
|
||||
Notify(WRITE_LOCK);
|
||||
WaitFor(TEST_DONE);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void WaitFor(State aState)
|
||||
{
|
||||
monitor.AssertCurrentThreadIn();
|
||||
while (mState != aState) {
|
||||
do_check_success(monitor.Wait());
|
||||
}
|
||||
}
|
||||
|
||||
void Notify(State aState)
|
||||
{
|
||||
monitor.AssertCurrentThreadIn();
|
||||
mState = aState;
|
||||
do_check_success(monitor.Notify());
|
||||
}
|
||||
|
||||
mozilla::ReentrantMonitor monitor;
|
||||
|
||||
protected:
|
||||
nsCOMPtr<nsIThread> mThread;
|
||||
const char *const mSQL;
|
||||
State mState;
|
||||
};
|
||||
|
||||
class DatabaseTester : public DatabaseLocker
|
||||
{
|
||||
public:
|
||||
DatabaseTester(mozIStorageConnection *aConnection,
|
||||
const char* aSQL)
|
||||
: DatabaseLocker(aSQL)
|
||||
, mConnection(aConnection)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
mozilla::ReentrantMonitorAutoEnter lock(monitor);
|
||||
WaitFor(READ_LOCK);
|
||||
|
||||
nsCString sql(mSQL);
|
||||
nsCOMPtr<mozIStorageStatement> stmt;
|
||||
do_check_success(mConnection->CreateStatement(sql, getter_AddRefs(stmt)));
|
||||
|
||||
bool hasResult;
|
||||
nsresult rv = stmt->ExecuteStep(&hasResult);
|
||||
do_check_eq(rv, NS_ERROR_FILE_IS_LOCKED);
|
||||
|
||||
// Finalize our statement and null out our connection before notifying to
|
||||
// ensure that we close on the proper thread.
|
||||
rv = stmt->Finalize();
|
||||
do_check_eq(rv, NS_ERROR_FILE_IS_LOCKED);
|
||||
mConnection = nullptr;
|
||||
|
||||
Notify(TEST_DONE);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsCOMPtr<mozIStorageConnection> mConnection;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Test Functions
|
||||
|
||||
void
|
||||
setup()
|
||||
{
|
||||
nsCOMPtr<mozIStorageConnection> db(getDatabase());
|
||||
|
||||
// Create and populate a dummy table.
|
||||
nsresult rv = db->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"CREATE TABLE test (id INTEGER PRIMARY KEY, data STRING)"
|
||||
));
|
||||
do_check_success(rv);
|
||||
rv = db->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"INSERT INTO test (data) VALUES ('foo')"
|
||||
));
|
||||
do_check_success(rv);
|
||||
rv = db->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"INSERT INTO test (data) VALUES ('bar')"
|
||||
));
|
||||
do_check_success(rv);
|
||||
rv = db->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"CREATE UNIQUE INDEX unique_data ON test (data)"
|
||||
));
|
||||
do_check_success(rv);
|
||||
}
|
||||
|
||||
void
|
||||
test_step_locked_does_not_block_main_thread()
|
||||
{
|
||||
nsCOMPtr<mozIStorageConnection> db(getDatabase());
|
||||
|
||||
// Need to prepare our statement ahead of time so we make sure to only test
|
||||
// step and not prepare.
|
||||
nsCOMPtr<mozIStorageStatement> stmt;
|
||||
nsresult rv = db->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"INSERT INTO test (data) VALUES ('test1')"
|
||||
), getter_AddRefs(stmt));
|
||||
do_check_success(rv);
|
||||
|
||||
RefPtr<DatabaseLocker> locker(new DatabaseLocker("SELECT * FROM test"));
|
||||
do_check_true(locker);
|
||||
{
|
||||
mozilla::ReentrantMonitorAutoEnter lock(locker->monitor);
|
||||
locker->RunInBackground();
|
||||
|
||||
// Wait for the locker to notify us that it has locked the database properly.
|
||||
locker->WaitFor(WRITE_LOCK);
|
||||
|
||||
bool hasResult;
|
||||
rv = stmt->ExecuteStep(&hasResult);
|
||||
do_check_eq(rv, NS_ERROR_FILE_IS_LOCKED);
|
||||
|
||||
locker->Notify(TEST_DONE);
|
||||
}
|
||||
locker->Shutdown();
|
||||
}
|
||||
|
||||
void
|
||||
test_drop_index_does_not_loop()
|
||||
{
|
||||
nsCOMPtr<mozIStorageConnection> db(getDatabase());
|
||||
|
||||
// Need to prepare our statement ahead of time so we make sure to only test
|
||||
// step and not prepare.
|
||||
nsCOMPtr<mozIStorageStatement> stmt;
|
||||
nsresult rv = db->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"SELECT * FROM test"
|
||||
), getter_AddRefs(stmt));
|
||||
do_check_success(rv);
|
||||
|
||||
RefPtr<DatabaseTester> tester =
|
||||
new DatabaseTester(db, "DROP INDEX unique_data");
|
||||
do_check_true(tester);
|
||||
{
|
||||
mozilla::ReentrantMonitorAutoEnter lock(tester->monitor);
|
||||
tester->RunInBackground();
|
||||
|
||||
// Hold a read lock on the database, and then let the tester try to execute.
|
||||
bool hasResult;
|
||||
rv = stmt->ExecuteStep(&hasResult);
|
||||
do_check_success(rv);
|
||||
do_check_true(hasResult);
|
||||
tester->Notify(READ_LOCK);
|
||||
|
||||
// Make sure the tester finishes its test before we move on.
|
||||
tester->WaitFor(TEST_DONE);
|
||||
}
|
||||
tester->Shutdown();
|
||||
}
|
||||
|
||||
void
|
||||
test_drop_table_does_not_loop()
|
||||
{
|
||||
nsCOMPtr<mozIStorageConnection> db(getDatabase());
|
||||
|
||||
// Need to prepare our statement ahead of time so we make sure to only test
|
||||
// step and not prepare.
|
||||
nsCOMPtr<mozIStorageStatement> stmt;
|
||||
nsresult rv = db->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"SELECT * FROM test"
|
||||
), getter_AddRefs(stmt));
|
||||
do_check_success(rv);
|
||||
|
||||
RefPtr<DatabaseTester> tester(new DatabaseTester(db, "DROP TABLE test"));
|
||||
do_check_true(tester);
|
||||
{
|
||||
mozilla::ReentrantMonitorAutoEnter lock(tester->monitor);
|
||||
tester->RunInBackground();
|
||||
|
||||
// Hold a read lock on the database, and then let the tester try to execute.
|
||||
bool hasResult;
|
||||
rv = stmt->ExecuteStep(&hasResult);
|
||||
do_check_success(rv);
|
||||
do_check_true(hasResult);
|
||||
tester->Notify(READ_LOCK);
|
||||
|
||||
// Make sure the tester finishes its test before we move on.
|
||||
tester->WaitFor(TEST_DONE);
|
||||
}
|
||||
tester->Shutdown();
|
||||
}
|
||||
|
||||
void (*gTests[])(void) = {
|
||||
setup,
|
||||
test_step_locked_does_not_block_main_thread,
|
||||
test_drop_index_does_not_loop,
|
||||
test_drop_table_does_not_loop,
|
||||
};
|
||||
|
||||
const char *file = __FILE__;
|
||||
#define TEST_NAME "sqlite3_unlock_notify"
|
||||
#define TEST_FILE file
|
||||
#include "storage_test_harness_tail.h"
|
||||
BIN
storage/test/unit/corruptDB.sqlite
Normal file
BIN
storage/test/unit/corruptDB.sqlite
Normal file
Binary file not shown.
1
storage/test/unit/fakeDB.sqlite
Normal file
1
storage/test/unit/fakeDB.sqlite
Normal file
|
|
@ -0,0 +1 @@
|
|||
BACON
|
||||
372
storage/test/unit/head_storage.js
Normal file
372
storage/test/unit/head_storage.js
Normal file
|
|
@ -0,0 +1,372 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const Ci = Components.interfaces;
|
||||
const Cc = Components.classes;
|
||||
const Cr = Components.results;
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
|
||||
"resource://gre/modules/Promise.jsm");
|
||||
|
||||
|
||||
do_get_profile();
|
||||
var dirSvc = Cc["@mozilla.org/file/directory_service;1"].
|
||||
getService(Ci.nsIProperties);
|
||||
|
||||
var gDBConn = null;
|
||||
|
||||
function getTestDB()
|
||||
{
|
||||
var db = dirSvc.get("ProfD", Ci.nsIFile);
|
||||
db.append("test_storage.sqlite");
|
||||
return db;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains a corrupt database to test against.
|
||||
*/
|
||||
function getCorruptDB()
|
||||
{
|
||||
return do_get_file("corruptDB.sqlite");
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains a fake (non-SQLite format) database to test against.
|
||||
*/
|
||||
function getFakeDB()
|
||||
{
|
||||
return do_get_file("fakeDB.sqlite");
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the test database file.
|
||||
*/
|
||||
function deleteTestDB()
|
||||
{
|
||||
print("*** Storage Tests: Trying to remove file!");
|
||||
var dbFile = getTestDB();
|
||||
if (dbFile.exists())
|
||||
try { dbFile.remove(false); } catch (e) { /* stupid windows box */ }
|
||||
}
|
||||
|
||||
function cleanup()
|
||||
{
|
||||
// close the connection
|
||||
print("*** Storage Tests: Trying to close!");
|
||||
getOpenedDatabase().close();
|
||||
|
||||
// we need to null out the database variable to get a new connection the next
|
||||
// time getOpenedDatabase is called
|
||||
gDBConn = null;
|
||||
|
||||
// removing test db
|
||||
deleteTestDB();
|
||||
}
|
||||
|
||||
/**
|
||||
* Use asyncClose to cleanup a connection. Synchronous by means of internally
|
||||
* spinning an event loop.
|
||||
*/
|
||||
function asyncCleanup()
|
||||
{
|
||||
let closed = false;
|
||||
|
||||
// close the connection
|
||||
print("*** Storage Tests: Trying to asyncClose!");
|
||||
getOpenedDatabase().asyncClose(function () { closed = true; });
|
||||
|
||||
let curThread = Components.classes["@mozilla.org/thread-manager;1"]
|
||||
.getService().currentThread;
|
||||
while (!closed)
|
||||
curThread.processNextEvent(true);
|
||||
|
||||
// we need to null out the database variable to get a new connection the next
|
||||
// time getOpenedDatabase is called
|
||||
gDBConn = null;
|
||||
|
||||
// removing test db
|
||||
deleteTestDB();
|
||||
}
|
||||
|
||||
function getService()
|
||||
{
|
||||
return Cc["@mozilla.org/storage/service;1"].getService(Ci.mozIStorageService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a connection to the test database. Creates and caches the connection
|
||||
* if necessary, otherwise reuses the existing cached connection. This
|
||||
* connection shares its cache.
|
||||
*
|
||||
* @returns the mozIStorageConnection for the file.
|
||||
*/
|
||||
function getOpenedDatabase()
|
||||
{
|
||||
if (!gDBConn) {
|
||||
gDBConn = getService().openDatabase(getTestDB());
|
||||
}
|
||||
return gDBConn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a connection to the test database. Creates and caches the connection
|
||||
* if necessary, otherwise reuses the existing cached connection. This
|
||||
* connection doesn't share its cache.
|
||||
*
|
||||
* @returns the mozIStorageConnection for the file.
|
||||
*/
|
||||
function getOpenedUnsharedDatabase()
|
||||
{
|
||||
if (!gDBConn) {
|
||||
gDBConn = getService().openUnsharedDatabase(getTestDB());
|
||||
}
|
||||
return gDBConn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains a specific database to use.
|
||||
*
|
||||
* @param aFile
|
||||
* The nsIFile representing the db file to open.
|
||||
* @returns the mozIStorageConnection for the file.
|
||||
*/
|
||||
function getDatabase(aFile)
|
||||
{
|
||||
return getService().openDatabase(aFile);
|
||||
}
|
||||
|
||||
function createStatement(aSQL)
|
||||
{
|
||||
return getOpenedDatabase().createStatement(aSQL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an asynchronous SQL statement.
|
||||
*
|
||||
* @param aSQL
|
||||
* The SQL to parse into a statement.
|
||||
* @returns a mozIStorageAsyncStatement from aSQL.
|
||||
*/
|
||||
function createAsyncStatement(aSQL)
|
||||
{
|
||||
return getOpenedDatabase().createAsyncStatement(aSQL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke the given function and assert that it throws an exception expressing
|
||||
* the provided error code in its 'result' attribute. JS function expressions
|
||||
* can be used to do this concisely.
|
||||
*
|
||||
* Example:
|
||||
* expectError(Cr.NS_ERROR_INVALID_ARG, () => explodingFunction());
|
||||
*
|
||||
* @param aErrorCode
|
||||
* The error code to expect from invocation of aFunction.
|
||||
* @param aFunction
|
||||
* The function to invoke and expect an XPCOM-style error from.
|
||||
*/
|
||||
function expectError(aErrorCode, aFunction)
|
||||
{
|
||||
let exceptionCaught = false;
|
||||
try {
|
||||
aFunction();
|
||||
}
|
||||
catch (e) {
|
||||
if (e.result != aErrorCode) {
|
||||
do_throw("Got an exception, but the result code was not the expected " +
|
||||
"one. Expected " + aErrorCode + ", got " + e.result);
|
||||
}
|
||||
exceptionCaught = true;
|
||||
}
|
||||
if (!exceptionCaught)
|
||||
do_throw(aFunction + " should have thrown an exception but did not!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a query synchronously and verify that we get back the expected results.
|
||||
*
|
||||
* @param aSQLString
|
||||
* The SQL string for the query.
|
||||
* @param aBind
|
||||
* The value to bind at index 0.
|
||||
* @param aResults
|
||||
* A list of the expected values returned in the sole result row.
|
||||
* Express blobs as lists.
|
||||
*/
|
||||
function verifyQuery(aSQLString, aBind, aResults)
|
||||
{
|
||||
let stmt = getOpenedDatabase().createStatement(aSQLString);
|
||||
stmt.bindByIndex(0, aBind);
|
||||
try {
|
||||
do_check_true(stmt.executeStep());
|
||||
let nCols = stmt.numEntries;
|
||||
if (aResults.length != nCols)
|
||||
do_throw("Expected " + aResults.length + " columns in result but " +
|
||||
"there are only " + aResults.length + "!");
|
||||
for (let iCol = 0; iCol < nCols; iCol++) {
|
||||
let expectedVal = aResults[iCol];
|
||||
let valType = stmt.getTypeOfIndex(iCol);
|
||||
if (expectedVal === null) {
|
||||
do_check_eq(stmt.VALUE_TYPE_NULL, valType);
|
||||
do_check_true(stmt.getIsNull(iCol));
|
||||
}
|
||||
else if (typeof expectedVal == "number") {
|
||||
if (Math.floor(expectedVal) == expectedVal) {
|
||||
do_check_eq(stmt.VALUE_TYPE_INTEGER, valType);
|
||||
do_check_eq(expectedVal, stmt.getInt32(iCol));
|
||||
}
|
||||
else {
|
||||
do_check_eq(stmt.VALUE_TYPE_FLOAT, valType);
|
||||
do_check_eq(expectedVal, stmt.getDouble(iCol));
|
||||
}
|
||||
}
|
||||
else if (typeof expectedVal == "string") {
|
||||
do_check_eq(stmt.VALUE_TYPE_TEXT, valType);
|
||||
do_check_eq(expectedVal, stmt.getUTF8String(iCol));
|
||||
}
|
||||
else { // blob
|
||||
do_check_eq(stmt.VALUE_TYPE_BLOB, valType);
|
||||
let count = { value: 0 }, blob = { value: null };
|
||||
stmt.getBlob(iCol, count, blob);
|
||||
do_check_eq(count.value, expectedVal.length);
|
||||
for (let i = 0; i < count.value; i++) {
|
||||
do_check_eq(expectedVal[i], blob.value[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
stmt.finalize();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of rows in the able with the given name using a synchronous
|
||||
* query.
|
||||
*
|
||||
* @param aTableName
|
||||
* The name of the table.
|
||||
* @return The number of rows.
|
||||
*/
|
||||
function getTableRowCount(aTableName)
|
||||
{
|
||||
var currentRows = 0;
|
||||
var countStmt = getOpenedDatabase().createStatement(
|
||||
"SELECT COUNT(1) AS count FROM " + aTableName
|
||||
);
|
||||
try {
|
||||
do_check_true(countStmt.executeStep());
|
||||
currentRows = countStmt.row.count;
|
||||
}
|
||||
finally {
|
||||
countStmt.finalize();
|
||||
}
|
||||
return currentRows;
|
||||
}
|
||||
|
||||
// Promise-Returning Functions
|
||||
|
||||
function asyncClone(db, readOnly) {
|
||||
let deferred = Promise.defer();
|
||||
db.asyncClone(readOnly, function (status, db2) {
|
||||
if (Components.isSuccessCode(status)) {
|
||||
deferred.resolve(db2);
|
||||
} else {
|
||||
deferred.reject(status);
|
||||
}
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function asyncClose(db) {
|
||||
let deferred = Promise.defer();
|
||||
db.asyncClose(function (status) {
|
||||
if (Components.isSuccessCode(status)) {
|
||||
deferred.resolve();
|
||||
} else {
|
||||
deferred.reject(status);
|
||||
}
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function openAsyncDatabase(file, options) {
|
||||
let deferred = Promise.defer();
|
||||
let properties;
|
||||
if (options) {
|
||||
properties = Cc["@mozilla.org/hash-property-bag;1"].
|
||||
createInstance(Ci.nsIWritablePropertyBag);
|
||||
for (let k in options) {
|
||||
properties.setProperty(k, options[k]);
|
||||
}
|
||||
}
|
||||
getService().openAsyncDatabase(file, properties, function (status, db) {
|
||||
if (Components.isSuccessCode(status)) {
|
||||
deferred.resolve(db.QueryInterface(Ci.mozIStorageAsyncConnection));
|
||||
} else {
|
||||
deferred.reject(status);
|
||||
}
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function executeAsync(statement, onResult) {
|
||||
let deferred = Promise.defer();
|
||||
statement.executeAsync({
|
||||
handleError: function (error) {
|
||||
deferred.reject(error);
|
||||
},
|
||||
handleResult: function (result) {
|
||||
if (onResult) {
|
||||
onResult(result);
|
||||
}
|
||||
},
|
||||
handleCompletion: function (result) {
|
||||
deferred.resolve(result);
|
||||
}
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function executeMultipleStatementsAsync(db, statements, onResult) {
|
||||
let deferred = Promise.defer();
|
||||
db.executeAsync(statements, statements.length, {
|
||||
handleError: function (error) {
|
||||
deferred.reject(error);
|
||||
},
|
||||
handleResult: function (result) {
|
||||
if (onResult) {
|
||||
onResult(result);
|
||||
}
|
||||
},
|
||||
handleCompletion: function (result) {
|
||||
deferred.resolve(result);
|
||||
}
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function executeSimpleSQLAsync(db, query, onResult) {
|
||||
let deferred = Promise.defer();
|
||||
db.executeSimpleSQLAsync(query, {
|
||||
handleError(error) {
|
||||
deferred.reject(error);
|
||||
},
|
||||
handleResult(result) {
|
||||
if (onResult) {
|
||||
onResult(result);
|
||||
} else {
|
||||
do_throw("No results were expected");
|
||||
}
|
||||
},
|
||||
handleCompletion(result) {
|
||||
deferred.resolve(result);
|
||||
}
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
cleanup();
|
||||
174
storage/test/unit/locale_collation.txt
Normal file
174
storage/test/unit/locale_collation.txt
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
|
||||
!
|
||||
"
|
||||
#
|
||||
$
|
||||
%
|
||||
&
|
||||
'
|
||||
(
|
||||
)
|
||||
*
|
||||
+
|
||||
,
|
||||
-
|
||||
.
|
||||
/
|
||||
0
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
||||
6
|
||||
7
|
||||
8
|
||||
9
|
||||
:
|
||||
;
|
||||
<
|
||||
=
|
||||
>
|
||||
?
|
||||
@
|
||||
A
|
||||
B
|
||||
C
|
||||
D
|
||||
E
|
||||
F
|
||||
G
|
||||
H
|
||||
I
|
||||
J
|
||||
K
|
||||
L
|
||||
M
|
||||
N
|
||||
O
|
||||
P
|
||||
Q
|
||||
R
|
||||
S
|
||||
T
|
||||
U
|
||||
V
|
||||
W
|
||||
X
|
||||
Y
|
||||
Z
|
||||
[
|
||||
\
|
||||
]
|
||||
^
|
||||
_
|
||||
`
|
||||
a
|
||||
b
|
||||
c
|
||||
d
|
||||
e
|
||||
f
|
||||
g
|
||||
h
|
||||
i
|
||||
j
|
||||
k
|
||||
l
|
||||
m
|
||||
n
|
||||
o
|
||||
p
|
||||
q
|
||||
r
|
||||
s
|
||||
t
|
||||
u
|
||||
v
|
||||
w
|
||||
x
|
||||
y
|
||||
z
|
||||
{
|
||||
|
|
||||
}
|
||||
~
|
||||
ludwig van beethoven
|
||||
Ludwig van Beethoven
|
||||
Ludwig van beethoven
|
||||
Jane
|
||||
jane
|
||||
JANE
|
||||
jAne
|
||||
jaNe
|
||||
janE
|
||||
JAne
|
||||
JaNe
|
||||
JanE
|
||||
JANe
|
||||
JaNE
|
||||
JAnE
|
||||
jANE
|
||||
Umberto Eco
|
||||
Umberto eco
|
||||
umberto eco
|
||||
umberto Eco
|
||||
UMBERTO ECO
|
||||
ace
|
||||
bash
|
||||
*ace
|
||||
!ace
|
||||
%ace
|
||||
~ace
|
||||
#ace
|
||||
cork
|
||||
denizen
|
||||
[denizen]
|
||||
(denizen)
|
||||
{denizen}
|
||||
/denizen/
|
||||
#denizen#
|
||||
$denizen$
|
||||
@denizen
|
||||
elf
|
||||
full
|
||||
gnome
|
||||
gnomic investigation of typological factors in the grammaticalization process of Malayo-Polynesian substaratum in the protoAltaic vocabulary core in the proto-layers of pre-historic Japanese
|
||||
gnomic investigation of typological factors in the grammaticalization process of Malayo-Polynesian substaratum in the protoAltaic vocabulary core in the proto-layers of pre-historic Javanese
|
||||
hint
|
||||
Internationalization
|
||||
Zinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalization
|
||||
Zinternationalization internationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizatio
|
||||
n
|
||||
Zinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalization internationalizationinternationalizationinternationalizationinternationalizationinternationalization
|
||||
ZinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizaTioninternationalizationinternationalizationinternationalizationinternationalization
|
||||
ZinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizaTion
|
||||
jostle
|
||||
kin
|
||||
Laymen
|
||||
lumens
|
||||
Lumens
|
||||
motleycrew
|
||||
motley crew
|
||||
niven's creative talents
|
||||
nivens creative talents
|
||||
opie
|
||||
posh restaurants in surbanized and still urban incorporated subsection of this beautifl city in the Rockies
|
||||
posh restaurants in surbanized and still urban incorporated subsection of this beautifl city in the Rokkies
|
||||
posh restaurants in surbanized and still urban incorporated subsection of this beautifl city in the rockies
|
||||
quilt's
|
||||
quilts
|
||||
quilt
|
||||
Rondo
|
||||
street
|
||||
tamale oxidization and iodization in rapid progress
|
||||
tamale oxidization and iodization in rapid Progress
|
||||
until
|
||||
vera
|
||||
Wobble
|
||||
Xanadu's legenary imaginary floccinaucinihilipilification in localization of theoretical portions of glottochronological understanding of the phoneme
|
||||
Xanadu's legenary imaginary floccinaucinihilipilification in localization of theoretical portions of glottochronological understanding of the phoname
|
||||
yearn
|
||||
zodiac
|
||||
a
|
||||
å
|
||||
26
storage/test/unit/test_bug-365166.js
Normal file
26
storage/test/unit/test_bug-365166.js
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
// Testcase for bug 365166 - crash [@ strlen] calling
|
||||
// mozIStorageStatement::getColumnName of a statement created with
|
||||
// "PRAGMA user_version" or "PRAGMA schema_version"
|
||||
function run_test() {
|
||||
test('user');
|
||||
test('schema');
|
||||
|
||||
function test(param)
|
||||
{
|
||||
var colName = param + "_version";
|
||||
var sql = "PRAGMA " + colName;
|
||||
|
||||
var file = getTestDB();
|
||||
var storageService = Components.classes["@mozilla.org/storage/service;1"].
|
||||
getService(Components.interfaces.mozIStorageService);
|
||||
var conn = storageService.openDatabase(file);
|
||||
var statement = conn.createStatement(sql);
|
||||
try {
|
||||
// This shouldn't crash:
|
||||
do_check_eq(statement.getColumnName(0), colName);
|
||||
} finally {
|
||||
statement.reset();
|
||||
statement.finalize();
|
||||
}
|
||||
}
|
||||
}
|
||||
38
storage/test/unit/test_bug-393952.js
Normal file
38
storage/test/unit/test_bug-393952.js
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// Testcase for bug 393952: crash when I try to VACUUM and one of the tables
|
||||
// has a UNIQUE text column. StorageUnicodeFunctions::likeFunction()
|
||||
// needs to handle null aArgv[0] and aArgv[1]
|
||||
|
||||
function setup()
|
||||
{
|
||||
getOpenedDatabase().createTable("t1", "x TEXT UNIQUE");
|
||||
|
||||
var stmt = createStatement("INSERT INTO t1 (x) VALUES ('a')");
|
||||
stmt.execute();
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
function test_vacuum()
|
||||
{
|
||||
var stmt = createStatement("VACUUM;");
|
||||
stmt.executeStep();
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
var tests = [test_vacuum];
|
||||
|
||||
function run_test()
|
||||
{
|
||||
setup();
|
||||
|
||||
for (var i = 0; i < tests.length; i++)
|
||||
tests[i]();
|
||||
|
||||
cleanup();
|
||||
}
|
||||
|
||||
46
storage/test/unit/test_bug-429521.js
Normal file
46
storage/test/unit/test_bug-429521.js
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
function setup() {
|
||||
getOpenedDatabase().createTable("t1", "x TEXT");
|
||||
|
||||
var stmt = createStatement("INSERT INTO t1 (x) VALUES ('/mozilla.org/20070129_1/Europe/Berlin')");
|
||||
stmt.execute();
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
function test_bug429521() {
|
||||
var stmt = createStatement(
|
||||
"SELECT DISTINCT(zone) FROM (" +
|
||||
"SELECT x AS zone FROM t1 WHERE x LIKE '/mozilla.org%'" +
|
||||
");");
|
||||
|
||||
print("*** test_bug429521: started");
|
||||
|
||||
try {
|
||||
while (stmt.executeStep()) {
|
||||
print("*** test_bug429521: step() Read wrapper.row.zone");
|
||||
|
||||
// BUG: the print commands after the following statement
|
||||
// are never executed. Script stops immediately.
|
||||
var tzId = stmt.row.zone;
|
||||
|
||||
print("*** test_bug429521: step() Read wrapper.row.zone finished");
|
||||
}
|
||||
} catch (e) {
|
||||
print("*** test_bug429521: " + e);
|
||||
}
|
||||
|
||||
print("*** test_bug429521: finished");
|
||||
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
setup();
|
||||
|
||||
test_bug429521();
|
||||
|
||||
cleanup();
|
||||
}
|
||||
51
storage/test/unit/test_bug-444233.js
Normal file
51
storage/test/unit/test_bug-444233.js
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
function setup() {
|
||||
// Create the table
|
||||
getOpenedDatabase().createTable("test_bug444233",
|
||||
"id INTEGER PRIMARY KEY, value TEXT");
|
||||
|
||||
// Insert dummy data, using wrapper methods
|
||||
var stmt = createStatement("INSERT INTO test_bug444233 (value) VALUES (:value)");
|
||||
stmt.params.value = "value1";
|
||||
stmt.execute();
|
||||
stmt.finalize();
|
||||
|
||||
stmt = createStatement("INSERT INTO test_bug444233 (value) VALUES (:value)");
|
||||
stmt.params.value = "value2";
|
||||
stmt.execute();
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
function test_bug444233() {
|
||||
print("*** test_bug444233: started");
|
||||
|
||||
// Check that there are 2 results
|
||||
var stmt = createStatement("SELECT COUNT(*) AS number FROM test_bug444233");
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_eq(2, stmt.row.number);
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
|
||||
print("*** test_bug444233: doing delete");
|
||||
|
||||
// Now try to delete using IN
|
||||
// Cheating since we know ids are 1,2
|
||||
try {
|
||||
var ids = [1, 2];
|
||||
stmt = createStatement("DELETE FROM test_bug444233 WHERE id IN (:ids)");
|
||||
stmt.params.ids = ids;
|
||||
} catch (e) {
|
||||
print("*** test_bug444233: successfully caught exception");
|
||||
}
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
setup();
|
||||
test_bug444233();
|
||||
cleanup();
|
||||
}
|
||||
|
||||
72
storage/test/unit/test_cache_size.js
Normal file
72
storage/test/unit/test_cache_size.js
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// This file tests that dbs of various page sizes are using the right cache
|
||||
// size (bug 703113).
|
||||
|
||||
/**
|
||||
* In order to change the cache size, we must open a DB, change the page
|
||||
* size, create a table, close the DB, then re-open the DB. We then check
|
||||
* the cache size after reopening.
|
||||
*
|
||||
* @param dbOpener
|
||||
* function that opens the DB specified in file
|
||||
* @param file
|
||||
* file holding the database
|
||||
* @param pageSize
|
||||
* the DB's page size
|
||||
* @param expectedCacheSize
|
||||
* the expected cache size for the given page size
|
||||
*/
|
||||
function check_size(dbOpener, file, pageSize, expectedCacheSize)
|
||||
{
|
||||
// Open the DB, immediately change its page size.
|
||||
let db = dbOpener(file);
|
||||
db.executeSimpleSQL("PRAGMA page_size = " + pageSize);
|
||||
|
||||
// Check the page size change worked.
|
||||
let stmt = db.createStatement("PRAGMA page_size");
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_eq(stmt.row.page_size, pageSize);
|
||||
stmt.finalize();
|
||||
|
||||
// Create a simple table.
|
||||
db.executeSimpleSQL("CREATE TABLE test ( id INTEGER PRIMARY KEY )");
|
||||
|
||||
// Close and re-open the DB.
|
||||
db.close();
|
||||
db = dbOpener(file);
|
||||
|
||||
// Check cache size is as expected.
|
||||
stmt = db.createStatement("PRAGMA cache_size");
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_eq(stmt.row.cache_size, expectedCacheSize);
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
function new_file(name)
|
||||
{
|
||||
let file = dirSvc.get("ProfD", Ci.nsIFile);
|
||||
file.append(name + ".sqlite");
|
||||
do_check_false(file.exists());
|
||||
return file;
|
||||
}
|
||||
|
||||
function run_test()
|
||||
{
|
||||
const kExpectedCacheSize = -2048; // 2MiB
|
||||
|
||||
let pageSizes = [
|
||||
1024,
|
||||
4096,
|
||||
32768,
|
||||
];
|
||||
|
||||
for (let i = 0; i < pageSizes.length; i++) {
|
||||
let pageSize = pageSizes[i];
|
||||
check_size(getDatabase,
|
||||
new_file("shared" + pageSize), pageSize, kExpectedCacheSize);
|
||||
check_size(getService().openUnsharedDatabase,
|
||||
new_file("unshared" + pageSize), pageSize, kExpectedCacheSize);
|
||||
}
|
||||
}
|
||||
52
storage/test/unit/test_chunk_growth.js
Normal file
52
storage/test/unit/test_chunk_growth.js
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
// This file tests SQLITE_FCNTL_CHUNK_SIZE behaves as expected
|
||||
|
||||
function run_sql(d, sql) {
|
||||
var stmt = d.createStatement(sql);
|
||||
stmt.execute();
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
function new_file(name) {
|
||||
var file = dirSvc.get("ProfD", Ci.nsIFile);
|
||||
file.append(name);
|
||||
return file;
|
||||
}
|
||||
|
||||
function get_size(name) {
|
||||
return new_file(name).fileSize;
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
const filename = "chunked.sqlite";
|
||||
const CHUNK_SIZE = 512 * 1024;
|
||||
var d = getDatabase(new_file(filename));
|
||||
try {
|
||||
d.setGrowthIncrement(CHUNK_SIZE, "");
|
||||
} catch (e) {
|
||||
if (e.result != Cr.NS_ERROR_FILE_TOO_BIG) {
|
||||
throw e;
|
||||
}
|
||||
print("Too little free space to set CHUNK_SIZE!");
|
||||
return;
|
||||
}
|
||||
run_sql(d, "CREATE TABLE bloat(data varchar)");
|
||||
|
||||
var orig_size = get_size(filename);
|
||||
/* Dump in at least 32K worth of data.
|
||||
* While writing ensure that the file size growth in chunksize set above.
|
||||
*/
|
||||
const str1024 = new Array(1024).join("T");
|
||||
for (var i = 0; i < 32; i++) {
|
||||
run_sql(d, "INSERT INTO bloat VALUES('" + str1024 + "')");
|
||||
var size = get_size(filename);
|
||||
// Must not grow in small increments.
|
||||
do_check_true(size == orig_size || size >= CHUNK_SIZE);
|
||||
}
|
||||
/* In addition to growing in chunk-size increments, the db
|
||||
* should shrink in chunk-size increments too.
|
||||
*/
|
||||
run_sql(d, "DELETE FROM bloat");
|
||||
run_sql(d, "VACUUM");
|
||||
do_check_true(get_size(filename) >= CHUNK_SIZE);
|
||||
}
|
||||
|
||||
125
storage/test/unit/test_connection_asyncClose.js
Normal file
125
storage/test/unit/test_connection_asyncClose.js
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/*
|
||||
* Thorough branch coverage for asyncClose.
|
||||
*
|
||||
* Coverage of asyncClose by connection state at time of AsyncClose invocation:
|
||||
* - (asyncThread && mDBConn) => AsyncCloseConnection used, actually closes
|
||||
* - test_asyncClose_does_not_complete_before_statements
|
||||
* - test_double_asyncClose_throws
|
||||
* - test_asyncClose_does_not_throw_without_callback
|
||||
* - (asyncThread && !mDBConn) => AsyncCloseConnection used, although no close
|
||||
* is required. Note that this is only possible in the event that
|
||||
* openAsyncDatabase was used and we failed to open the database.
|
||||
* Additionally, the async connection will never be exposed to the caller and
|
||||
* AsyncInitDatabase will be the one to (automatically) call AsyncClose.
|
||||
* - test_asyncClose_failed_open
|
||||
* - (!asyncThread && mDBConn) => Close() invoked, actually closes
|
||||
* - test_asyncClose_on_sync_db
|
||||
* - (!asyncThread && !mDBConn) => Close() invoked, no close needed, errors.
|
||||
* This happens if the database has already been closed.
|
||||
* - test_double_asyncClose_throws
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Sanity check that our close indeed happens after asynchronously executed
|
||||
* statements scheduled during the same turn of the event loop. Note that we
|
||||
* just care that the statement says it completed without error, we're not
|
||||
* worried that the close will happen and then the statement will magically
|
||||
* complete.
|
||||
*/
|
||||
add_task(function* test_asyncClose_does_not_complete_before_statements() {
|
||||
let db = getService().openDatabase(getTestDB());
|
||||
let stmt = db.createStatement("SELECT * FROM sqlite_master");
|
||||
// Issue the executeAsync but don't yield for it...
|
||||
let asyncStatementPromise = executeAsync(stmt);
|
||||
stmt.finalize();
|
||||
|
||||
// Issue the close. (And now the order of yielding doesn't matter.)
|
||||
// Branch coverage: (asyncThread && mDBConn)
|
||||
yield asyncClose(db);
|
||||
equal((yield asyncStatementPromise),
|
||||
Ci.mozIStorageStatementCallback.REASON_FINISHED);
|
||||
});
|
||||
|
||||
/**
|
||||
* Open an async database (ensures the async thread is created) and then invoke
|
||||
* AsyncClose() twice without yielding control flow. The first will initiate
|
||||
* the actual async close after calling setClosedState which synchronously
|
||||
* impacts what the second call will observe. The second call will then see the
|
||||
* async thread is not available and fall back to invoking Close() which will
|
||||
* notice the mDBConn is already gone.
|
||||
*/
|
||||
add_task(function* test_double_asyncClose_throws() {
|
||||
let db = yield openAsyncDatabase(getTestDB());
|
||||
|
||||
// (Don't yield control flow yet, save the promise for after we make the
|
||||
// second call.)
|
||||
// Branch coverage: (asyncThread && mDBConn)
|
||||
let realClosePromise = yield asyncClose(db);
|
||||
try {
|
||||
// Branch coverage: (!asyncThread && !mDBConn)
|
||||
db.asyncClose();
|
||||
ok(false, "should have thrown");
|
||||
} catch (e) {
|
||||
equal(e.result, Cr.NS_ERROR_NOT_INITIALIZED);
|
||||
}
|
||||
|
||||
yield realClosePromise;
|
||||
});
|
||||
|
||||
/**
|
||||
* Create a sync db connection and never take it asynchronous and then call
|
||||
* asyncClose on it. This will bring the async thread to life to perform the
|
||||
* shutdown to avoid blocking the main thread, although we won't be able to
|
||||
* tell the difference between this happening and the method secretly shunting
|
||||
* to close().
|
||||
*/
|
||||
add_task(function* test_asyncClose_on_sync_db() {
|
||||
let db = getService().openDatabase(getTestDB());
|
||||
|
||||
// Branch coverage: (!asyncThread && mDBConn)
|
||||
yield asyncClose(db);
|
||||
ok(true, 'closed sync connection asynchronously');
|
||||
});
|
||||
|
||||
/**
|
||||
* Fail to asynchronously open a DB in order to get an async thread existing
|
||||
* without having an open database and asyncClose invoked. As per the file
|
||||
* doc-block, note that asyncClose will automatically be invoked by the
|
||||
* AsyncInitDatabase when it fails to open the database. We will never be
|
||||
* provided with a reference to the connection and so cannot call AsyncClose on
|
||||
* it ourselves.
|
||||
*/
|
||||
add_task(function* test_asyncClose_failed_open() {
|
||||
// This will fail and the promise will be rejected.
|
||||
let openPromise = openAsyncDatabase(getFakeDB());
|
||||
yield openPromise.then(
|
||||
() => {
|
||||
ok(false, 'we should have failed to open the db; this test is broken!');
|
||||
},
|
||||
() => {
|
||||
ok(true, 'correctly failed to open db; bg asyncClose should happen');
|
||||
}
|
||||
);
|
||||
// (NB: we are unable to observe the thread shutdown, but since we never open
|
||||
// a database, this test is not going to interfere with other tests so much.)
|
||||
});
|
||||
|
||||
// THE TEST BELOW WANTS TO BE THE LAST TEST WE RUN. DO NOT MAKE IT SAD.
|
||||
/**
|
||||
* Verify that asyncClose without a callback does not explode. Without a
|
||||
* callback the shutdown is not actually observable, so we run this test last
|
||||
* in order to avoid weird overlaps.
|
||||
*/
|
||||
add_task(function* test_asyncClose_does_not_throw_without_callback() {
|
||||
let db = yield openAsyncDatabase(getTestDB());
|
||||
// Branch coverage: (asyncThread && mDBConn)
|
||||
db.asyncClose();
|
||||
ok(true, 'if we shutdown cleanly and do not crash, then we succeeded');
|
||||
});
|
||||
// OBEY SHOUTING UPPER-CASE COMMENTS.
|
||||
// ADD TESTS ABOVE THE FORMER TEST, NOT BELOW IT.
|
||||
171
storage/test/unit/test_connection_executeAsync.js
Normal file
171
storage/test/unit/test_connection_executeAsync.js
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/*
|
||||
* This file tests the functionality of mozIStorageConnection::executeAsync for
|
||||
* both mozIStorageStatement and mozIStorageAsyncStatement.
|
||||
*
|
||||
* A single database connection is used for the entirety of the test, which is
|
||||
* a legacy thing, but we otherwise use the modern promise-based driver and
|
||||
* async helpers.
|
||||
*/
|
||||
|
||||
const INTEGER = 1;
|
||||
const TEXT = "this is test text";
|
||||
const REAL = 3.23;
|
||||
const BLOB = [1, 2];
|
||||
|
||||
add_task(function* test_first_create_and_add() {
|
||||
// synchronously open the database and let gDBConn hold onto it because we
|
||||
// use this database
|
||||
let db = getOpenedDatabase();
|
||||
// synchronously set up our table *that will be used for the rest of the file*
|
||||
db.executeSimpleSQL(
|
||||
"CREATE TABLE test (" +
|
||||
"id INTEGER, " +
|
||||
"string TEXT, " +
|
||||
"number REAL, " +
|
||||
"nuller NULL, " +
|
||||
"blober BLOB" +
|
||||
")"
|
||||
);
|
||||
|
||||
let stmts = [];
|
||||
stmts[0] = db.createStatement(
|
||||
"INSERT INTO test (id, string, number, nuller, blober) VALUES (?, ?, ?, ?, ?)"
|
||||
);
|
||||
stmts[0].bindByIndex(0, INTEGER);
|
||||
stmts[0].bindByIndex(1, TEXT);
|
||||
stmts[0].bindByIndex(2, REAL);
|
||||
stmts[0].bindByIndex(3, null);
|
||||
stmts[0].bindBlobByIndex(4, BLOB, BLOB.length);
|
||||
stmts[1] = getOpenedDatabase().createAsyncStatement(
|
||||
"INSERT INTO test (string, number, nuller, blober) VALUES (?, ?, ?, ?)"
|
||||
);
|
||||
stmts[1].bindByIndex(0, TEXT);
|
||||
stmts[1].bindByIndex(1, REAL);
|
||||
stmts[1].bindByIndex(2, null);
|
||||
stmts[1].bindBlobByIndex(3, BLOB, BLOB.length);
|
||||
|
||||
// asynchronously execute the statements
|
||||
let execResult = yield executeMultipleStatementsAsync(
|
||||
db,
|
||||
stmts,
|
||||
function(aResultSet) {
|
||||
ok(false, 'we only did inserts so we should not have gotten results!');
|
||||
});
|
||||
equal(Ci.mozIStorageStatementCallback.REASON_FINISHED, execResult,
|
||||
'execution should have finished successfully.');
|
||||
|
||||
// Check that the result is in the table
|
||||
let stmt = db.createStatement(
|
||||
"SELECT string, number, nuller, blober FROM test WHERE id = ?"
|
||||
);
|
||||
stmt.bindByIndex(0, INTEGER);
|
||||
try {
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_eq(TEXT, stmt.getString(0));
|
||||
do_check_eq(REAL, stmt.getDouble(1));
|
||||
do_check_true(stmt.getIsNull(2));
|
||||
let count = { value: 0 };
|
||||
let blob = { value: null };
|
||||
stmt.getBlob(3, count, blob);
|
||||
do_check_eq(BLOB.length, count.value);
|
||||
for (let i = 0; i < BLOB.length; i++)
|
||||
do_check_eq(BLOB[i], blob.value[i]);
|
||||
}
|
||||
finally {
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
// Make sure we have two rows in the table
|
||||
stmt = db.createStatement(
|
||||
"SELECT COUNT(1) FROM test"
|
||||
);
|
||||
try {
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_eq(2, stmt.getInt32(0));
|
||||
}
|
||||
finally {
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
stmts[0].finalize();
|
||||
stmts[1].finalize();
|
||||
});
|
||||
|
||||
add_task(function* test_last_multiple_bindings_on_statements() {
|
||||
// This tests to make sure that we pass all the statements multiply bound
|
||||
// parameters when we call executeAsync.
|
||||
const AMOUNT_TO_ADD = 5;
|
||||
const ITERATIONS = 5;
|
||||
|
||||
let stmts = [];
|
||||
let db = getOpenedDatabase();
|
||||
let sqlString = "INSERT INTO test (id, string, number, nuller, blober) " +
|
||||
"VALUES (:int, :text, :real, :null, :blob)";
|
||||
// We run the same statement twice, and should insert 2 * AMOUNT_TO_ADD.
|
||||
for (let i = 0; i < ITERATIONS; i++) {
|
||||
// alternate the type of statement we create
|
||||
if (i % 2)
|
||||
stmts[i] = db.createStatement(sqlString);
|
||||
else
|
||||
stmts[i] = db.createAsyncStatement(sqlString);
|
||||
|
||||
let params = stmts[i].newBindingParamsArray();
|
||||
for (let j = 0; j < AMOUNT_TO_ADD; j++) {
|
||||
let bp = params.newBindingParams();
|
||||
bp.bindByName("int", INTEGER);
|
||||
bp.bindByName("text", TEXT);
|
||||
bp.bindByName("real", REAL);
|
||||
bp.bindByName("null", null);
|
||||
bp.bindBlobByName("blob", BLOB, BLOB.length);
|
||||
params.addParams(bp);
|
||||
}
|
||||
stmts[i].bindParameters(params);
|
||||
}
|
||||
|
||||
// Get our current number of rows in the table.
|
||||
let currentRows = 0;
|
||||
let countStmt = getOpenedDatabase().createStatement(
|
||||
"SELECT COUNT(1) AS count FROM test"
|
||||
);
|
||||
try {
|
||||
do_check_true(countStmt.executeStep());
|
||||
currentRows = countStmt.row.count;
|
||||
}
|
||||
finally {
|
||||
countStmt.reset();
|
||||
}
|
||||
|
||||
// Execute asynchronously.
|
||||
let execResult = yield executeMultipleStatementsAsync(
|
||||
db,
|
||||
stmts,
|
||||
function(aResultSet) {
|
||||
ok(false, 'we only did inserts so we should not have gotten results!');
|
||||
});
|
||||
equal(Ci.mozIStorageStatementCallback.REASON_FINISHED, execResult,
|
||||
'execution should have finished successfully.');
|
||||
|
||||
// Check to make sure we added all of our rows.
|
||||
try {
|
||||
do_check_true(countStmt.executeStep());
|
||||
do_check_eq(currentRows + (ITERATIONS * AMOUNT_TO_ADD),
|
||||
countStmt.row.count);
|
||||
}
|
||||
finally {
|
||||
countStmt.finalize();
|
||||
}
|
||||
|
||||
stmts.forEach(stmt => stmt.finalize());
|
||||
|
||||
// we are the last test using this connection and since it has gone async
|
||||
// we *must* call asyncClose on it.
|
||||
yield asyncClose(db);
|
||||
gDBConn = null;
|
||||
});
|
||||
|
||||
// If you add a test down here you will need to move the asyncClose or clean
|
||||
// things up a little more.
|
||||
79
storage/test/unit/test_connection_executeSimpleSQLAsync.js
Normal file
79
storage/test/unit/test_connection_executeSimpleSQLAsync.js
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/*
|
||||
* This file tests the functionality of
|
||||
* mozIStorageAsyncConnection::executeSimpleSQLAsync.
|
||||
*/
|
||||
|
||||
const INTEGER = 1;
|
||||
const TEXT = "this is test text";
|
||||
const REAL = 3.23;
|
||||
|
||||
add_task(function* test_create_and_add() {
|
||||
let adb = yield openAsyncDatabase(getTestDB());
|
||||
|
||||
let completion = yield executeSimpleSQLAsync(adb,
|
||||
"CREATE TABLE test (id INTEGER, string TEXT, number REAL)");
|
||||
|
||||
do_check_eq(Ci.mozIStorageStatementCallback.REASON_FINISHED, completion);
|
||||
|
||||
completion = yield executeSimpleSQLAsync(adb,
|
||||
"INSERT INTO test (id, string, number) " +
|
||||
"VALUES (" + INTEGER + ", \"" + TEXT + "\", " + REAL + ")");
|
||||
|
||||
do_check_eq(Ci.mozIStorageStatementCallback.REASON_FINISHED, completion);
|
||||
|
||||
let result = null;
|
||||
|
||||
completion = yield executeSimpleSQLAsync(adb,
|
||||
"SELECT string, number FROM test WHERE id = 1",
|
||||
function (aResultSet) {
|
||||
result = aResultSet.getNextRow();
|
||||
do_check_eq(2, result.numEntries);
|
||||
do_check_eq(TEXT, result.getString(0));
|
||||
do_check_eq(REAL, result.getDouble(1));
|
||||
}
|
||||
);
|
||||
|
||||
do_check_eq(Ci.mozIStorageStatementCallback.REASON_FINISHED, completion);
|
||||
do_check_neq(result, null);
|
||||
result = null;
|
||||
|
||||
yield executeSimpleSQLAsync(adb, "SELECT COUNT(0) FROM test",
|
||||
function (aResultSet) {
|
||||
result = aResultSet.getNextRow();
|
||||
do_check_eq(1, result.getInt32(0));
|
||||
});
|
||||
|
||||
do_check_neq(result, null);
|
||||
|
||||
yield asyncClose(adb);
|
||||
});
|
||||
|
||||
|
||||
add_task(function* test_asyncClose_does_not_complete_before_statement() {
|
||||
let adb = yield openAsyncDatabase(getTestDB());
|
||||
let executed = false;
|
||||
|
||||
let reason = yield executeSimpleSQLAsync(adb, "SELECT * FROM test",
|
||||
function (aResultSet) {
|
||||
let result = aResultSet.getNextRow();
|
||||
|
||||
do_check_neq(result, null);
|
||||
do_check_eq(3, result.numEntries);
|
||||
do_check_eq(INTEGER, result.getInt32(0));
|
||||
do_check_eq(TEXT, result.getString(1));
|
||||
do_check_eq(REAL, result.getDouble(2));
|
||||
executed = true;
|
||||
}
|
||||
);
|
||||
|
||||
do_check_eq(Ci.mozIStorageStatementCallback.REASON_FINISHED, reason);
|
||||
|
||||
// Ensure that the statement executed to completion.
|
||||
do_check_true(executed);
|
||||
|
||||
yield asyncClose(adb);
|
||||
});
|
||||
125
storage/test/unit/test_js_helpers.js
Normal file
125
storage/test/unit/test_js_helpers.js
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set sw=2 ts=2 sts=2 et : */
|
||||
/**
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
/**
|
||||
* This file tests that the JS language helpers in various ways.
|
||||
*/
|
||||
|
||||
// Test Functions
|
||||
|
||||
function test_params_enumerate()
|
||||
{
|
||||
let stmt = createStatement(
|
||||
"SELECT * FROM test WHERE id IN (:a, :b, :c)"
|
||||
);
|
||||
|
||||
// Make sure they are right.
|
||||
let expected = ["a", "b", "c"];
|
||||
let index = 0;
|
||||
for (let name in stmt.params) {
|
||||
if (name == "QueryInterface")
|
||||
continue;
|
||||
do_check_eq(name, expected[index++]);
|
||||
}
|
||||
}
|
||||
|
||||
function test_params_prototype()
|
||||
{
|
||||
let stmt = createStatement(
|
||||
"SELECT * FROM sqlite_master"
|
||||
);
|
||||
|
||||
// Set a property on the prototype and make sure it exist (will not be a
|
||||
// bindable parameter, however).
|
||||
Object.getPrototypeOf(stmt.params).test = 2;
|
||||
do_check_eq(stmt.params.test, 2);
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
function test_row_prototype()
|
||||
{
|
||||
let stmt = createStatement(
|
||||
"SELECT * FROM sqlite_master"
|
||||
);
|
||||
|
||||
do_check_true(stmt.executeStep());
|
||||
|
||||
// Set a property on the prototype and make sure it exists (will not be in the
|
||||
// results, however).
|
||||
Object.getPrototypeOf(stmt.row).test = 2;
|
||||
do_check_eq(stmt.row.test, 2);
|
||||
|
||||
// Clean up after ourselves.
|
||||
delete Object.getPrototypeOf(stmt.row).test;
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
function test_params_gets_sync()
|
||||
{
|
||||
// Added for bug 562866.
|
||||
/*
|
||||
let stmt = createStatement(
|
||||
"SELECT * FROM test WHERE id IN (:a, :b, :c)"
|
||||
);
|
||||
|
||||
// Make sure we do not assert in getting the value.
|
||||
let originalCount = Object.getOwnPropertyNames(stmt.params).length;
|
||||
let expected = ["a", "b", "c"];
|
||||
for (let name of expected) {
|
||||
stmt.params[name];
|
||||
}
|
||||
|
||||
// Now make sure we didn't magically get any additional properties.
|
||||
let finalCount = Object.getOwnPropertyNames(stmt.params).length;
|
||||
do_check_eq(originalCount + expected.length, finalCount);
|
||||
*/
|
||||
}
|
||||
|
||||
function test_params_gets_async()
|
||||
{
|
||||
// Added for bug 562866.
|
||||
/*
|
||||
let stmt = createAsyncStatement(
|
||||
"SELECT * FROM test WHERE id IN (:a, :b, :c)"
|
||||
);
|
||||
|
||||
// Make sure we do not assert in getting the value.
|
||||
let originalCount = Object.getOwnPropertyNames(stmt.params).length;
|
||||
let expected = ["a", "b", "c"];
|
||||
for (let name of expected) {
|
||||
stmt.params[name];
|
||||
}
|
||||
|
||||
// Now make sure we didn't magically get any additional properties.
|
||||
let finalCount = Object.getOwnPropertyNames(stmt.params).length;
|
||||
do_check_eq(originalCount + expected.length, finalCount);
|
||||
*/
|
||||
}
|
||||
|
||||
// Test Runner
|
||||
|
||||
var tests = [
|
||||
test_params_enumerate,
|
||||
test_params_prototype,
|
||||
test_row_prototype,
|
||||
test_params_gets_sync,
|
||||
test_params_gets_async,
|
||||
];
|
||||
function run_test()
|
||||
{
|
||||
cleanup();
|
||||
|
||||
// Create our database.
|
||||
getOpenedDatabase().executeSimpleSQL(
|
||||
"CREATE TABLE test (" +
|
||||
"id INTEGER PRIMARY KEY " +
|
||||
")"
|
||||
);
|
||||
|
||||
// Run the tests.
|
||||
tests.forEach(test => test());
|
||||
}
|
||||
74
storage/test/unit/test_levenshtein.js
Normal file
74
storage/test/unit/test_levenshtein.js
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// This file tests the Levenshtein Distance function we've registered.
|
||||
|
||||
function createUtf16Database()
|
||||
{
|
||||
print("Creating the in-memory UTF-16-encoded database.");
|
||||
let conn = getService().openSpecialDatabase("memory");
|
||||
conn.executeSimpleSQL("PRAGMA encoding = 'UTF-16'");
|
||||
|
||||
print("Make sure the encoding was set correctly and is now UTF-16.");
|
||||
let stmt = conn.createStatement("PRAGMA encoding");
|
||||
do_check_true(stmt.executeStep());
|
||||
let enc = stmt.getString(0);
|
||||
stmt.finalize();
|
||||
|
||||
// The value returned will actually be UTF-16le or UTF-16be.
|
||||
do_check_true(enc === "UTF-16le" || enc === "UTF-16be");
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
function check_levenshtein(db, s, t, expectedDistance)
|
||||
{
|
||||
var stmt = db.createStatement("SELECT levenshteinDistance(:s, :t) AS result");
|
||||
stmt.params.s = s;
|
||||
stmt.params.t = t;
|
||||
try {
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_eq(expectedDistance, stmt.row.result);
|
||||
} finally {
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
}
|
||||
}
|
||||
|
||||
function testLevenshtein(db)
|
||||
{
|
||||
// Basic tests.
|
||||
check_levenshtein(db, "", "", 0);
|
||||
check_levenshtein(db, "foo", "", 3);
|
||||
check_levenshtein(db, "", "bar", 3);
|
||||
check_levenshtein(db, "yellow", "hello", 2);
|
||||
check_levenshtein(db, "gumbo", "gambol", 2);
|
||||
check_levenshtein(db, "kitten", "sitten", 1);
|
||||
check_levenshtein(db, "sitten", "sittin", 1);
|
||||
check_levenshtein(db, "sittin", "sitting", 1);
|
||||
check_levenshtein(db, "kitten", "sitting", 3);
|
||||
check_levenshtein(db, "Saturday", "Sunday", 3);
|
||||
check_levenshtein(db, "YHCQPGK", "LAHYQQKPGKA", 6);
|
||||
|
||||
// Test SQL NULL handling.
|
||||
check_levenshtein(db, "foo", null, null);
|
||||
check_levenshtein(db, null, "bar", null);
|
||||
check_levenshtein(db, null, null, null);
|
||||
|
||||
// The levenshteinDistance function allocates temporary memory on the stack
|
||||
// if it can. Test some strings long enough to force a heap allocation.
|
||||
var dots1000 = Array(1001).join(".");
|
||||
var dashes1000 = Array(1001).join("-");
|
||||
check_levenshtein(db, dots1000, dashes1000, 1000);
|
||||
}
|
||||
|
||||
function run_test()
|
||||
{
|
||||
testLevenshtein(getOpenedDatabase());
|
||||
testLevenshtein(createUtf16Database());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
202
storage/test/unit/test_like.js
Normal file
202
storage/test/unit/test_like.js
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// This file tests our LIKE implementation since we override it for unicode
|
||||
|
||||
function setup()
|
||||
{
|
||||
getOpenedDatabase().createTable("t1", "x TEXT");
|
||||
|
||||
var stmt = createStatement("INSERT INTO t1 (x) VALUES ('a')");
|
||||
stmt.execute();
|
||||
stmt.finalize();
|
||||
|
||||
stmt = createStatement("INSERT INTO t1 (x) VALUES ('ab')");
|
||||
stmt.execute();
|
||||
stmt.finalize();
|
||||
|
||||
stmt = createStatement("INSERT INTO t1 (x) VALUES ('abc')");
|
||||
stmt.execute();
|
||||
stmt.finalize();
|
||||
|
||||
stmt = createStatement("INSERT INTO t1 (x) VALUES ('abcd')");
|
||||
stmt.execute();
|
||||
stmt.finalize();
|
||||
|
||||
stmt = createStatement("INSERT INTO t1 (x) VALUES ('acd')");
|
||||
stmt.execute();
|
||||
stmt.finalize();
|
||||
|
||||
stmt = createStatement("INSERT INTO t1 (x) VALUES ('abd')");
|
||||
stmt.execute();
|
||||
stmt.finalize();
|
||||
|
||||
stmt = createStatement("INSERT INTO t1 (x) VALUES ('bc')");
|
||||
stmt.execute();
|
||||
stmt.finalize();
|
||||
|
||||
stmt = createStatement("INSERT INTO t1 (x) VALUES ('bcd')");
|
||||
stmt.execute();
|
||||
stmt.finalize();
|
||||
|
||||
stmt = createStatement("INSERT INTO t1 (x) VALUES ('xyz')");
|
||||
stmt.execute();
|
||||
stmt.finalize();
|
||||
|
||||
stmt = createStatement("INSERT INTO t1 (x) VALUES ('ABC')");
|
||||
stmt.execute();
|
||||
stmt.finalize();
|
||||
|
||||
stmt = createStatement("INSERT INTO t1 (x) VALUES ('CDE')");
|
||||
stmt.execute();
|
||||
stmt.finalize();
|
||||
|
||||
stmt = createStatement("INSERT INTO t1 (x) VALUES ('ABC abc xyz')");
|
||||
stmt.execute();
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
function test_count()
|
||||
{
|
||||
var stmt = createStatement("SELECT count(*) FROM t1;");
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_eq(stmt.getInt32(0), 12);
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
function test_like_1()
|
||||
{
|
||||
var stmt = createStatement("SELECT x FROM t1 WHERE x LIKE ?;");
|
||||
stmt.bindByIndex(0, 'abc');
|
||||
var solutions = ["abc", "ABC"];
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_true(solutions.indexOf(stmt.getString(0)) != -1);
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_true(solutions.indexOf(stmt.getString(0)) != -1);
|
||||
do_check_false(stmt.executeStep());
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
function test_like_2()
|
||||
{
|
||||
var stmt = createStatement("SELECT x FROM t1 WHERE x LIKE ?;");
|
||||
stmt.bindByIndex(0, 'ABC');
|
||||
var solutions = ["abc", "ABC"];
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_true(solutions.indexOf(stmt.getString(0)) != -1);
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_true(solutions.indexOf(stmt.getString(0)) != -1);
|
||||
do_check_false(stmt.executeStep());
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
function test_like_3()
|
||||
{
|
||||
var stmt = createStatement("SELECT x FROM t1 WHERE x LIKE ?;");
|
||||
stmt.bindByIndex(0, 'aBc');
|
||||
var solutions = ["abc", "ABC"];
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_true(solutions.indexOf(stmt.getString(0)) != -1);
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_true(solutions.indexOf(stmt.getString(0)) != -1);
|
||||
do_check_false(stmt.executeStep());
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
function test_like_4()
|
||||
{
|
||||
var stmt = createStatement("SELECT x FROM t1 WHERE x LIKE ?;");
|
||||
stmt.bindByIndex(0, 'abc%');
|
||||
var solutions = ["abc", "abcd", "ABC", "ABC abc xyz"];
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_true(solutions.indexOf(stmt.getString(0)) != -1);
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_true(solutions.indexOf(stmt.getString(0)) != -1);
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_true(solutions.indexOf(stmt.getString(0)) != -1);
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_true(solutions.indexOf(stmt.getString(0)) != -1);
|
||||
do_check_false(stmt.executeStep());
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
function test_like_5()
|
||||
{
|
||||
var stmt = createStatement("SELECT x FROM t1 WHERE x LIKE ?;");
|
||||
stmt.bindByIndex(0, 'a_c');
|
||||
var solutions = ["abc", "ABC"];
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_true(solutions.indexOf(stmt.getString(0)) != -1);
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_true(solutions.indexOf(stmt.getString(0)) != -1);
|
||||
do_check_false(stmt.executeStep());
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
function test_like_6()
|
||||
{
|
||||
var stmt = createStatement("SELECT x FROM t1 WHERE x LIKE ?;");
|
||||
stmt.bindByIndex(0, 'ab%d');
|
||||
var solutions = ["abcd", "abd"];
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_true(solutions.indexOf(stmt.getString(0)) != -1);
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_true(solutions.indexOf(stmt.getString(0)) != -1);
|
||||
do_check_false(stmt.executeStep());
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
function test_like_7()
|
||||
{
|
||||
var stmt = createStatement("SELECT x FROM t1 WHERE x LIKE ?;");
|
||||
stmt.bindByIndex(0, 'a_c%');
|
||||
var solutions = ["abc", "abcd", "ABC", "ABC abc xyz"];
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_true(solutions.indexOf(stmt.getString(0)) != -1);
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_true(solutions.indexOf(stmt.getString(0)) != -1);
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_true(solutions.indexOf(stmt.getString(0)) != -1);
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_true(solutions.indexOf(stmt.getString(0)) != -1);
|
||||
do_check_false(stmt.executeStep());
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
function test_like_8()
|
||||
{
|
||||
var stmt = createStatement("SELECT x FROM t1 WHERE x LIKE ?;");
|
||||
stmt.bindByIndex(0, '%bcd');
|
||||
var solutions = ["abcd", "bcd"];
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_true(solutions.indexOf(stmt.getString(0)) != -1);
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_true(solutions.indexOf(stmt.getString(0)) != -1);
|
||||
do_check_false(stmt.executeStep());
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
var tests = [test_count, test_like_1, test_like_2, test_like_3, test_like_4,
|
||||
test_like_5, test_like_6, test_like_7, test_like_8];
|
||||
|
||||
function run_test()
|
||||
{
|
||||
setup();
|
||||
|
||||
for (var i = 0; i < tests.length; i++) {
|
||||
tests[i]();
|
||||
}
|
||||
|
||||
cleanup();
|
||||
}
|
||||
|
||||
60
storage/test/unit/test_like_escape.js
Normal file
60
storage/test/unit/test_like_escape.js
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const LATIN1_AE = "\xc6";
|
||||
const LATIN1_ae = "\xe6";
|
||||
|
||||
function setup()
|
||||
{
|
||||
getOpenedDatabase().createTable("t1", "x TEXT");
|
||||
|
||||
var stmt = createStatement("INSERT INTO t1 (x) VALUES ('foo/bar_baz%20cheese')");
|
||||
stmt.execute();
|
||||
stmt.finalize();
|
||||
|
||||
stmt = createStatement("INSERT INTO t1 (x) VALUES (?1)");
|
||||
// insert LATIN_ae, but search on LATIN_AE
|
||||
stmt.bindByIndex(0, "foo%20" + LATIN1_ae + "/_bar");
|
||||
stmt.execute();
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
function test_escape_for_like_ascii()
|
||||
{
|
||||
var stmt = createStatement("SELECT x FROM t1 WHERE x LIKE ?1 ESCAPE '/'");
|
||||
var paramForLike = stmt.escapeStringForLIKE("oo/bar_baz%20chees", '/');
|
||||
// verify that we escaped / _ and %
|
||||
do_check_eq(paramForLike, "oo//bar/_baz/%20chees");
|
||||
// prepend and append with % for "contains"
|
||||
stmt.bindByIndex(0, "%" + paramForLike + "%");
|
||||
stmt.executeStep();
|
||||
do_check_eq("foo/bar_baz%20cheese", stmt.getString(0));
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
function test_escape_for_like_non_ascii()
|
||||
{
|
||||
var stmt = createStatement("SELECT x FROM t1 WHERE x LIKE ?1 ESCAPE '/'");
|
||||
var paramForLike = stmt.escapeStringForLIKE("oo%20" + LATIN1_AE + "/_ba", '/');
|
||||
// verify that we escaped / _ and %
|
||||
do_check_eq(paramForLike, "oo/%20" + LATIN1_AE + "///_ba");
|
||||
// prepend and append with % for "contains"
|
||||
stmt.bindByIndex(0, "%" + paramForLike + "%");
|
||||
stmt.executeStep();
|
||||
do_check_eq("foo%20" + LATIN1_ae + "/_bar", stmt.getString(0));
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
var tests = [test_escape_for_like_ascii, test_escape_for_like_non_ascii];
|
||||
|
||||
function run_test()
|
||||
{
|
||||
setup();
|
||||
|
||||
for (var i = 0; i < tests.length; i++) {
|
||||
tests[i]();
|
||||
}
|
||||
|
||||
cleanup();
|
||||
}
|
||||
304
storage/test/unit/test_locale_collation.js
Normal file
304
storage/test/unit/test_locale_collation.js
Normal file
|
|
@ -0,0 +1,304 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
|
||||
* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* Bug 499990 - Locale-aware collation
|
||||
*
|
||||
* Tests our custom, locale-aware collating sequences.
|
||||
*/
|
||||
|
||||
// The name of the file containing the strings we'll sort during this test.
|
||||
// The file's data is taken from intl/locale/tests/sort/us-ascii_base.txt and
|
||||
// and intl/locale/tests/sort/us-ascii_sort.txt.
|
||||
const DATA_BASENAME = "locale_collation.txt";
|
||||
|
||||
// The test data from DATA_BASENAME is read into this array.
|
||||
var gStrings;
|
||||
|
||||
// A collation created from the application's locale. Used by localeCompare().
|
||||
var gLocaleCollation;
|
||||
|
||||
// A connection to our in-memory UTF-16-encoded database.
|
||||
var gUtf16Conn;
|
||||
|
||||
// Helper Functions
|
||||
|
||||
/**
|
||||
* Since we create a UTF-16 database we have to clean it up, in addition to
|
||||
* the normal cleanup of Storage tests.
|
||||
*/
|
||||
function cleanupLocaleTests()
|
||||
{
|
||||
print("-- Cleaning up test_locale_collation.js suite.");
|
||||
gUtf16Conn.close();
|
||||
cleanup();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a test database similar to the default one created in
|
||||
* head_storage.js, except that this one uses UTF-16 encoding.
|
||||
*
|
||||
* @return A connection to the database.
|
||||
*/
|
||||
function createUtf16Database()
|
||||
{
|
||||
print("Creating the in-memory UTF-16-encoded database.");
|
||||
let conn = getService().openSpecialDatabase("memory");
|
||||
conn.executeSimpleSQL("PRAGMA encoding = 'UTF-16'");
|
||||
|
||||
print("Make sure the encoding was set correctly and is now UTF-16.");
|
||||
let stmt = conn.createStatement("PRAGMA encoding");
|
||||
do_check_true(stmt.executeStep());
|
||||
let enc = stmt.getString(0);
|
||||
stmt.finalize();
|
||||
|
||||
// The value returned will actually be UTF-16le or UTF-16be.
|
||||
do_check_true(enc === "UTF-16le" || enc === "UTF-16be");
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares aActual to aExpected, ensuring that the numbers and orderings of
|
||||
* the two arrays' elements are the same.
|
||||
*
|
||||
* @param aActual
|
||||
* An array of strings retrieved from the database.
|
||||
* @param aExpected
|
||||
* An array of strings to which aActual should be equivalent.
|
||||
*/
|
||||
function ensureResultsAreCorrect(aActual, aExpected)
|
||||
{
|
||||
print("Actual results: " + aActual);
|
||||
print("Expected results: " + aExpected);
|
||||
|
||||
do_check_eq(aActual.length, aExpected.length);
|
||||
for (let i = 0; i < aActual.length; i++)
|
||||
do_check_eq(aActual[i], aExpected[i]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronously SELECTs all rows from the test table of the given database
|
||||
* using the given collation.
|
||||
*
|
||||
* @param aCollation
|
||||
* The name of one of our custom locale collations. The rows are
|
||||
* ordered by this collation.
|
||||
* @param aConn
|
||||
* A connection to either the UTF-8 database or the UTF-16 database.
|
||||
* @return The resulting strings in an array.
|
||||
*/
|
||||
function getResults(aCollation, aConn)
|
||||
{
|
||||
let results = [];
|
||||
let stmt = aConn.createStatement("SELECT t FROM test " +
|
||||
"ORDER BY t COLLATE " + aCollation + " ASC");
|
||||
while (stmt.executeStep())
|
||||
results.push(stmt.row.t);
|
||||
stmt.finalize();
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts strings into our test table of the given database in the order given.
|
||||
*
|
||||
* @param aStrings
|
||||
* An array of strings.
|
||||
* @param aConn
|
||||
* A connection to either the UTF-8 database or the UTF-16 database.
|
||||
*/
|
||||
function initTableWithStrings(aStrings, aConn)
|
||||
{
|
||||
print("Initializing test table.");
|
||||
|
||||
aConn.executeSimpleSQL("DROP TABLE IF EXISTS test");
|
||||
aConn.createTable("test", "t TEXT");
|
||||
let stmt = aConn.createStatement("INSERT INTO test (t) VALUES (:t)");
|
||||
aStrings.forEach(function (str) {
|
||||
stmt.params.t = str;
|
||||
stmt.execute();
|
||||
stmt.reset();
|
||||
});
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a sorting function suitable for passing to Array.prototype.sort().
|
||||
* The returned function uses the application's locale to compare strings.
|
||||
*
|
||||
* @param aCollation
|
||||
* The name of one of our custom locale collations. The sorting
|
||||
* strength is computed from this value.
|
||||
* @return A function to use as a sorting callback.
|
||||
*/
|
||||
function localeCompare(aCollation)
|
||||
{
|
||||
var strength;
|
||||
|
||||
switch (aCollation) {
|
||||
case "locale":
|
||||
strength = Ci.nsICollation.kCollationCaseInSensitive;
|
||||
break;
|
||||
case "locale_case_sensitive":
|
||||
strength = Ci.nsICollation.kCollationAccentInsenstive;
|
||||
break;
|
||||
case "locale_accent_sensitive":
|
||||
strength = Ci.nsICollation.kCollationCaseInsensitiveAscii;
|
||||
break;
|
||||
case "locale_case_accent_sensitive":
|
||||
strength = Ci.nsICollation.kCollationCaseSensitive;
|
||||
break;
|
||||
default:
|
||||
do_throw("Error in test: unknown collation '" + aCollation + "'");
|
||||
break;
|
||||
}
|
||||
return function (aStr1, aStr2) {
|
||||
return gLocaleCollation.compareString(strength, aStr1, aStr2);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads in the test data from the file DATA_BASENAME and returns it as an array
|
||||
* of strings.
|
||||
*
|
||||
* @return The test data as an array of strings.
|
||||
*/
|
||||
function readTestData()
|
||||
{
|
||||
print("Reading in test data.");
|
||||
|
||||
let file = do_get_file(DATA_BASENAME);
|
||||
|
||||
let istream = Cc["@mozilla.org/network/file-input-stream;1"].
|
||||
createInstance(Ci.nsIFileInputStream);
|
||||
istream.init(file, -1, -1, 0);
|
||||
istream.QueryInterface(Components.interfaces.nsILineInputStream);
|
||||
|
||||
let line = {};
|
||||
let lines = [];
|
||||
while (istream.readLine(line)) {
|
||||
lines.push(line.value);
|
||||
}
|
||||
istream.close();
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the results from the given database using the given collation and
|
||||
* ensures that they match gStrings sorted by the same collation.
|
||||
*
|
||||
* @param aCollation
|
||||
* The name of one of our custom locale collations. The rows from the
|
||||
* database and the expected results are ordered by this collation.
|
||||
* @param aConn
|
||||
* A connection to either the UTF-8 database or the UTF-16 database.
|
||||
*/
|
||||
function runTest(aCollation, aConn)
|
||||
{
|
||||
ensureResultsAreCorrect(getResults(aCollation, aConn),
|
||||
gStrings.slice(0).sort(localeCompare(aCollation)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the results from the UTF-8 database using the given collation and
|
||||
* ensures that they match gStrings sorted by the same collation.
|
||||
*
|
||||
* @param aCollation
|
||||
* The name of one of our custom locale collations. The rows from the
|
||||
* database and the expected results are ordered by this collation.
|
||||
*/
|
||||
function runUtf8Test(aCollation)
|
||||
{
|
||||
runTest(aCollation, getOpenedDatabase());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the results from the UTF-16 database using the given collation and
|
||||
* ensures that they match gStrings sorted by the same collation.
|
||||
*
|
||||
* @param aCollation
|
||||
* The name of one of our custom locale collations. The rows from the
|
||||
* database and the expected results are ordered by this collation.
|
||||
*/
|
||||
function runUtf16Test(aCollation)
|
||||
{
|
||||
runTest(aCollation, gUtf16Conn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the test suite.
|
||||
*/
|
||||
function setup()
|
||||
{
|
||||
print("-- Setting up the test_locale_collation.js suite.");
|
||||
|
||||
gStrings = readTestData();
|
||||
|
||||
initTableWithStrings(gStrings, getOpenedDatabase());
|
||||
|
||||
gUtf16Conn = createUtf16Database();
|
||||
initTableWithStrings(gStrings, gUtf16Conn);
|
||||
|
||||
let localeSvc = Cc["@mozilla.org/intl/nslocaleservice;1"].
|
||||
getService(Ci.nsILocaleService);
|
||||
let collFact = Cc["@mozilla.org/intl/collation-factory;1"].
|
||||
createInstance(Ci.nsICollationFactory);
|
||||
gLocaleCollation = collFact.CreateCollation(localeSvc.getApplicationLocale());
|
||||
}
|
||||
|
||||
// Test Runs
|
||||
|
||||
var gTests = [
|
||||
{
|
||||
desc: "Case and accent sensitive UTF-8",
|
||||
run: () => runUtf8Test("locale_case_accent_sensitive")
|
||||
},
|
||||
|
||||
{
|
||||
desc: "Case sensitive, accent insensitive UTF-8",
|
||||
run: () => runUtf8Test("locale_case_sensitive")
|
||||
},
|
||||
|
||||
{
|
||||
desc: "Case insensitive, accent sensitive UTF-8",
|
||||
run: () => runUtf8Test("locale_accent_sensitive")
|
||||
},
|
||||
|
||||
{
|
||||
desc: "Case and accent insensitive UTF-8",
|
||||
run: () => runUtf8Test("locale")
|
||||
},
|
||||
|
||||
{
|
||||
desc: "Case and accent sensitive UTF-16",
|
||||
run: () => runUtf16Test("locale_case_accent_sensitive")
|
||||
},
|
||||
|
||||
{
|
||||
desc: "Case sensitive, accent insensitive UTF-16",
|
||||
run: () => runUtf16Test("locale_case_sensitive")
|
||||
},
|
||||
|
||||
{
|
||||
desc: "Case insensitive, accent sensitive UTF-16",
|
||||
run: () => runUtf16Test("locale_accent_sensitive")
|
||||
},
|
||||
|
||||
{
|
||||
desc: "Case and accent insensitive UTF-16",
|
||||
run: () => runUtf16Test("locale")
|
||||
},
|
||||
];
|
||||
|
||||
function run_test()
|
||||
{
|
||||
setup();
|
||||
gTests.forEach(function (test) {
|
||||
print("-- Running test: " + test.desc);
|
||||
test.run();
|
||||
});
|
||||
}
|
||||
35
storage/test/unit/test_page_size_is_32k.js
Normal file
35
storage/test/unit/test_page_size_is_32k.js
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
// This file tests that dbs are using 32k pagesize
|
||||
|
||||
const kExpectedPageSize = 32768; // 32K
|
||||
const kExpectedCacheSize = -2048; // 2MiB
|
||||
|
||||
function check_size(db)
|
||||
{
|
||||
var stmt = db.createStatement("PRAGMA page_size");
|
||||
stmt.executeStep();
|
||||
do_check_eq(stmt.getInt32(0), kExpectedPageSize);
|
||||
stmt.finalize();
|
||||
stmt = db.createStatement("PRAGMA cache_size");
|
||||
stmt.executeStep();
|
||||
do_check_eq(stmt.getInt32(0), kExpectedCacheSize);
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
function new_file(name)
|
||||
{
|
||||
var file = dirSvc.get("ProfD", Ci.nsIFile);
|
||||
file.append(name + ".sqlite");
|
||||
do_check_false(file.exists());
|
||||
return file;
|
||||
}
|
||||
|
||||
function run_test()
|
||||
{
|
||||
check_size(getDatabase(new_file("shared32k")));
|
||||
check_size(getService().openUnsharedDatabase(new_file("unshared32k")));
|
||||
}
|
||||
|
||||
80
storage/test/unit/test_sqlite_secure_delete.js
Normal file
80
storage/test/unit/test_sqlite_secure_delete.js
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
|
||||
*vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* This file tests to make sure that SQLite was compiled with
|
||||
* SQLITE_SECURE_DELETE=1.
|
||||
*/
|
||||
|
||||
// Helper Methods
|
||||
|
||||
/**
|
||||
* Reads the contents of a file and returns it as a string.
|
||||
*
|
||||
* @param aFile
|
||||
* The file to return from.
|
||||
* @return the contents of the file in the form of a string.
|
||||
*/
|
||||
function getFileContents(aFile)
|
||||
{
|
||||
let fstream = Cc["@mozilla.org/network/file-input-stream;1"].
|
||||
createInstance(Ci.nsIFileInputStream);
|
||||
fstream.init(aFile, -1, 0, 0);
|
||||
|
||||
let bstream = Cc["@mozilla.org/binaryinputstream;1"].
|
||||
createInstance(Ci.nsIBinaryInputStream);
|
||||
bstream.setInputStream(fstream);
|
||||
return bstream.readBytes(bstream.available());
|
||||
}
|
||||
|
||||
// Tests
|
||||
|
||||
add_test(function test_delete_removes_data() {
|
||||
const TEST_STRING = "SomeRandomStringToFind";
|
||||
|
||||
let file = getTestDB();
|
||||
let db = getService().openDatabase(file);
|
||||
|
||||
// Create the table and insert the data.
|
||||
db.createTable("test", "data TEXT");
|
||||
let stmt = db.createStatement("INSERT INTO test VALUES(:data)");
|
||||
stmt.params.data = TEST_STRING;
|
||||
try {
|
||||
stmt.execute();
|
||||
}
|
||||
finally {
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
// Make sure this test is actually testing what it thinks by making sure the
|
||||
// string shows up in the database. Because the previous statement was
|
||||
// automatically wrapped in a transaction, the contents are already on disk.
|
||||
let contents = getFileContents(file);
|
||||
do_check_neq(-1, contents.indexOf(TEST_STRING));
|
||||
|
||||
// Delete the data, and then close the database.
|
||||
stmt = db.createStatement("DELETE FROM test WHERE data = :data");
|
||||
stmt.params.data = TEST_STRING;
|
||||
try {
|
||||
stmt.execute();
|
||||
}
|
||||
finally {
|
||||
stmt.finalize();
|
||||
}
|
||||
db.close();
|
||||
|
||||
// Check the file to see if the string can be found.
|
||||
contents = getFileContents(file);
|
||||
do_check_eq(-1, contents.indexOf(TEST_STRING));
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
function run_test()
|
||||
{
|
||||
cleanup();
|
||||
run_next_test();
|
||||
}
|
||||
998
storage/test/unit/test_statement_executeAsync.js
Normal file
998
storage/test/unit/test_statement_executeAsync.js
Normal file
|
|
@ -0,0 +1,998 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/*
|
||||
* This file tests the functionality of mozIStorageBaseStatement::executeAsync
|
||||
* for both mozIStorageStatement and mozIStorageAsyncStatement.
|
||||
*/
|
||||
|
||||
const INTEGER = 1;
|
||||
const TEXT = "this is test text";
|
||||
const REAL = 3.23;
|
||||
const BLOB = [1, 2];
|
||||
|
||||
/**
|
||||
* Execute the given statement asynchronously, spinning an event loop until the
|
||||
* async statement completes.
|
||||
*
|
||||
* @param aStmt
|
||||
* The statement to execute.
|
||||
* @param [aOptions={}]
|
||||
* @param [aOptions.error=false]
|
||||
* If true we should expect an error whose code we do not care about. If
|
||||
* a numeric value, that's the error code we expect and require. If we
|
||||
* are expecting an error, we expect a completion reason of REASON_ERROR.
|
||||
* Otherwise we expect no error notification and a completion reason of
|
||||
* REASON_FINISHED.
|
||||
* @param [aOptions.cancel]
|
||||
* If true we cancel the pending statement and additionally return the
|
||||
* pending statement in case you want to further manipulate it.
|
||||
* @param [aOptions.returnPending=false]
|
||||
* If true we keep the pending statement around and return it to you. We
|
||||
* normally avoid doing this to try and minimize the amount of time a
|
||||
* reference is held to the returned pending statement.
|
||||
* @param [aResults]
|
||||
* If omitted, we assume no results rows are expected. If it is a
|
||||
* number, we assume it is the number of results rows expected. If it is
|
||||
* a function, we assume it is a function that takes the 1) result row
|
||||
* number, 2) result tuple, 3) call stack for the original call to
|
||||
* execAsync as arguments. If it is a list, we currently assume it is a
|
||||
* list of functions where each function is intended to evaluate the
|
||||
* result row at that ordinal position and takes the result tuple and
|
||||
* the call stack for the original call.
|
||||
*/
|
||||
function execAsync(aStmt, aOptions, aResults)
|
||||
{
|
||||
let caller = Components.stack.caller;
|
||||
if (aOptions == null)
|
||||
aOptions = {};
|
||||
|
||||
let resultsExpected;
|
||||
let resultsChecker;
|
||||
if (aResults == null) {
|
||||
resultsExpected = 0;
|
||||
}
|
||||
else if (typeof aResults == "number") {
|
||||
resultsExpected = aResults;
|
||||
}
|
||||
else if (typeof aResults == "function") {
|
||||
resultsChecker = aResults;
|
||||
}
|
||||
else { // array
|
||||
resultsExpected = aResults.length;
|
||||
resultsChecker = function (aResultNum, aTup, aCaller) {
|
||||
aResults[aResultNum](aTup, aCaller);
|
||||
};
|
||||
}
|
||||
let resultsSeen = 0;
|
||||
|
||||
let errorCodeExpected = false;
|
||||
let reasonExpected = Ci.mozIStorageStatementCallback.REASON_FINISHED;
|
||||
let altReasonExpected = null;
|
||||
if ("error" in aOptions) {
|
||||
errorCodeExpected = aOptions.error;
|
||||
if (errorCodeExpected)
|
||||
reasonExpected = Ci.mozIStorageStatementCallback.REASON_ERROR;
|
||||
}
|
||||
let errorCodeSeen = false;
|
||||
|
||||
if ("cancel" in aOptions && aOptions.cancel)
|
||||
altReasonExpected = Ci.mozIStorageStatementCallback.REASON_CANCELED;
|
||||
|
||||
let completed = false;
|
||||
|
||||
let listener = {
|
||||
handleResult(aResultSet)
|
||||
{
|
||||
let row, resultsSeenThisCall = 0;
|
||||
while ((row = aResultSet.getNextRow()) != null) {
|
||||
if (resultsChecker)
|
||||
resultsChecker(resultsSeen, row, caller);
|
||||
resultsSeen++;
|
||||
resultsSeenThisCall++;
|
||||
}
|
||||
|
||||
if (!resultsSeenThisCall)
|
||||
do_throw("handleResult invoked with 0 result rows!");
|
||||
},
|
||||
handleError(aError)
|
||||
{
|
||||
if (errorCodeSeen != false)
|
||||
do_throw("handleError called when we already had an error!");
|
||||
errorCodeSeen = aError.result;
|
||||
},
|
||||
handleCompletion(aReason)
|
||||
{
|
||||
if (completed) // paranoia check
|
||||
do_throw("Received a second handleCompletion notification!", caller);
|
||||
|
||||
if (resultsSeen != resultsExpected)
|
||||
do_throw("Expected " + resultsExpected + " rows of results but " +
|
||||
"got " + resultsSeen + " rows!", caller);
|
||||
|
||||
if (errorCodeExpected == true && errorCodeSeen == false)
|
||||
do_throw("Expected an error, but did not see one.", caller);
|
||||
else if (errorCodeExpected != errorCodeSeen)
|
||||
do_throw("Expected error code " + errorCodeExpected + " but got " +
|
||||
errorCodeSeen, caller);
|
||||
|
||||
if (aReason != reasonExpected && aReason != altReasonExpected)
|
||||
do_throw("Expected reason " + reasonExpected +
|
||||
(altReasonExpected ? (" or " + altReasonExpected) : "") +
|
||||
" but got " + aReason, caller);
|
||||
|
||||
completed = true;
|
||||
}
|
||||
};
|
||||
|
||||
let pending;
|
||||
// Only get a pending reference if we're supposed to do.
|
||||
// (note: This does not stop XPConnect from holding onto one currently.)
|
||||
if (("cancel" in aOptions && aOptions.cancel) ||
|
||||
("returnPending" in aOptions && aOptions.returnPending)) {
|
||||
pending = aStmt.executeAsync(listener);
|
||||
}
|
||||
else {
|
||||
aStmt.executeAsync(listener);
|
||||
}
|
||||
|
||||
if ("cancel" in aOptions && aOptions.cancel)
|
||||
pending.cancel();
|
||||
|
||||
let curThread = Components.classes["@mozilla.org/thread-manager;1"]
|
||||
.getService().currentThread;
|
||||
while (!completed && !_quit)
|
||||
curThread.processNextEvent(true);
|
||||
|
||||
return pending;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure that illegal SQL generates the expected runtime error and does not
|
||||
* result in any crashes. Async-only since the synchronous case generates the
|
||||
* error synchronously (and is tested elsewhere).
|
||||
*/
|
||||
function test_illegal_sql_async_deferred()
|
||||
{
|
||||
// gibberish
|
||||
let stmt = makeTestStatement("I AM A ROBOT. DO AS I SAY.");
|
||||
execAsync(stmt, {error: Ci.mozIStorageError.ERROR});
|
||||
stmt.finalize();
|
||||
|
||||
// legal SQL syntax, but with semantics issues.
|
||||
stmt = makeTestStatement("SELECT destination FROM funkytown");
|
||||
execAsync(stmt, {error: Ci.mozIStorageError.ERROR});
|
||||
stmt.finalize();
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
test_illegal_sql_async_deferred.asyncOnly = true;
|
||||
|
||||
function test_create_table()
|
||||
{
|
||||
// Ensure our table doesn't exist
|
||||
do_check_false(getOpenedDatabase().tableExists("test"));
|
||||
|
||||
var stmt = makeTestStatement(
|
||||
"CREATE TABLE test (" +
|
||||
"id INTEGER, " +
|
||||
"string TEXT, " +
|
||||
"number REAL, " +
|
||||
"nuller NULL, " +
|
||||
"blober BLOB" +
|
||||
")"
|
||||
);
|
||||
execAsync(stmt);
|
||||
stmt.finalize();
|
||||
|
||||
// Check that the table has been created
|
||||
do_check_true(getOpenedDatabase().tableExists("test"));
|
||||
|
||||
// Verify that it's created correctly (this will throw if it wasn't)
|
||||
let checkStmt = getOpenedDatabase().createStatement(
|
||||
"SELECT id, string, number, nuller, blober FROM test"
|
||||
);
|
||||
checkStmt.finalize();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function test_add_data()
|
||||
{
|
||||
var stmt = makeTestStatement(
|
||||
"INSERT INTO test (id, string, number, nuller, blober) " +
|
||||
"VALUES (?, ?, ?, ?, ?)"
|
||||
);
|
||||
stmt.bindBlobByIndex(4, BLOB, BLOB.length);
|
||||
stmt.bindByIndex(3, null);
|
||||
stmt.bindByIndex(2, REAL);
|
||||
stmt.bindByIndex(1, TEXT);
|
||||
stmt.bindByIndex(0, INTEGER);
|
||||
|
||||
execAsync(stmt);
|
||||
stmt.finalize();
|
||||
|
||||
// Check that the result is in the table
|
||||
verifyQuery("SELECT string, number, nuller, blober FROM test WHERE id = ?",
|
||||
INTEGER,
|
||||
[TEXT, REAL, null, BLOB]);
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function test_get_data()
|
||||
{
|
||||
var stmt = makeTestStatement(
|
||||
"SELECT string, number, nuller, blober, id FROM test WHERE id = ?"
|
||||
);
|
||||
stmt.bindByIndex(0, INTEGER);
|
||||
execAsync(stmt, {}, [
|
||||
function (tuple) {
|
||||
do_check_neq(null, tuple);
|
||||
|
||||
// Check that it's what we expect
|
||||
do_check_false(tuple.getIsNull(0));
|
||||
do_check_eq(tuple.getResultByName("string"), tuple.getResultByIndex(0));
|
||||
do_check_eq(TEXT, tuple.getResultByName("string"));
|
||||
do_check_eq(Ci.mozIStorageValueArray.VALUE_TYPE_TEXT,
|
||||
tuple.getTypeOfIndex(0));
|
||||
|
||||
do_check_false(tuple.getIsNull(1));
|
||||
do_check_eq(tuple.getResultByName("number"), tuple.getResultByIndex(1));
|
||||
do_check_eq(REAL, tuple.getResultByName("number"));
|
||||
do_check_eq(Ci.mozIStorageValueArray.VALUE_TYPE_FLOAT,
|
||||
tuple.getTypeOfIndex(1));
|
||||
|
||||
do_check_true(tuple.getIsNull(2));
|
||||
do_check_eq(tuple.getResultByName("nuller"), tuple.getResultByIndex(2));
|
||||
do_check_eq(null, tuple.getResultByName("nuller"));
|
||||
do_check_eq(Ci.mozIStorageValueArray.VALUE_TYPE_NULL,
|
||||
tuple.getTypeOfIndex(2));
|
||||
|
||||
do_check_false(tuple.getIsNull(3));
|
||||
var blobByName = tuple.getResultByName("blober");
|
||||
do_check_eq(BLOB.length, blobByName.length);
|
||||
var blobByIndex = tuple.getResultByIndex(3);
|
||||
do_check_eq(BLOB.length, blobByIndex.length);
|
||||
for (let i = 0; i < BLOB.length; i++) {
|
||||
do_check_eq(BLOB[i], blobByName[i]);
|
||||
do_check_eq(BLOB[i], blobByIndex[i]);
|
||||
}
|
||||
var count = { value: 0 };
|
||||
var blob = { value: null };
|
||||
tuple.getBlob(3, count, blob);
|
||||
do_check_eq(BLOB.length, count.value);
|
||||
for (let i = 0; i < BLOB.length; i++)
|
||||
do_check_eq(BLOB[i], blob.value[i]);
|
||||
do_check_eq(Ci.mozIStorageValueArray.VALUE_TYPE_BLOB,
|
||||
tuple.getTypeOfIndex(3));
|
||||
|
||||
do_check_false(tuple.getIsNull(4));
|
||||
do_check_eq(tuple.getResultByName("id"), tuple.getResultByIndex(4));
|
||||
do_check_eq(INTEGER, tuple.getResultByName("id"));
|
||||
do_check_eq(Ci.mozIStorageValueArray.VALUE_TYPE_INTEGER,
|
||||
tuple.getTypeOfIndex(4));
|
||||
}]);
|
||||
stmt.finalize();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function test_tuple_out_of_bounds()
|
||||
{
|
||||
var stmt = makeTestStatement(
|
||||
"SELECT string FROM test"
|
||||
);
|
||||
execAsync(stmt, {}, [
|
||||
function (tuple) {
|
||||
do_check_neq(null, tuple);
|
||||
|
||||
// Check all out of bounds - should throw
|
||||
var methods = [
|
||||
"getTypeOfIndex",
|
||||
"getInt32",
|
||||
"getInt64",
|
||||
"getDouble",
|
||||
"getUTF8String",
|
||||
"getString",
|
||||
"getIsNull",
|
||||
];
|
||||
for (var i in methods) {
|
||||
try {
|
||||
tuple[methods[i]](tuple.numEntries);
|
||||
do_throw("did not throw :(");
|
||||
}
|
||||
catch (e) {
|
||||
do_check_eq(Cr.NS_ERROR_ILLEGAL_VALUE, e.result);
|
||||
}
|
||||
}
|
||||
|
||||
// getBlob requires more args...
|
||||
try {
|
||||
var blob = { value: null };
|
||||
var size = { value: 0 };
|
||||
tuple.getBlob(tuple.numEntries, blob, size);
|
||||
do_throw("did not throw :(");
|
||||
}
|
||||
catch (e) {
|
||||
do_check_eq(Cr.NS_ERROR_ILLEGAL_VALUE, e.result);
|
||||
}
|
||||
}]);
|
||||
stmt.finalize();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function test_no_listener_works_on_success()
|
||||
{
|
||||
var stmt = makeTestStatement(
|
||||
"DELETE FROM test WHERE id = ?"
|
||||
);
|
||||
stmt.bindByIndex(0, 0);
|
||||
stmt.executeAsync();
|
||||
stmt.finalize();
|
||||
|
||||
// Run the next test.
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function test_no_listener_works_on_results()
|
||||
{
|
||||
var stmt = makeTestStatement(
|
||||
"SELECT ?"
|
||||
);
|
||||
stmt.bindByIndex(0, 1);
|
||||
stmt.executeAsync();
|
||||
stmt.finalize();
|
||||
|
||||
// Run the next test.
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function test_no_listener_works_on_error()
|
||||
{
|
||||
// commit without a transaction will trigger an error
|
||||
var stmt = makeTestStatement(
|
||||
"COMMIT"
|
||||
);
|
||||
stmt.executeAsync();
|
||||
stmt.finalize();
|
||||
|
||||
// Run the next test.
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function test_partial_listener_works()
|
||||
{
|
||||
var stmt = makeTestStatement(
|
||||
"DELETE FROM test WHERE id = ?"
|
||||
);
|
||||
stmt.bindByIndex(0, 0);
|
||||
stmt.executeAsync({
|
||||
handleResult(aResultSet) {}
|
||||
});
|
||||
stmt.executeAsync({
|
||||
handleError(aError) {}
|
||||
});
|
||||
stmt.executeAsync({
|
||||
handleCompletion(aReason) {}
|
||||
});
|
||||
stmt.finalize();
|
||||
|
||||
// Run the next test.
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
/**
|
||||
* Dubious cancellation test that depends on system loading may or may not
|
||||
* succeed in canceling things. It does at least test if calling cancel blows
|
||||
* up. test_AsyncCancellation in test_true_async.cpp is our test that canceling
|
||||
* actually works correctly.
|
||||
*/
|
||||
function test_immediate_cancellation()
|
||||
{
|
||||
var stmt = makeTestStatement(
|
||||
"DELETE FROM test WHERE id = ?"
|
||||
);
|
||||
stmt.bindByIndex(0, 0);
|
||||
execAsync(stmt, {cancel: true});
|
||||
stmt.finalize();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that calling cancel twice throws the second time.
|
||||
*/
|
||||
function test_double_cancellation()
|
||||
{
|
||||
var stmt = makeTestStatement(
|
||||
"DELETE FROM test WHERE id = ?"
|
||||
);
|
||||
stmt.bindByIndex(0, 0);
|
||||
let pendingStatement = execAsync(stmt, {cancel: true});
|
||||
// And cancel again - expect an exception
|
||||
expectError(Cr.NS_ERROR_UNEXPECTED,
|
||||
() => pendingStatement.cancel());
|
||||
|
||||
stmt.finalize();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that nothing untoward happens if we try and cancel something after it
|
||||
* has fully run to completion.
|
||||
*/
|
||||
function test_cancellation_after_execution()
|
||||
{
|
||||
var stmt = makeTestStatement(
|
||||
"DELETE FROM test WHERE id = ?"
|
||||
);
|
||||
stmt.bindByIndex(0, 0);
|
||||
let pendingStatement = execAsync(stmt, {returnPending: true});
|
||||
// (the statement has fully executed at this point)
|
||||
// canceling after the statement has run to completion should not throw!
|
||||
pendingStatement.cancel();
|
||||
|
||||
stmt.finalize();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that a single statement can be executed more than once. Might once
|
||||
* have been intended to also ensure that callback notifications were not
|
||||
* incorrectly interleaved, but that part was brittle (it's totally fine for
|
||||
* handleResult to get called multiple times) and not comprehensive.
|
||||
*/
|
||||
function test_double_execute()
|
||||
{
|
||||
var stmt = makeTestStatement(
|
||||
"SELECT 1"
|
||||
);
|
||||
execAsync(stmt, null, 1);
|
||||
execAsync(stmt, null, 1);
|
||||
stmt.finalize();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function test_finalized_statement_does_not_crash()
|
||||
{
|
||||
var stmt = makeTestStatement(
|
||||
"SELECT * FROM TEST"
|
||||
);
|
||||
stmt.finalize();
|
||||
// we are concerned about a crash here; an error is fine.
|
||||
try {
|
||||
stmt.executeAsync();
|
||||
} catch (ex) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
// Run the next test.
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind by mozIStorageBindingParams on the mozIStorageBaseStatement by index.
|
||||
*/
|
||||
function test_bind_direct_binding_params_by_index()
|
||||
{
|
||||
var stmt = makeTestStatement(
|
||||
"INSERT INTO test (id, string, number, nuller, blober) " +
|
||||
"VALUES (?, ?, ?, ?, ?)"
|
||||
);
|
||||
let insertId = nextUniqueId++;
|
||||
stmt.bindByIndex(0, insertId);
|
||||
stmt.bindByIndex(1, TEXT);
|
||||
stmt.bindByIndex(2, REAL);
|
||||
stmt.bindByIndex(3, null);
|
||||
stmt.bindBlobByIndex(4, BLOB, BLOB.length);
|
||||
execAsync(stmt);
|
||||
stmt.finalize();
|
||||
verifyQuery("SELECT string, number, nuller, blober FROM test WHERE id = ?",
|
||||
insertId,
|
||||
[TEXT, REAL, null, BLOB]);
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind by mozIStorageBindingParams on the mozIStorageBaseStatement by name.
|
||||
*/
|
||||
function test_bind_direct_binding_params_by_name()
|
||||
{
|
||||
var stmt = makeTestStatement(
|
||||
"INSERT INTO test (id, string, number, nuller, blober) " +
|
||||
"VALUES (:int, :text, :real, :null, :blob)"
|
||||
);
|
||||
let insertId = nextUniqueId++;
|
||||
stmt.bindByName("int", insertId);
|
||||
stmt.bindByName("text", TEXT);
|
||||
stmt.bindByName("real", REAL);
|
||||
stmt.bindByName("null", null);
|
||||
stmt.bindBlobByName("blob", BLOB, BLOB.length);
|
||||
execAsync(stmt);
|
||||
stmt.finalize();
|
||||
verifyQuery("SELECT string, number, nuller, blober FROM test WHERE id = ?",
|
||||
insertId,
|
||||
[TEXT, REAL, null, BLOB]);
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function test_bind_js_params_helper_by_index()
|
||||
{
|
||||
var stmt = makeTestStatement(
|
||||
"INSERT INTO test (id, string, number, nuller, blober) " +
|
||||
"VALUES (?, ?, ?, ?, NULL)"
|
||||
);
|
||||
let insertId = nextUniqueId++;
|
||||
// we cannot bind blobs this way; no blober
|
||||
stmt.params[3] = null;
|
||||
stmt.params[2] = REAL;
|
||||
stmt.params[1] = TEXT;
|
||||
stmt.params[0] = insertId;
|
||||
execAsync(stmt);
|
||||
stmt.finalize();
|
||||
verifyQuery("SELECT string, number, nuller FROM test WHERE id = ?", insertId,
|
||||
[TEXT, REAL, null]);
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function test_bind_js_params_helper_by_name()
|
||||
{
|
||||
var stmt = makeTestStatement(
|
||||
"INSERT INTO test (id, string, number, nuller, blober) " +
|
||||
"VALUES (:int, :text, :real, :null, NULL)"
|
||||
);
|
||||
let insertId = nextUniqueId++;
|
||||
// we cannot bind blobs this way; no blober
|
||||
stmt.params.null = null;
|
||||
stmt.params.real = REAL;
|
||||
stmt.params.text = TEXT;
|
||||
stmt.params.int = insertId;
|
||||
execAsync(stmt);
|
||||
stmt.finalize();
|
||||
verifyQuery("SELECT string, number, nuller FROM test WHERE id = ?", insertId,
|
||||
[TEXT, REAL, null]);
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function test_bind_multiple_rows_by_index()
|
||||
{
|
||||
const AMOUNT_TO_ADD = 5;
|
||||
var stmt = makeTestStatement(
|
||||
"INSERT INTO test (id, string, number, nuller, blober) " +
|
||||
"VALUES (?, ?, ?, ?, ?)"
|
||||
);
|
||||
var array = stmt.newBindingParamsArray();
|
||||
for (let i = 0; i < AMOUNT_TO_ADD; i++) {
|
||||
let bp = array.newBindingParams();
|
||||
bp.bindByIndex(0, INTEGER);
|
||||
bp.bindByIndex(1, TEXT);
|
||||
bp.bindByIndex(2, REAL);
|
||||
bp.bindByIndex(3, null);
|
||||
bp.bindBlobByIndex(4, BLOB, BLOB.length);
|
||||
array.addParams(bp);
|
||||
do_check_eq(array.length, i + 1);
|
||||
}
|
||||
stmt.bindParameters(array);
|
||||
|
||||
let rowCount = getTableRowCount("test");
|
||||
execAsync(stmt);
|
||||
do_check_eq(rowCount + AMOUNT_TO_ADD, getTableRowCount("test"));
|
||||
stmt.finalize();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function test_bind_multiple_rows_by_name()
|
||||
{
|
||||
const AMOUNT_TO_ADD = 5;
|
||||
var stmt = makeTestStatement(
|
||||
"INSERT INTO test (id, string, number, nuller, blober) " +
|
||||
"VALUES (:int, :text, :real, :null, :blob)"
|
||||
);
|
||||
var array = stmt.newBindingParamsArray();
|
||||
for (let i = 0; i < AMOUNT_TO_ADD; i++) {
|
||||
let bp = array.newBindingParams();
|
||||
bp.bindByName("int", INTEGER);
|
||||
bp.bindByName("text", TEXT);
|
||||
bp.bindByName("real", REAL);
|
||||
bp.bindByName("null", null);
|
||||
bp.bindBlobByName("blob", BLOB, BLOB.length);
|
||||
array.addParams(bp);
|
||||
do_check_eq(array.length, i + 1);
|
||||
}
|
||||
stmt.bindParameters(array);
|
||||
|
||||
let rowCount = getTableRowCount("test");
|
||||
execAsync(stmt);
|
||||
do_check_eq(rowCount + AMOUNT_TO_ADD, getTableRowCount("test"));
|
||||
stmt.finalize();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that a mozIStorageStatement instance throws immediately when we
|
||||
* try and bind to an illegal index.
|
||||
*/
|
||||
function test_bind_out_of_bounds_sync_immediate()
|
||||
{
|
||||
let stmt = makeTestStatement(
|
||||
"INSERT INTO test (id) " +
|
||||
"VALUES (?)"
|
||||
);
|
||||
|
||||
let array = stmt.newBindingParamsArray();
|
||||
let bp = array.newBindingParams();
|
||||
|
||||
// Check variant binding.
|
||||
expectError(Cr.NS_ERROR_INVALID_ARG,
|
||||
() => bp.bindByIndex(1, INTEGER));
|
||||
// Check blob binding.
|
||||
expectError(Cr.NS_ERROR_INVALID_ARG,
|
||||
() => bp.bindBlobByIndex(1, BLOB, BLOB.length));
|
||||
|
||||
stmt.finalize();
|
||||
run_next_test();
|
||||
}
|
||||
test_bind_out_of_bounds_sync_immediate.syncOnly = true;
|
||||
|
||||
/**
|
||||
* Verify that a mozIStorageAsyncStatement reports an error asynchronously when
|
||||
* we bind to an illegal index.
|
||||
*/
|
||||
function test_bind_out_of_bounds_async_deferred()
|
||||
{
|
||||
let stmt = makeTestStatement(
|
||||
"INSERT INTO test (id) " +
|
||||
"VALUES (?)"
|
||||
);
|
||||
|
||||
let array = stmt.newBindingParamsArray();
|
||||
let bp = array.newBindingParams();
|
||||
|
||||
// There is no difference between variant and blob binding for async purposes.
|
||||
bp.bindByIndex(1, INTEGER);
|
||||
array.addParams(bp);
|
||||
stmt.bindParameters(array);
|
||||
execAsync(stmt, {error: Ci.mozIStorageError.RANGE});
|
||||
|
||||
stmt.finalize();
|
||||
run_next_test();
|
||||
}
|
||||
test_bind_out_of_bounds_async_deferred.asyncOnly = true;
|
||||
|
||||
function test_bind_no_such_name_sync_immediate()
|
||||
{
|
||||
let stmt = makeTestStatement(
|
||||
"INSERT INTO test (id) " +
|
||||
"VALUES (:foo)"
|
||||
);
|
||||
|
||||
let array = stmt.newBindingParamsArray();
|
||||
let bp = array.newBindingParams();
|
||||
|
||||
// Check variant binding.
|
||||
expectError(Cr.NS_ERROR_INVALID_ARG,
|
||||
() => bp.bindByName("doesnotexist", INTEGER));
|
||||
// Check blob binding.
|
||||
expectError(Cr.NS_ERROR_INVALID_ARG,
|
||||
() => bp.bindBlobByName("doesnotexist", BLOB, BLOB.length));
|
||||
|
||||
stmt.finalize();
|
||||
run_next_test();
|
||||
}
|
||||
test_bind_no_such_name_sync_immediate.syncOnly = true;
|
||||
|
||||
function test_bind_no_such_name_async_deferred()
|
||||
{
|
||||
let stmt = makeTestStatement(
|
||||
"INSERT INTO test (id) " +
|
||||
"VALUES (:foo)"
|
||||
);
|
||||
|
||||
let array = stmt.newBindingParamsArray();
|
||||
let bp = array.newBindingParams();
|
||||
|
||||
bp.bindByName("doesnotexist", INTEGER);
|
||||
array.addParams(bp);
|
||||
stmt.bindParameters(array);
|
||||
execAsync(stmt, {error: Ci.mozIStorageError.RANGE});
|
||||
|
||||
stmt.finalize();
|
||||
run_next_test();
|
||||
}
|
||||
test_bind_no_such_name_async_deferred.asyncOnly = true;
|
||||
|
||||
function test_bind_bogus_type_by_index()
|
||||
{
|
||||
// We try to bind a JS Object here that should fail to bind.
|
||||
let stmt = makeTestStatement(
|
||||
"INSERT INTO test (blober) " +
|
||||
"VALUES (?)"
|
||||
);
|
||||
|
||||
let array = stmt.newBindingParamsArray();
|
||||
let bp = array.newBindingParams();
|
||||
Assert.throws(() => bp.bindByIndex(0, run_test), /NS_ERROR_UNEXPECTED/);
|
||||
|
||||
stmt.finalize();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function test_bind_bogus_type_by_name()
|
||||
{
|
||||
// We try to bind a JS Object here that should fail to bind.
|
||||
let stmt = makeTestStatement(
|
||||
"INSERT INTO test (blober) " +
|
||||
"VALUES (:blob)"
|
||||
);
|
||||
|
||||
let array = stmt.newBindingParamsArray();
|
||||
let bp = array.newBindingParams();
|
||||
Assert.throws(() => bp.bindByName("blob", run_test), /NS_ERROR_UNEXPECTED/);
|
||||
|
||||
stmt.finalize();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function test_bind_params_already_locked()
|
||||
{
|
||||
let stmt = makeTestStatement(
|
||||
"INSERT INTO test (id) " +
|
||||
"VALUES (:int)"
|
||||
);
|
||||
|
||||
let array = stmt.newBindingParamsArray();
|
||||
let bp = array.newBindingParams();
|
||||
bp.bindByName("int", INTEGER);
|
||||
array.addParams(bp);
|
||||
|
||||
// We should get an error after we call addParams and try to bind again.
|
||||
expectError(Cr.NS_ERROR_UNEXPECTED,
|
||||
() => bp.bindByName("int", INTEGER));
|
||||
|
||||
stmt.finalize();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function test_bind_params_array_already_locked()
|
||||
{
|
||||
let stmt = makeTestStatement(
|
||||
"INSERT INTO test (id) " +
|
||||
"VALUES (:int)"
|
||||
);
|
||||
|
||||
let array = stmt.newBindingParamsArray();
|
||||
let bp1 = array.newBindingParams();
|
||||
bp1.bindByName("int", INTEGER);
|
||||
array.addParams(bp1);
|
||||
let bp2 = array.newBindingParams();
|
||||
stmt.bindParameters(array);
|
||||
bp2.bindByName("int", INTEGER);
|
||||
|
||||
// We should get an error after we have bound the array to the statement.
|
||||
expectError(Cr.NS_ERROR_UNEXPECTED,
|
||||
() => array.addParams(bp2));
|
||||
|
||||
stmt.finalize();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function test_no_binding_params_from_locked_array()
|
||||
{
|
||||
let stmt = makeTestStatement(
|
||||
"INSERT INTO test (id) " +
|
||||
"VALUES (:int)"
|
||||
);
|
||||
|
||||
let array = stmt.newBindingParamsArray();
|
||||
let bp = array.newBindingParams();
|
||||
bp.bindByName("int", INTEGER);
|
||||
array.addParams(bp);
|
||||
stmt.bindParameters(array);
|
||||
|
||||
// We should not be able to get a new BindingParams object after we have bound
|
||||
// to the statement.
|
||||
expectError(Cr.NS_ERROR_UNEXPECTED,
|
||||
() => array.newBindingParams());
|
||||
|
||||
stmt.finalize();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function test_not_right_owning_array()
|
||||
{
|
||||
let stmt = makeTestStatement(
|
||||
"INSERT INTO test (id) " +
|
||||
"VALUES (:int)"
|
||||
);
|
||||
|
||||
let array1 = stmt.newBindingParamsArray();
|
||||
let array2 = stmt.newBindingParamsArray();
|
||||
let bp = array1.newBindingParams();
|
||||
bp.bindByName("int", INTEGER);
|
||||
|
||||
// We should not be able to add bp to array2 since it was created from array1.
|
||||
expectError(Cr.NS_ERROR_UNEXPECTED,
|
||||
() => array2.addParams(bp));
|
||||
|
||||
stmt.finalize();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function test_not_right_owning_statement()
|
||||
{
|
||||
let stmt1 = makeTestStatement(
|
||||
"INSERT INTO test (id) " +
|
||||
"VALUES (:int)"
|
||||
);
|
||||
let stmt2 = makeTestStatement(
|
||||
"INSERT INTO test (id) " +
|
||||
"VALUES (:int)"
|
||||
);
|
||||
|
||||
let array1 = stmt1.newBindingParamsArray();
|
||||
let array2 = stmt2.newBindingParamsArray();
|
||||
let bp = array1.newBindingParams();
|
||||
bp.bindByName("int", INTEGER);
|
||||
array1.addParams(bp);
|
||||
|
||||
// We should not be able to bind array1 since it was created from stmt1.
|
||||
expectError(Cr.NS_ERROR_UNEXPECTED,
|
||||
() => stmt2.bindParameters(array1));
|
||||
|
||||
stmt1.finalize();
|
||||
stmt2.finalize();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function test_bind_empty_array()
|
||||
{
|
||||
let stmt = makeTestStatement(
|
||||
"INSERT INTO test (id) " +
|
||||
"VALUES (:int)"
|
||||
);
|
||||
|
||||
let paramsArray = stmt.newBindingParamsArray();
|
||||
|
||||
// We should not be able to bind this array to the statement because it is
|
||||
// empty.
|
||||
expectError(Cr.NS_ERROR_UNEXPECTED,
|
||||
() => stmt.bindParameters(paramsArray));
|
||||
|
||||
stmt.finalize();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function test_multiple_results()
|
||||
{
|
||||
let expectedResults = getTableRowCount("test");
|
||||
// Sanity check - we should have more than one result, but let's be sure.
|
||||
do_check_true(expectedResults > 1);
|
||||
|
||||
// Now check that we get back two rows of data from our async query.
|
||||
let stmt = makeTestStatement("SELECT * FROM test");
|
||||
execAsync(stmt, {}, expectedResults);
|
||||
|
||||
stmt.finalize();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
// Test Runner
|
||||
|
||||
const TEST_PASS_SYNC = 0;
|
||||
const TEST_PASS_ASYNC = 1;
|
||||
/**
|
||||
* We run 2 passes against the test. One where makeTestStatement generates
|
||||
* synchronous (mozIStorageStatement) statements and one where it generates
|
||||
* asynchronous (mozIStorageAsyncStatement) statements.
|
||||
*
|
||||
* Because of differences in the ability to know the number of parameters before
|
||||
* dispatching, some tests are sync/async specific. These functions are marked
|
||||
* with 'syncOnly' or 'asyncOnly' attributes and run_next_test knows what to do.
|
||||
*/
|
||||
var testPass = TEST_PASS_SYNC;
|
||||
|
||||
/**
|
||||
* Create a statement of the type under test per testPass.
|
||||
*
|
||||
* @param aSQL
|
||||
* The SQL string from which to build a statement.
|
||||
* @return a statement of the type under test per testPass.
|
||||
*/
|
||||
function makeTestStatement(aSQL) {
|
||||
if (testPass == TEST_PASS_SYNC) {
|
||||
return getOpenedDatabase().createStatement(aSQL);
|
||||
}
|
||||
return getOpenedDatabase().createAsyncStatement(aSQL);
|
||||
}
|
||||
|
||||
var tests = [
|
||||
test_illegal_sql_async_deferred,
|
||||
test_create_table,
|
||||
test_add_data,
|
||||
test_get_data,
|
||||
test_tuple_out_of_bounds,
|
||||
test_no_listener_works_on_success,
|
||||
test_no_listener_works_on_results,
|
||||
test_no_listener_works_on_error,
|
||||
test_partial_listener_works,
|
||||
test_immediate_cancellation,
|
||||
test_double_cancellation,
|
||||
test_cancellation_after_execution,
|
||||
test_double_execute,
|
||||
test_finalized_statement_does_not_crash,
|
||||
test_bind_direct_binding_params_by_index,
|
||||
test_bind_direct_binding_params_by_name,
|
||||
test_bind_js_params_helper_by_index,
|
||||
test_bind_js_params_helper_by_name,
|
||||
test_bind_multiple_rows_by_index,
|
||||
test_bind_multiple_rows_by_name,
|
||||
test_bind_out_of_bounds_sync_immediate,
|
||||
test_bind_out_of_bounds_async_deferred,
|
||||
test_bind_no_such_name_sync_immediate,
|
||||
test_bind_no_such_name_async_deferred,
|
||||
test_bind_bogus_type_by_index,
|
||||
test_bind_bogus_type_by_name,
|
||||
test_bind_params_already_locked,
|
||||
test_bind_params_array_already_locked,
|
||||
test_bind_empty_array,
|
||||
test_no_binding_params_from_locked_array,
|
||||
test_not_right_owning_array,
|
||||
test_not_right_owning_statement,
|
||||
test_multiple_results,
|
||||
];
|
||||
var index = 0;
|
||||
|
||||
const STARTING_UNIQUE_ID = 2;
|
||||
var nextUniqueId = STARTING_UNIQUE_ID;
|
||||
|
||||
function run_next_test()
|
||||
{
|
||||
function _run_next_test() {
|
||||
// use a loop so we can skip tests...
|
||||
while (index < tests.length) {
|
||||
let test = tests[index++];
|
||||
// skip tests not appropriate to the current test pass
|
||||
if ((testPass == TEST_PASS_SYNC && ("asyncOnly" in test)) ||
|
||||
(testPass == TEST_PASS_ASYNC && ("syncOnly" in test)))
|
||||
continue;
|
||||
|
||||
// Asynchronous tests means that exceptions don't kill the test.
|
||||
try {
|
||||
print("****** Running the next test: " + test.name);
|
||||
test();
|
||||
return;
|
||||
}
|
||||
catch (e) {
|
||||
do_throw(e);
|
||||
}
|
||||
}
|
||||
|
||||
// if we only completed the first pass, move to the next pass
|
||||
if (testPass == TEST_PASS_SYNC) {
|
||||
print("********* Beginning mozIStorageAsyncStatement pass.");
|
||||
testPass++;
|
||||
index = 0;
|
||||
// a new pass demands a new database
|
||||
asyncCleanup();
|
||||
nextUniqueId = STARTING_UNIQUE_ID;
|
||||
_run_next_test();
|
||||
return;
|
||||
}
|
||||
|
||||
// we did some async stuff; we need to clean up.
|
||||
asyncCleanup();
|
||||
do_test_finished();
|
||||
}
|
||||
|
||||
// Don't actually schedule another test if we're quitting.
|
||||
if (!_quit) {
|
||||
// For saner stacks, we execute this code RSN.
|
||||
do_execute_soon(_run_next_test);
|
||||
}
|
||||
}
|
||||
|
||||
function run_test()
|
||||
{
|
||||
cleanup();
|
||||
|
||||
do_test_pending();
|
||||
run_next_test();
|
||||
}
|
||||
167
storage/test/unit/test_statement_wrapper_automatically.js
Normal file
167
storage/test/unit/test_statement_wrapper_automatically.js
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
|
||||
vim:set ts=2 sw=2 sts=2 et:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// This file tests the functions of mozIStorageStatementWrapper
|
||||
|
||||
function setup()
|
||||
{
|
||||
getOpenedDatabase().createTable("test", "id INTEGER PRIMARY KEY, val NONE," +
|
||||
"alt_val NONE");
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience wrapper for do_check_eq. Calls do_check_eq on aActualVal
|
||||
* and aReturnedVal, with one caveat.
|
||||
*
|
||||
* Date objects are converted before parameter binding to PRTime's (microsecs
|
||||
* since epoch). They are not reconverted when retrieved from the database.
|
||||
* This function abstracts away this reconversion so that you can pass in,
|
||||
* for example:
|
||||
*
|
||||
* checkVal(new Date(), aReturnedVal) // this
|
||||
* checkVal(new Date().valueOf() * 1000.0, aReturnedVal) // instead of this
|
||||
*
|
||||
* Should any other types require conversion in the future, their conversions
|
||||
* may also be abstracted away here.
|
||||
*
|
||||
* @param aActualVal
|
||||
* the value inserted into the database
|
||||
* @param aReturnedVal
|
||||
* the value retrieved from the database
|
||||
*/
|
||||
function checkVal(aActualVal, aReturnedVal)
|
||||
{
|
||||
if (aActualVal instanceof Date) aActualVal = aActualVal.valueOf() * 1000.0;
|
||||
do_check_eq(aActualVal, aReturnedVal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all rows from our test table.
|
||||
*/
|
||||
function clearTable()
|
||||
{
|
||||
var stmt = createStatement("DELETE FROM test");
|
||||
stmt.execute();
|
||||
stmt.finalize();
|
||||
ensureNumRows(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the number of rows in our test table is equal to aNumRows.
|
||||
* Calls do_check_eq on aNumRows and the value retrieved by SELECT'ing COUNT(*).
|
||||
*
|
||||
* @param aNumRows
|
||||
* the number of rows our test table should contain
|
||||
*/
|
||||
function ensureNumRows(aNumRows)
|
||||
{
|
||||
var stmt = createStatement("SELECT COUNT(*) AS number FROM test");
|
||||
do_check_true(stmt.step());
|
||||
do_check_eq(aNumRows, stmt.row.number);
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts aVal into our test table and checks that insertion was successful by
|
||||
* retrieving the newly inserted value from the database and comparing it
|
||||
* against aVal. aVal is bound to a single parameter.
|
||||
*
|
||||
* @param aVal
|
||||
* value to insert into our test table and check
|
||||
*/
|
||||
function insertAndCheckSingleParam(aVal)
|
||||
{
|
||||
clearTable();
|
||||
|
||||
var stmt = createStatement("INSERT INTO test (val) VALUES (:val)");
|
||||
stmt.params.val = aVal;
|
||||
stmt.execute();
|
||||
stmt.finalize();
|
||||
|
||||
ensureNumRows(1);
|
||||
|
||||
stmt = createStatement("SELECT val FROM test WHERE id = 1");
|
||||
do_check_true(stmt.step());
|
||||
checkVal(aVal, stmt.row.val);
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts aVal into our test table and checks that insertion was successful by
|
||||
* retrieving the newly inserted value from the database and comparing it
|
||||
* against aVal. aVal is bound to two separate parameters, both of which are
|
||||
* checked against aVal.
|
||||
*
|
||||
* @param aVal
|
||||
* value to insert into our test table and check
|
||||
*/
|
||||
function insertAndCheckMultipleParams(aVal)
|
||||
{
|
||||
clearTable();
|
||||
|
||||
var stmt = createStatement("INSERT INTO test (val, alt_val) " +
|
||||
"VALUES (:val, :val)");
|
||||
stmt.params.val = aVal;
|
||||
stmt.execute();
|
||||
stmt.finalize();
|
||||
|
||||
ensureNumRows(1);
|
||||
|
||||
stmt = createStatement("SELECT val, alt_val FROM test WHERE id = 1");
|
||||
do_check_true(stmt.step());
|
||||
checkVal(aVal, stmt.row.val);
|
||||
checkVal(aVal, stmt.row.alt_val);
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience function that prints out a description of aVal using
|
||||
* aVal.toString and aVal.toSource. Output is useful when the test fails.
|
||||
*
|
||||
* @param aVal
|
||||
* a value inserted or to be inserted into our test table
|
||||
*/
|
||||
function printValDesc(aVal)
|
||||
{
|
||||
try {
|
||||
var toSource = aVal.toSource();
|
||||
} catch (ex) {
|
||||
toSource = "";
|
||||
}
|
||||
print("Testing value: toString=" + aVal +
|
||||
(toSource ? " toSource=" + toSource : ""));
|
||||
}
|
||||
|
||||
function run_test()
|
||||
{
|
||||
setup();
|
||||
|
||||
// function JSValStorageStatementBinder in
|
||||
// storage/mozStorageStatementParams.cpp tells us that the following types
|
||||
// and only the following types are valid as statement parameters:
|
||||
var vals = [
|
||||
1337, // int
|
||||
3.1337, // double
|
||||
"foo", // string
|
||||
true, // boolean
|
||||
null, // null
|
||||
new Date(), // Date object
|
||||
];
|
||||
|
||||
vals.forEach(function (val)
|
||||
{
|
||||
printValDesc(val);
|
||||
print("Single parameter");
|
||||
insertAndCheckSingleParam(val);
|
||||
print("Multiple parameters");
|
||||
insertAndCheckMultipleParams(val);
|
||||
});
|
||||
|
||||
cleanup();
|
||||
}
|
||||
116
storage/test/unit/test_storage_aggregates.js
Normal file
116
storage/test/unit/test_storage_aggregates.js
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// This file tests the custom aggregate functions
|
||||
|
||||
var testNums = [1, 2, 3, 4];
|
||||
|
||||
function setup()
|
||||
{
|
||||
getOpenedDatabase().createTable("function_tests", "id INTEGER PRIMARY KEY");
|
||||
|
||||
var stmt = createStatement("INSERT INTO function_tests (id) VALUES(?1)");
|
||||
for (let i = 0; i < testNums.length; ++i) {
|
||||
stmt.bindByIndex(0, testNums[i]);
|
||||
stmt.execute();
|
||||
}
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
var testSquareAndSumFunction = {
|
||||
calls: 0,
|
||||
_sas: 0,
|
||||
|
||||
reset() {
|
||||
this.calls = 0;
|
||||
this._sas = 0;
|
||||
},
|
||||
|
||||
onStep(val) {
|
||||
++this.calls;
|
||||
this._sas += val.getInt32(0) * val.getInt32(0);
|
||||
},
|
||||
|
||||
onFinal() {
|
||||
var retval = this._sas;
|
||||
this._sas = 0; // Prepare for next group
|
||||
return retval;
|
||||
}
|
||||
};
|
||||
|
||||
function test_aggregate_registration()
|
||||
{
|
||||
var msc = getOpenedDatabase();
|
||||
msc.createAggregateFunction("test_sas_aggr", 1, testSquareAndSumFunction);
|
||||
}
|
||||
|
||||
function test_aggregate_no_double_registration()
|
||||
{
|
||||
var msc = getOpenedDatabase();
|
||||
try {
|
||||
msc.createAggregateFunction("test_sas_aggr", 2, testSquareAndSumFunction);
|
||||
do_throw("We shouldn't get here!");
|
||||
} catch (e) {
|
||||
do_check_eq(Cr.NS_ERROR_FAILURE, e.result);
|
||||
}
|
||||
}
|
||||
|
||||
function test_aggregate_removal()
|
||||
{
|
||||
var msc = getOpenedDatabase();
|
||||
msc.removeFunction("test_sas_aggr");
|
||||
// Should be Ok now
|
||||
msc.createAggregateFunction("test_sas_aggr", 1, testSquareAndSumFunction);
|
||||
}
|
||||
|
||||
function test_aggregate_no_aliases()
|
||||
{
|
||||
var msc = getOpenedDatabase();
|
||||
try {
|
||||
msc.createAggregateFunction("test_sas_aggr2", 1, testSquareAndSumFunction);
|
||||
do_throw("We shouldn't get here!");
|
||||
} catch (e) {
|
||||
do_check_eq(Cr.NS_ERROR_FAILURE, e.result);
|
||||
}
|
||||
}
|
||||
|
||||
function test_aggregate_call()
|
||||
{
|
||||
var stmt = createStatement("SELECT test_sas_aggr(id) FROM function_tests");
|
||||
while (stmt.executeStep()) {
|
||||
// Do nothing.
|
||||
}
|
||||
do_check_eq(testNums.length, testSquareAndSumFunction.calls);
|
||||
testSquareAndSumFunction.reset();
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
function test_aggregate_result()
|
||||
{
|
||||
var sas = 0;
|
||||
for (var i = 0; i < testNums.length; ++i) {
|
||||
sas += testNums[i] * testNums[i];
|
||||
}
|
||||
var stmt = createStatement("SELECT test_sas_aggr(id) FROM function_tests");
|
||||
stmt.executeStep();
|
||||
do_check_eq(sas, stmt.getInt32(0));
|
||||
testSquareAndSumFunction.reset();
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
var tests = [test_aggregate_registration, test_aggregate_no_double_registration,
|
||||
test_aggregate_removal, test_aggregate_no_aliases, test_aggregate_call,
|
||||
test_aggregate_result];
|
||||
|
||||
function run_test()
|
||||
{
|
||||
setup();
|
||||
|
||||
for (var i = 0; i < tests.length; i++) {
|
||||
tests[i]();
|
||||
}
|
||||
|
||||
cleanup();
|
||||
}
|
||||
763
storage/test/unit/test_storage_connection.js
Normal file
763
storage/test/unit/test_storage_connection.js
Normal file
|
|
@ -0,0 +1,763 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// This file tests the functions of mozIStorageConnection
|
||||
|
||||
// Test Functions
|
||||
|
||||
add_task(function* test_connectionReady_open() {
|
||||
// there doesn't seem to be a way for the connection to not be ready (unless
|
||||
// we close it with mozIStorageConnection::Close(), but we don't for this).
|
||||
// It can only fail if GetPath fails on the database file, or if we run out
|
||||
// of memory trying to use an in-memory database
|
||||
|
||||
var msc = getOpenedDatabase();
|
||||
do_check_true(msc.connectionReady);
|
||||
});
|
||||
|
||||
add_task(function* test_connectionReady_closed() {
|
||||
// This also tests mozIStorageConnection::Close()
|
||||
|
||||
var msc = getOpenedDatabase();
|
||||
msc.close();
|
||||
do_check_false(msc.connectionReady);
|
||||
gDBConn = null; // this is so later tests don't start to fail.
|
||||
});
|
||||
|
||||
add_task(function* test_databaseFile() {
|
||||
var msc = getOpenedDatabase();
|
||||
do_check_true(getTestDB().equals(msc.databaseFile));
|
||||
});
|
||||
|
||||
add_task(function* test_tableExists_not_created() {
|
||||
var msc = getOpenedDatabase();
|
||||
do_check_false(msc.tableExists("foo"));
|
||||
});
|
||||
|
||||
add_task(function* test_indexExists_not_created() {
|
||||
var msc = getOpenedDatabase();
|
||||
do_check_false(msc.indexExists("foo"));
|
||||
});
|
||||
|
||||
add_task(function* test_temp_tableExists_and_indexExists() {
|
||||
var msc = getOpenedDatabase();
|
||||
msc.executeSimpleSQL("CREATE TEMP TABLE test_temp(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)");
|
||||
do_check_true(msc.tableExists("test_temp"));
|
||||
|
||||
msc.executeSimpleSQL("CREATE INDEX test_temp_ind ON test_temp (name)");
|
||||
do_check_true(msc.indexExists("test_temp_ind"));
|
||||
|
||||
msc.executeSimpleSQL("DROP INDEX test_temp_ind");
|
||||
msc.executeSimpleSQL("DROP TABLE test_temp");
|
||||
});
|
||||
|
||||
add_task(function* test_createTable_not_created() {
|
||||
var msc = getOpenedDatabase();
|
||||
msc.createTable("test", "id INTEGER PRIMARY KEY, name TEXT");
|
||||
do_check_true(msc.tableExists("test"));
|
||||
});
|
||||
|
||||
add_task(function* test_indexExists_created() {
|
||||
var msc = getOpenedDatabase();
|
||||
msc.executeSimpleSQL("CREATE INDEX name_ind ON test (name)");
|
||||
do_check_true(msc.indexExists("name_ind"));
|
||||
});
|
||||
|
||||
add_task(function* test_createTable_already_created() {
|
||||
var msc = getOpenedDatabase();
|
||||
do_check_true(msc.tableExists("test"));
|
||||
Assert.throws(() => msc.createTable("test", "id INTEGER PRIMARY KEY, name TEXT"),
|
||||
/NS_ERROR_FAILURE/);
|
||||
});
|
||||
|
||||
add_task(function* test_attach_createTable_tableExists_indexExists() {
|
||||
var msc = getOpenedDatabase();
|
||||
var file = do_get_file("storage_attach.sqlite", true);
|
||||
var msc2 = getDatabase(file);
|
||||
msc.executeSimpleSQL("ATTACH DATABASE '" + file.path + "' AS sample");
|
||||
|
||||
do_check_false(msc.tableExists("sample.test"));
|
||||
msc.createTable("sample.test", "id INTEGER PRIMARY KEY, name TEXT");
|
||||
do_check_true(msc.tableExists("sample.test"));
|
||||
Assert.throws(() => msc.createTable("sample.test", "id INTEGER PRIMARY KEY, name TEXT"),
|
||||
/NS_ERROR_FAILURE/);
|
||||
|
||||
do_check_false(msc.indexExists("sample.test_ind"));
|
||||
msc.executeSimpleSQL("CREATE INDEX sample.test_ind ON test (name)");
|
||||
do_check_true(msc.indexExists("sample.test_ind"));
|
||||
|
||||
msc.executeSimpleSQL("DETACH DATABASE sample");
|
||||
msc2.close();
|
||||
try {
|
||||
file.remove(false);
|
||||
} catch (e) {
|
||||
// Do nothing.
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function* test_lastInsertRowID() {
|
||||
var msc = getOpenedDatabase();
|
||||
msc.executeSimpleSQL("INSERT INTO test (name) VALUES ('foo')");
|
||||
do_check_eq(1, msc.lastInsertRowID);
|
||||
});
|
||||
|
||||
add_task(function* test_transactionInProgress_no() {
|
||||
var msc = getOpenedDatabase();
|
||||
do_check_false(msc.transactionInProgress);
|
||||
});
|
||||
|
||||
add_task(function* test_transactionInProgress_yes() {
|
||||
var msc = getOpenedDatabase();
|
||||
msc.beginTransaction();
|
||||
do_check_true(msc.transactionInProgress);
|
||||
msc.commitTransaction();
|
||||
do_check_false(msc.transactionInProgress);
|
||||
|
||||
msc.beginTransaction();
|
||||
do_check_true(msc.transactionInProgress);
|
||||
msc.rollbackTransaction();
|
||||
do_check_false(msc.transactionInProgress);
|
||||
});
|
||||
|
||||
add_task(function* test_commitTransaction_no_transaction() {
|
||||
var msc = getOpenedDatabase();
|
||||
do_check_false(msc.transactionInProgress);
|
||||
Assert.throws(() => msc.commitTransaction(), /NS_ERROR_UNEXPECTED/);
|
||||
});
|
||||
|
||||
add_task(function* test_rollbackTransaction_no_transaction() {
|
||||
var msc = getOpenedDatabase();
|
||||
do_check_false(msc.transactionInProgress);
|
||||
Assert.throws(() => msc.rollbackTransaction(), /NS_ERROR_UNEXPECTED/);
|
||||
});
|
||||
|
||||
add_task(function* test_get_schemaVersion_not_set() {
|
||||
do_check_eq(0, getOpenedDatabase().schemaVersion);
|
||||
});
|
||||
|
||||
add_task(function* test_set_schemaVersion() {
|
||||
var msc = getOpenedDatabase();
|
||||
const version = 1;
|
||||
msc.schemaVersion = version;
|
||||
do_check_eq(version, msc.schemaVersion);
|
||||
});
|
||||
|
||||
add_task(function* test_set_schemaVersion_same() {
|
||||
var msc = getOpenedDatabase();
|
||||
const version = 1;
|
||||
msc.schemaVersion = version; // should still work ok
|
||||
do_check_eq(version, msc.schemaVersion);
|
||||
});
|
||||
|
||||
add_task(function* test_set_schemaVersion_negative() {
|
||||
var msc = getOpenedDatabase();
|
||||
const version = -1;
|
||||
msc.schemaVersion = version;
|
||||
do_check_eq(version, msc.schemaVersion);
|
||||
});
|
||||
|
||||
add_task(function* test_createTable() {
|
||||
var temp = getTestDB().parent;
|
||||
temp.append("test_db_table");
|
||||
try {
|
||||
var con = getService().openDatabase(temp);
|
||||
con.createTable("a", "");
|
||||
} catch (e) {
|
||||
if (temp.exists()) {
|
||||
try {
|
||||
temp.remove(false);
|
||||
} catch (e2) {
|
||||
// Do nothing.
|
||||
}
|
||||
}
|
||||
do_check_true(e.result == Cr.NS_ERROR_NOT_INITIALIZED ||
|
||||
e.result == Cr.NS_ERROR_FAILURE);
|
||||
} finally {
|
||||
if (con) {
|
||||
con.close();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function* test_defaultSynchronousAtNormal() {
|
||||
var msc = getOpenedDatabase();
|
||||
var stmt = createStatement("PRAGMA synchronous;");
|
||||
try {
|
||||
stmt.executeStep();
|
||||
do_check_eq(1, stmt.getInt32(0));
|
||||
}
|
||||
finally {
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
}
|
||||
});
|
||||
|
||||
// must be ran before executeAsync tests
|
||||
add_task(function* test_close_does_not_spin_event_loop() {
|
||||
// We want to make sure that the event loop on the calling thread does not
|
||||
// spin when close is called.
|
||||
let event = {
|
||||
ran: false,
|
||||
run() {
|
||||
this.ran = true;
|
||||
},
|
||||
};
|
||||
|
||||
// Post the event before we call close, so it would run if the event loop was
|
||||
// spun during close.
|
||||
let thread = Cc["@mozilla.org/thread-manager;1"].
|
||||
getService(Ci.nsIThreadManager).
|
||||
currentThread;
|
||||
thread.dispatch(event, Ci.nsIThread.DISPATCH_NORMAL);
|
||||
|
||||
// Sanity check, then close the database. Afterwards, we should not have ran!
|
||||
do_check_false(event.ran);
|
||||
getOpenedDatabase().close();
|
||||
do_check_false(event.ran);
|
||||
|
||||
// Reset gDBConn so that later tests will get a new connection object.
|
||||
gDBConn = null;
|
||||
});
|
||||
|
||||
add_task(function* test_asyncClose_succeeds_with_finalized_async_statement() {
|
||||
// XXX this test isn't perfect since we can't totally control when events will
|
||||
// run. If this paticular function fails randomly, it means we have a
|
||||
// real bug.
|
||||
|
||||
// We want to make sure we create a cached async statement to make sure that
|
||||
// when we finalize our statement, we end up finalizing the async one too so
|
||||
// close will succeed.
|
||||
let stmt = createStatement("SELECT * FROM test");
|
||||
stmt.executeAsync();
|
||||
stmt.finalize();
|
||||
|
||||
yield asyncClose(getOpenedDatabase());
|
||||
// Reset gDBConn so that later tests will get a new connection object.
|
||||
gDBConn = null;
|
||||
});
|
||||
|
||||
add_task(function* test_close_then_release_statement() {
|
||||
// Testing the behavior in presence of a bad client that finalizes
|
||||
// statements after the database has been closed (typically by
|
||||
// letting the gc finalize the statement).
|
||||
let db = getOpenedDatabase();
|
||||
let stmt = createStatement("SELECT * FROM test -- test_close_then_release_statement");
|
||||
db.close();
|
||||
stmt.finalize(); // Finalize too late - this should not crash
|
||||
|
||||
// Reset gDBConn so that later tests will get a new connection object.
|
||||
gDBConn = null;
|
||||
});
|
||||
|
||||
add_task(function* test_asyncClose_then_release_statement() {
|
||||
// Testing the behavior in presence of a bad client that finalizes
|
||||
// statements after the database has been async closed (typically by
|
||||
// letting the gc finalize the statement).
|
||||
let db = getOpenedDatabase();
|
||||
let stmt = createStatement("SELECT * FROM test -- test_asyncClose_then_release_statement");
|
||||
yield asyncClose(db);
|
||||
stmt.finalize(); // Finalize too late - this should not crash
|
||||
|
||||
// Reset gDBConn so that later tests will get a new connection object.
|
||||
gDBConn = null;
|
||||
});
|
||||
|
||||
add_task(function* test_close_fails_with_async_statement_ran() {
|
||||
let deferred = Promise.defer();
|
||||
let stmt = createStatement("SELECT * FROM test");
|
||||
stmt.executeAsync();
|
||||
stmt.finalize();
|
||||
|
||||
let db = getOpenedDatabase();
|
||||
Assert.throws(() => db.close(), /NS_ERROR_UNEXPECTED/);
|
||||
|
||||
// Clean up after ourselves.
|
||||
db.asyncClose(function () {
|
||||
// Reset gDBConn so that later tests will get a new connection object.
|
||||
gDBConn = null;
|
||||
deferred.resolve();
|
||||
});
|
||||
|
||||
yield deferred.promise;
|
||||
});
|
||||
|
||||
add_task(function* test_clone_optional_param() {
|
||||
let db1 = getService().openUnsharedDatabase(getTestDB());
|
||||
let db2 = db1.clone();
|
||||
do_check_true(db2.connectionReady);
|
||||
|
||||
// A write statement should not fail here.
|
||||
let stmt = db2.createStatement("INSERT INTO test (name) VALUES (:name)");
|
||||
stmt.params.name = "dwitte";
|
||||
stmt.execute();
|
||||
stmt.finalize();
|
||||
|
||||
// And a read statement should succeed.
|
||||
stmt = db2.createStatement("SELECT * FROM test");
|
||||
do_check_true(stmt.executeStep());
|
||||
stmt.finalize();
|
||||
|
||||
// Additionally check that it is a connection on the same database.
|
||||
do_check_true(db1.databaseFile.equals(db2.databaseFile));
|
||||
|
||||
db1.close();
|
||||
db2.close();
|
||||
});
|
||||
|
||||
function* standardAsyncTest(promisedDB, name, shouldInit = false) {
|
||||
do_print("Performing standard async test " + name);
|
||||
|
||||
let adb = yield promisedDB;
|
||||
do_check_true(adb instanceof Ci.mozIStorageAsyncConnection);
|
||||
do_check_false(adb instanceof Ci.mozIStorageConnection);
|
||||
|
||||
if (shouldInit) {
|
||||
let stmt = adb.createAsyncStatement("CREATE TABLE test(name TEXT)");
|
||||
yield executeAsync(stmt);
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
// Generate a name to insert and fetch back
|
||||
name = "worker bee " + Math.random() + " (" + name + ")";
|
||||
|
||||
let stmt = adb.createAsyncStatement("INSERT INTO test (name) VALUES (:name)");
|
||||
stmt.params.name = name;
|
||||
let result = yield executeAsync(stmt);
|
||||
do_print("Request complete");
|
||||
stmt.finalize();
|
||||
do_check_true(Components.isSuccessCode(result));
|
||||
do_print("Extracting data");
|
||||
stmt = adb.createAsyncStatement("SELECT * FROM test");
|
||||
let found = false;
|
||||
yield executeAsync(stmt, function (results) {
|
||||
do_print("Data has been extracted");
|
||||
for (let row = results.getNextRow(); row != null; row = results.getNextRow()) {
|
||||
if (row.getResultByName("name") == name) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
do_check_true(found);
|
||||
stmt.finalize();
|
||||
yield asyncClose(adb);
|
||||
|
||||
do_print("Standard async test " + name + " complete");
|
||||
}
|
||||
|
||||
add_task(function* test_open_async() {
|
||||
yield standardAsyncTest(openAsyncDatabase(getTestDB(), null), "default");
|
||||
yield standardAsyncTest(openAsyncDatabase(getTestDB()), "no optional arg");
|
||||
yield standardAsyncTest(openAsyncDatabase(getTestDB(),
|
||||
{shared: false, growthIncrement: 54}), "non-default options");
|
||||
yield standardAsyncTest(openAsyncDatabase("memory"),
|
||||
"in-memory database", true);
|
||||
yield standardAsyncTest(openAsyncDatabase("memory",
|
||||
{shared: false}),
|
||||
"in-memory database and options", true);
|
||||
|
||||
do_print("Testing async opening with bogus options 0");
|
||||
let raised = false;
|
||||
let adb = null;
|
||||
|
||||
try {
|
||||
adb = yield openAsyncDatabase("memory", {shared: false, growthIncrement: 54});
|
||||
} catch (ex) {
|
||||
raised = true;
|
||||
} finally {
|
||||
if (adb) {
|
||||
yield asyncClose(adb);
|
||||
}
|
||||
}
|
||||
do_check_true(raised);
|
||||
|
||||
do_print("Testing async opening with bogus options 1");
|
||||
raised = false;
|
||||
adb = null;
|
||||
try {
|
||||
adb = yield openAsyncDatabase(getTestDB(), {shared: "forty-two"});
|
||||
} catch (ex) {
|
||||
raised = true;
|
||||
} finally {
|
||||
if (adb) {
|
||||
yield asyncClose(adb);
|
||||
}
|
||||
}
|
||||
do_check_true(raised);
|
||||
|
||||
do_print("Testing async opening with bogus options 2");
|
||||
raised = false;
|
||||
adb = null;
|
||||
try {
|
||||
adb = yield openAsyncDatabase(getTestDB(), {growthIncrement: "forty-two"});
|
||||
} catch (ex) {
|
||||
raised = true;
|
||||
} finally {
|
||||
if (adb) {
|
||||
yield asyncClose(adb);
|
||||
}
|
||||
}
|
||||
do_check_true(raised);
|
||||
});
|
||||
|
||||
|
||||
add_task(function* test_async_open_with_shared_cache() {
|
||||
do_print("Testing that opening with a shared cache doesn't break stuff");
|
||||
let adb = yield openAsyncDatabase(getTestDB(), {shared: true});
|
||||
|
||||
let stmt = adb.createAsyncStatement("INSERT INTO test (name) VALUES (:name)");
|
||||
stmt.params.name = "clockworker";
|
||||
let result = yield executeAsync(stmt);
|
||||
do_print("Request complete");
|
||||
stmt.finalize();
|
||||
do_check_true(Components.isSuccessCode(result));
|
||||
do_print("Extracting data");
|
||||
stmt = adb.createAsyncStatement("SELECT * FROM test");
|
||||
let found = false;
|
||||
yield executeAsync(stmt, function (results) {
|
||||
do_print("Data has been extracted");
|
||||
for (let row = results.getNextRow(); row != null; row = results.getNextRow()) {
|
||||
if (row.getResultByName("name") == "clockworker") {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
do_check_true(found);
|
||||
stmt.finalize();
|
||||
yield asyncClose(adb);
|
||||
});
|
||||
|
||||
add_task(function* test_clone_trivial_async() {
|
||||
do_print("Open connection");
|
||||
let db = getService().openDatabase(getTestDB());
|
||||
do_check_true(db instanceof Ci.mozIStorageAsyncConnection);
|
||||
do_print("AsyncClone connection");
|
||||
let clone = yield asyncClone(db, true);
|
||||
do_check_true(clone instanceof Ci.mozIStorageAsyncConnection);
|
||||
do_print("Close connection");
|
||||
yield asyncClose(db);
|
||||
do_print("Close clone");
|
||||
yield asyncClose(clone);
|
||||
});
|
||||
|
||||
add_task(function* test_clone_no_optional_param_async() {
|
||||
"use strict";
|
||||
do_print("Testing async cloning");
|
||||
let adb1 = yield openAsyncDatabase(getTestDB(), null);
|
||||
do_check_true(adb1 instanceof Ci.mozIStorageAsyncConnection);
|
||||
|
||||
do_print("Cloning database");
|
||||
|
||||
let adb2 = yield asyncClone(adb1);
|
||||
do_print("Testing that the cloned db is a mozIStorageAsyncConnection " +
|
||||
"and not a mozIStorageConnection");
|
||||
do_check_true(adb2 instanceof Ci.mozIStorageAsyncConnection);
|
||||
do_check_false(adb2 instanceof Ci.mozIStorageConnection);
|
||||
|
||||
do_print("Inserting data into source db");
|
||||
let stmt = adb1.
|
||||
createAsyncStatement("INSERT INTO test (name) VALUES (:name)");
|
||||
|
||||
stmt.params.name = "yoric";
|
||||
let result = yield executeAsync(stmt);
|
||||
do_print("Request complete");
|
||||
stmt.finalize();
|
||||
do_check_true(Components.isSuccessCode(result));
|
||||
do_print("Extracting data from clone db");
|
||||
stmt = adb2.createAsyncStatement("SELECT * FROM test");
|
||||
let found = false;
|
||||
yield executeAsync(stmt, function (results) {
|
||||
do_print("Data has been extracted");
|
||||
for (let row = results.getNextRow(); row != null; row = results.getNextRow()) {
|
||||
if (row.getResultByName("name") == "yoric") {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
do_check_true(found);
|
||||
stmt.finalize();
|
||||
do_print("Closing databases");
|
||||
yield asyncClose(adb2);
|
||||
do_print("First db closed");
|
||||
|
||||
yield asyncClose(adb1);
|
||||
do_print("Second db closed");
|
||||
});
|
||||
|
||||
add_task(function* test_clone_readonly() {
|
||||
let db1 = getService().openUnsharedDatabase(getTestDB());
|
||||
let db2 = db1.clone(true);
|
||||
do_check_true(db2.connectionReady);
|
||||
|
||||
// A write statement should fail here.
|
||||
let stmt = db2.createStatement("INSERT INTO test (name) VALUES (:name)");
|
||||
stmt.params.name = "reed";
|
||||
expectError(Cr.NS_ERROR_FILE_READ_ONLY, () => stmt.execute());
|
||||
stmt.finalize();
|
||||
|
||||
// And a read statement should succeed.
|
||||
stmt = db2.createStatement("SELECT * FROM test");
|
||||
do_check_true(stmt.executeStep());
|
||||
stmt.finalize();
|
||||
|
||||
db1.close();
|
||||
db2.close();
|
||||
});
|
||||
|
||||
add_task(function* test_clone_shared_readonly() {
|
||||
let db1 = getService().openDatabase(getTestDB());
|
||||
let db2 = db1.clone(true);
|
||||
do_check_true(db2.connectionReady);
|
||||
|
||||
let stmt = db2.createStatement("INSERT INTO test (name) VALUES (:name)");
|
||||
stmt.params.name = "parker";
|
||||
// TODO currently SQLite does not actually work correctly here. The behavior
|
||||
// we want is commented out, and the current behavior is being tested
|
||||
// for. Our IDL comments will have to be updated when this starts to
|
||||
// work again.
|
||||
stmt.execute();
|
||||
// expectError(Components.results.NS_ERROR_FILE_READ_ONLY, () => stmt.execute());
|
||||
stmt.finalize();
|
||||
|
||||
// And a read statement should succeed.
|
||||
stmt = db2.createStatement("SELECT * FROM test");
|
||||
do_check_true(stmt.executeStep());
|
||||
stmt.finalize();
|
||||
|
||||
db1.close();
|
||||
db2.close();
|
||||
});
|
||||
|
||||
add_task(function* test_close_clone_fails() {
|
||||
let calls = [
|
||||
"openDatabase",
|
||||
"openUnsharedDatabase",
|
||||
];
|
||||
calls.forEach(function (methodName) {
|
||||
let db = getService()[methodName](getTestDB());
|
||||
db.close();
|
||||
expectError(Cr.NS_ERROR_NOT_INITIALIZED, () => db.clone());
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* test_memory_clone_fails() {
|
||||
let db = getService().openSpecialDatabase("memory");
|
||||
db.close();
|
||||
expectError(Cr.NS_ERROR_NOT_INITIALIZED, () => db.clone());
|
||||
});
|
||||
|
||||
add_task(function* test_clone_copies_functions() {
|
||||
const FUNC_NAME = "test_func";
|
||||
let calls = [
|
||||
"openDatabase",
|
||||
"openUnsharedDatabase",
|
||||
];
|
||||
let functionMethods = [
|
||||
"createFunction",
|
||||
"createAggregateFunction",
|
||||
];
|
||||
calls.forEach(function (methodName) {
|
||||
[true, false].forEach(function (readOnly) {
|
||||
functionMethods.forEach(function (functionMethod) {
|
||||
let db1 = getService()[methodName](getTestDB());
|
||||
// Create a function for db1.
|
||||
db1[functionMethod](FUNC_NAME, 1, {
|
||||
onFunctionCall: () => 0,
|
||||
onStep: () => 0,
|
||||
onFinal: () => 0,
|
||||
});
|
||||
|
||||
// Clone it, and make sure the function exists still.
|
||||
let db2 = db1.clone(readOnly);
|
||||
// Note: this would fail if the function did not exist.
|
||||
let stmt = db2.createStatement("SELECT " + FUNC_NAME + "(id) FROM test");
|
||||
stmt.finalize();
|
||||
db1.close();
|
||||
db2.close();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* test_clone_copies_overridden_functions() {
|
||||
const FUNC_NAME = "lower";
|
||||
function test_func() {
|
||||
this.called = false;
|
||||
}
|
||||
test_func.prototype = {
|
||||
onFunctionCall() {
|
||||
this.called = true;
|
||||
},
|
||||
onStep() {
|
||||
this.called = true;
|
||||
},
|
||||
onFinal: () => 0,
|
||||
};
|
||||
|
||||
let calls = [
|
||||
"openDatabase",
|
||||
"openUnsharedDatabase",
|
||||
];
|
||||
let functionMethods = [
|
||||
"createFunction",
|
||||
"createAggregateFunction",
|
||||
];
|
||||
calls.forEach(function (methodName) {
|
||||
[true, false].forEach(function (readOnly) {
|
||||
functionMethods.forEach(function (functionMethod) {
|
||||
let db1 = getService()[methodName](getTestDB());
|
||||
// Create a function for db1.
|
||||
let func = new test_func();
|
||||
db1[functionMethod](FUNC_NAME, 1, func);
|
||||
do_check_false(func.called);
|
||||
|
||||
// Clone it, and make sure the function gets called.
|
||||
let db2 = db1.clone(readOnly);
|
||||
let stmt = db2.createStatement("SELECT " + FUNC_NAME + "(id) FROM test");
|
||||
stmt.executeStep();
|
||||
do_check_true(func.called);
|
||||
stmt.finalize();
|
||||
db1.close();
|
||||
db2.close();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* test_clone_copies_pragmas() {
|
||||
const PRAGMAS = [
|
||||
{ name: "cache_size", value: 500, copied: true },
|
||||
{ name: "temp_store", value: 2, copied: true },
|
||||
{ name: "foreign_keys", value: 1, copied: true },
|
||||
{ name: "journal_size_limit", value: 524288, copied: true },
|
||||
{ name: "synchronous", value: 2, copied: true },
|
||||
{ name: "wal_autocheckpoint", value: 16, copied: true },
|
||||
{ name: "busy_timeout", value: 50, copied: true },
|
||||
{ name: "ignore_check_constraints", value: 1, copied: false },
|
||||
];
|
||||
|
||||
let db1 = getService().openUnsharedDatabase(getTestDB());
|
||||
|
||||
// Sanity check initial values are different from enforced ones.
|
||||
PRAGMAS.forEach(function (pragma) {
|
||||
let stmt = db1.createStatement("PRAGMA " + pragma.name);
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_neq(pragma.value, stmt.getInt32(0));
|
||||
stmt.finalize();
|
||||
});
|
||||
// Execute pragmas.
|
||||
PRAGMAS.forEach(function (pragma) {
|
||||
db1.executeSimpleSQL("PRAGMA " + pragma.name + " = " + pragma.value);
|
||||
});
|
||||
|
||||
let db2 = db1.clone();
|
||||
do_check_true(db2.connectionReady);
|
||||
|
||||
// Check cloned connection inherited pragma values.
|
||||
PRAGMAS.forEach(function (pragma) {
|
||||
let stmt = db2.createStatement("PRAGMA " + pragma.name);
|
||||
do_check_true(stmt.executeStep());
|
||||
let validate = pragma.copied ? do_check_eq : do_check_neq;
|
||||
validate(pragma.value, stmt.getInt32(0));
|
||||
stmt.finalize();
|
||||
});
|
||||
|
||||
db1.close();
|
||||
db2.close();
|
||||
});
|
||||
|
||||
add_task(function* test_readonly_clone_copies_pragmas() {
|
||||
const PRAGMAS = [
|
||||
{ name: "cache_size", value: 500, copied: true },
|
||||
{ name: "temp_store", value: 2, copied: true },
|
||||
{ name: "foreign_keys", value: 1, copied: false },
|
||||
{ name: "journal_size_limit", value: 524288, copied: false },
|
||||
{ name: "synchronous", value: 2, copied: false },
|
||||
{ name: "wal_autocheckpoint", value: 16, copied: false },
|
||||
{ name: "busy_timeout", value: 50, copied: false },
|
||||
{ name: "ignore_check_constraints", value: 1, copied: false },
|
||||
];
|
||||
|
||||
let db1 = getService().openUnsharedDatabase(getTestDB());
|
||||
|
||||
// Sanity check initial values are different from enforced ones.
|
||||
PRAGMAS.forEach(function (pragma) {
|
||||
let stmt = db1.createStatement("PRAGMA " + pragma.name);
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_neq(pragma.value, stmt.getInt32(0));
|
||||
stmt.finalize();
|
||||
});
|
||||
// Execute pragmas.
|
||||
PRAGMAS.forEach(function (pragma) {
|
||||
db1.executeSimpleSQL("PRAGMA " + pragma.name + " = " + pragma.value);
|
||||
});
|
||||
|
||||
let db2 = db1.clone(true);
|
||||
do_check_true(db2.connectionReady);
|
||||
|
||||
// Check cloned connection inherited pragma values.
|
||||
PRAGMAS.forEach(function (pragma) {
|
||||
let stmt = db2.createStatement("PRAGMA " + pragma.name);
|
||||
do_check_true(stmt.executeStep());
|
||||
let validate = pragma.copied ? do_check_eq : do_check_neq;
|
||||
validate(pragma.value, stmt.getInt32(0));
|
||||
stmt.finalize();
|
||||
});
|
||||
|
||||
db1.close();
|
||||
db2.close();
|
||||
});
|
||||
|
||||
add_task(function* test_clone_attach_database() {
|
||||
let db1 = getService().openUnsharedDatabase(getTestDB());
|
||||
|
||||
let c = 0;
|
||||
function attachDB(conn, name) {
|
||||
let file = dirSvc.get("ProfD", Ci.nsIFile);
|
||||
file.append("test_storage_" + (++c) + ".sqlite");
|
||||
let db = getService().openUnsharedDatabase(file);
|
||||
conn.executeSimpleSQL(`ATTACH DATABASE '${db.databaseFile.path}' AS ${name}`);
|
||||
db.close();
|
||||
}
|
||||
attachDB(db1, "attached_1");
|
||||
attachDB(db1, "attached_2");
|
||||
|
||||
// These should not throw.
|
||||
db1.createStatement("SELECT * FROM attached_1.sqlite_master");
|
||||
db1.createStatement("SELECT * FROM attached_2.sqlite_master");
|
||||
|
||||
// R/W clone.
|
||||
let db2 = db1.clone();
|
||||
do_check_true(db2.connectionReady);
|
||||
|
||||
// These should not throw.
|
||||
db2.createStatement("SELECT * FROM attached_1.sqlite_master");
|
||||
db2.createStatement("SELECT * FROM attached_2.sqlite_master");
|
||||
|
||||
// R/O clone.
|
||||
let db3 = db1.clone(true);
|
||||
do_check_true(db3.connectionReady);
|
||||
|
||||
// These should not throw.
|
||||
db3.createStatement("SELECT * FROM attached_1.sqlite_master");
|
||||
db3.createStatement("SELECT * FROM attached_2.sqlite_master");
|
||||
|
||||
db1.close();
|
||||
db2.close();
|
||||
db3.close();
|
||||
});
|
||||
|
||||
add_task(function* test_getInterface() {
|
||||
let db = getOpenedDatabase();
|
||||
let target = db.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIEventTarget);
|
||||
// Just check that target is non-null. Other tests will ensure that it has
|
||||
// the correct value.
|
||||
do_check_true(target != null);
|
||||
|
||||
yield asyncClose(db);
|
||||
gDBConn = null;
|
||||
});
|
||||
86
storage/test/unit/test_storage_fulltextindex.js
Normal file
86
storage/test/unit/test_storage_fulltextindex.js
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// This file tests support for the fts3 (full-text index) module.
|
||||
|
||||
// Example statements in these tests are taken from the Full Text Index page
|
||||
// on the SQLite wiki: http://www.sqlite.org/cvstrac/wiki?p=FullTextIndex
|
||||
|
||||
function test_table_creation()
|
||||
{
|
||||
var msc = getOpenedUnsharedDatabase();
|
||||
|
||||
msc.executeSimpleSQL(
|
||||
"CREATE VIRTUAL TABLE recipe USING fts3(name, ingredients)");
|
||||
|
||||
do_check_true(msc.tableExists("recipe"));
|
||||
}
|
||||
|
||||
function test_insertion()
|
||||
{
|
||||
var msc = getOpenedUnsharedDatabase();
|
||||
|
||||
msc.executeSimpleSQL("INSERT INTO recipe (name, ingredients) VALUES " +
|
||||
"('broccoli stew', 'broccoli peppers cheese tomatoes')");
|
||||
msc.executeSimpleSQL("INSERT INTO recipe (name, ingredients) VALUES " +
|
||||
"('pumpkin stew', 'pumpkin onions garlic celery')");
|
||||
msc.executeSimpleSQL("INSERT INTO recipe (name, ingredients) VALUES " +
|
||||
"('broccoli pie', 'broccoli cheese onions flour')");
|
||||
msc.executeSimpleSQL("INSERT INTO recipe (name, ingredients) VALUES " +
|
||||
"('pumpkin pie', 'pumpkin sugar flour butter')");
|
||||
|
||||
var stmt = msc.createStatement("SELECT COUNT(*) FROM recipe");
|
||||
stmt.executeStep();
|
||||
|
||||
do_check_eq(stmt.getInt32(0), 4);
|
||||
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
function test_selection()
|
||||
{
|
||||
var msc = getOpenedUnsharedDatabase();
|
||||
|
||||
var stmt = msc.createStatement(
|
||||
"SELECT rowid, name, ingredients FROM recipe WHERE name MATCH 'pie'");
|
||||
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_eq(stmt.getInt32(0), 3);
|
||||
do_check_eq(stmt.getString(1), "broccoli pie");
|
||||
do_check_eq(stmt.getString(2), "broccoli cheese onions flour");
|
||||
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_eq(stmt.getInt32(0), 4);
|
||||
do_check_eq(stmt.getString(1), "pumpkin pie");
|
||||
do_check_eq(stmt.getString(2), "pumpkin sugar flour butter");
|
||||
|
||||
do_check_false(stmt.executeStep());
|
||||
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
var tests = [test_table_creation, test_insertion, test_selection];
|
||||
|
||||
function run_test()
|
||||
{
|
||||
// It's extra important to start from scratch, since these tests won't work
|
||||
// with an existing shared cache connection, so we do it even though the last
|
||||
// test probably did it already.
|
||||
cleanup();
|
||||
|
||||
try {
|
||||
for (var i = 0; i < tests.length; i++) {
|
||||
tests[i]();
|
||||
}
|
||||
}
|
||||
// It's extra important to clean up afterwards, since later tests that use
|
||||
// a shared cache connection will not be able to read the database we create,
|
||||
// so we do this in a finally block to ensure it happens even if some of our
|
||||
// tests fail.
|
||||
finally {
|
||||
cleanup();
|
||||
}
|
||||
}
|
||||
95
storage/test/unit/test_storage_function.js
Normal file
95
storage/test/unit/test_storage_function.js
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// This file tests the custom functions
|
||||
|
||||
var testNums = [1, 2, 3, 4];
|
||||
|
||||
function setup()
|
||||
{
|
||||
getOpenedDatabase().createTable("function_tests", "id INTEGER PRIMARY KEY");
|
||||
|
||||
var stmt = createStatement("INSERT INTO function_tests (id) VALUES(?1)");
|
||||
for (let i = 0; i < testNums.length; ++i) {
|
||||
stmt.bindByIndex(0, testNums[i]);
|
||||
stmt.execute();
|
||||
}
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
var testSquareFunction = {
|
||||
calls: 0,
|
||||
|
||||
onFunctionCall(val) {
|
||||
++this.calls;
|
||||
return val.getInt32(0) * val.getInt32(0);
|
||||
}
|
||||
};
|
||||
|
||||
function test_function_registration()
|
||||
{
|
||||
var msc = getOpenedDatabase();
|
||||
msc.createFunction("test_square", 1, testSquareFunction);
|
||||
}
|
||||
|
||||
function test_function_no_double_registration()
|
||||
{
|
||||
var msc = getOpenedDatabase();
|
||||
try {
|
||||
msc.createFunction("test_square", 2, testSquareFunction);
|
||||
do_throw("We shouldn't get here!");
|
||||
} catch (e) {
|
||||
do_check_eq(Cr.NS_ERROR_FAILURE, e.result);
|
||||
}
|
||||
}
|
||||
|
||||
function test_function_removal()
|
||||
{
|
||||
var msc = getOpenedDatabase();
|
||||
msc.removeFunction("test_square");
|
||||
// Should be Ok now
|
||||
msc.createFunction("test_square", 1, testSquareFunction);
|
||||
}
|
||||
|
||||
function test_function_aliases()
|
||||
{
|
||||
var msc = getOpenedDatabase();
|
||||
msc.createFunction("test_square2", 1, testSquareFunction);
|
||||
}
|
||||
|
||||
function test_function_call()
|
||||
{
|
||||
var stmt = createStatement("SELECT test_square(id) FROM function_tests");
|
||||
while (stmt.executeStep()) {
|
||||
// Do nothing.
|
||||
}
|
||||
do_check_eq(testNums.length, testSquareFunction.calls);
|
||||
testSquareFunction.calls = 0;
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
function test_function_result()
|
||||
{
|
||||
var stmt = createStatement("SELECT test_square(42) FROM function_tests");
|
||||
stmt.executeStep();
|
||||
do_check_eq(42 * 42, stmt.getInt32(0));
|
||||
testSquareFunction.calls = 0;
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
var tests = [test_function_registration, test_function_no_double_registration,
|
||||
test_function_removal, test_function_aliases, test_function_call,
|
||||
test_function_result];
|
||||
|
||||
function run_test()
|
||||
{
|
||||
setup();
|
||||
|
||||
for (var i = 0; i < tests.length; i++) {
|
||||
tests[i]();
|
||||
}
|
||||
|
||||
cleanup();
|
||||
}
|
||||
111
storage/test/unit/test_storage_progresshandler.js
Normal file
111
storage/test/unit/test_storage_progresshandler.js
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// This file tests the custom progress handlers
|
||||
|
||||
function setup()
|
||||
{
|
||||
var msc = getOpenedDatabase();
|
||||
msc.createTable("handler_tests", "id INTEGER PRIMARY KEY, num INTEGER");
|
||||
msc.beginTransaction();
|
||||
|
||||
var stmt = createStatement("INSERT INTO handler_tests (id, num) VALUES(?1, ?2)");
|
||||
for (let i = 0; i < 100; ++i) {
|
||||
stmt.bindByIndex(0, i);
|
||||
stmt.bindByIndex(1, Math.floor(Math.random() * 1000));
|
||||
stmt.execute();
|
||||
}
|
||||
stmt.reset();
|
||||
msc.commitTransaction();
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
var testProgressHandler = {
|
||||
calls: 0,
|
||||
abort: false,
|
||||
|
||||
onProgress(comm) {
|
||||
++this.calls;
|
||||
return this.abort;
|
||||
}
|
||||
};
|
||||
|
||||
function test_handler_registration()
|
||||
{
|
||||
var msc = getOpenedDatabase();
|
||||
msc.setProgressHandler(10, testProgressHandler);
|
||||
}
|
||||
|
||||
function test_handler_return()
|
||||
{
|
||||
var msc = getOpenedDatabase();
|
||||
var oldH = msc.setProgressHandler(5, testProgressHandler);
|
||||
do_check_true(oldH instanceof Ci.mozIStorageProgressHandler);
|
||||
}
|
||||
|
||||
function test_handler_removal()
|
||||
{
|
||||
var msc = getOpenedDatabase();
|
||||
msc.removeProgressHandler();
|
||||
var oldH = msc.removeProgressHandler();
|
||||
do_check_eq(oldH, null);
|
||||
}
|
||||
|
||||
function test_handler_call()
|
||||
{
|
||||
var msc = getOpenedDatabase();
|
||||
msc.setProgressHandler(50, testProgressHandler);
|
||||
// Some long-executing request
|
||||
var stmt = createStatement(
|
||||
"SELECT SUM(t1.num * t2.num) FROM handler_tests AS t1, handler_tests AS t2");
|
||||
while (stmt.executeStep()) {
|
||||
// Do nothing.
|
||||
}
|
||||
do_check_true(testProgressHandler.calls > 0);
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
function test_handler_abort()
|
||||
{
|
||||
var msc = getOpenedDatabase();
|
||||
testProgressHandler.abort = true;
|
||||
msc.setProgressHandler(50, testProgressHandler);
|
||||
// Some long-executing request
|
||||
var stmt = createStatement(
|
||||
"SELECT SUM(t1.num * t2.num) FROM handler_tests AS t1, handler_tests AS t2");
|
||||
|
||||
const SQLITE_INTERRUPT = 9;
|
||||
try {
|
||||
while (stmt.executeStep()) {
|
||||
// Do nothing.
|
||||
}
|
||||
do_throw("We shouldn't get here!");
|
||||
} catch (e) {
|
||||
do_check_eq(Cr.NS_ERROR_ABORT, e.result);
|
||||
do_check_eq(SQLITE_INTERRUPT, msc.lastError);
|
||||
}
|
||||
try {
|
||||
stmt.finalize();
|
||||
do_throw("We shouldn't get here!");
|
||||
} catch (e) {
|
||||
// finalize should return the error code since we encountered an error
|
||||
do_check_eq(Cr.NS_ERROR_ABORT, e.result);
|
||||
do_check_eq(SQLITE_INTERRUPT, msc.lastError);
|
||||
}
|
||||
}
|
||||
|
||||
var tests = [test_handler_registration, test_handler_return,
|
||||
test_handler_removal, test_handler_call,
|
||||
test_handler_abort];
|
||||
|
||||
function run_test()
|
||||
{
|
||||
setup();
|
||||
|
||||
for (var i = 0; i < tests.length; i++) {
|
||||
tests[i]();
|
||||
}
|
||||
|
||||
cleanup();
|
||||
}
|
||||
142
storage/test/unit/test_storage_service.js
Normal file
142
storage/test/unit/test_storage_service.js
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// This file tests the functions of mozIStorageService except for
|
||||
// openUnsharedDatabase, which is tested by test_storage_service_unshared.js.
|
||||
|
||||
const BACKUP_FILE_NAME = "test_storage.sqlite.backup";
|
||||
|
||||
function test_openSpecialDatabase_invalid_arg()
|
||||
{
|
||||
try {
|
||||
getService().openSpecialDatabase("abcd");
|
||||
do_throw("We should not get here!");
|
||||
} catch (e) {
|
||||
print(e);
|
||||
print("e.result is " + e.result);
|
||||
do_check_eq(Cr.NS_ERROR_INVALID_ARG, e.result);
|
||||
}
|
||||
}
|
||||
|
||||
function test_openDatabase_null_file()
|
||||
{
|
||||
try {
|
||||
getService().openDatabase(null);
|
||||
do_throw("We should not get here!");
|
||||
} catch (e) {
|
||||
print(e);
|
||||
print("e.result is " + e.result);
|
||||
do_check_eq(Cr.NS_ERROR_INVALID_ARG, e.result);
|
||||
}
|
||||
}
|
||||
|
||||
function test_openUnsharedDatabase_null_file()
|
||||
{
|
||||
try {
|
||||
getService().openUnsharedDatabase(null);
|
||||
do_throw("We should not get here!");
|
||||
} catch (e) {
|
||||
print(e);
|
||||
print("e.result is " + e.result);
|
||||
do_check_eq(Cr.NS_ERROR_INVALID_ARG, e.result);
|
||||
}
|
||||
}
|
||||
|
||||
function test_openDatabase_file_DNE()
|
||||
{
|
||||
// the file should be created after calling
|
||||
var db = getTestDB();
|
||||
do_check_false(db.exists());
|
||||
getService().openDatabase(db);
|
||||
do_check_true(db.exists());
|
||||
}
|
||||
|
||||
function test_openDatabase_file_exists()
|
||||
{
|
||||
// it should already exist from our last test
|
||||
var db = getTestDB();
|
||||
do_check_true(db.exists());
|
||||
getService().openDatabase(db);
|
||||
do_check_true(db.exists());
|
||||
}
|
||||
|
||||
function test_corrupt_db_throws_with_openDatabase()
|
||||
{
|
||||
try {
|
||||
getDatabase(getCorruptDB());
|
||||
do_throw("should not be here");
|
||||
}
|
||||
catch (e) {
|
||||
do_check_eq(Cr.NS_ERROR_FILE_CORRUPTED, e.result);
|
||||
}
|
||||
}
|
||||
|
||||
function test_fake_db_throws_with_openDatabase()
|
||||
{
|
||||
try {
|
||||
getDatabase(getFakeDB());
|
||||
do_throw("should not be here");
|
||||
}
|
||||
catch (e) {
|
||||
do_check_eq(Cr.NS_ERROR_FILE_CORRUPTED, e.result);
|
||||
}
|
||||
}
|
||||
|
||||
function test_backup_not_new_filename()
|
||||
{
|
||||
const fname = getTestDB().leafName;
|
||||
|
||||
var backup = getService().backupDatabaseFile(getTestDB(), fname);
|
||||
do_check_neq(fname, backup.leafName);
|
||||
|
||||
backup.remove(false);
|
||||
}
|
||||
|
||||
function test_backup_new_filename()
|
||||
{
|
||||
var backup = getService().backupDatabaseFile(getTestDB(), BACKUP_FILE_NAME);
|
||||
do_check_eq(BACKUP_FILE_NAME, backup.leafName);
|
||||
|
||||
backup.remove(false);
|
||||
}
|
||||
|
||||
function test_backup_new_folder()
|
||||
{
|
||||
var parentDir = getTestDB().parent;
|
||||
parentDir.append("test_storage_temp");
|
||||
if (parentDir.exists())
|
||||
parentDir.remove(true);
|
||||
parentDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
|
||||
do_check_true(parentDir.exists());
|
||||
|
||||
var backup = getService().backupDatabaseFile(getTestDB(), BACKUP_FILE_NAME,
|
||||
parentDir);
|
||||
do_check_eq(BACKUP_FILE_NAME, backup.leafName);
|
||||
do_check_true(parentDir.equals(backup.parent));
|
||||
|
||||
parentDir.remove(true);
|
||||
}
|
||||
|
||||
var tests = [
|
||||
test_openSpecialDatabase_invalid_arg,
|
||||
test_openDatabase_null_file,
|
||||
test_openUnsharedDatabase_null_file,
|
||||
test_openDatabase_file_DNE,
|
||||
test_openDatabase_file_exists,
|
||||
test_corrupt_db_throws_with_openDatabase,
|
||||
test_fake_db_throws_with_openDatabase,
|
||||
test_backup_not_new_filename,
|
||||
test_backup_new_filename,
|
||||
test_backup_new_folder,
|
||||
];
|
||||
|
||||
function run_test()
|
||||
{
|
||||
for (var i = 0; i < tests.length; i++) {
|
||||
tests[i]();
|
||||
}
|
||||
|
||||
cleanup();
|
||||
}
|
||||
|
||||
35
storage/test/unit/test_storage_service_unshared.js
Normal file
35
storage/test/unit/test_storage_service_unshared.js
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// This file tests the openUnsharedDatabase function of mozIStorageService.
|
||||
|
||||
function test_openUnsharedDatabase_file_DNE()
|
||||
{
|
||||
// the file should be created after calling
|
||||
var db = getTestDB();
|
||||
do_check_false(db.exists());
|
||||
getService().openUnsharedDatabase(db);
|
||||
do_check_true(db.exists());
|
||||
}
|
||||
|
||||
function test_openUnsharedDatabase_file_exists()
|
||||
{
|
||||
// it should already exist from our last test
|
||||
var db = getTestDB();
|
||||
do_check_true(db.exists());
|
||||
getService().openUnsharedDatabase(db);
|
||||
do_check_true(db.exists());
|
||||
}
|
||||
|
||||
var tests = [test_openUnsharedDatabase_file_DNE,
|
||||
test_openUnsharedDatabase_file_exists];
|
||||
|
||||
function run_test()
|
||||
{
|
||||
for (var i = 0; i < tests.length; i++)
|
||||
tests[i]();
|
||||
|
||||
cleanup();
|
||||
}
|
||||
|
||||
184
storage/test/unit/test_storage_statement.js
Normal file
184
storage/test/unit/test_storage_statement.js
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// This file tests the functions of mozIStorageStatement
|
||||
|
||||
function setup()
|
||||
{
|
||||
getOpenedDatabase().createTable("test", "id INTEGER PRIMARY KEY, name TEXT");
|
||||
}
|
||||
|
||||
function test_parameterCount_none()
|
||||
{
|
||||
var stmt = createStatement("SELECT * FROM test");
|
||||
do_check_eq(0, stmt.parameterCount);
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
function test_parameterCount_one()
|
||||
{
|
||||
var stmt = createStatement("SELECT * FROM test WHERE id = ?1");
|
||||
do_check_eq(1, stmt.parameterCount);
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
function test_getParameterName()
|
||||
{
|
||||
var stmt = createStatement("SELECT * FROM test WHERE id = :id");
|
||||
do_check_eq(":id", stmt.getParameterName(0));
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
function test_getParameterIndex_different()
|
||||
{
|
||||
var stmt = createStatement("SELECT * FROM test WHERE id = :id OR name = :name");
|
||||
do_check_eq(0, stmt.getParameterIndex("id"));
|
||||
do_check_eq(1, stmt.getParameterIndex("name"));
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
function test_getParameterIndex_same()
|
||||
{
|
||||
var stmt = createStatement("SELECT * FROM test WHERE id = :test OR name = :test");
|
||||
do_check_eq(0, stmt.getParameterIndex("test"));
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
function test_columnCount()
|
||||
{
|
||||
var stmt = createStatement("SELECT * FROM test WHERE id = ?1 OR name = ?2");
|
||||
do_check_eq(2, stmt.columnCount);
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
function test_getColumnName()
|
||||
{
|
||||
var stmt = createStatement("SELECT name, id FROM test");
|
||||
do_check_eq("id", stmt.getColumnName(1));
|
||||
do_check_eq("name", stmt.getColumnName(0));
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
function test_getColumnIndex_same_case()
|
||||
{
|
||||
var stmt = createStatement("SELECT name, id FROM test");
|
||||
do_check_eq(0, stmt.getColumnIndex("name"));
|
||||
do_check_eq(1, stmt.getColumnIndex("id"));
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
function test_getColumnIndex_different_case()
|
||||
{
|
||||
var stmt = createStatement("SELECT name, id FROM test");
|
||||
try {
|
||||
do_check_eq(0, stmt.getColumnIndex("NaMe"));
|
||||
do_throw("should not get here");
|
||||
} catch (e) {
|
||||
do_check_eq(Cr.NS_ERROR_INVALID_ARG, e.result);
|
||||
}
|
||||
try {
|
||||
do_check_eq(1, stmt.getColumnIndex("Id"));
|
||||
do_throw("should not get here");
|
||||
} catch (e) {
|
||||
do_check_eq(Cr.NS_ERROR_INVALID_ARG, e.result);
|
||||
}
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
function test_state_ready()
|
||||
{
|
||||
var stmt = createStatement("SELECT name, id FROM test");
|
||||
do_check_eq(Ci.mozIStorageStatement.MOZ_STORAGE_STATEMENT_READY, stmt.state);
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
function test_state_executing()
|
||||
{
|
||||
var stmt = createStatement("INSERT INTO test (name) VALUES ('foo')");
|
||||
stmt.execute();
|
||||
stmt.execute();
|
||||
stmt.finalize();
|
||||
|
||||
stmt = createStatement("SELECT name, id FROM test");
|
||||
stmt.executeStep();
|
||||
do_check_eq(Ci.mozIStorageStatement.MOZ_STORAGE_STATEMENT_EXECUTING,
|
||||
stmt.state);
|
||||
stmt.executeStep();
|
||||
do_check_eq(Ci.mozIStorageStatement.MOZ_STORAGE_STATEMENT_EXECUTING,
|
||||
stmt.state);
|
||||
stmt.reset();
|
||||
do_check_eq(Ci.mozIStorageStatement.MOZ_STORAGE_STATEMENT_READY, stmt.state);
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
function test_state_after_finalize()
|
||||
{
|
||||
var stmt = createStatement("SELECT name, id FROM test");
|
||||
stmt.executeStep();
|
||||
stmt.finalize();
|
||||
do_check_eq(Ci.mozIStorageStatement.MOZ_STORAGE_STATEMENT_INVALID, stmt.state);
|
||||
}
|
||||
|
||||
function test_failed_execute()
|
||||
{
|
||||
var stmt = createStatement("INSERT INTO test (name) VALUES ('foo')");
|
||||
stmt.execute();
|
||||
stmt.finalize();
|
||||
var id = getOpenedDatabase().lastInsertRowID;
|
||||
stmt = createStatement("INSERT INTO test(id, name) VALUES(:id, 'bar')");
|
||||
stmt.params.id = id;
|
||||
try {
|
||||
// Should throw a constraint error
|
||||
stmt.execute();
|
||||
do_throw("Should have seen a constraint error");
|
||||
}
|
||||
catch (e) {
|
||||
do_check_eq(getOpenedDatabase().lastError, Ci.mozIStorageError.CONSTRAINT);
|
||||
}
|
||||
do_check_eq(Ci.mozIStorageStatement.MOZ_STORAGE_STATEMENT_READY, stmt.state);
|
||||
// Should succeed without needing to reset the statement manually
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
function test_bind_undefined()
|
||||
{
|
||||
var stmt = createStatement("INSERT INTO test (name) VALUES ('foo')");
|
||||
|
||||
expectError(Cr.NS_ERROR_ILLEGAL_VALUE,
|
||||
() => stmt.bindParameters(undefined));
|
||||
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
var tests = [test_parameterCount_none, test_parameterCount_one,
|
||||
test_getParameterName, test_getParameterIndex_different,
|
||||
test_getParameterIndex_same, test_columnCount,
|
||||
test_getColumnName, test_getColumnIndex_same_case,
|
||||
test_getColumnIndex_different_case, test_state_ready,
|
||||
test_state_executing, test_state_after_finalize,
|
||||
test_failed_execute,
|
||||
test_bind_undefined,
|
||||
];
|
||||
|
||||
function run_test()
|
||||
{
|
||||
setup();
|
||||
|
||||
for (var i = 0; i < tests.length; i++) {
|
||||
tests[i]();
|
||||
}
|
||||
|
||||
cleanup();
|
||||
}
|
||||
|
||||
182
storage/test/unit/test_storage_value_array.js
Normal file
182
storage/test/unit/test_storage_value_array.js
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// This file tests the functions of mozIStorageValueArray
|
||||
|
||||
add_task(function* setup() {
|
||||
getOpenedDatabase().createTable("test", "id INTEGER PRIMARY KEY, name TEXT," +
|
||||
"number REAL, nuller NULL, blobber BLOB");
|
||||
|
||||
var stmt = createStatement("INSERT INTO test (name, number, blobber) " +
|
||||
"VALUES (?1, ?2, ?3)");
|
||||
stmt.bindByIndex(0, "foo");
|
||||
stmt.bindByIndex(1, 2.34);
|
||||
stmt.bindBlobByIndex(2, [], 0);
|
||||
stmt.execute();
|
||||
|
||||
stmt.bindByIndex(0, "");
|
||||
stmt.bindByIndex(1, 1.23);
|
||||
stmt.bindBlobByIndex(2, [1, 2], 2);
|
||||
stmt.execute();
|
||||
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
|
||||
do_register_cleanup(cleanup);
|
||||
});
|
||||
|
||||
add_task(function* test_getIsNull_for_null() {
|
||||
var stmt = createStatement("SELECT nuller, blobber FROM test WHERE id = ?1");
|
||||
stmt.bindByIndex(0, 1);
|
||||
do_check_true(stmt.executeStep());
|
||||
|
||||
do_check_true(stmt.getIsNull(0)); // null field
|
||||
do_check_true(stmt.getIsNull(1)); // data is null if size is 0
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
});
|
||||
|
||||
add_task(function* test_getIsNull_for_non_null() {
|
||||
var stmt = createStatement("SELECT name, blobber FROM test WHERE id = ?1");
|
||||
stmt.bindByIndex(0, 2);
|
||||
do_check_true(stmt.executeStep());
|
||||
|
||||
do_check_false(stmt.getIsNull(0));
|
||||
do_check_false(stmt.getIsNull(1));
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
});
|
||||
|
||||
add_task(function* test_value_type_null() {
|
||||
var stmt = createStatement("SELECT nuller FROM test WHERE id = ?1");
|
||||
stmt.bindByIndex(0, 1);
|
||||
do_check_true(stmt.executeStep());
|
||||
|
||||
do_check_eq(Ci.mozIStorageValueArray.VALUE_TYPE_NULL,
|
||||
stmt.getTypeOfIndex(0));
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
});
|
||||
|
||||
add_task(function* test_value_type_integer() {
|
||||
var stmt = createStatement("SELECT id FROM test WHERE id = ?1");
|
||||
stmt.bindByIndex(0, 1);
|
||||
do_check_true(stmt.executeStep());
|
||||
|
||||
do_check_eq(Ci.mozIStorageValueArray.VALUE_TYPE_INTEGER,
|
||||
stmt.getTypeOfIndex(0));
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
});
|
||||
|
||||
add_task(function* test_value_type_float() {
|
||||
var stmt = createStatement("SELECT number FROM test WHERE id = ?1");
|
||||
stmt.bindByIndex(0, 1);
|
||||
do_check_true(stmt.executeStep());
|
||||
|
||||
do_check_eq(Ci.mozIStorageValueArray.VALUE_TYPE_FLOAT,
|
||||
stmt.getTypeOfIndex(0));
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
});
|
||||
|
||||
add_task(function* test_value_type_text() {
|
||||
var stmt = createStatement("SELECT name FROM test WHERE id = ?1");
|
||||
stmt.bindByIndex(0, 1);
|
||||
do_check_true(stmt.executeStep());
|
||||
|
||||
do_check_eq(Ci.mozIStorageValueArray.VALUE_TYPE_TEXT,
|
||||
stmt.getTypeOfIndex(0));
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
});
|
||||
|
||||
add_task(function* test_value_type_blob() {
|
||||
var stmt = createStatement("SELECT blobber FROM test WHERE id = ?1");
|
||||
stmt.bindByIndex(0, 2);
|
||||
do_check_true(stmt.executeStep());
|
||||
|
||||
do_check_eq(Ci.mozIStorageValueArray.VALUE_TYPE_BLOB,
|
||||
stmt.getTypeOfIndex(0));
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
});
|
||||
|
||||
add_task(function* test_numEntries_one() {
|
||||
var stmt = createStatement("SELECT blobber FROM test WHERE id = ?1");
|
||||
stmt.bindByIndex(0, 2);
|
||||
do_check_true(stmt.executeStep());
|
||||
|
||||
do_check_eq(1, stmt.numEntries);
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
});
|
||||
|
||||
add_task(function* test_numEntries_all() {
|
||||
var stmt = createStatement("SELECT * FROM test WHERE id = ?1");
|
||||
stmt.bindByIndex(0, 2);
|
||||
do_check_true(stmt.executeStep());
|
||||
|
||||
do_check_eq(5, stmt.numEntries);
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
});
|
||||
|
||||
add_task(function* test_getInt() {
|
||||
var stmt = createStatement("SELECT id FROM test WHERE id = ?1");
|
||||
stmt.bindByIndex(0, 2);
|
||||
do_check_true(stmt.executeStep());
|
||||
|
||||
do_check_eq(2, stmt.getInt32(0));
|
||||
do_check_eq(2, stmt.getInt64(0));
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
});
|
||||
|
||||
add_task(function* test_getDouble() {
|
||||
var stmt = createStatement("SELECT number FROM test WHERE id = ?1");
|
||||
stmt.bindByIndex(0, 2);
|
||||
do_check_true(stmt.executeStep());
|
||||
|
||||
do_check_eq(1.23, stmt.getDouble(0));
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
});
|
||||
|
||||
add_task(function* test_getUTF8String() {
|
||||
var stmt = createStatement("SELECT name FROM test WHERE id = ?1");
|
||||
stmt.bindByIndex(0, 1);
|
||||
do_check_true(stmt.executeStep());
|
||||
|
||||
do_check_eq("foo", stmt.getUTF8String(0));
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
});
|
||||
|
||||
add_task(function* test_getString() {
|
||||
var stmt = createStatement("SELECT name FROM test WHERE id = ?1");
|
||||
stmt.bindByIndex(0, 2);
|
||||
do_check_true(stmt.executeStep());
|
||||
|
||||
do_check_eq("", stmt.getString(0));
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
});
|
||||
|
||||
add_task(function* test_getBlob() {
|
||||
var stmt = createStatement("SELECT blobber FROM test WHERE id = ?1");
|
||||
stmt.bindByIndex(0, 2);
|
||||
do_check_true(stmt.executeStep());
|
||||
|
||||
var count = { value: 0 };
|
||||
var arr = { value: null };
|
||||
stmt.getBlob(0, count, arr);
|
||||
do_check_eq(2, count.value);
|
||||
do_check_eq(1, arr.value[0]);
|
||||
do_check_eq(2, arr.value[1]);
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
});
|
||||
|
||||
|
||||
30
storage/test/unit/test_telemetry_vfs.js
Normal file
30
storage/test/unit/test_telemetry_vfs.js
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
// Make sure that there are telemetry entries created by sqlite io
|
||||
|
||||
function run_sql(d, sql) {
|
||||
var stmt = d.createStatement(sql);
|
||||
stmt.execute();
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
function new_file(name)
|
||||
{
|
||||
var file = dirSvc.get("ProfD", Ci.nsIFile);
|
||||
file.append(name);
|
||||
return file;
|
||||
}
|
||||
function run_test()
|
||||
{
|
||||
const Telemetry = Cc["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry);
|
||||
let read_hgram = Telemetry.getHistogramById("MOZ_SQLITE_OTHER_READ_B");
|
||||
let old_sum = read_hgram.snapshot().sum;
|
||||
const file = new_file("telemetry.sqlite");
|
||||
var d = getDatabase(file);
|
||||
run_sql(d, "CREATE TABLE bloat(data varchar)");
|
||||
run_sql(d, "DROP TABLE bloat");
|
||||
do_check_true(read_hgram.snapshot().sum > old_sum);
|
||||
}
|
||||
|
||||
83
storage/test/unit/test_unicode.js
Normal file
83
storage/test/unit/test_unicode.js
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// This file tests the unicode functions that we have added
|
||||
|
||||
const LATIN1_AE = "\xc6"; // "Æ"
|
||||
const LATIN1_ae = "\xe6"; // "æ"
|
||||
|
||||
add_task(function* setup() {
|
||||
getOpenedDatabase().createTable("test", "id INTEGER PRIMARY KEY, name TEXT");
|
||||
|
||||
var stmt = createStatement("INSERT INTO test (name, id) VALUES (?1, ?2)");
|
||||
stmt.bindByIndex(0, LATIN1_AE);
|
||||
stmt.bindByIndex(1, 1);
|
||||
stmt.execute();
|
||||
stmt.bindByIndex(0, "A");
|
||||
stmt.bindByIndex(1, 2);
|
||||
stmt.execute();
|
||||
stmt.bindByIndex(0, "b");
|
||||
stmt.bindByIndex(1, 3);
|
||||
stmt.execute();
|
||||
stmt.bindByIndex(0, LATIN1_ae);
|
||||
stmt.bindByIndex(1, 4);
|
||||
stmt.execute();
|
||||
stmt.finalize();
|
||||
|
||||
do_register_cleanup(cleanup);
|
||||
});
|
||||
|
||||
add_task(function* test_upper_ascii() {
|
||||
var stmt = createStatement("SELECT name, id FROM test WHERE name = upper('a')");
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_eq("A", stmt.getString(0));
|
||||
do_check_eq(2, stmt.getInt32(1));
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
});
|
||||
|
||||
add_task(function* test_upper_non_ascii() {
|
||||
var stmt = createStatement("SELECT name, id FROM test WHERE name = upper(?1)");
|
||||
stmt.bindByIndex(0, LATIN1_ae);
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_eq(LATIN1_AE, stmt.getString(0));
|
||||
do_check_eq(1, stmt.getInt32(1));
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
});
|
||||
|
||||
add_task(function* test_lower_ascii() {
|
||||
var stmt = createStatement("SELECT name, id FROM test WHERE name = lower('B')");
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_eq("b", stmt.getString(0));
|
||||
do_check_eq(3, stmt.getInt32(1));
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
});
|
||||
|
||||
add_task(function* test_lower_non_ascii() {
|
||||
var stmt = createStatement("SELECT name, id FROM test WHERE name = lower(?1)");
|
||||
stmt.bindByIndex(0, LATIN1_AE);
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_eq(LATIN1_ae, stmt.getString(0));
|
||||
do_check_eq(4, stmt.getInt32(1));
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
});
|
||||
|
||||
add_task(function* test_like_search_different() {
|
||||
var stmt = createStatement("SELECT COUNT(*) FROM test WHERE name LIKE ?1");
|
||||
stmt.bindByIndex(0, LATIN1_AE);
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_eq(2, stmt.getInt32(0));
|
||||
stmt.finalize();
|
||||
});
|
||||
|
||||
add_task(function* test_like_search_same() {
|
||||
var stmt = createStatement("SELECT COUNT(*) FROM test WHERE name LIKE ?1");
|
||||
stmt.bindByIndex(0, LATIN1_ae);
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_eq(2, stmt.getInt32(0));
|
||||
stmt.finalize();
|
||||
});
|
||||
335
storage/test/unit/test_vacuum.js
Normal file
335
storage/test/unit/test_vacuum.js
Normal file
|
|
@ -0,0 +1,335 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
// This file tests the Vacuum Manager.
|
||||
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
/**
|
||||
* Loads a test component that will register as a vacuum-participant.
|
||||
* If other participants are found they will be unregistered, to avoid conflicts
|
||||
* with the test itself.
|
||||
*/
|
||||
function load_test_vacuum_component()
|
||||
{
|
||||
const CATEGORY_NAME = "vacuum-participant";
|
||||
|
||||
do_load_manifest("vacuumParticipant.manifest");
|
||||
|
||||
// This is a lazy check, there could be more participants than just this test
|
||||
// we just mind that the test exists though.
|
||||
const EXPECTED_ENTRIES = ["vacuumParticipant"];
|
||||
let catMan = Cc["@mozilla.org/categorymanager;1"].
|
||||
getService(Ci.nsICategoryManager);
|
||||
let found = false;
|
||||
let entries = catMan.enumerateCategory(CATEGORY_NAME);
|
||||
while (entries.hasMoreElements()) {
|
||||
let entry = entries.getNext().QueryInterface(Ci.nsISupportsCString).data;
|
||||
print("Check if the found category entry (" + entry + ") is expected.");
|
||||
if (EXPECTED_ENTRIES.indexOf(entry) != -1) {
|
||||
print("Check that only one test entry exists.");
|
||||
do_check_false(found);
|
||||
found = true;
|
||||
}
|
||||
else {
|
||||
// Temporary unregister other participants for this test.
|
||||
catMan.deleteCategoryEntry("vacuum-participant", entry, false);
|
||||
}
|
||||
}
|
||||
print("Check the test entry exists.");
|
||||
do_check_true(found);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a fake idle-daily notification to the VACUUM Manager.
|
||||
*/
|
||||
function synthesize_idle_daily()
|
||||
{
|
||||
let vm = Cc["@mozilla.org/storage/vacuum;1"].getService(Ci.nsIObserver);
|
||||
vm.observe(null, "idle-daily", null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new nsIFile reference for a profile database.
|
||||
* @param filename for the database, excluded the .sqlite extension.
|
||||
*/
|
||||
function new_db_file(name)
|
||||
{
|
||||
let file = Services.dirsvc.get("ProfD", Ci.nsIFile);
|
||||
file.append(name + ".sqlite");
|
||||
return file;
|
||||
}
|
||||
|
||||
function run_test()
|
||||
{
|
||||
do_test_pending();
|
||||
|
||||
// Change initial page size. Do it immediately since it would require an
|
||||
// additional vacuum op to do it later. As a bonus this makes the page size
|
||||
// change test really fast since it only has to check results.
|
||||
let conn = getDatabase(new_db_file("testVacuum"));
|
||||
conn.executeSimpleSQL("PRAGMA page_size = 1024");
|
||||
print("Check current page size.");
|
||||
let stmt = conn.createStatement("PRAGMA page_size");
|
||||
try {
|
||||
while (stmt.executeStep()) {
|
||||
do_check_eq(stmt.row.page_size, 1024);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
load_test_vacuum_component();
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
const TESTS = [
|
||||
|
||||
function test_common_vacuum()
|
||||
{
|
||||
print("\n*** Test that a VACUUM correctly happens and all notifications are fired.");
|
||||
// Wait for VACUUM begin.
|
||||
let beginVacuumReceived = false;
|
||||
Services.obs.addObserver(function onVacuum(aSubject, aTopic, aData) {
|
||||
Services.obs.removeObserver(onVacuum, aTopic);
|
||||
beginVacuumReceived = true;
|
||||
}, "test-begin-vacuum", false);
|
||||
|
||||
// Wait for heavy IO notifications.
|
||||
let heavyIOTaskBeginReceived = false;
|
||||
let heavyIOTaskEndReceived = false;
|
||||
Services.obs.addObserver(function onVacuum(aSubject, aTopic, aData) {
|
||||
if (heavyIOTaskBeginReceived && heavyIOTaskEndReceived) {
|
||||
Services.obs.removeObserver(onVacuum, aTopic);
|
||||
}
|
||||
|
||||
if (aData == "vacuum-begin") {
|
||||
heavyIOTaskBeginReceived = true;
|
||||
}
|
||||
else if (aData == "vacuum-end") {
|
||||
heavyIOTaskEndReceived = true;
|
||||
}
|
||||
}, "heavy-io-task", false);
|
||||
|
||||
// Wait for VACUUM end.
|
||||
Services.obs.addObserver(function onVacuum(aSubject, aTopic, aData) {
|
||||
Services.obs.removeObserver(onVacuum, aTopic);
|
||||
print("Check we received onBeginVacuum");
|
||||
do_check_true(beginVacuumReceived);
|
||||
print("Check we received heavy-io-task notifications");
|
||||
do_check_true(heavyIOTaskBeginReceived);
|
||||
do_check_true(heavyIOTaskEndReceived);
|
||||
print("Received onEndVacuum");
|
||||
run_next_test();
|
||||
}, "test-end-vacuum", false);
|
||||
|
||||
synthesize_idle_daily();
|
||||
},
|
||||
|
||||
function test_skipped_if_recent_vacuum()
|
||||
{
|
||||
print("\n*** Test that a VACUUM is skipped if it was run recently.");
|
||||
Services.prefs.setIntPref("storage.vacuum.last.testVacuum.sqlite",
|
||||
parseInt(Date.now() / 1000));
|
||||
|
||||
// Wait for VACUUM begin.
|
||||
let vacuumObserver = {
|
||||
gotNotification: false,
|
||||
observe: function VO_observe(aSubject, aTopic, aData) {
|
||||
this.gotNotification = true;
|
||||
},
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver])
|
||||
};
|
||||
Services.obs.addObserver(vacuumObserver, "test-begin-vacuum", false);
|
||||
|
||||
// Check after a couple seconds that no VACUUM has been run.
|
||||
do_timeout(2000, function () {
|
||||
print("Check VACUUM did not run.");
|
||||
do_check_false(vacuumObserver.gotNotification);
|
||||
Services.obs.removeObserver(vacuumObserver, "test-begin-vacuum");
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
synthesize_idle_daily();
|
||||
},
|
||||
|
||||
function test_page_size_change()
|
||||
{
|
||||
print("\n*** Test that a VACUUM changes page_size");
|
||||
|
||||
// We did setup the database with a small page size, the previous vacuum
|
||||
// should have updated it.
|
||||
print("Check that page size was updated.");
|
||||
let conn = getDatabase(new_db_file("testVacuum"));
|
||||
let stmt = conn.createStatement("PRAGMA page_size");
|
||||
try {
|
||||
while (stmt.executeStep()) {
|
||||
do_check_eq(stmt.row.page_size, conn.defaultPageSize);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
run_next_test();
|
||||
},
|
||||
|
||||
function test_skipped_optout_vacuum()
|
||||
{
|
||||
print("\n*** Test that a VACUUM is skipped if the participant wants to opt-out.");
|
||||
Services.obs.notifyObservers(null, "test-options", "opt-out");
|
||||
|
||||
// Wait for VACUUM begin.
|
||||
let vacuumObserver = {
|
||||
gotNotification: false,
|
||||
observe: function VO_observe(aSubject, aTopic, aData) {
|
||||
this.gotNotification = true;
|
||||
},
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver])
|
||||
};
|
||||
Services.obs.addObserver(vacuumObserver, "test-begin-vacuum", false);
|
||||
|
||||
// Check after a couple seconds that no VACUUM has been run.
|
||||
do_timeout(2000, function () {
|
||||
print("Check VACUUM did not run.");
|
||||
do_check_false(vacuumObserver.gotNotification);
|
||||
Services.obs.removeObserver(vacuumObserver, "test-begin-vacuum");
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
synthesize_idle_daily();
|
||||
},
|
||||
|
||||
/* Changing page size on WAL is not supported till Bug 634374 is properly fixed.
|
||||
function test_page_size_change_with_wal()
|
||||
{
|
||||
print("\n*** Test that a VACUUM changes page_size with WAL mode");
|
||||
Services.obs.notifyObservers(null, "test-options", "wal");
|
||||
|
||||
// Set a small page size.
|
||||
let conn = getDatabase(new_db_file("testVacuum2"));
|
||||
conn.executeSimpleSQL("PRAGMA page_size = 1024");
|
||||
let stmt = conn.createStatement("PRAGMA page_size");
|
||||
try {
|
||||
while (stmt.executeStep()) {
|
||||
do_check_eq(stmt.row.page_size, 1024);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
// Use WAL journal mode.
|
||||
conn.executeSimpleSQL("PRAGMA journal_mode = WAL");
|
||||
stmt = conn.createStatement("PRAGMA journal_mode");
|
||||
try {
|
||||
while (stmt.executeStep()) {
|
||||
do_check_eq(stmt.row.journal_mode, "wal");
|
||||
}
|
||||
}
|
||||
finally {
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
// Wait for VACUUM end.
|
||||
let vacuumObserver = {
|
||||
observe: function VO_observe(aSubject, aTopic, aData) {
|
||||
Services.obs.removeObserver(this, aTopic);
|
||||
print("Check page size has been updated.");
|
||||
let stmt = conn.createStatement("PRAGMA page_size");
|
||||
try {
|
||||
while (stmt.executeStep()) {
|
||||
do_check_eq(stmt.row.page_size, Ci.mozIStorageConnection.DEFAULT_PAGE_SIZE);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
print("Check journal mode has been restored.");
|
||||
stmt = conn.createStatement("PRAGMA journal_mode");
|
||||
try {
|
||||
while (stmt.executeStep()) {
|
||||
do_check_eq(stmt.row.journal_mode, "wal");
|
||||
}
|
||||
}
|
||||
finally {
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
run_next_test();
|
||||
},
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver])
|
||||
}
|
||||
Services.obs.addObserver(vacuumObserver, "test-end-vacuum", false);
|
||||
|
||||
synthesize_idle_daily();
|
||||
},
|
||||
*/
|
||||
|
||||
function test_memory_database_crash()
|
||||
{
|
||||
print("\n*** Test that we don't crash trying to vacuum a memory database");
|
||||
Services.obs.notifyObservers(null, "test-options", "memory");
|
||||
|
||||
// Wait for VACUUM begin.
|
||||
let vacuumObserver = {
|
||||
gotNotification: false,
|
||||
observe: function VO_observe(aSubject, aTopic, aData) {
|
||||
this.gotNotification = true;
|
||||
},
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver])
|
||||
};
|
||||
Services.obs.addObserver(vacuumObserver, "test-begin-vacuum", false);
|
||||
|
||||
// Check after a couple seconds that no VACUUM has been run.
|
||||
do_timeout(2000, function () {
|
||||
print("Check VACUUM did not run.");
|
||||
do_check_false(vacuumObserver.gotNotification);
|
||||
Services.obs.removeObserver(vacuumObserver, "test-begin-vacuum");
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
synthesize_idle_daily();
|
||||
},
|
||||
|
||||
/* Changing page size on WAL is not supported till Bug 634374 is properly fixed.
|
||||
function test_wal_restore_fail()
|
||||
{
|
||||
print("\n*** Test that a failing WAL restoration notifies failure");
|
||||
Services.obs.notifyObservers(null, "test-options", "wal-fail");
|
||||
|
||||
// Wait for VACUUM end.
|
||||
let vacuumObserver = {
|
||||
observe: function VO_observe(aSubject, aTopic, aData) {
|
||||
Services.obs.removeObserver(vacuumObserver, "test-end-vacuum");
|
||||
print("Check WAL restoration failed.");
|
||||
do_check_false(aData);
|
||||
run_next_test();
|
||||
},
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver])
|
||||
}
|
||||
Services.obs.addObserver(vacuumObserver, "test-end-vacuum", false);
|
||||
|
||||
synthesize_idle_daily();
|
||||
},
|
||||
*/
|
||||
];
|
||||
|
||||
function run_next_test()
|
||||
{
|
||||
if (TESTS.length == 0) {
|
||||
Services.obs.notifyObservers(null, "test-options", "dispose");
|
||||
do_test_finished();
|
||||
}
|
||||
else {
|
||||
// Set last VACUUM to a date in the past.
|
||||
Services.prefs.setIntPref("storage.vacuum.last.testVacuum.sqlite",
|
||||
parseInt(Date.now() / 1000 - 31 * 86400));
|
||||
do_execute_soon(TESTS.shift());
|
||||
}
|
||||
}
|
||||
125
storage/test/unit/vacuumParticipant.js
Normal file
125
storage/test/unit/vacuumParticipant.js
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
// This testing component is used in test_vacuum* tests.
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
/**
|
||||
* Returns a new nsIFile reference for a profile database.
|
||||
* @param filename for the database, excluded the .sqlite extension.
|
||||
*/
|
||||
function new_db_file(name)
|
||||
{
|
||||
let file = Services.dirsvc.get("ProfD", Ci.nsIFile);
|
||||
file.append(name + ".sqlite");
|
||||
return file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens and returns a connection to the provided database file.
|
||||
* @param nsIFile interface to the database file.
|
||||
*/
|
||||
function getDatabase(aFile)
|
||||
{
|
||||
return Cc["@mozilla.org/storage/service;1"].getService(Ci.mozIStorageService)
|
||||
.openDatabase(aFile);
|
||||
}
|
||||
|
||||
function vacuumParticipant()
|
||||
{
|
||||
this._dbConn = getDatabase(new_db_file("testVacuum"));
|
||||
Services.obs.addObserver(this, "test-options", false);
|
||||
}
|
||||
|
||||
vacuumParticipant.prototype =
|
||||
{
|
||||
classDescription: "vacuumParticipant",
|
||||
classID: Components.ID("{52aa0b22-b82f-4e38-992a-c3675a3355d2}"),
|
||||
contractID: "@unit.test.com/test-vacuum-participant;1",
|
||||
|
||||
get expectedDatabasePageSize() {
|
||||
return this._dbConn.defaultPageSize;
|
||||
},
|
||||
get databaseConnection() {
|
||||
return this._dbConn;
|
||||
},
|
||||
|
||||
_grant: true,
|
||||
onBeginVacuum: function TVP_onBeginVacuum()
|
||||
{
|
||||
if (!this._grant) {
|
||||
this._grant = true;
|
||||
return false;
|
||||
}
|
||||
Services.obs.notifyObservers(null, "test-begin-vacuum", null);
|
||||
return true;
|
||||
},
|
||||
onEndVacuum: function TVP_EndVacuum(aSucceeded)
|
||||
{
|
||||
if (this._stmt) {
|
||||
this._stmt.finalize();
|
||||
}
|
||||
Services.obs.notifyObservers(null, "test-end-vacuum", aSucceeded);
|
||||
},
|
||||
|
||||
observe: function TVP_observe(aSubject, aTopic, aData)
|
||||
{
|
||||
if (aData == "opt-out") {
|
||||
this._grant = false;
|
||||
}
|
||||
else if (aData == "wal") {
|
||||
try {
|
||||
this._dbConn.close();
|
||||
} catch (e) {
|
||||
// Do nothing.
|
||||
}
|
||||
this._dbConn = getDatabase(new_db_file("testVacuum2"));
|
||||
}
|
||||
else if (aData == "wal-fail") {
|
||||
try {
|
||||
this._dbConn.close();
|
||||
} catch (e) {
|
||||
// Do nothing.
|
||||
}
|
||||
this._dbConn = getDatabase(new_db_file("testVacuum3"));
|
||||
// Use WAL journal mode.
|
||||
this._dbConn.executeSimpleSQL("PRAGMA journal_mode = WAL");
|
||||
// Create a not finalized statement.
|
||||
this._stmt = this._dbConn.createStatement("SELECT :test");
|
||||
this._stmt.params.test = 1;
|
||||
this._stmt.executeStep();
|
||||
}
|
||||
else if (aData == "memory") {
|
||||
try {
|
||||
this._dbConn.asyncClose();
|
||||
} catch (e) {
|
||||
// Do nothing.
|
||||
}
|
||||
this._dbConn = Cc["@mozilla.org/storage/service;1"].
|
||||
getService(Ci.mozIStorageService).
|
||||
openSpecialDatabase("memory");
|
||||
}
|
||||
else if (aData == "dispose") {
|
||||
Services.obs.removeObserver(this, "test-options");
|
||||
try {
|
||||
this._dbConn.asyncClose();
|
||||
} catch (e) {
|
||||
// Do nothing.
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([
|
||||
Ci.mozIStorageVacuumParticipant,
|
||||
Ci.nsIObserver,
|
||||
])
|
||||
};
|
||||
|
||||
var gComponentsArray = [vacuumParticipant];
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory(gComponentsArray);
|
||||
3
storage/test/unit/vacuumParticipant.manifest
Normal file
3
storage/test/unit/vacuumParticipant.manifest
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
component {52aa0b22-b82f-4e38-992a-c3675a3355d2} vacuumParticipant.js
|
||||
contract @unit.test.com/test-vacuum-participant;1 {52aa0b22-b82f-4e38-992a-c3675a3355d2}
|
||||
category vacuum-participant vacuumParticipant @unit.test.com/test-vacuum-participant;1
|
||||
46
storage/test/unit/xpcshell.ini
Normal file
46
storage/test/unit/xpcshell.ini
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
[DEFAULT]
|
||||
head = head_storage.js
|
||||
tail =
|
||||
support-files =
|
||||
corruptDB.sqlite
|
||||
fakeDB.sqlite
|
||||
locale_collation.txt
|
||||
vacuumParticipant.js
|
||||
vacuumParticipant.manifest
|
||||
|
||||
[test_bug-365166.js]
|
||||
[test_bug-393952.js]
|
||||
[test_bug-429521.js]
|
||||
[test_bug-444233.js]
|
||||
[test_cache_size.js]
|
||||
[test_chunk_growth.js]
|
||||
# Bug 676981: test fails consistently on Android
|
||||
fail-if = os == "android"
|
||||
[test_connection_asyncClose.js]
|
||||
[test_connection_executeAsync.js]
|
||||
[test_connection_executeSimpleSQLAsync.js]
|
||||
[test_js_helpers.js]
|
||||
[test_levenshtein.js]
|
||||
[test_like.js]
|
||||
[test_like_escape.js]
|
||||
[test_locale_collation.js]
|
||||
[test_page_size_is_32k.js]
|
||||
[test_sqlite_secure_delete.js]
|
||||
[test_statement_executeAsync.js]
|
||||
[test_statement_wrapper_automatically.js]
|
||||
[test_storage_aggregates.js]
|
||||
[test_storage_connection.js]
|
||||
# Bug 676981: test fails consistently on Android
|
||||
fail-if = os == "android"
|
||||
[test_storage_fulltextindex.js]
|
||||
[test_storage_function.js]
|
||||
[test_storage_progresshandler.js]
|
||||
[test_storage_service.js]
|
||||
[test_storage_service_unshared.js]
|
||||
[test_storage_statement.js]
|
||||
[test_storage_value_array.js]
|
||||
[test_unicode.js]
|
||||
[test_vacuum.js]
|
||||
[test_telemetry_vfs.js]
|
||||
# Bug 676981: test fails consistently on Android
|
||||
# fail-if = os == "android"
|
||||
Loading…
Add table
Add a link
Reference in a new issue