/* 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 "FileSystemParentTest.h"
#include "FileSystemParentTestHelpers.h"
#include "FileSystemParentTypes.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "mozilla/dom/FileSystemQuotaClient.h"
#include "mozilla/dom/FileSystemQuotaClientFactory.h"
#include "mozilla/dom/quota/UsageInfo.h"
#include "nsIPrefBranch.h"
#include "nsIPrefService.h"

// This file is intended for integration tests which verify origin
// initialization.

namespace mozilla::dom::fs::test {

using ::testing::_;
using ::testing::Expectation;
using ::testing::Sequence;

namespace {

class MockFileSystemQuotaClient : public FileSystemQuotaClient {
 public:
  MOCK_METHOD((Result<quota::UsageInfo, nsresult>), InitOrigin,
              (quota::PersistenceType aPersistenceType,
               const quota::OriginMetadata& aOriginMetadata,
               const AtomicBool& aCanceled),
              (override));

  MOCK_METHOD((Result<quota::UsageInfo, nsresult>), GetUsageForOrigin,
              (quota::PersistenceType aPersistenceType,
               const quota::OriginMetadata& aOriginMetadata,
               const AtomicBool& aCanceled),
              (override));

  void DelegateToBase() {
    // This exists just to workaround the false positive:
    // ERROR: Refcounted variable 'this' of type 'mozilla::dom::fs::test::
    // (anonymous namespace)::MockFileSystemQuotaClient' cannot be captured by
    // a lambda
    bool dummy;

    ON_CALL(*this, InitOrigin)
        .WillByDefault(
            [&dummy, this](quota::PersistenceType aPersistenceType,
                           const quota::OriginMetadata& aOriginMetadata,
                           const Atomic<bool>& aCanceled) {
              (void)dummy;
              return FileSystemQuotaClient::InitOrigin(
                  aPersistenceType, aOriginMetadata, aCanceled);
            });

    ON_CALL(*this, GetUsageForOrigin)
        .WillByDefault(
            [&dummy, this](quota::PersistenceType aPersistenceType,
                           const quota::OriginMetadata& aOriginMetadata,
                           const Atomic<bool>& aCanceled) {
              (void)dummy;
              return FileSystemQuotaClient::GetUsageForOrigin(
                  aPersistenceType, aOriginMetadata, aCanceled);
            });
  }
};

class TestFileSystemQuotaClientFactory final
    : public FileSystemQuotaClientFactory {
 public:
  already_AddRefed<MockFileSystemQuotaClient> GetQuotaClient() {
    return do_AddRef(mQuotaClient);
  }

 protected:
  already_AddRefed<quota::Client> AllocQuotaClient() override {
    mQuotaClient = MakeRefPtr<MockFileSystemQuotaClient>();
    mQuotaClient->DelegateToBase();
    return do_AddRef(mQuotaClient);
  }

  RefPtr<MockFileSystemQuotaClient> mQuotaClient;
};

}  // namespace

class TestFileSystemOriginInitialization : public FileSystemParentTest {
 protected:
  static void SetUpTestCase() {
    nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
    prefs->SetBoolPref("dom.quotaManager.loadQuotaFromCache", false);

    auto factory = MakeRefPtr<TestFileSystemQuotaClientFactory>();

    FileSystemQuotaClientFactory::SetCustomFactory(factory);

    ASSERT_NO_FATAL_FAILURE(FileSystemParentTest::SetUpTestCase());

    sQuotaClient = factory->GetQuotaClient();
  }

  static void TearDownTestCase() {
    sQuotaClient = nullptr;

    ASSERT_NO_FATAL_FAILURE(FileSystemParentTest::TearDownTestCase());

    FileSystemQuotaClientFactory::SetCustomFactory(nullptr);
  }

  static RefPtr<MockFileSystemQuotaClient> sQuotaClient;
};

constinit RefPtr<MockFileSystemQuotaClient>
    TestFileSystemOriginInitialization::sQuotaClient;

TEST_F(TestFileSystemOriginInitialization, EmptyOriginDirectory) {
  ASSERT_NO_FATAL_FAILURE(ShutdownStorage());

  // Set expectations
  {
    Sequence s;

    EXPECT_CALL(*sQuotaClient, InitOrigin(_, _, _)).Times(0).InSequence(s);

    EXPECT_CALL(*sQuotaClient, GetUsageForOrigin(_, _, _))
        .Times(0)
        .InSequence(s);
  }

  // Initialize origin
  ASSERT_NO_FATAL_FAILURE(InitializeStorage());
  ASSERT_NO_FATAL_FAILURE(InitializeTemporaryStorage());
  ASSERT_NO_FATAL_FAILURE(
      InitializeTemporaryOrigin(/* aCreateIfNonExistent */ true));

  // After initialization,
  // * origin usage is nothing
  // * cached origin usage is zero
  quota::UsageInfo usageNow;
  ASSERT_NO_FATAL_FAILURE(GetOriginUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageIsNothing(usageNow));

  ASSERT_NO_FATAL_FAILURE(GetCachedOriginUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageEqualTo(usageNow, 0u));

  // Shutdown temporary storage
  ASSERT_NO_FATAL_FAILURE(ShutdownTemporaryStorage());

  // After temporary storage shutdown,
  // * origin usage is still nothing
  // * cached origin is still zero
  ASSERT_NO_FATAL_FAILURE(GetOriginUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageIsNothing(usageNow));

  ASSERT_NO_FATAL_FAILURE(GetCachedOriginUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageEqualTo(usageNow, 0u));

  // Initialize temporary storage again.
  ASSERT_NO_FATAL_FAILURE(AssertTemporaryStorageNotInitialized());
  ASSERT_NO_FATAL_FAILURE(InitializeTemporaryStorage());

  // After repeated temporary storage initialization,
  // * origin usage is still nothing
  // * cached origin is still zero
  ASSERT_NO_FATAL_FAILURE(GetOriginUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageIsNothing(usageNow));

  ASSERT_NO_FATAL_FAILURE(GetCachedOriginUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageEqualTo(usageNow, 0u));
}

