summaryrefslogtreecommitdiffstats
path: root/dom/quota/test/gtest/TestQuotaManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/quota/test/gtest/TestQuotaManager.cpp')
-rw-r--r--dom/quota/test/gtest/TestQuotaManager.cpp1611
1 files changed, 1611 insertions, 0 deletions
diff --git a/dom/quota/test/gtest/TestQuotaManager.cpp b/dom/quota/test/gtest/TestQuotaManager.cpp
new file mode 100644
index 0000000000..c93f18ca61
--- /dev/null
+++ b/dom/quota/test/gtest/TestQuotaManager.cpp
@@ -0,0 +1,1611 @@
+/* -*- 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 "gtest/gtest.h"
+#include "mozilla/SpinEventLoopUntil.h"
+#include "mozilla/dom/quota/DirectoryLock.h"
+#include "mozilla/dom/quota/OriginScope.h"
+#include "mozilla/dom/quota/QuotaManager.h"
+#include "mozilla/gtest/MozAssertions.h"
+#include "QuotaManagerDependencyFixture.h"
+
+namespace mozilla::dom::quota::test {
+
+class TestQuotaManager : public QuotaManagerDependencyFixture {
+ public:
+ static void SetUpTestCase() { ASSERT_NO_FATAL_FAILURE(InitializeFixture()); }
+
+ static void TearDownTestCase() { ASSERT_NO_FATAL_FAILURE(ShutdownFixture()); }
+};
+
+// Test OpenStorageDirectory when an opening of the storage directory is
+// already ongoing and storage shutdown is scheduled after that.
+TEST_F(TestQuotaManager, OpenStorageDirectory_OngoingWithScheduledShutdown) {
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageNotInitialized());
+
+ PerformOnBackgroundThread([]() {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ RefPtr<UniversalDirectoryLock> directoryLock;
+
+ nsTArray<RefPtr<BoolPromise>> promises;
+
+ promises.AppendElement(
+ quotaManager
+ ->OpenStorageDirectory(
+ Nullable<PersistenceType>(PERSISTENCE_TYPE_PERSISTENT),
+ OriginScope::FromNull(), Nullable<Client::Type>(),
+ /* aExclusive */ false)
+ ->Then(GetCurrentSerialEventTarget(), __func__,
+ [&directoryLock](
+ UniversalDirectoryLockPromise::ResolveOrRejectValue&&
+ aValue) {
+ if (aValue.IsReject()) {
+ return BoolPromise::CreateAndReject(aValue.RejectValue(),
+ __func__);
+ }
+
+ [&aValue]() { ASSERT_TRUE(aValue.ResolveValue()); }();
+
+ directoryLock = std::move(aValue.ResolveValue());
+
+ return BoolPromise::CreateAndResolve(true, __func__);
+ })
+ ->Then(quotaManager->IOThread(), __func__,
+ [](const BoolPromise::ResolveOrRejectValue& aValue) {
+ if (aValue.IsReject()) {
+ return BoolPromise::CreateAndReject(aValue.RejectValue(),
+ __func__);
+ }
+
+ []() {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ ASSERT_TRUE(
+ quotaManager->IsStorageInitializedInternal());
+ }();
+
+ return BoolPromise::CreateAndResolve(true, __func__);
+ })
+ ->Then(GetCurrentSerialEventTarget(), __func__,
+ [&directoryLock](
+ const BoolPromise::ResolveOrRejectValue& aValue) {
+ directoryLock = nullptr;
+
+ if (aValue.IsReject()) {
+ return BoolPromise::CreateAndReject(aValue.RejectValue(),
+ __func__);
+ }
+
+ return BoolPromise::CreateAndResolve(true, __func__);
+ }));
+ promises.AppendElement(quotaManager->ShutdownStorage());
+ promises.AppendElement(
+ quotaManager
+ ->OpenStorageDirectory(
+ Nullable<PersistenceType>(PERSISTENCE_TYPE_PERSISTENT),
+ OriginScope::FromNull(), Nullable<Client::Type>(),
+ /* aExclusive */ false)
+ ->Then(GetCurrentSerialEventTarget(), __func__,
+ [](const UniversalDirectoryLockPromise::ResolveOrRejectValue&
+ aValue) {
+ if (aValue.IsReject()) {
+ return BoolPromise::CreateAndReject(aValue.RejectValue(),
+ __func__);
+ }
+
+ return BoolPromise::CreateAndResolve(true, __func__);
+ }));
+
+ bool done = false;
+
+ BoolPromise::All(GetCurrentSerialEventTarget(), promises)
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [&done](const CopyableTArray<bool>& aResolveValues) {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ ASSERT_TRUE(quotaManager->IsStorageInitialized());
+
+ done = true;
+ },
+ [&done](nsresult aRejectValue) {
+ ASSERT_TRUE(false);
+
+ done = true;
+ });
+
+ SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
+ });
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageInitialized());
+
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+}
+
+// Test OpenStorageDirectory when an opening of the storage directory is
+// already ongoing and an exclusive directory lock is requested after that.
+TEST_F(TestQuotaManager,
+ OpenStorageDirectory_OngoingWithExclusiveDirectoryLock) {
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageNotInitialized());
+
+ PerformOnBackgroundThread([]() {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ RefPtr<UniversalDirectoryLock> directoryLock =
+ quotaManager->CreateDirectoryLockInternal(Nullable<PersistenceType>(),
+ OriginScope::FromNull(),
+ Nullable<Client::Type>(),
+ /* aExclusive */ true);
+
+ nsTArray<RefPtr<BoolPromise>> promises;
+
+ promises.AppendElement(
+ quotaManager
+ ->OpenStorageDirectory(
+ Nullable<PersistenceType>(PERSISTENCE_TYPE_PERSISTENT),
+ OriginScope::FromNull(), Nullable<Client::Type>(),
+ /* aExclusive */ false)
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [&directoryLock](
+ const UniversalDirectoryLockPromise::ResolveOrRejectValue&
+ aValue) {
+ directoryLock = nullptr;
+
+ if (aValue.IsReject()) {
+ return BoolPromise::CreateAndReject(aValue.RejectValue(),
+ __func__);
+ }
+
+ return BoolPromise::CreateAndResolve(true, __func__);
+ }));
+ promises.AppendElement(directoryLock->Acquire());
+ promises.AppendElement(
+ quotaManager
+ ->OpenStorageDirectory(
+ Nullable<PersistenceType>(PERSISTENCE_TYPE_PERSISTENT),
+ OriginScope::FromNull(), Nullable<Client::Type>(),
+ /* aExclusive */ false)
+ ->Then(GetCurrentSerialEventTarget(), __func__,
+ [](const UniversalDirectoryLockPromise::ResolveOrRejectValue&
+ aValue) {
+ if (aValue.IsReject()) {
+ return BoolPromise::CreateAndReject(aValue.RejectValue(),
+ __func__);
+ }
+
+ return BoolPromise::CreateAndResolve(true, __func__);
+ }));
+
+ bool done = false;
+
+ BoolPromise::All(GetCurrentSerialEventTarget(), promises)
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [&done](const CopyableTArray<bool>& aResolveValues) {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ ASSERT_TRUE(quotaManager->IsStorageInitialized());
+
+ done = true;
+ },
+ [&done](nsresult aRejectValue) {
+ ASSERT_TRUE(false);
+
+ done = true;
+ });
+
+ SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
+ });
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageInitialized());
+
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+}
+
+// Test OpenStorageDirectory when an opening of the storage directory already
+// finished.
+TEST_F(TestQuotaManager, OpenStorageDirectory_Finished) {
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageNotInitialized());
+
+ PerformOnBackgroundThread([]() {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ bool done = false;
+
+ quotaManager
+ ->OpenStorageDirectory(
+ Nullable<PersistenceType>(PERSISTENCE_TYPE_PERSISTENT),
+ OriginScope::FromNull(), Nullable<Client::Type>(),
+ /* aExclusive */ false)
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [&done](const UniversalDirectoryLockPromise::ResolveOrRejectValue&
+ aValue) {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ ASSERT_TRUE(quotaManager->IsStorageInitialized());
+
+ done = true;
+ });
+
+ SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
+
+ done = false;
+
+ quotaManager
+ ->OpenStorageDirectory(
+ Nullable<PersistenceType>(PERSISTENCE_TYPE_PERSISTENT),
+ OriginScope::FromNull(), Nullable<Client::Type>(),
+ /* aExclusive */ false)
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [&done](const UniversalDirectoryLockPromise::ResolveOrRejectValue&
+ aValue) {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ ASSERT_TRUE(quotaManager->IsStorageInitialized());
+
+ done = true;
+ });
+
+ SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
+ });
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageInitialized());
+
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+}
+
+// Test OpenStorageDirectory when an opening of the storage directory already
+// finished but storage shutdown has just been scheduled.
+TEST_F(TestQuotaManager, OpenStorageDirectory_FinishedWithScheduledShutdown) {
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageNotInitialized());
+
+ PerformOnBackgroundThread([]() {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ bool done = false;
+
+ quotaManager
+ ->OpenStorageDirectory(
+ Nullable<PersistenceType>(PERSISTENCE_TYPE_PERSISTENT),
+ OriginScope::FromNull(), Nullable<Client::Type>(),
+ /* aExclusive */ false)
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [&done](const UniversalDirectoryLockPromise::ResolveOrRejectValue&
+ aValue) {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ ASSERT_TRUE(quotaManager->IsStorageInitialized());
+
+ done = true;
+ });
+
+ SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
+
+ nsTArray<RefPtr<BoolPromise>> promises;
+
+ promises.AppendElement(quotaManager->ShutdownStorage());
+ promises.AppendElement(
+ quotaManager
+ ->OpenStorageDirectory(
+ Nullable<PersistenceType>(PERSISTENCE_TYPE_PERSISTENT),
+ OriginScope::FromNull(), Nullable<Client::Type>(),
+ /* aExclusive */ false)
+ ->Then(GetCurrentSerialEventTarget(), __func__,
+ [](const UniversalDirectoryLockPromise::ResolveOrRejectValue&
+ aValue) {
+ if (aValue.IsReject()) {
+ return BoolPromise::CreateAndReject(aValue.RejectValue(),
+ __func__);
+ }
+
+ return BoolPromise::CreateAndResolve(true, __func__);
+ }));
+
+ done = false;
+
+ BoolPromise::All(GetCurrentSerialEventTarget(), promises)
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [&done](const CopyableTArray<bool>& aResolveValues) {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ ASSERT_TRUE(quotaManager->IsStorageInitialized());
+
+ done = true;
+ },
+ [&done](nsresult aRejectValue) {
+ ASSERT_TRUE(false);
+
+ done = true;
+ });
+
+ SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
+ });
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageInitialized());
+
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+}
+
+// Test OpenStorageDirectory when an opening of the storage directory already
+// finished and an exclusive client directory lock for a non-overlapping
+// origin is acquired in between.
+TEST_F(TestQuotaManager,
+ OpenStorageDirectory_FinishedWithExclusiveClientDirectoryLock) {
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageNotInitialized());
+
+ PerformOnBackgroundThread([]() {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ bool done = false;
+
+ quotaManager
+ ->OpenStorageDirectory(
+ Nullable<PersistenceType>(PERSISTENCE_TYPE_PERSISTENT),
+ OriginScope::FromNull(), Nullable<Client::Type>(),
+ /* aExclusive */ false)
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [&done](const UniversalDirectoryLockPromise::ResolveOrRejectValue&
+ aValue) {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ ASSERT_TRUE(quotaManager->IsStorageInitialized());
+
+ done = true;
+ });
+
+ SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
+
+ RefPtr<ClientDirectoryLock> directoryLock =
+ quotaManager->CreateDirectoryLock(GetTestClientMetadata(),
+ /* aExclusive */ true);
+
+ done = false;
+
+ directoryLock->Acquire()->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [&done](const BoolPromise::ResolveOrRejectValue& aValue) {
+ done = true;
+ });
+
+ SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
+
+ done = false;
+
+ quotaManager
+ ->OpenStorageDirectory(
+ Nullable<PersistenceType>(PERSISTENCE_TYPE_PERSISTENT),
+ OriginScope::FromNull(), Nullable<Client::Type>(),
+ /* aExclusive */ false)
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [&done](const UniversalDirectoryLockPromise::ResolveOrRejectValue&
+ aValue) {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ ASSERT_TRUE(quotaManager->IsStorageInitialized());
+
+ done = true;
+ });
+
+ SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
+ });
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageInitialized());
+
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+}
+
+// Test OpenClientDirctory when an opening of a client directory is already
+// ongoing and storage shutdown is scheduled after that.
+TEST_F(TestQuotaManager, OpenClientDirectory_OngoingWithScheduledShutdown) {
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageNotInitialized());
+
+ PerformOnBackgroundThread([]() {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ RefPtr<ClientDirectoryLock> directoryLock;
+
+ nsTArray<RefPtr<BoolPromise>> promises;
+
+ promises.AppendElement(
+ quotaManager->OpenClientDirectory(GetTestClientMetadata())
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [&directoryLock](
+ ClientDirectoryLockPromise::ResolveOrRejectValue&& aValue) {
+ if (aValue.IsReject()) {
+ return BoolPromise::CreateAndReject(aValue.RejectValue(),
+ __func__);
+ }
+
+ [&aValue]() { ASSERT_TRUE(aValue.ResolveValue()); }();
+
+ directoryLock = std::move(aValue.ResolveValue());
+
+ return BoolPromise::CreateAndResolve(true, __func__);
+ })
+ ->Then(quotaManager->IOThread(), __func__,
+ [](const BoolPromise::ResolveOrRejectValue& aValue) {
+ if (aValue.IsReject()) {
+ return BoolPromise::CreateAndReject(aValue.RejectValue(),
+ __func__);
+ }
+
+ []() {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ ASSERT_TRUE(
+ quotaManager->IsStorageInitializedInternal());
+ }();
+
+ return BoolPromise::CreateAndResolve(true, __func__);
+ })
+ ->Then(GetCurrentSerialEventTarget(), __func__,
+ [&directoryLock](
+ const BoolPromise::ResolveOrRejectValue& aValue) {
+ directoryLock = nullptr;
+
+ if (aValue.IsReject()) {
+ return BoolPromise::CreateAndReject(aValue.RejectValue(),
+ __func__);
+ }
+
+ return BoolPromise::CreateAndResolve(true, __func__);
+ }));
+ promises.AppendElement(quotaManager->ShutdownStorage());
+ promises.AppendElement(
+ quotaManager->OpenClientDirectory(GetTestClientMetadata())
+ ->Then(GetCurrentSerialEventTarget(), __func__,
+ [](const ClientDirectoryLockPromise::ResolveOrRejectValue&
+ aValue) {
+ if (aValue.IsReject()) {
+ return BoolPromise::CreateAndReject(aValue.RejectValue(),
+ __func__);
+ }
+
+ return BoolPromise::CreateAndResolve(true, __func__);
+ }));
+
+ bool done = false;
+
+ BoolPromise::All(GetCurrentSerialEventTarget(), promises)
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [&done](const CopyableTArray<bool>& aResolveValues) {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ ASSERT_TRUE(quotaManager->IsStorageInitialized());
+
+ done = true;
+ },
+ [&done](nsresult aRejectValue) {
+ ASSERT_TRUE(false);
+
+ done = true;
+ });
+
+ SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
+ });
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageInitialized());
+
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+}
+
+// Test OpenClientDirectory when an opening of a client directory is already
+// ongoing and an exclusive directory lock is requested after that.
+TEST_F(TestQuotaManager,
+ OpenClientDirectory_OngoingWithExclusiveDirectoryLock) {
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageNotInitialized());
+
+ PerformOnBackgroundThread([]() {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ RefPtr<UniversalDirectoryLock> directoryLock =
+ quotaManager->CreateDirectoryLockInternal(Nullable<PersistenceType>(),
+ OriginScope::FromNull(),
+ Nullable<Client::Type>(),
+ /* aExclusive */ true);
+
+ nsTArray<RefPtr<BoolPromise>> promises;
+
+ promises.AppendElement(
+ quotaManager->OpenClientDirectory(GetTestClientMetadata())
+ ->Then(GetCurrentSerialEventTarget(), __func__,
+ [&directoryLock](
+ const ClientDirectoryLockPromise::ResolveOrRejectValue&
+ aValue) {
+ directoryLock = nullptr;
+
+ if (aValue.IsReject()) {
+ return BoolPromise::CreateAndReject(aValue.RejectValue(),
+ __func__);
+ }
+
+ return BoolPromise::CreateAndResolve(true, __func__);
+ }));
+ promises.AppendElement(directoryLock->Acquire());
+ promises.AppendElement(
+ quotaManager->OpenClientDirectory(GetTestClientMetadata())
+ ->Then(GetCurrentSerialEventTarget(), __func__,
+ [](const ClientDirectoryLockPromise::ResolveOrRejectValue&
+ aValue) {
+ if (aValue.IsReject()) {
+ return BoolPromise::CreateAndReject(aValue.RejectValue(),
+ __func__);
+ }
+
+ return BoolPromise::CreateAndResolve(true, __func__);
+ }));
+
+ bool done = false;
+
+ BoolPromise::All(GetCurrentSerialEventTarget(), promises)
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [&done](const CopyableTArray<bool>& aResolveValues) {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ ASSERT_TRUE(quotaManager->IsStorageInitialized());
+
+ done = true;
+ },
+ [&done](nsresult aRejectValue) {
+ ASSERT_TRUE(false);
+
+ done = true;
+ });
+
+ SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
+ });
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageInitialized());
+
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+}
+
+// Test OpenClientDirectory when an opening of a client directory already
+// finished.
+TEST_F(TestQuotaManager, OpenClientDirectory_Finished) {
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageNotInitialized());
+
+ PerformOnBackgroundThread([]() {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ bool done = false;
+
+ quotaManager->OpenClientDirectory(GetTestClientMetadata())
+ ->Then(GetCurrentSerialEventTarget(), __func__,
+ [&done](const ClientDirectoryLockPromise::ResolveOrRejectValue&
+ aValue) {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ ASSERT_TRUE(quotaManager->IsStorageInitialized());
+
+ done = true;
+ });
+
+ SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
+
+ done = false;
+
+ quotaManager->OpenClientDirectory(GetTestClientMetadata())
+ ->Then(GetCurrentSerialEventTarget(), __func__,
+ [&done](const ClientDirectoryLockPromise::ResolveOrRejectValue&
+ aValue) {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ ASSERT_TRUE(quotaManager->IsStorageInitialized());
+
+ done = true;
+ });
+
+ SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
+ });
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageInitialized());
+
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+}
+
+// Test OpenClientDirectory when an opening of a client directory already
+// finished but storage shutdown has just been scheduled.
+TEST_F(TestQuotaManager, OpenClientDirectory_FinishedWithScheduledShutdown) {
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageNotInitialized());
+
+ PerformOnBackgroundThread([]() {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ bool done = false;
+
+ quotaManager->OpenClientDirectory(GetTestClientMetadata())
+ ->Then(GetCurrentSerialEventTarget(), __func__,
+ [&done](const ClientDirectoryLockPromise::ResolveOrRejectValue&
+ aValue) {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ ASSERT_TRUE(quotaManager->IsStorageInitialized());
+
+ done = true;
+ });
+
+ SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
+
+ nsTArray<RefPtr<BoolPromise>> promises;
+
+ promises.AppendElement(quotaManager->ShutdownStorage());
+ promises.AppendElement(
+ quotaManager->OpenClientDirectory(GetTestClientMetadata())
+ ->Then(GetCurrentSerialEventTarget(), __func__,
+ [](const ClientDirectoryLockPromise::ResolveOrRejectValue&
+ aValue) {
+ if (aValue.IsReject()) {
+ return BoolPromise::CreateAndReject(aValue.RejectValue(),
+ __func__);
+ }
+
+ return BoolPromise::CreateAndResolve(true, __func__);
+ }));
+
+ done = false;
+
+ BoolPromise::All(GetCurrentSerialEventTarget(), promises)
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [&done](const CopyableTArray<bool>& aResolveValues) {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ ASSERT_TRUE(quotaManager->IsStorageInitialized());
+
+ done = true;
+ },
+ [&done](nsresult aRejectValue) {
+ ASSERT_TRUE(false);
+
+ done = true;
+ });
+
+ SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
+ });
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageInitialized());
+
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+}
+
+// Test OpenClientDirectory when an opening of a client directory already
+// finished with an exclusive client directory lock for a different origin is
+// acquired in between.
+TEST_F(TestQuotaManager,
+ OpenClientDirectory_FinishedWithOtherExclusiveClientDirectoryLock) {
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageNotInitialized());
+
+ PerformOnBackgroundThread([]() {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ bool done = false;
+
+ quotaManager->OpenClientDirectory(GetTestClientMetadata())
+ ->Then(GetCurrentSerialEventTarget(), __func__,
+ [&done](const ClientDirectoryLockPromise::ResolveOrRejectValue&
+ aValue) {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ ASSERT_TRUE(quotaManager->IsStorageInitialized());
+
+ done = true;
+ });
+
+ SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
+
+ RefPtr<ClientDirectoryLock> directoryLock =
+ quotaManager->CreateDirectoryLock(GetOtherTestClientMetadata(),
+ /* aExclusive */ true);
+
+ done = false;
+
+ directoryLock->Acquire()->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [&done](const BoolPromise::ResolveOrRejectValue& aValue) {
+ done = true;
+ });
+
+ SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
+
+ done = false;
+
+ quotaManager->OpenClientDirectory(GetTestClientMetadata())
+ ->Then(GetCurrentSerialEventTarget(), __func__,
+ [&done](const ClientDirectoryLockPromise::ResolveOrRejectValue&
+ aValue) {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ ASSERT_TRUE(quotaManager->IsStorageInitialized());
+
+ done = true;
+ });
+
+ SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
+ });
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageInitialized());
+
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+}
+
+// Test simple InitializeStorage.
+TEST_F(TestQuotaManager, InitializeStorage_Simple) {
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageNotInitialized());
+
+ PerformOnBackgroundThread([]() {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ bool done = false;
+
+ quotaManager->InitializeStorage()->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [&done](const BoolPromise::ResolveOrRejectValue& aValue) {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ ASSERT_TRUE(quotaManager->IsStorageInitialized());
+
+ done = true;
+ });
+
+ SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
+ });
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageInitialized());
+
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+}
+
+// Test InitializeStorage when a storage initialization is already ongoing.
+TEST_F(TestQuotaManager, InitializeStorage_Ongoing) {
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageNotInitialized());
+
+ PerformOnBackgroundThread([]() {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ nsTArray<RefPtr<BoolPromise>> promises;
+
+ promises.AppendElement(quotaManager->InitializeStorage());
+ promises.AppendElement(quotaManager->InitializeStorage());
+
+ bool done = false;
+
+ BoolPromise::All(GetCurrentSerialEventTarget(), promises)
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [&done](const CopyableTArray<bool>& aResolveValues) {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ ASSERT_TRUE(quotaManager->IsStorageInitialized());
+
+ done = true;
+ },
+ [&done](nsresult aRejectValue) {
+ ASSERT_TRUE(false);
+
+ done = true;
+ });
+
+ SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
+ });
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageInitialized());
+
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+}
+
+// Test InitializeStorage when a storage initialization is already ongoing and
+// storage shutdown is scheduled after that.
+TEST_F(TestQuotaManager, InitializeStorage_OngoingWithScheduledShutdown) {
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageNotInitialized());
+
+ PerformOnBackgroundThread([]() {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ nsTArray<RefPtr<BoolPromise>> promises;
+
+ promises.AppendElement(quotaManager->InitializeStorage());
+ promises.AppendElement(quotaManager->ShutdownStorage());
+ promises.AppendElement(quotaManager->InitializeStorage());
+
+ bool done = false;
+
+ BoolPromise::All(GetCurrentSerialEventTarget(), promises)
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [&done](const CopyableTArray<bool>& aResolveValues) {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ ASSERT_TRUE(quotaManager->IsStorageInitialized());
+
+ done = true;
+ },
+ [&done](nsresult aRejectValue) {
+ ASSERT_TRUE(false);
+
+ done = true;
+ });
+
+ SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
+ });
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageInitialized());
+
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+}
+
+// Test InitializeStorage when a storage initialization is already ongoing and
+// an exclusive directory lock is requested after that.
+TEST_F(TestQuotaManager, InitializeStorage_OngoingWithExclusiveDirectoryLock) {
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageNotInitialized());
+
+ PerformOnBackgroundThread([]() {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ RefPtr<UniversalDirectoryLock> directoryLock =
+ quotaManager->CreateDirectoryLockInternal(Nullable<PersistenceType>(),
+ OriginScope::FromNull(),
+ Nullable<Client::Type>(),
+ /* aExclusive */ true);
+
+ nsTArray<RefPtr<BoolPromise>> promises;
+
+ promises.AppendElement(quotaManager->InitializeStorage()->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [&directoryLock](const BoolPromise::ResolveOrRejectValue& aValue) {
+ // The exclusive directory lock must be released when the first
+ // storage initialization is finished, otherwise it would endlessly
+ // block the second storage initialization.
+ directoryLock = nullptr;
+
+ if (aValue.IsReject()) {
+ return BoolPromise::CreateAndReject(aValue.RejectValue(), __func__);
+ }
+
+ return BoolPromise::CreateAndResolve(true, __func__);
+ }));
+ promises.AppendElement(directoryLock->Acquire());
+ promises.AppendElement(quotaManager->InitializeStorage());
+
+ bool done = false;
+
+ BoolPromise::All(GetCurrentSerialEventTarget(), promises)
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [&done](const CopyableTArray<bool>& aResolveValues) {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ ASSERT_TRUE(quotaManager->IsStorageInitialized());
+
+ done = true;
+ },
+ [&done](nsresult aRejectValue) {
+ ASSERT_TRUE(false);
+
+ done = true;
+ });
+
+ SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
+ });
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageInitialized());
+
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+}
+
+// Test InitializeStorage when a storage initialization is already ongoing and
+// shared client directory locks are requested after that.
+// The shared client directory locks don't have to be released in this case.
+TEST_F(TestQuotaManager, InitializeStorage_OngoingWithClientDirectoryLocks) {
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageNotInitialized());
+
+ PerformOnBackgroundThread([]() {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ RefPtr<ClientDirectoryLock> directoryLock =
+ quotaManager->CreateDirectoryLock(GetTestClientMetadata(),
+ /* aExclusive */ false);
+
+ RefPtr<ClientDirectoryLock> directoryLock2 =
+ quotaManager->CreateDirectoryLock(GetTestClientMetadata(),
+ /* aExclusive */ false);
+
+ nsTArray<RefPtr<BoolPromise>> promises;
+
+ promises.AppendElement(quotaManager->InitializeStorage());
+ promises.AppendElement(directoryLock->Acquire());
+ promises.AppendElement(quotaManager->InitializeStorage());
+ promises.AppendElement(directoryLock2->Acquire());
+
+ bool done = false;
+
+ BoolPromise::All(GetCurrentSerialEventTarget(), promises)
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [&done](const CopyableTArray<bool>& aResolveValues) {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ ASSERT_TRUE(quotaManager->IsStorageInitialized());
+
+ done = true;
+ },
+ [&done](nsresult aRejectValue) {
+ ASSERT_TRUE(false);
+
+ done = true;
+ });
+
+ SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
+ });
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageInitialized());
+
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+}
+
+// Test InitializeStorage when a storage initialization is already ongoing and
+// shared client directory locks are requested after that with storage shutdown
+// scheduled in between.
+TEST_F(TestQuotaManager,
+ InitializeStorage_OngoingWithClientDirectoryLocksAndScheduledShutdown) {
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageNotInitialized());
+
+ PerformOnBackgroundThread([]() {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ RefPtr<ClientDirectoryLock> directoryLock =
+ quotaManager->CreateDirectoryLock(GetTestClientMetadata(),
+ /* aExclusive */ false);
+
+ directoryLock->OnInvalidate(
+ [&directoryLock]() { directoryLock = nullptr; });
+
+ RefPtr<ClientDirectoryLock> directoryLock2 =
+ quotaManager->CreateDirectoryLock(GetTestClientMetadata(),
+ /* aExclusive */ false);
+
+ nsTArray<RefPtr<BoolPromise>> promises;
+
+ promises.AppendElement(quotaManager->InitializeStorage());
+ promises.AppendElement(directoryLock->Acquire());
+ promises.AppendElement(quotaManager->ShutdownStorage());
+ promises.AppendElement(quotaManager->InitializeStorage());
+ promises.AppendElement(directoryLock2->Acquire());
+
+ bool done = false;
+
+ BoolPromise::All(GetCurrentSerialEventTarget(), promises)
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [&done](const CopyableTArray<bool>& aResolveValues) {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ ASSERT_TRUE(quotaManager->IsStorageInitialized());
+
+ done = true;
+ },
+ [&done](nsresult aRejectValue) {
+ ASSERT_TRUE(false);
+
+ done = true;
+ });
+
+ SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
+ });
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageInitialized());
+
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+}
+
+// Test InitializeStorage when a storage initialization already finished.
+TEST_F(TestQuotaManager, InitializeStorage_Finished) {
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageNotInitialized());
+
+ PerformOnBackgroundThread([]() {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ bool done = false;
+
+ quotaManager->InitializeStorage()->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [&done](const BoolPromise::ResolveOrRejectValue& aValue) {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ ASSERT_TRUE(quotaManager->IsStorageInitialized());
+
+ done = true;
+ });
+
+ SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
+
+ done = false;
+
+ quotaManager->InitializeStorage()->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [&done](const BoolPromise::ResolveOrRejectValue& aValue) {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ ASSERT_TRUE(quotaManager->IsStorageInitialized());
+
+ done = true;
+ });
+
+ SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
+ });
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageInitialized());
+
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+}
+
+// Test InitializeStorage when a storage initialization already finished but
+// storage shutdown has just been scheduled.
+TEST_F(TestQuotaManager, InitializeStorage_FinishedWithScheduledShutdown) {
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageNotInitialized());
+
+ PerformOnBackgroundThread([]() {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ bool done = false;
+
+ quotaManager->InitializeStorage()->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [&done](const BoolPromise::ResolveOrRejectValue& aValue) {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ ASSERT_TRUE(quotaManager->IsStorageInitialized());
+
+ done = true;
+ });
+
+ SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
+
+ nsTArray<RefPtr<BoolPromise>> promises;
+
+ promises.AppendElement(quotaManager->ShutdownStorage());
+ promises.AppendElement(quotaManager->InitializeStorage());
+
+ done = false;
+
+ BoolPromise::All(GetCurrentSerialEventTarget(), promises)
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [&done](const CopyableTArray<bool>& aResolveValues) {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ ASSERT_TRUE(quotaManager->IsStorageInitialized());
+
+ done = true;
+ },
+ [&done](nsresult aRejectValue) {
+ ASSERT_TRUE(false);
+
+ done = true;
+ });
+
+ SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
+ });
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageInitialized());
+
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+}
+
+// Test InitializeStorage when a storage initialization already finished and
+// shared client directory locks are requested immediately after requesting
+// storage initialization.
+TEST_F(TestQuotaManager, InitializeStorage_FinishedWithClientDirectoryLocks) {
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageNotInitialized());
+
+ PerformOnBackgroundThread([]() {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ RefPtr<ClientDirectoryLock> directoryLock =
+ quotaManager->CreateDirectoryLock(GetTestClientMetadata(),
+ /* aExclusive */ false);
+
+ nsTArray<RefPtr<BoolPromise>> promises;
+
+ promises.AppendElement(quotaManager->InitializeStorage());
+ promises.AppendElement(directoryLock->Acquire());
+
+ bool done = false;
+
+ BoolPromise::All(GetCurrentSerialEventTarget(), promises)
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [&done](const CopyableTArray<bool>& aResolveValues) {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ ASSERT_TRUE(quotaManager->IsStorageInitialized());
+
+ done = true;
+ },
+ [&done](nsresult aRejectValue) {
+ ASSERT_TRUE(false);
+
+ done = true;
+ });
+
+ SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
+
+ RefPtr<ClientDirectoryLock> directoryLock2 =
+ quotaManager->CreateDirectoryLock(GetTestClientMetadata(),
+ /* aExclusive */ false);
+
+ promises.Clear();
+
+ promises.AppendElement(quotaManager->InitializeStorage());
+ promises.AppendElement(directoryLock2->Acquire());
+
+ done = false;
+
+ BoolPromise::All(GetCurrentSerialEventTarget(), promises)
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [&done](const CopyableTArray<bool>& aResolveValues) {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ ASSERT_TRUE(quotaManager->IsStorageInitialized());
+
+ done = true;
+ },
+ [&done](nsresult aRejectValue) {
+ ASSERT_TRUE(false);
+
+ done = true;
+ });
+
+ SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
+ });
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageInitialized());
+
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+}
+
+// Test InitializeStorage when a storage initialization already finished and
+// shared client directory locks are requested immediatelly after requesting
+// storage initialization with storage shutdown performed in between.
+// The shared client directory lock is released when it gets invalidated by
+// storage shutdown which then unblocks the shutdown.
+TEST_F(TestQuotaManager,
+ InitializeStorage_FinishedWithClientDirectoryLocksAndScheduledShutdown) {
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageNotInitialized());
+
+ PerformOnBackgroundThread([]() {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ RefPtr<ClientDirectoryLock> directoryLock =
+ quotaManager->CreateDirectoryLock(GetTestClientMetadata(),
+ /* aExclusive */ false);
+
+ directoryLock->OnInvalidate(
+ [&directoryLock]() { directoryLock = nullptr; });
+
+ nsTArray<RefPtr<BoolPromise>> promises;
+
+ promises.AppendElement(quotaManager->InitializeStorage());
+ promises.AppendElement(directoryLock->Acquire());
+
+ bool done = false;
+
+ BoolPromise::All(GetCurrentSerialEventTarget(), promises)
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [&done](const CopyableTArray<bool>& aResolveValues) {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ ASSERT_TRUE(quotaManager->IsStorageInitialized());
+
+ done = true;
+ },
+ [&done](nsresult aRejectValue) {
+ ASSERT_TRUE(false);
+
+ done = true;
+ });
+
+ SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
+
+ done = false;
+
+ quotaManager->ShutdownStorage()->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [&done](const BoolPromise::ResolveOrRejectValue& aValue) {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ ASSERT_FALSE(quotaManager->IsStorageInitialized());
+
+ done = true;
+ });
+
+ SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
+
+ RefPtr<ClientDirectoryLock> directoryLock2 =
+ quotaManager->CreateDirectoryLock(GetTestClientMetadata(),
+ /* aExclusive */ false);
+
+ promises.Clear();
+
+ promises.AppendElement(quotaManager->InitializeStorage());
+ promises.AppendElement(directoryLock2->Acquire());
+
+ done = false;
+
+ BoolPromise::All(GetCurrentSerialEventTarget(), promises)
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [&done](const CopyableTArray<bool>& aResolveValues) {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ ASSERT_TRUE(quotaManager->IsStorageInitialized());
+
+ done = true;
+ },
+ [&done](nsresult aRejectValue) {
+ ASSERT_TRUE(false);
+
+ done = true;
+ });
+
+ SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
+ });
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageInitialized());
+
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+}
+
+TEST_F(TestQuotaManager,
+ InitializeTemporaryStorage_FinishedWithScheduledShutdown) {
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageNotInitialized());
+
+ PerformOnBackgroundThread([]() {
+ nsTArray<RefPtr<BoolPromise>> promises;
+
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ promises.AppendElement(quotaManager->InitializeStorage());
+ promises.AppendElement(quotaManager->InitializeTemporaryStorage());
+
+ bool done = false;
+
+ BoolPromise::All(GetCurrentSerialEventTarget(), promises)
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [&done](const CopyableTArray<bool>& aResolveValues) {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ ASSERT_TRUE(quotaManager->IsStorageInitialized());
+ ASSERT_TRUE(quotaManager->IsTemporaryStorageInitialized());
+
+ done = true;
+ },
+ [&done](nsresult aRejectValue) {
+ ASSERT_TRUE(false);
+
+ done = true;
+ });
+
+ SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
+
+ promises.Clear();
+
+ promises.AppendElement(quotaManager->ShutdownStorage());
+ promises.AppendElement(quotaManager->InitializeStorage());
+ promises.AppendElement(quotaManager->InitializeTemporaryStorage());
+
+ done = false;
+
+ BoolPromise::All(GetCurrentSerialEventTarget(), promises)
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [&done](const CopyableTArray<bool>& aResolveValues) {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ ASSERT_TRUE(quotaManager->IsStorageInitialized());
+ ASSERT_TRUE(quotaManager->IsTemporaryStorageInitialized());
+
+ done = true;
+ },
+ [&done](nsresult aRejectValue) {
+ ASSERT_TRUE(false);
+
+ done = true;
+ });
+
+ SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
+ });
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageInitialized());
+
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+}
+
+// Test simple ShutdownStorage.
+TEST_F(TestQuotaManager, ShutdownStorage_Simple) {
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageNotInitialized());
+
+ ASSERT_NO_FATAL_FAILURE(InitializeStorage());
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageInitialized());
+
+ PerformOnBackgroundThread([]() {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ bool done = false;
+
+ quotaManager->ShutdownStorage()->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [&done](const BoolPromise::ResolveOrRejectValue& aValue) {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ ASSERT_FALSE(quotaManager->IsStorageInitialized());
+
+ done = true;
+ });
+
+ SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
+ });
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageNotInitialized());
+
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+}
+
+// Test ShutdownStorage when a storage shutdown is already ongoing.
+TEST_F(TestQuotaManager, ShutdownStorage_Ongoing) {
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageNotInitialized());
+
+ ASSERT_NO_FATAL_FAILURE(InitializeStorage());
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageInitialized());
+
+ PerformOnBackgroundThread([]() {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ nsTArray<RefPtr<BoolPromise>> promises;
+
+ promises.AppendElement(quotaManager->ShutdownStorage());
+ promises.AppendElement(quotaManager->ShutdownStorage());
+
+ bool done = false;
+
+ BoolPromise::All(GetCurrentSerialEventTarget(), promises)
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [&done](const CopyableTArray<bool>& aResolveValues) {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ ASSERT_FALSE(quotaManager->IsStorageInitialized());
+
+ done = true;
+ },
+ [&done](nsresult aRejectValue) {
+ ASSERT_TRUE(false);
+
+ done = true;
+ });
+
+ SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
+ });
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageNotInitialized());
+
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+}
+
+// Test ShutdownStorage when a storage shutdown is already ongoing and storage
+// initialization is scheduled after that.
+TEST_F(TestQuotaManager, ShutdownStorage_OngoingWithScheduledInitialization) {
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageNotInitialized());
+
+ ASSERT_NO_FATAL_FAILURE(InitializeStorage());
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageInitialized());
+
+ PerformOnBackgroundThread([]() {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ nsTArray<RefPtr<BoolPromise>> promises;
+
+ promises.AppendElement(quotaManager->ShutdownStorage());
+ promises.AppendElement(quotaManager->InitializeStorage());
+ promises.AppendElement(quotaManager->ShutdownStorage());
+
+ bool done = false;
+
+ BoolPromise::All(GetCurrentSerialEventTarget(), promises)
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [&done](const CopyableTArray<bool>& aResolveValues) {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ ASSERT_FALSE(quotaManager->IsStorageInitialized());
+
+ done = true;
+ },
+ [&done](nsresult aRejectValue) {
+ ASSERT_TRUE(false);
+
+ done = true;
+ });
+
+ SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
+ });
+
+ ASSERT_NO_FATAL_FAILURE(AssertStorageNotInitialized());
+
+ ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
+}
+
+// Test ShutdownStorage when a storage shutdown is already ongoing and a shared
+// client directory lock is requested after that.
+// The shared client directory lock doesn't have to be explicitly released
+// because it gets invalidated while it's still pending which causes that any
+// directory locks that were blocked by the shared client directory lock become
+// unblocked.
+TEST_F(TestQuotaManager, ShutdownStorage_OngoingWithClientDirectoryLock) {
+ PerformOnBackgroundThread([]() {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ ASSERT_TRUE(quotaManager);
+
+ RefPtr<ClientDirectoryLock> directoryLock =
+ quotaManager->CreateDirectoryLock(GetTestClientMetadata(),
+ /* aExclusive */ false);
+
+ nsTArray<RefPtr<BoolPromise>> promises;
+
+ // This creates an exclusive directory lock internally.
+ promises.AppendElement(quotaManager->ShutdownStorage());
+
+ // This directory lock can't be acquired yet because a storage shutdown
+ // (which uses an exclusive diretory lock internall) is ongoing.
+ promises.AppendElement(directoryLock->Acquire());
+
+ // This second ShutdownStorage invalidates the directoryLock, so that
+ // directory lock can't ever be successfully acquired, the promise for it
+ // will be rejected when the first ShutdownStorage is finished (it releases
+ // its exclusive directory lock);
+ promises.AppendElement(quotaManager->ShutdownStorage());
+
+ bool done = false;
+
+ BoolPromise::AllSettled(GetCurrentSerialEventTarget(), promises)
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [&done](
+ const BoolPromise::AllSettledPromiseType::ResolveOrRejectValue&
+ aValues) { done = true; });
+
+ SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
+ });
+}
+
+} // namespace mozilla::dom::quota::test