gecko-dev/dom/quota/test/gtest/TestFileOutputStream.cpp
Jan Varga a8fe94a27e Bug 1957367 - QM: Convert OpenClientDirectory and its callers to use ClientDirectoryLockHandle; r=dom-storage-reviewers,jstutte
Converts OpenClientDirectory to return ClientDirectoryLockHandle instead of
RefPtr<ClientDirectoryLock>, and updates all its callers accordingly.

ClientDirectoryLockHandle is a move-only RAII wrapper that ensures the lock
is automatically dropped when the handle goes out of scope. This simplifies
ownership semantics and aligns with diagnostic assertions that verify proper
lock dropping.

This patch finalizes the transition for quota clients to use
ClientDirectoryLockHandle instead of client directory locks directly.

Differential Revision: https://phabricator.services.mozilla.com/D243665
2025-04-15 12:25:49 +00:00

223 lines
6.9 KiB
C++

/* -*- 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 "mozilla/dom/quota/Client.h"
#include "mozilla/dom/quota/ClientDirectoryLock.h"
#include "mozilla/dom/quota/ClientDirectoryLockHandle.h"
#include "mozilla/dom/quota/CommonMetadata.h"
#include "mozilla/dom/quota/FileStreams.h"
#include "mozilla/dom/quota/QuotaManager.h"
#include "mozilla/gtest/MozAssertions.h"
#include "nsIPrefBranch.h"
#include "nsIPrefService.h"
#include "QuotaManagerDependencyFixture.h"
namespace mozilla::dom::quota::test {
quota::OriginMetadata GetOutputStreamTestOriginMetadata() {
return quota::OriginMetadata{""_ns,
"example.com"_ns,
"http://example.com"_ns,
"http://example.com"_ns,
/* aIsPrivate */ false,
quota::PERSISTENCE_TYPE_DEFAULT};
}
class TestFileOutputStream : public QuotaManagerDependencyFixture {
public:
static void SetUpTestCase() {
ASSERT_NO_FATAL_FAILURE(InitializeFixture());
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
prefs->SetIntPref("dom.quotaManager.temporaryStorage.fixedLimit",
mQuotaLimit);
}
static void TearDownTestCase() {
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
prefs->ClearUserPref("dom.quotaManager.temporaryStorage.fixedLimit");
EXPECT_NO_FATAL_FAILURE(
ClearStoragesForOrigin(GetOutputStreamTestOriginMetadata()));
ASSERT_NO_FATAL_FAILURE(ShutdownFixture());
}
static const int32_t mQuotaLimit = 8192;
};
TEST_F(TestFileOutputStream, extendFileStreamWithSetEOF) {
auto backgroundTask = []() {
auto ioTask = []() {
quota::QuotaManager* quotaManager = quota::QuotaManager::Get();
auto originMetadata = GetOutputStreamTestOriginMetadata();
const int64_t groupLimit =
static_cast<int64_t>(quotaManager->GetGroupLimit());
ASSERT_TRUE(mQuotaLimit * 1024LL == groupLimit);
// We don't use the tested stream itself to check the file size as it
// may report values which have not been written to disk.
RefPtr<quota::FileOutputStream> check =
MakeRefPtr<quota::FileOutputStream>(quota::PERSISTENCE_TYPE_DEFAULT,
originMetadata,
quota::Client::Type::SDB);
RefPtr<quota::FileOutputStream> stream =
MakeRefPtr<quota::FileOutputStream>(quota::PERSISTENCE_TYPE_DEFAULT,
originMetadata,
quota::Client::Type::SDB);
{
auto testPathRes =
quotaManager->GetOrCreateTemporaryOriginDirectory(originMetadata);
ASSERT_TRUE(testPathRes.isOk());
nsCOMPtr<nsIFile> testPath = testPathRes.unwrap();
ASSERT_NS_SUCCEEDED(testPath->AppendRelativePath(u"sdb"_ns));
ASSERT_NS_SUCCEEDED(
testPath->AppendRelativePath(u"tTestFileOutputStream.txt"_ns));
bool exists = true;
ASSERT_NS_SUCCEEDED(testPath->Exists(&exists));
if (exists) {
ASSERT_NS_SUCCEEDED(testPath->Remove(/* recursive */ false));
}
ASSERT_NS_SUCCEEDED(testPath->Exists(&exists));
ASSERT_FALSE(exists);
ASSERT_NS_SUCCEEDED(testPath->Create(nsIFile::NORMAL_FILE_TYPE, 0666));
ASSERT_NS_SUCCEEDED(testPath->Exists(&exists));
ASSERT_TRUE(exists);
nsCOMPtr<nsIFile> checkPath;
ASSERT_NS_SUCCEEDED(testPath->Clone(getter_AddRefs(checkPath)));
const int32_t IOFlags = -1;
const int32_t perm = -1;
const int32_t behaviorFlags = 0;
ASSERT_NS_SUCCEEDED(
stream->Init(testPath, IOFlags, perm, behaviorFlags));
ASSERT_NS_SUCCEEDED(
check->Init(testPath, IOFlags, perm, behaviorFlags));
}
// Check that we start with an empty file
int64_t avail = 42;
ASSERT_NS_SUCCEEDED(check->GetSize(&avail));
ASSERT_TRUE(0 == avail);
// Enlarge the file
const int64_t toSize = groupLimit;
ASSERT_NS_SUCCEEDED(stream->Seek(nsISeekableStream::NS_SEEK_SET, toSize));
ASSERT_NS_SUCCEEDED(check->GetSize(&avail));
ASSERT_TRUE(0 == avail);
ASSERT_NS_SUCCEEDED(stream->SetEOF());
ASSERT_NS_SUCCEEDED(check->GetSize(&avail));
ASSERT_TRUE(toSize == avail);
// Try to enlarge the file past the limit
const int64_t overGroupLimit = groupLimit + 1;
// Seeking is allowed
ASSERT_NS_SUCCEEDED(
stream->Seek(nsISeekableStream::NS_SEEK_SET, overGroupLimit));
ASSERT_NS_SUCCEEDED(check->GetSize(&avail));
ASSERT_TRUE(toSize == avail);
// Setting file size to exceed quota should yield no device space error
ASSERT_TRUE(NS_ERROR_FILE_NO_DEVICE_SPACE == stream->SetEOF());
ASSERT_NS_SUCCEEDED(check->GetSize(&avail));
ASSERT_TRUE(toSize == avail);
// Shrink the file
const int64_t toHalfSize = toSize / 2;
ASSERT_NS_SUCCEEDED(
stream->Seek(nsISeekableStream::NS_SEEK_SET, toHalfSize));
ASSERT_NS_SUCCEEDED(check->GetSize(&avail));
ASSERT_TRUE(toSize == avail);
ASSERT_NS_SUCCEEDED(stream->SetEOF());
ASSERT_NS_SUCCEEDED(check->GetSize(&avail));
ASSERT_TRUE(toHalfSize == avail);
// Shrink the file back to nothing
ASSERT_NS_SUCCEEDED(stream->Seek(nsISeekableStream::NS_SEEK_SET, 0));
ASSERT_NS_SUCCEEDED(check->GetSize(&avail));
ASSERT_TRUE(toHalfSize == avail);
ASSERT_NS_SUCCEEDED(stream->SetEOF());
ASSERT_NS_SUCCEEDED(check->GetSize(&avail));
ASSERT_TRUE(0 == avail);
};
ClientDirectoryLockHandle directoryLockHandle;
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
bool done = false;
quotaManager
->OpenClientDirectory(
{GetOutputStreamTestOriginMetadata(), Client::SDB})
->Then(
GetCurrentSerialEventTarget(), __func__,
[&directoryLockHandle,
&done](ClientDirectoryLockHandle&& aResolveValue) {
directoryLockHandle = std::move(aResolveValue);
done = true;
},
[&done](const nsresult aRejectValue) {
ASSERT_TRUE(false);
done = true;
});
SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
ASSERT_TRUE(directoryLockHandle);
PerformOnIOThread(std::move(ioTask));
{
auto destroyingDirectoryLockHandle = std::move(directoryLockHandle);
}
};
PerformOnBackgroundThread(std::move(backgroundTask));
}
} // namespace mozilla::dom::quota::test