TEST_F(TestFileSystemOriginInitialization, EmptyFileSystemDirectory) {
  ASSERT_NO_FATAL_FAILURE(ShutdownStorage());

  // Set expectations
  {
    Sequence s;

    EXPECT_CALL(*sQuotaClient, InitOrigin(_, _, _)).Times(0).InSequence(s);

    EXPECT_CALL(*sQuotaClient, GetUsageForOrigin(_, _, _))
        .Times(0)
        .InSequence(s);
  }

  // Initialize client
  ASSERT_NO_FATAL_FAILURE(InitializeStorage());
  ASSERT_NO_FATAL_FAILURE(InitializeTemporaryStorage());
  ASSERT_NO_FATAL_FAILURE(InitializeTemporaryOrigin());
  ASSERT_NO_FATAL_FAILURE(InitializeTemporaryClient());

  // After initialization,
  // * origin usage is nothing
  // * cached origin usage is zero
  quota::UsageInfo usageNow;
  ASSERT_NO_FATAL_FAILURE(GetOriginUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageIsNothing(usageNow));

  ASSERT_NO_FATAL_FAILURE(GetCachedOriginUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageEqualTo(usageNow, 0u));

  // Shutdown temporary storage.
  ASSERT_NO_FATAL_FAILURE(ShutdownTemporaryStorage());

  // After temporary storage shutdown,
  // * origin usage is still nothing
  // * cached origin usage is still zero
  ASSERT_NO_FATAL_FAILURE(GetOriginUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageIsNothing(usageNow));

  ASSERT_NO_FATAL_FAILURE(GetCachedOriginUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageEqualTo(usageNow, 0u));

  // Initialize temporary storage again.
  ASSERT_NO_FATAL_FAILURE(InitializeTemporaryStorage());

  // After repeated temporary storage initialization,
  // * origin usage is still nothing
  // * cached origin usage is still zero
  ASSERT_NO_FATAL_FAILURE(GetOriginUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageIsNothing(usageNow));

  ASSERT_NO_FATAL_FAILURE(GetCachedOriginUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageEqualTo(usageNow, 0u));
}

