/* -*- 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/CommonMetadata.h" #include "mozilla/dom/quota/DirectoryLock.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 prefs = do_GetService(NS_PREFSERVICE_CONTRACTID); prefs->SetIntPref("dom.quotaManager.temporaryStorage.fixedLimit", mQuotaLimit); } static void TearDownTestCase() { nsCOMPtr 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(); { ASSERT_NS_SUCCEEDED( quotaManager->EnsureTemporaryStorageIsInitializedInternal()); auto res = quotaManager->EnsureTemporaryOriginIsInitialized( quota::PERSISTENCE_TYPE_DEFAULT, originMetadata); ASSERT_TRUE(res.isOk()); } const int64_t groupLimit = static_cast(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 check = MakeRefPtr(quota::PERSISTENCE_TYPE_DEFAULT, originMetadata, quota::Client::Type::SDB); RefPtr stream = MakeRefPtr(quota::PERSISTENCE_TYPE_DEFAULT, originMetadata, quota::Client::Type::SDB); { auto testPathRes = quotaManager->GetOriginDirectory(originMetadata); ASSERT_TRUE(testPathRes.isOk()); nsCOMPtr 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 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); }; RefPtr directoryLock; QuotaManager* quotaManager = QuotaManager::Get(); ASSERT_TRUE(quotaManager); bool done = false; quotaManager ->OpenClientDirectory( {GetOutputStreamTestOriginMetadata(), Client::SDB}) ->Then( GetCurrentSerialEventTarget(), __func__, [&directoryLock, &done](RefPtr aResolveValue) { directoryLock = std::move(aResolveValue); done = true; }, [&done](const nsresult aRejectValue) { ASSERT_TRUE(false); done = true; }); SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; }); ASSERT_TRUE(directoryLock); PerformOnIOThread(std::move(ioTask)); directoryLock = nullptr; }; PerformOnBackgroundThread(std::move(backgroundTask)); } } // namespace mozilla::dom::quota::test