382 lines
13 KiB
C++
382 lines
13 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 "FileSystemBackgroundRequestHandler.h"
|
|
#include "FileSystemEntryMetadataArray.h"
|
|
#include "FileSystemMocks.h"
|
|
#include "fs/FileSystemRequestHandler.h"
|
|
#include "gtest/gtest.h"
|
|
#include "mozilla/SpinEventLoopUntil.h"
|
|
#include "mozilla/UniquePtr.h"
|
|
#include "mozilla/dom/FileBlobImpl.h"
|
|
#include "mozilla/dom/FileSystemManager.h"
|
|
#include "mozilla/dom/FileSystemManagerChild.h"
|
|
#include "mozilla/dom/IPCBlob.h"
|
|
#include "mozilla/dom/IPCBlobUtils.h"
|
|
#include "mozilla/dom/PFileSystemManager.h"
|
|
#include "mozilla/dom/StorageManager.h"
|
|
#include "mozilla/ipc/FileDescriptorUtils.h"
|
|
#include "mozilla/ipc/IPCCore.h"
|
|
#include "mozilla/net/CookieJarSettings.h"
|
|
#include "nsDirectoryServiceDefs.h"
|
|
#include "nsIFile.h"
|
|
|
|
using ::testing::_;
|
|
using ::testing::ByRef;
|
|
using ::testing::Invoke;
|
|
using ::testing::Return;
|
|
|
|
namespace mozilla::dom::fs::test {
|
|
|
|
class TestFileSystemRequestHandler : public ::testing::Test {
|
|
protected:
|
|
void SetUp() override {
|
|
mMockGlobal = GetMockGlobal();
|
|
mGlobal = mMockGlobal;
|
|
|
|
mCookieJarSettings = mozilla::net::CookieJarSettings::Create(
|
|
mozilla::net::CookieJarSettings::CreateMode::eRegular,
|
|
/* Should resist fingerprinting */ true);
|
|
mListener = MakeAndAddRef<ExpectResolveCalled>();
|
|
|
|
mChild = FileSystemChildMetadata("parent"_ns, u"ChildName"_ns);
|
|
mEntry = FileSystemEntryMetadata("myid"_ns, u"EntryName"_ns,
|
|
/* directory */ false);
|
|
mName = u"testDir"_ns;
|
|
mFileSystemManagerChild = MakeAndAddRef<TestFileSystemManagerChild>();
|
|
mManager = MakeAndAddRef<FileSystemManager>(
|
|
mGlobal, nullptr,
|
|
MakeRefPtr<FileSystemBackgroundRequestHandler>(
|
|
mFileSystemManagerChild));
|
|
}
|
|
|
|
void TearDown() override {
|
|
if (!mManager->IsShutdown()) {
|
|
EXPECT_NO_FATAL_FAILURE(ShutdownFileSystemManager());
|
|
}
|
|
}
|
|
|
|
already_AddRefed<Promise> GetDefaultPromise() {
|
|
IgnoredErrorResult rv;
|
|
RefPtr<Promise> result = Promise::Create(mGlobal, rv);
|
|
mListener->ClearDone();
|
|
result->AppendNativeHandler(mListener->AsHandler());
|
|
|
|
return result.forget();
|
|
}
|
|
|
|
already_AddRefed<Promise> GetSimplePromise() {
|
|
IgnoredErrorResult rv;
|
|
RefPtr<Promise> result = Promise::Create(mGlobal, rv);
|
|
|
|
return result.forget();
|
|
}
|
|
|
|
already_AddRefed<Promise> GetShutdownPromise() {
|
|
RefPtr<Promise> promise = GetDefaultPromise();
|
|
EXPECT_CALL(*mFileSystemManagerChild, Shutdown())
|
|
.WillOnce(Invoke([promise]() { promise->MaybeResolveWithUndefined(); }))
|
|
.WillOnce(Return());
|
|
EXPECT_CALL(mListener->GetSuccessHandler(), InvokeMe());
|
|
|
|
return promise.forget();
|
|
}
|
|
|
|
UniquePtr<FileSystemRequestHandler> GetFileSystemRequestHandler() {
|
|
return MakeUnique<FileSystemRequestHandler>();
|
|
}
|
|
|
|
void ShutdownFileSystemManager() {
|
|
RefPtr<Promise> promise = GetShutdownPromise();
|
|
|
|
mManager->Shutdown();
|
|
|
|
SpinEventLoopUntil("Promise is fulfilled or timeout"_ns,
|
|
[this]() { return mListener->IsDone(); });
|
|
ASSERT_TRUE(mManager->IsShutdown());
|
|
}
|
|
|
|
void AllowStorageAccess() {
|
|
EXPECT_CALL(*mMockGlobal, GetCookieJarSettings())
|
|
.WillOnce(::testing::Return(&*mCookieJarSettings));
|
|
|
|
EXPECT_CALL(*mMockGlobal, GetStorageAccess())
|
|
.WillOnce(::testing::Return(mozilla::StorageAccess::eAllow));
|
|
}
|
|
|
|
MockGlobalObject* mMockGlobal;
|
|
nsCOMPtr<nsIGlobalObject> mGlobal;
|
|
|
|
RefPtr<ExpectResolveCalled> mListener;
|
|
|
|
FileSystemChildMetadata mChild;
|
|
FileSystemEntryMetadata mEntry;
|
|
nsString mName;
|
|
RefPtr<TestFileSystemManagerChild> mFileSystemManagerChild;
|
|
RefPtr<FileSystemManager> mManager;
|
|
RefPtr<nsICookieJarSettings> mCookieJarSettings;
|
|
};
|
|
|
|
TEST_F(TestFileSystemRequestHandler, isGetRootHandleSuccessful) {
|
|
AllowStorageAccess();
|
|
|
|
auto fakeResponse = [](auto&& aResolve, auto&& /* aReject */) {
|
|
EntryId expected = "expected"_ns;
|
|
FileSystemGetHandleResponse response(expected);
|
|
aResolve(std::move(response));
|
|
};
|
|
|
|
EXPECT_CALL(mListener->GetSuccessHandler(), InvokeMe());
|
|
EXPECT_CALL(*mFileSystemManagerChild, SendGetRootHandle(_, _))
|
|
.WillOnce(Invoke(fakeResponse));
|
|
|
|
RefPtr<Promise> promise = GetDefaultPromise();
|
|
auto testable = GetFileSystemRequestHandler();
|
|
testable->GetRootHandle(mManager, promise, IgnoredErrorResult());
|
|
SpinEventLoopUntil("Promise is fulfilled or timeout"_ns,
|
|
[this]() { return mListener->IsDone(); });
|
|
}
|
|
|
|
TEST_F(TestFileSystemRequestHandler, isGetRootHandleBlockedAfterShutdown) {
|
|
ASSERT_NO_FATAL_FAILURE(ShutdownFileSystemManager());
|
|
|
|
IgnoredErrorResult error;
|
|
GetFileSystemRequestHandler()->GetRootHandle(mManager, GetSimplePromise(),
|
|
error);
|
|
|
|
ASSERT_TRUE(error.Failed());
|
|
ASSERT_TRUE(error.ErrorCodeIs(NS_ERROR_ILLEGAL_DURING_SHUTDOWN));
|
|
}
|
|
|
|
TEST_F(TestFileSystemRequestHandler, isGetDirectoryHandleSuccessful) {
|
|
AllowStorageAccess();
|
|
|
|
auto fakeResponse = [](const auto& /* aRequest */, auto&& aResolve,
|
|
auto&& /* aReject */) {
|
|
EntryId expected = "expected"_ns;
|
|
FileSystemGetHandleResponse response(expected);
|
|
aResolve(std::move(response));
|
|
};
|
|
|
|
EXPECT_CALL(mListener->GetSuccessHandler(), InvokeMe());
|
|
EXPECT_CALL(*mFileSystemManagerChild, SendGetDirectoryHandle(_, _, _))
|
|
.WillOnce(Invoke(fakeResponse));
|
|
|
|
RefPtr<Promise> promise = GetDefaultPromise();
|
|
auto testable = GetFileSystemRequestHandler();
|
|
testable->GetDirectoryHandle(mManager, mChild,
|
|
/* create */ true, promise,
|
|
IgnoredErrorResult());
|
|
SpinEventLoopUntil("Promise is fulfilled or timeout"_ns,
|
|
[this]() { return mListener->IsDone(); });
|
|
}
|
|
|
|
TEST_F(TestFileSystemRequestHandler, isGetDirectoryHandleBlockedAfterShutdown) {
|
|
ASSERT_NO_FATAL_FAILURE(ShutdownFileSystemManager());
|
|
|
|
IgnoredErrorResult error;
|
|
GetFileSystemRequestHandler()->GetDirectoryHandle(
|
|
mManager, mChild, /* aCreate */ true, GetSimplePromise(), error);
|
|
|
|
ASSERT_TRUE(error.Failed());
|
|
ASSERT_TRUE(error.ErrorCodeIs(NS_ERROR_ILLEGAL_DURING_SHUTDOWN));
|
|
}
|
|
|
|
TEST_F(TestFileSystemRequestHandler, isGetFileHandleSuccessful) {
|
|
AllowStorageAccess();
|
|
|
|
auto fakeResponse = [](const auto& /* aRequest */, auto&& aResolve,
|
|
auto&& /* aReject */) {
|
|
EntryId expected = "expected"_ns;
|
|
FileSystemGetHandleResponse response(expected);
|
|
aResolve(std::move(response));
|
|
};
|
|
|
|
EXPECT_CALL(mListener->GetSuccessHandler(), InvokeMe());
|
|
EXPECT_CALL(*mFileSystemManagerChild, SendGetFileHandle(_, _, _))
|
|
.WillOnce(Invoke(fakeResponse));
|
|
|
|
RefPtr<Promise> promise = GetDefaultPromise();
|
|
auto testable = GetFileSystemRequestHandler();
|
|
testable->GetFileHandle(mManager, mChild, /* create */ true, promise,
|
|
IgnoredErrorResult());
|
|
SpinEventLoopUntil("Promise is fulfilled or timeout"_ns,
|
|
[this]() { return mListener->IsDone(); });
|
|
}
|
|
|
|
TEST_F(TestFileSystemRequestHandler, isGetFileHandleBlockedAfterShutdown) {
|
|
ASSERT_NO_FATAL_FAILURE(ShutdownFileSystemManager());
|
|
|
|
IgnoredErrorResult error;
|
|
GetFileSystemRequestHandler()->GetFileHandle(
|
|
mManager, mChild, /* aCreate */ true, GetSimplePromise(), error);
|
|
|
|
ASSERT_TRUE(error.Failed());
|
|
ASSERT_TRUE(error.ErrorCodeIs(NS_ERROR_ILLEGAL_DURING_SHUTDOWN));
|
|
}
|
|
|
|
TEST_F(TestFileSystemRequestHandler, isGetFileSuccessful) {
|
|
AllowStorageAccess();
|
|
|
|
auto fakeResponse = [](const auto& /* aRequest */, auto&& aResolve,
|
|
auto&& /* aReject */) {
|
|
// We have to create a temporary file
|
|
nsCOMPtr<nsIFile> tmpfile;
|
|
nsresult rv =
|
|
NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tmpfile));
|
|
ASSERT_EQ(NS_SUCCEEDED(rv), true);
|
|
|
|
rv = tmpfile->AppendNative("GetFileTestBlob"_ns);
|
|
ASSERT_EQ(NS_SUCCEEDED(rv), true);
|
|
|
|
rv = tmpfile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0666);
|
|
ASSERT_EQ(NS_SUCCEEDED(rv), true);
|
|
|
|
auto blob = MakeRefPtr<FileBlobImpl>(tmpfile);
|
|
|
|
TimeStamp last_modified_ms = 0;
|
|
ContentType type = "txt"_ns;
|
|
IPCBlob file;
|
|
IPCBlobUtils::Serialize(blob, file);
|
|
|
|
nsTArray<Name> path;
|
|
path.AppendElement(u"root"_ns);
|
|
path.AppendElement(u"Trash"_ns);
|
|
|
|
FileSystemFileProperties properties(last_modified_ms, file, type, path);
|
|
FileSystemGetFileResponse response(properties);
|
|
aResolve(std::move(response));
|
|
};
|
|
|
|
EXPECT_CALL(mListener->GetSuccessHandler(), InvokeMe());
|
|
EXPECT_CALL(*mFileSystemManagerChild, SendGetFile(_, _, _))
|
|
.WillOnce(Invoke(fakeResponse));
|
|
|
|
RefPtr<Promise> promise = GetDefaultPromise();
|
|
auto testable = GetFileSystemRequestHandler();
|
|
testable->GetFile(mManager, mEntry, promise, IgnoredErrorResult());
|
|
SpinEventLoopUntil("Promise is fulfilled or timeout"_ns,
|
|
[this]() { return mListener->IsDone(); });
|
|
}
|
|
|
|
TEST_F(TestFileSystemRequestHandler, isGetFileBlockedAfterShutdown) {
|
|
ASSERT_NO_FATAL_FAILURE(ShutdownFileSystemManager());
|
|
|
|
IgnoredErrorResult error;
|
|
GetFileSystemRequestHandler()->GetFile(mManager, mEntry, GetSimplePromise(),
|
|
error);
|
|
|
|
ASSERT_TRUE(error.Failed());
|
|
ASSERT_TRUE(error.ErrorCodeIs(NS_ERROR_ILLEGAL_DURING_SHUTDOWN));
|
|
}
|
|
|
|
TEST_F(TestFileSystemRequestHandler, isGetAccessHandleBlockedAfterShutdown) {
|
|
RefPtr<Promise> promise = GetShutdownPromise();
|
|
|
|
mManager->Shutdown();
|
|
|
|
SpinEventLoopUntil("Promise is fulfilled or timeout"_ns,
|
|
[this]() { return mListener->IsDone(); });
|
|
ASSERT_TRUE(mManager->IsShutdown());
|
|
|
|
IgnoredErrorResult error;
|
|
GetFileSystemRequestHandler()->GetAccessHandle(mManager, mEntry,
|
|
GetSimplePromise(), error);
|
|
|
|
ASSERT_TRUE(error.Failed());
|
|
ASSERT_TRUE(error.ErrorCodeIs(NS_ERROR_ILLEGAL_DURING_SHUTDOWN));
|
|
}
|
|
|
|
TEST_F(TestFileSystemRequestHandler, isGetWritableBlockedAfterShutdown) {
|
|
ASSERT_NO_FATAL_FAILURE(ShutdownFileSystemManager());
|
|
|
|
IgnoredErrorResult error;
|
|
GetFileSystemRequestHandler()->GetWritable(
|
|
mManager, mEntry, /* aKeepData */ false, GetSimplePromise(), error);
|
|
|
|
ASSERT_TRUE(error.Failed());
|
|
ASSERT_TRUE(error.ErrorCodeIs(NS_ERROR_ILLEGAL_DURING_SHUTDOWN));
|
|
}
|
|
|
|
TEST_F(TestFileSystemRequestHandler, isGetEntriesSuccessful) {
|
|
AllowStorageAccess();
|
|
|
|
auto fakeResponse = [](const auto& /* aRequest */, auto&& aResolve,
|
|
auto&& /* aReject */) {
|
|
nsTArray<FileSystemEntryMetadata> files;
|
|
nsTArray<FileSystemEntryMetadata> directories;
|
|
FileSystemDirectoryListing listing(files, directories);
|
|
FileSystemGetEntriesResponse response(listing);
|
|
aResolve(std::move(response));
|
|
};
|
|
|
|
RefPtr<ExpectResolveCalled> listener = MakeAndAddRef<ExpectResolveCalled>();
|
|
IgnoredErrorResult rv;
|
|
listener->ClearDone();
|
|
EXPECT_CALL(listener->GetSuccessHandler(), InvokeMe());
|
|
|
|
RefPtr<Promise> promise = Promise::Create(mGlobal, rv);
|
|
promise->AppendNativeHandler(listener);
|
|
|
|
EXPECT_CALL(*mFileSystemManagerChild, SendGetEntries(_, _, _))
|
|
.WillOnce(Invoke(fakeResponse));
|
|
|
|
auto testable = GetFileSystemRequestHandler();
|
|
RefPtr<FileSystemEntryMetadataArray> sink;
|
|
|
|
testable->GetEntries(mManager, mEntry.entryId(), /* page */ 0, promise, sink,
|
|
IgnoredErrorResult());
|
|
SpinEventLoopUntil("Promise is fulfilled or timeout"_ns,
|
|
[listener]() { return listener->IsDone(); });
|
|
}
|
|
|
|
TEST_F(TestFileSystemRequestHandler, isGetEntriesBlockedAfterShutdown) {
|
|
ASSERT_NO_FATAL_FAILURE(ShutdownFileSystemManager());
|
|
|
|
RefPtr<FileSystemEntryMetadataArray> sink;
|
|
|
|
IgnoredErrorResult error;
|
|
GetFileSystemRequestHandler()->GetEntries(mManager, mEntry.entryId(),
|
|
/* aPage */ 0, GetSimplePromise(),
|
|
sink, error);
|
|
|
|
ASSERT_TRUE(error.Failed());
|
|
ASSERT_TRUE(error.ErrorCodeIs(NS_ERROR_ILLEGAL_DURING_SHUTDOWN));
|
|
}
|
|
|
|
TEST_F(TestFileSystemRequestHandler, isRemoveEntrySuccessful) {
|
|
AllowStorageAccess();
|
|
|
|
auto fakeResponse = [](const auto& /* aRequest */, auto&& aResolve,
|
|
auto&& /* aReject */) {
|
|
FileSystemRemoveEntryResponse response(mozilla::void_t{});
|
|
aResolve(std::move(response));
|
|
};
|
|
|
|
EXPECT_CALL(mListener->GetSuccessHandler(), InvokeMe());
|
|
EXPECT_CALL(*mFileSystemManagerChild, SendRemoveEntry(_, _, _))
|
|
.WillOnce(Invoke(fakeResponse));
|
|
|
|
auto testable = GetFileSystemRequestHandler();
|
|
RefPtr<Promise> promise = GetDefaultPromise();
|
|
testable->RemoveEntry(mManager, mChild, /* recursive */ true, promise,
|
|
IgnoredErrorResult());
|
|
SpinEventLoopUntil("Promise is fulfilled or timeout"_ns,
|
|
[this]() { return mListener->IsDone(); });
|
|
}
|
|
|
|
TEST_F(TestFileSystemRequestHandler, isRemoveEntryBlockedAfterShutdown) {
|
|
ASSERT_NO_FATAL_FAILURE(ShutdownFileSystemManager());
|
|
|
|
IgnoredErrorResult error;
|
|
GetFileSystemRequestHandler()->RemoveEntry(
|
|
mManager, mChild, /* aRecursive */ true, GetSimplePromise(), error);
|
|
|
|
ASSERT_TRUE(error.Failed());
|
|
ASSERT_TRUE(error.ErrorCodeIs(NS_ERROR_ILLEGAL_DURING_SHUTDOWN));
|
|
}
|
|
|
|
} // namespace mozilla::dom::fs::test
|