TEST_F(TestFileSystemOriginInitialization, EmptyFileSystemDatabase) {
  ASSERT_NO_FATAL_FAILURE(ShutdownStorage());

  // Set expectations
  {
    Sequence s;

    // GetOriginUsage check after database initialization.
    EXPECT_CALL(*sQuotaClient, GetUsageForOrigin(_, _, _)).InSequence(s);

    // GetOriginUsage check when temporary storage is not initialized.
    EXPECT_CALL(*sQuotaClient, InitOrigin(_, _, _)).InSequence(s);

    // Repeated temporary storage initialization.
    EXPECT_CALL(*sQuotaClient, InitOrigin(_, _, _)).InSequence(s);

    // Final GetOriginUsage check.
    EXPECT_CALL(*sQuotaClient, GetUsageForOrigin(_, _, _)).InSequence(s);
  }

  // Initialize database
  ASSERT_NO_FATAL_FAILURE(EnsureDataManager());

  // After initialization,
  // * origin usage is not zero
  // * GetOriginUsage and GetCachedOriginUsage should agree
  quota::UsageInfo usageNow;
  ASSERT_NO_FATAL_FAILURE(GetOriginUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageGreaterThan(usageNow, 0u));

  uint64_t beforeShutdownUsage;
  ASSERT_NO_FATAL_FAILURE(GetUsageValue(usageNow, beforeShutdownUsage));

  ASSERT_NO_FATAL_FAILURE(GetCachedOriginUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageEqualTo(usageNow, beforeShutdownUsage));

  // Shutdown temporary storage.
  ASSERT_NO_FATAL_FAILURE(ReleaseDataManager());
  ASSERT_NO_FATAL_FAILURE(ShutdownTemporaryStorage());

  // After temporary storage shutdown,
  // * origin usage is still the same as before shutdown
  // * cached origin usage is zero
  ASSERT_NO_FATAL_FAILURE(GetOriginUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageEqualTo(usageNow, beforeShutdownUsage));

  ASSERT_NO_FATAL_FAILURE(GetCachedOriginUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageEqualTo(usageNow, 0u));

  // Initialize temporary storage again.
  ASSERT_NO_FATAL_FAILURE(InitializeTemporaryStorage());

  // After repeated temporary storage initialization,
  // * origin usage is still the same as before shutdown
  // * GetOriginUsage and GetCachedOriginUsage should agree again
  ASSERT_NO_FATAL_FAILURE(GetOriginUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageEqualTo(usageNow, beforeShutdownUsage));

  ASSERT_NO_FATAL_FAILURE(GetCachedOriginUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageEqualTo(usageNow, beforeShutdownUsage));
}

TEST_F(TestFileSystemOriginInitialization, EmptyFileSystemFile) {
  ASSERT_NO_FATAL_FAILURE(ShutdownStorage());

  // Set expectations
  {
    Sequence s;

    // GetOriginUsage check after file creation.
    EXPECT_CALL(*sQuotaClient, GetUsageForOrigin(_, _, _)).InSequence(s);

    // GetOriginUsage check when temporary storage is not initialized.
    EXPECT_CALL(*sQuotaClient, InitOrigin(_, _, _)).InSequence(s);

    // Repeated temporary storage initialization.
    EXPECT_CALL(*sQuotaClient, InitOrigin(_, _, _)).InSequence(s);

    // Final GetOriginUsage check.
    EXPECT_CALL(*sQuotaClient, GetUsageForOrigin(_, _, _)).InSequence(s);
  }

  // Initialize database
  ASSERT_NO_FATAL_FAILURE(EnsureDataManager());

  // Create a new empty file
  EntryId testFileId;
  ASSERT_NO_FATAL_FAILURE(CreateNewEmptyFile(testFileId));

  // After a new file has been created (only in the database),
  // * origin usage is not zero
  // * GetOriginUsage and GetCachedOriginUsage should agree
  quota::UsageInfo usageNow;
  ASSERT_NO_FATAL_FAILURE(GetOriginUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageGreaterThan(usageNow, 0u));

  uint64_t beforeShutdownUsage;
  ASSERT_NO_FATAL_FAILURE(GetUsageValue(usageNow, beforeShutdownUsage));

  ASSERT_NO_FATAL_FAILURE(GetCachedOriginUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageEqualTo(usageNow, beforeShutdownUsage));

  // Shutdown temporary storage.
  ASSERT_NO_FATAL_FAILURE(ReleaseDataManager());
  ASSERT_NO_FATAL_FAILURE(ShutdownTemporaryStorage());

  // After temporary storage shutdown,
  // * origin usage is still the same as before shutdown
  // * cached origin usage is zero
  ASSERT_NO_FATAL_FAILURE(GetOriginUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageEqualTo(usageNow, beforeShutdownUsage));

  ASSERT_NO_FATAL_FAILURE(GetCachedOriginUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageEqualTo(usageNow, 0u));

  // Initialize temporary storage again.
  ASSERT_NO_FATAL_FAILURE(InitializeTemporaryStorage());

  // After repeated temporary storage initialization,
  // * origin usage is still the same as before shutdown
  // * GetOriginUsage and GetCachedOriginUsage should agree
  ASSERT_NO_FATAL_FAILURE(GetOriginUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageEqualTo(usageNow, beforeShutdownUsage));

  ASSERT_NO_FATAL_FAILURE(GetCachedOriginUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageEqualTo(usageNow, beforeShutdownUsage));
}

