1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "places_test_harness.h"
#include "../../ConcurrentConnection.h"
#include "../../Helpers.h"
#include "mozilla/Assertions.h"
#include "mozIStorageBindingParamsArray.h"
#include "mozIStorageResultSet.h"
#include "mozIStorageRow.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsIFile.h"
using namespace mozilla;
using namespace mozilla::places;
/**
* This file tests the ConcurrentConnection class.
*/
namespace {
class StatementCallback final : public PendingStatementCallback {
public:
NS_INLINE_DECL_REFCOUNTING_INHERITED(StatementCallback,
PendingStatementCallback);
explicit StatementCallback(const nsCString& aParamValue)
: mParamValue(aParamValue) {};
NS_IMETHOD HandleResult(mozIStorageResultSet* aResultSet) override {
nsCOMPtr<mozIStorageRow> row;
MOZ_ALWAYS_SUCCEEDS(aResultSet->GetNextRow(getter_AddRefs(row)));
MOZ_ALWAYS_SUCCEEDS(row->GetUTF8String(0, mValue));
return NS_OK;
}
NS_IMETHOD
HandleError(mozIStorageError* aError) override {
MOZ_DIAGNOSTIC_CRASH("Unexpected error");
return NS_OK;
}
NS_IMETHOD HandleCompletion(uint16_t aReason) override {
mRv = aReason;
mCompleted = true;
return NS_OK;
}
nsresult BindParams(mozIStorageBindingParamsArray* aParamsArray) override {
NS_ENSURE_ARG(aParamsArray);
nsCOMPtr<mozIStorageBindingParams> params;
nsresult rv = aParamsArray->NewBindingParams(getter_AddRefs(params));
NS_ENSURE_SUCCESS(rv, rv);
rv = params->BindUTF8StringByIndex(0, mParamValue);
NS_ENSURE_SUCCESS(rv, rv);
return aParamsArray->AddParams(params);
}
uint16_t SpinUntilCompleted() {
nsCOMPtr<nsIThread> thread(::do_GetCurrentThread());
nsresult rv = NS_OK;
bool processed = true;
while (!mCompleted && NS_SUCCEEDED(rv)) {
rv = thread->ProcessNextEvent(true, &processed);
}
return mRv;
}
nsCString mValue;
private:
~StatementCallback() = default;
bool mCompleted = false;
uint16_t mRv = 0;
nsCString mParamValue;
};
class TestRunnable : public Runnable {
public:
TestRunnable()
: Runnable("places::TestRunnable"), mHasResult(false), mDidRun(false) {};
NS_IMETHOD Run() override {
MOZ_DIAGNOSTIC_ASSERT(!NS_IsMainThread(),
"Should not be called on the main thread");
auto conn = ConcurrentConnection::GetInstance();
MOZ_DIAGNOSTIC_ASSERT(conn.isSome());
nsCOMPtr<mozIStorageStatement> stmt =
conn.value()->GetStatementOnHelperThread(
"SELECT * FROM sqlite_master WHERE tbl_name = 'moz_places'"_ns);
MOZ_DIAGNOSTIC_ASSERT(stmt);
mozStorageStatementScoper scoper(stmt);
bool hasResult;
MOZ_ALWAYS_SUCCEEDS(stmt->ExecuteStep(&hasResult));
mHasResult = hasResult;
mDidRun = true;
return NS_OK;
}
bool SpinUntilResult() {
nsCOMPtr<nsIThread> thread(::do_GetCurrentThread());
nsresult rv = NS_OK;
bool processed = true;
while (!mDidRun && NS_SUCCEEDED(rv)) {
rv = thread->ProcessNextEvent(true, &processed);
}
return mHasResult;
}
private:
Atomic<bool> mHasResult;
Atomic<bool> mDidRun;
~TestRunnable() = default;
};
} // Anonymous namespace
TEST(test_ConcurrentConnection, test_setup)
{
// Tinderboxes are constantly on idle. Since idle tasks can interact with
// tests, causing random failures, disable the idle service.
disable_idle_service();
// Check there's no Places database file.
nsCOMPtr<nsIFile> file;
NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(file));
(void)file->Append(u"places.sqlite"_ns);
bool exists = false;
ASSERT_TRUE(NS_SUCCEEDED(file->Exists(&exists)));
if (exists) {
(void)file->Remove(false);
(void)file->SetLeafName(u"favicons.sqlite"_ns);
(void)file->Remove(false);
}
}
TEST(test_ConcurrentConnection, test_database_not_present)
{
// Initialize ConcurrentConnection.
auto conn = ConcurrentConnection::GetInstance();
MOZ_DIAGNOSTIC_ASSERT(conn.isSome());
RefPtr<StatementCallback> cb =
MakeAndAddRef<StatementCallback>("moz_icons"_ns);
conn.value()->Queue(
"SELECT name FROM favicons.sqlite_master WHERE type = 'table' AND tbl_name = ?"_ns,
cb);
RefPtr<TestRunnable> event = MakeAndAddRef<TestRunnable>();
conn.value()->Queue(event);
// Must await for Places to create and initialize the database as there's no
// database file at this time. This initialized Places.
nsCOMPtr<mozIStorageConnection> placesConn = do_get_db();
do_check_true(placesConn);
ASSERT_EQ(cb->SpinUntilCompleted(),
mozIStorageStatementCallback::REASON_FINISHED);
ASSERT_TRUE(cb->mValue.EqualsLiteral("moz_icons"));
ASSERT_TRUE(event->SpinUntilResult());
}
TEST(test_ConcurrentConnection, test_database_initialized)
{
// Initialize ConcurrentConnection.
auto conn = ConcurrentConnection::GetInstance();
MOZ_DIAGNOSTIC_ASSERT(conn.isSome());
RefPtr<StatementCallback> cb =
MakeAndAddRef<StatementCallback>("moz_places"_ns);
conn.value()->Queue(
"SELECT name FROM sqlite_master WHERE type = 'table' AND tbl_name = ?"_ns,
cb);
// Statement should be executed as Places was already initialized.
ASSERT_EQ(cb->SpinUntilCompleted(),
mozIStorageStatementCallback::REASON_FINISHED);
ASSERT_TRUE(cb->mValue.EqualsLiteral("moz_places"));
}
TEST(test_ConcurrentConnection, test_shutdown)
{
RefPtr<WaitForConnectionClosed> spinClose = new WaitForConnectionClosed();
// And let any other events finish before we quit.
(void)NS_ProcessPendingEvents(nullptr);
}
|