/* -*- 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 directoryLock; nsTArray> promises; promises.AppendElement( quotaManager ->OpenStorageDirectory( Nullable(PERSISTENCE_TYPE_PERSISTENT), OriginScope::FromNull(), Nullable(), /* 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(PERSISTENCE_TYPE_PERSISTENT), OriginScope::FromNull(), Nullable(), /* 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& 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 directoryLock = quotaManager->CreateDirectoryLockInternal(Nullable(), OriginScope::FromNull(), Nullable(), /* aExclusive */ true); nsTArray> promises; promises.AppendElement( quotaManager ->OpenStorageDirectory( Nullable(PERSISTENCE_TYPE_PERSISTENT), OriginScope::FromNull(), Nullable(), /* 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(PERSISTENCE_TYPE_PERSISTENT), OriginScope::FromNull(), Nullable(), /* 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& 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(PERSISTENCE_TYPE_PERSISTENT), OriginScope::FromNull(), Nullable(), /* 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(PERSISTENCE_TYPE_PERSISTENT), OriginScope::FromNull(), Nullable(), /* 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(PERSISTENCE_TYPE_PERSISTENT), OriginScope::FromNull(), Nullable(), /* 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> promises; promises.AppendElement(quotaManager->ShutdownStorage()); promises.AppendElement( quotaManager ->OpenStorageDirectory( Nullable(PERSISTENCE_TYPE_PERSISTENT), OriginScope::FromNull(), Nullable(), /* 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& 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(PERSISTENCE_TYPE_PERSISTENT), OriginScope::FromNull(), Nullable(), /* 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 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(PERSISTENCE_TYPE_PERSISTENT), OriginScope::FromNull(), Nullable(), /* 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 directoryLock; nsTArray> 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& 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 directoryLock = quotaManager->CreateDirectoryLockInternal(Nullable(), OriginScope::FromNull(), Nullable(), /* aExclusive */ true); nsTArray> 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& 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> 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& 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 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> promises; promises.AppendElement(quotaManager->InitializeStorage()); promises.AppendElement(quotaManager->InitializeStorage()); bool done = false; BoolPromise::All(GetCurrentSerialEventTarget(), promises) ->Then( GetCurrentSerialEventTarget(), __func__, [&done](const CopyableTArray& 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> 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& 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 directoryLock = quotaManager->CreateDirectoryLockInternal(Nullable(), OriginScope::FromNull(), Nullable(), /* aExclusive */ true); nsTArray> 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& 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 directoryLock = quotaManager->CreateDirectoryLock(GetTestClientMetadata(), /* aExclusive */ false); RefPtr directoryLock2 = quotaManager->CreateDirectoryLock(GetTestClientMetadata(), /* aExclusive */ false); nsTArray> 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& 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 directoryLock = quotaManager->CreateDirectoryLock(GetTestClientMetadata(), /* aExclusive */ false); directoryLock->OnInvalidate( [&directoryLock]() { directoryLock = nullptr; }); RefPtr directoryLock2 = quotaManager->CreateDirectoryLock(GetTestClientMetadata(), /* aExclusive */ false); nsTArray> 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& 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> promises; promises.AppendElement(quotaManager->ShutdownStorage()); promises.AppendElement(quotaManager->InitializeStorage()); done = false; BoolPromise::All(GetCurrentSerialEventTarget(), promises) ->Then( GetCurrentSerialEventTarget(), __func__, [&done](const CopyableTArray& 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 directoryLock = quotaManager->CreateDirectoryLock(GetTestClientMetadata(), /* aExclusive */ false); nsTArray> promises; promises.AppendElement(quotaManager->InitializeStorage()); promises.AppendElement(directoryLock->Acquire()); bool done = false; BoolPromise::All(GetCurrentSerialEventTarget(), promises) ->Then( GetCurrentSerialEventTarget(), __func__, [&done](const CopyableTArray& 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 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& 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 directoryLock = quotaManager->CreateDirectoryLock(GetTestClientMetadata(), /* aExclusive */ false); directoryLock->OnInvalidate( [&directoryLock]() { directoryLock = nullptr; }); nsTArray> promises; promises.AppendElement(quotaManager->InitializeStorage()); promises.AppendElement(directoryLock->Acquire()); bool done = false; BoolPromise::All(GetCurrentSerialEventTarget(), promises) ->Then( GetCurrentSerialEventTarget(), __func__, [&done](const CopyableTArray& 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 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& 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> 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& 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& 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> promises; promises.AppendElement(quotaManager->ShutdownStorage()); promises.AppendElement(quotaManager->ShutdownStorage()); bool done = false; BoolPromise::All(GetCurrentSerialEventTarget(), promises) ->Then( GetCurrentSerialEventTarget(), __func__, [&done](const CopyableTArray& 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> 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& 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 directoryLock = quotaManager->CreateDirectoryLock(GetTestClientMetadata(), /* aExclusive */ false); nsTArray> 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