TEST_F(TestFileSystemOriginInitialization, NonEmptyFileSystemFile) {
  ASSERT_NO_FATAL_FAILURE(ShutdownStorage());

  // Set expectations
  {
    Sequence s;

    // GetOriginUsage check after filling the file with content.
    EXPECT_CALL(*sQuotaClient, GetUsageForOrigin(_, _, _)).InSequence(s);

    // GetOriginUsage check after unlocking the file..
    EXPECT_CALL(*sQuotaClient, GetUsageForOrigin(_, _, _)).InSequence(s);

    // GetOriginUsage check when temporary storage is not initialized.
    EXPECT_CALL(*sQuotaClient, InitOrigin(_, _, _)).InSequence(s);

    // Repeated temporary storage initialization.
    EXPECT_CALL(*sQuotaClient, InitOrigin(_, _, _)).InSequence(s);

    // Final GetOriginUsage check.
    EXPECT_CALL(*sQuotaClient, GetUsageForOrigin(_, _, _)).InSequence(s);
  }

  // Initialize database
  ASSERT_NO_FATAL_FAILURE(EnsureDataManager());

  // Create a new empty file
  EntryId testFileId;
  ASSERT_NO_FATAL_FAILURE(CreateNewEmptyFile(testFileId));

  // Fill the file with some content
  ASSERT_NO_FATAL_FAILURE(LockExclusive(testFileId));

  const nsCString& testData = GetTestData();

  ASSERT_NO_FATAL_FAILURE(WriteDataToFile(testFileId, testData));

  // After the content has been written to the file,
  // * origin usage is not zero
  // * GetOriginUsage and GetCachedOriginUsage should agree
  quota::UsageInfo usageNow;
  ASSERT_NO_FATAL_FAILURE(GetOriginUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageGreaterThan(usageNow, 0u));

  uint64_t beforeShutdownUsage;
  ASSERT_NO_FATAL_FAILURE(GetUsageValue(usageNow, beforeShutdownUsage));

  ASSERT_NO_FATAL_FAILURE(GetCachedOriginUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageEqualTo(usageNow, beforeShutdownUsage));

  ASSERT_NO_FATAL_FAILURE(UnlockExclusive(testFileId));

  // After the file has been unlocked,
  // * origin usage is still the same as before unlocking
  // * GetOriginUsage and GetCachedOriginUsage should still agree
  ASSERT_NO_FATAL_FAILURE(GetOriginUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageEqualTo(usageNow, beforeShutdownUsage));

  ASSERT_NO_FATAL_FAILURE(GetCachedOriginUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageEqualTo(usageNow, beforeShutdownUsage));

  // Shutdown temporary storage.
  ASSERT_NO_FATAL_FAILURE(ReleaseDataManager());
  ASSERT_NO_FATAL_FAILURE(ShutdownTemporaryStorage());

  // After temporary storage shutdown,
  // * origin usage is still the same as before shutdown
  // * cached origin usage is zero
  ASSERT_NO_FATAL_FAILURE(GetOriginUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageEqualTo(usageNow, beforeShutdownUsage));

  ASSERT_NO_FATAL_FAILURE(GetCachedOriginUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageEqualTo(usageNow, 0u));

  // Initialize temporary storage again.
  ASSERT_NO_FATAL_FAILURE(InitializeTemporaryStorage());

  // After repeated temporary storage initialization,
  // * origin usage is still the same as before shutdown
  // * GetOriginUsage and GetCachedOriginUsage should agree again
  ASSERT_NO_FATAL_FAILURE(GetOriginUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageEqualTo(usageNow, beforeShutdownUsage));

  ASSERT_NO_FATAL_FAILURE(GetCachedOriginUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageEqualTo(usageNow, beforeShutdownUsage));
}

TEST_F(TestFileSystemOriginInitialization,
       NonEmptyFileSystemFile_UncleanShutdown) {
  ASSERT_NO_FATAL_FAILURE(ShutdownStorage());

  // Set expectations
  {
    Sequence s;

    // GetOriginUsage check after file creation.
    EXPECT_CALL(*sQuotaClient, GetUsageForOrigin(_, _, _)).InSequence(s);

    // GetOriginUsage check after filling the file with content.
    EXPECT_CALL(*sQuotaClient, GetUsageForOrigin(_, _, _)).InSequence(s);

    // GetOriginUsage check when temporary storage is not initialized.
    EXPECT_CALL(*sQuotaClient, InitOrigin(_, _, _)).InSequence(s);

    // Repeated temporary storage initialization.
    EXPECT_CALL(*sQuotaClient, InitOrigin(_, _, _)).InSequence(s);

    // Final GetOriginUsage check.
    EXPECT_CALL(*sQuotaClient, GetUsageForOrigin(_, _, _)).InSequence(s);
  }

  // Initialize database
  ASSERT_NO_FATAL_FAILURE(EnsureDataManager());

  // Create a new empty file
  EntryId testFileId;
  ASSERT_NO_FATAL_FAILURE(CreateNewEmptyFile(testFileId));

  // After a new file has been created (only in the database),
  // * origin usage is not zero
  // * GetOriginUsage and GetCachedOriginUsage should agree
  quota::UsageInfo usageNow;
  ASSERT_NO_FATAL_FAILURE(GetOriginUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageGreaterThan(usageNow, 0u));

  uint64_t beforeWriteUsage;
  ASSERT_NO_FATAL_FAILURE(GetUsageValue(usageNow, beforeWriteUsage));

  ASSERT_NO_FATAL_FAILURE(GetCachedOriginUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageEqualTo(usageNow, beforeWriteUsage));

  // Fill the file with some content
  ASSERT_NO_FATAL_FAILURE(LockExclusive(testFileId));

  const nsCString& testData = GetTestData();

  ASSERT_NO_FATAL_FAILURE(WriteDataToFile(testFileId, testData));

  // After the content has been written to the file,
  // * origin usage is not the same as before writing
  // * GetOriginUsage and GetCachedOriginUsage should still agree
  ASSERT_NO_FATAL_FAILURE(GetOriginUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageGreaterThan(usageNow, beforeWriteUsage));

  uint64_t beforeShutdownUsage;
  ASSERT_NO_FATAL_FAILURE(GetUsageValue(usageNow, beforeShutdownUsage));

  ASSERT_NO_FATAL_FAILURE(GetCachedOriginUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageEqualTo(usageNow, beforeShutdownUsage));

  // UnlockExclusive is not called here on purpose to simmulate unclean
  // shutdown.

  // Shutdown temporary storage.
  ASSERT_NO_FATAL_FAILURE(ReleaseDataManager());
  ASSERT_NO_FATAL_FAILURE(ShutdownTemporaryStorage());

  // After temporary storage shutdown,
  // * static database usage is the same as before writing
  // * origin usage is still the same as before shutdown
  // * cached origin usage is zero
  ASSERT_NO_FATAL_FAILURE(GetStaticDatabaseUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageEqualTo(usageNow, beforeWriteUsage));

  ASSERT_NO_FATAL_FAILURE(GetOriginUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageEqualTo(usageNow, beforeShutdownUsage));

  ASSERT_NO_FATAL_FAILURE(GetCachedOriginUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageEqualTo(usageNow, 0u));

  // Initialize temporary storage again.
  ASSERT_NO_FATAL_FAILURE(InitializeTemporaryStorage());

  // After repeated temporary storage initialization,
  // * static database usage is the same as before shutdown
  // * GetStaticDatabaseUsage, GetOriginUsage and GetCachedOriginUsage should
  // all agree again
  ASSERT_NO_FATAL_FAILURE(GetStaticDatabaseUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageEqualTo(usageNow, beforeShutdownUsage));

  ASSERT_NO_FATAL_FAILURE(GetOriginUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageEqualTo(usageNow, beforeShutdownUsage));

  ASSERT_NO_FATAL_FAILURE(GetCachedOriginUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageEqualTo(usageNow, beforeShutdownUsage));
}

TEST_F(TestFileSystemOriginInitialization, RemovedFileSystemFile) {
  ASSERT_NO_FATAL_FAILURE(ShutdownStorage());

  // Set expectations
  {
    Sequence s;

    // GetOriginUsage check after removing the file..
    EXPECT_CALL(*sQuotaClient, GetUsageForOrigin(_, _, _)).InSequence(s);

    // GetOriginUsage check when temporary storage is not initialized.
    EXPECT_CALL(*sQuotaClient, InitOrigin(_, _, _)).InSequence(s);

    // Repeated temporary storage initialization.
    EXPECT_CALL(*sQuotaClient, InitOrigin(_, _, _)).InSequence(s);

    // Final GetOriginUsage check.
    EXPECT_CALL(*sQuotaClient, GetUsageForOrigin(_, _, _)).InSequence(s);
  }

  // Initialize database
  ASSERT_NO_FATAL_FAILURE(EnsureDataManager());

  // Create a new empty file
  EntryId testFileId;
  ASSERT_NO_FATAL_FAILURE(CreateNewEmptyFile(testFileId));

  // Fill the file with some content
  ASSERT_NO_FATAL_FAILURE(LockExclusive(testFileId));

  const nsCString& testData = GetTestData();

  ASSERT_NO_FATAL_FAILURE(WriteDataToFile(testFileId, testData));

  ASSERT_NO_FATAL_FAILURE(UnlockExclusive(testFileId));

  // Remove the file
  bool wasRemoved;
  ASSERT_NO_FATAL_FAILURE(RemoveFile(wasRemoved));
  ASSERT_TRUE(wasRemoved);

  // After the file has been removed,
  // * origin usage is not zero
  // * GetOriginUsage and GetCachedOriginUsage should agree
  quota::UsageInfo usageNow;
  ASSERT_NO_FATAL_FAILURE(GetOriginUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageGreaterThan(usageNow, 0u));

  uint64_t beforeShutdownUsage;
  ASSERT_NO_FATAL_FAILURE(GetUsageValue(usageNow, beforeShutdownUsage));

  ASSERT_NO_FATAL_FAILURE(GetCachedOriginUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageEqualTo(usageNow, beforeShutdownUsage));

  // Shutdown temporary storage.
  ASSERT_NO_FATAL_FAILURE(ReleaseDataManager());
  ASSERT_NO_FATAL_FAILURE(ShutdownTemporaryStorage());

  // After temporary storage shutdown,
  // * origin usage is still the same as before shutdown
  // * cached origin usage is zero
  ASSERT_NO_FATAL_FAILURE(GetOriginUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageEqualTo(usageNow, beforeShutdownUsage));

  ASSERT_NO_FATAL_FAILURE(GetCachedOriginUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageEqualTo(usageNow, 0u));

  // Initialize temporary storage again.
  ASSERT_NO_FATAL_FAILURE(InitializeTemporaryStorage());

  // After repeated temporary storage initialization,
  // * origin usage is still the same as before shutdown
  // * GetOriginUsage and GetCachedOriginUsage should agree again
  ASSERT_NO_FATAL_FAILURE(GetOriginUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageEqualTo(usageNow, beforeShutdownUsage));

  ASSERT_NO_FATAL_FAILURE(GetCachedOriginUsage(usageNow));
  ASSERT_NO_FATAL_FAILURE(CheckUsageEqualTo(usageNow, beforeShutdownUsage));
}

}  // namespace mozilla::dom::fs::test
