tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

TestFileSystemRequestHandler.cpp (13513B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "FileSystemBackgroundRequestHandler.h"
      8 #include "FileSystemEntryMetadataArray.h"
      9 #include "FileSystemMocks.h"
     10 #include "fs/FileSystemRequestHandler.h"
     11 #include "gtest/gtest.h"
     12 #include "mozilla/SpinEventLoopUntil.h"
     13 #include "mozilla/UniquePtr.h"
     14 #include "mozilla/dom/FileBlobImpl.h"
     15 #include "mozilla/dom/FileSystemManager.h"
     16 #include "mozilla/dom/FileSystemManagerChild.h"
     17 #include "mozilla/dom/IPCBlob.h"
     18 #include "mozilla/dom/IPCBlobUtils.h"
     19 #include "mozilla/dom/PFileSystemManager.h"
     20 #include "mozilla/dom/StorageManager.h"
     21 #include "mozilla/ipc/FileDescriptorUtils.h"
     22 #include "mozilla/ipc/IPCCore.h"
     23 #include "mozilla/net/CookieJarSettings.h"
     24 #include "nsDirectoryServiceDefs.h"
     25 #include "nsIFile.h"
     26 
     27 using ::testing::_;
     28 using ::testing::ByRef;
     29 using ::testing::Invoke;
     30 using ::testing::Return;
     31 
     32 namespace mozilla::dom::fs::test {
     33 
     34 class TestFileSystemRequestHandler : public ::testing::Test {
     35 protected:
     36  void SetUp() override {
     37    mMockGlobal = GetMockGlobal();
     38    mGlobal = mMockGlobal;
     39 
     40    mCookieJarSettings = mozilla::net::CookieJarSettings::Create(
     41        mozilla::net::CookieJarSettings::CreateMode::eRegular,
     42        /* Should resist fingerprinting */ true);
     43    mListener = MakeAndAddRef<ExpectResolveCalled>();
     44 
     45    mChild = FileSystemChildMetadata("parent"_ns, u"ChildName"_ns);
     46    mEntry = FileSystemEntryMetadata("myid"_ns, u"EntryName"_ns,
     47                                     /* directory */ false);
     48    mName = u"testDir"_ns;
     49    mFileSystemManagerChild = MakeAndAddRef<TestFileSystemManagerChild>();
     50    mManager = MakeAndAddRef<FileSystemManager>(
     51        mGlobal, nullptr,
     52        MakeRefPtr<FileSystemBackgroundRequestHandler>(
     53            mFileSystemManagerChild));
     54  }
     55 
     56  void TearDown() override {
     57    if (!mManager->IsShutdown()) {
     58      EXPECT_NO_FATAL_FAILURE(ShutdownFileSystemManager());
     59    }
     60  }
     61 
     62  already_AddRefed<Promise> GetDefaultPromise() {
     63    IgnoredErrorResult rv;
     64    RefPtr<Promise> result = Promise::Create(mGlobal, rv);
     65    mListener->ClearDone();
     66    result->AppendNativeHandler(mListener->AsHandler());
     67 
     68    return result.forget();
     69  }
     70 
     71  already_AddRefed<Promise> GetSimplePromise() {
     72    IgnoredErrorResult rv;
     73    RefPtr<Promise> result = Promise::Create(mGlobal, rv);
     74 
     75    return result.forget();
     76  }
     77 
     78  already_AddRefed<Promise> GetShutdownPromise() {
     79    RefPtr<Promise> promise = GetDefaultPromise();
     80    EXPECT_CALL(*mFileSystemManagerChild, Shutdown())
     81        .WillOnce(Invoke([promise]() { promise->MaybeResolveWithUndefined(); }))
     82        .WillOnce(Return());
     83    EXPECT_CALL(mListener->GetSuccessHandler(), InvokeMe());
     84 
     85    return promise.forget();
     86  }
     87 
     88  UniquePtr<FileSystemRequestHandler> GetFileSystemRequestHandler() {
     89    return MakeUnique<FileSystemRequestHandler>();
     90  }
     91 
     92  void ShutdownFileSystemManager() {
     93    RefPtr<Promise> promise = GetShutdownPromise();
     94 
     95    mManager->Shutdown();
     96 
     97    SpinEventLoopUntil("Promise is fulfilled or timeout"_ns,
     98                       [this]() { return mListener->IsDone(); });
     99    ASSERT_TRUE(mManager->IsShutdown());
    100  }
    101 
    102  void AllowStorageAccess() {
    103    EXPECT_CALL(*mMockGlobal, GetCookieJarSettings())
    104        .WillOnce(::testing::Return(&*mCookieJarSettings));
    105 
    106    EXPECT_CALL(*mMockGlobal, GetStorageAccess())
    107        .WillOnce(::testing::Return(mozilla::StorageAccess::eAllow));
    108  }
    109 
    110  MockGlobalObject* mMockGlobal;
    111  nsCOMPtr<nsIGlobalObject> mGlobal;
    112 
    113  RefPtr<ExpectResolveCalled> mListener;
    114 
    115  FileSystemChildMetadata mChild;
    116  FileSystemEntryMetadata mEntry;
    117  nsString mName;
    118  RefPtr<TestFileSystemManagerChild> mFileSystemManagerChild;
    119  RefPtr<FileSystemManager> mManager;
    120  RefPtr<nsICookieJarSettings> mCookieJarSettings;
    121 };
    122 
    123 TEST_F(TestFileSystemRequestHandler, isGetRootHandleSuccessful) {
    124  AllowStorageAccess();
    125 
    126  auto fakeResponse = [](auto&& aResolve, auto&& /* aReject */) {
    127    EntryId expected = "expected"_ns;
    128    FileSystemGetHandleResponse response(expected);
    129    aResolve(std::move(response));
    130  };
    131 
    132  EXPECT_CALL(mListener->GetSuccessHandler(), InvokeMe());
    133  EXPECT_CALL(*mFileSystemManagerChild, SendGetRootHandle(_, _))
    134      .WillOnce(Invoke(fakeResponse));
    135 
    136  RefPtr<Promise> promise = GetDefaultPromise();
    137  auto testable = GetFileSystemRequestHandler();
    138  testable->GetRootHandle(mManager, promise, IgnoredErrorResult());
    139  SpinEventLoopUntil("Promise is fulfilled or timeout"_ns,
    140                     [this]() { return mListener->IsDone(); });
    141 }
    142 
    143 TEST_F(TestFileSystemRequestHandler, isGetRootHandleBlockedAfterShutdown) {
    144  ASSERT_NO_FATAL_FAILURE(ShutdownFileSystemManager());
    145 
    146  IgnoredErrorResult error;
    147  GetFileSystemRequestHandler()->GetRootHandle(mManager, GetSimplePromise(),
    148                                               error);
    149 
    150  ASSERT_TRUE(error.Failed());
    151  ASSERT_TRUE(error.ErrorCodeIs(NS_ERROR_ILLEGAL_DURING_SHUTDOWN));
    152 }
    153 
    154 TEST_F(TestFileSystemRequestHandler, isGetDirectoryHandleSuccessful) {
    155  AllowStorageAccess();
    156 
    157  auto fakeResponse = [](const auto& /* aRequest */, auto&& aResolve,
    158                         auto&& /* aReject */) {
    159    EntryId expected = "expected"_ns;
    160    FileSystemGetHandleResponse response(expected);
    161    aResolve(std::move(response));
    162  };
    163 
    164  EXPECT_CALL(mListener->GetSuccessHandler(), InvokeMe());
    165  EXPECT_CALL(*mFileSystemManagerChild, SendGetDirectoryHandle(_, _, _))
    166      .WillOnce(Invoke(fakeResponse));
    167 
    168  RefPtr<Promise> promise = GetDefaultPromise();
    169  auto testable = GetFileSystemRequestHandler();
    170  testable->GetDirectoryHandle(mManager, mChild,
    171                               /* create */ true, promise,
    172                               IgnoredErrorResult());
    173  SpinEventLoopUntil("Promise is fulfilled or timeout"_ns,
    174                     [this]() { return mListener->IsDone(); });
    175 }
    176 
    177 TEST_F(TestFileSystemRequestHandler, isGetDirectoryHandleBlockedAfterShutdown) {
    178  ASSERT_NO_FATAL_FAILURE(ShutdownFileSystemManager());
    179 
    180  IgnoredErrorResult error;
    181  GetFileSystemRequestHandler()->GetDirectoryHandle(
    182      mManager, mChild, /* aCreate */ true, GetSimplePromise(), error);
    183 
    184  ASSERT_TRUE(error.Failed());
    185  ASSERT_TRUE(error.ErrorCodeIs(NS_ERROR_ILLEGAL_DURING_SHUTDOWN));
    186 }
    187 
    188 TEST_F(TestFileSystemRequestHandler, isGetFileHandleSuccessful) {
    189  AllowStorageAccess();
    190 
    191  auto fakeResponse = [](const auto& /* aRequest */, auto&& aResolve,
    192                         auto&& /* aReject */) {
    193    EntryId expected = "expected"_ns;
    194    FileSystemGetHandleResponse response(expected);
    195    aResolve(std::move(response));
    196  };
    197 
    198  EXPECT_CALL(mListener->GetSuccessHandler(), InvokeMe());
    199  EXPECT_CALL(*mFileSystemManagerChild, SendGetFileHandle(_, _, _))
    200      .WillOnce(Invoke(fakeResponse));
    201 
    202  RefPtr<Promise> promise = GetDefaultPromise();
    203  auto testable = GetFileSystemRequestHandler();
    204  testable->GetFileHandle(mManager, mChild, /* create */ true, promise,
    205                          IgnoredErrorResult());
    206  SpinEventLoopUntil("Promise is fulfilled or timeout"_ns,
    207                     [this]() { return mListener->IsDone(); });
    208 }
    209 
    210 TEST_F(TestFileSystemRequestHandler, isGetFileHandleBlockedAfterShutdown) {
    211  ASSERT_NO_FATAL_FAILURE(ShutdownFileSystemManager());
    212 
    213  IgnoredErrorResult error;
    214  GetFileSystemRequestHandler()->GetFileHandle(
    215      mManager, mChild, /* aCreate */ true, GetSimplePromise(), error);
    216 
    217  ASSERT_TRUE(error.Failed());
    218  ASSERT_TRUE(error.ErrorCodeIs(NS_ERROR_ILLEGAL_DURING_SHUTDOWN));
    219 }
    220 
    221 TEST_F(TestFileSystemRequestHandler, isGetFileSuccessful) {
    222  AllowStorageAccess();
    223 
    224  auto fakeResponse = [](const auto& /* aRequest */, auto&& aResolve,
    225                         auto&& /* aReject */) {
    226    // We have to create a temporary file
    227    nsCOMPtr<nsIFile> tmpfile;
    228    nsresult rv =
    229        NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tmpfile));
    230    ASSERT_EQ(NS_SUCCEEDED(rv), true);
    231 
    232    rv = tmpfile->AppendNative("GetFileTestBlob"_ns);
    233    ASSERT_EQ(NS_SUCCEEDED(rv), true);
    234 
    235    rv = tmpfile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0666);
    236    ASSERT_EQ(NS_SUCCEEDED(rv), true);
    237 
    238    auto blob = MakeRefPtr<FileBlobImpl>(tmpfile);
    239 
    240    TimeStamp last_modified_ms = 0;
    241    ContentType type = "txt"_ns;
    242    IPCBlob file;
    243    IPCBlobUtils::Serialize(blob, file);
    244 
    245    nsTArray<Name> path;
    246    path.AppendElement(u"root"_ns);
    247    path.AppendElement(u"Trash"_ns);
    248 
    249    FileSystemFileProperties properties(last_modified_ms, file, type, path);
    250    FileSystemGetFileResponse response(properties);
    251    aResolve(std::move(response));
    252  };
    253 
    254  EXPECT_CALL(mListener->GetSuccessHandler(), InvokeMe());
    255  EXPECT_CALL(*mFileSystemManagerChild, SendGetFile(_, _, _))
    256      .WillOnce(Invoke(fakeResponse));
    257 
    258  RefPtr<Promise> promise = GetDefaultPromise();
    259  auto testable = GetFileSystemRequestHandler();
    260  testable->GetFile(mManager, mEntry, promise, IgnoredErrorResult());
    261  SpinEventLoopUntil("Promise is fulfilled or timeout"_ns,
    262                     [this]() { return mListener->IsDone(); });
    263 }
    264 
    265 TEST_F(TestFileSystemRequestHandler, isGetFileBlockedAfterShutdown) {
    266  ASSERT_NO_FATAL_FAILURE(ShutdownFileSystemManager());
    267 
    268  IgnoredErrorResult error;
    269  GetFileSystemRequestHandler()->GetFile(mManager, mEntry, GetSimplePromise(),
    270                                         error);
    271 
    272  ASSERT_TRUE(error.Failed());
    273  ASSERT_TRUE(error.ErrorCodeIs(NS_ERROR_ILLEGAL_DURING_SHUTDOWN));
    274 }
    275 
    276 TEST_F(TestFileSystemRequestHandler, isGetAccessHandleBlockedAfterShutdown) {
    277  RefPtr<Promise> promise = GetShutdownPromise();
    278 
    279  mManager->Shutdown();
    280 
    281  SpinEventLoopUntil("Promise is fulfilled or timeout"_ns,
    282                     [this]() { return mListener->IsDone(); });
    283  ASSERT_TRUE(mManager->IsShutdown());
    284 
    285  IgnoredErrorResult error;
    286  GetFileSystemRequestHandler()->GetAccessHandle(mManager, mEntry,
    287                                                 GetSimplePromise(), error);
    288 
    289  ASSERT_TRUE(error.Failed());
    290  ASSERT_TRUE(error.ErrorCodeIs(NS_ERROR_ILLEGAL_DURING_SHUTDOWN));
    291 }
    292 
    293 TEST_F(TestFileSystemRequestHandler, isGetWritableBlockedAfterShutdown) {
    294  ASSERT_NO_FATAL_FAILURE(ShutdownFileSystemManager());
    295 
    296  IgnoredErrorResult error;
    297  GetFileSystemRequestHandler()->GetWritable(
    298      mManager, mEntry, /* aKeepData */ false, GetSimplePromise(), error);
    299 
    300  ASSERT_TRUE(error.Failed());
    301  ASSERT_TRUE(error.ErrorCodeIs(NS_ERROR_ILLEGAL_DURING_SHUTDOWN));
    302 }
    303 
    304 TEST_F(TestFileSystemRequestHandler, isGetEntriesSuccessful) {
    305  AllowStorageAccess();
    306 
    307  auto fakeResponse = [](const auto& /* aRequest */, auto&& aResolve,
    308                         auto&& /* aReject */) {
    309    nsTArray<FileSystemEntryMetadata> files;
    310    nsTArray<FileSystemEntryMetadata> directories;
    311    FileSystemDirectoryListing listing(files, directories);
    312    FileSystemGetEntriesResponse response(listing);
    313    aResolve(std::move(response));
    314  };
    315 
    316  RefPtr<ExpectResolveCalled> listener = MakeAndAddRef<ExpectResolveCalled>();
    317  IgnoredErrorResult rv;
    318  listener->ClearDone();
    319  EXPECT_CALL(listener->GetSuccessHandler(), InvokeMe());
    320 
    321  RefPtr<Promise> promise = Promise::Create(mGlobal, rv);
    322  promise->AppendNativeHandler(listener);
    323 
    324  EXPECT_CALL(*mFileSystemManagerChild, SendGetEntries(_, _, _))
    325      .WillOnce(Invoke(fakeResponse));
    326 
    327  auto testable = GetFileSystemRequestHandler();
    328  RefPtr<FileSystemEntryMetadataArray> sink;
    329 
    330  testable->GetEntries(mManager, mEntry.entryId(), /* page */ 0, promise, sink,
    331                       IgnoredErrorResult());
    332  SpinEventLoopUntil("Promise is fulfilled or timeout"_ns,
    333                     [listener]() { return listener->IsDone(); });
    334 }
    335 
    336 TEST_F(TestFileSystemRequestHandler, isGetEntriesBlockedAfterShutdown) {
    337  ASSERT_NO_FATAL_FAILURE(ShutdownFileSystemManager());
    338 
    339  RefPtr<FileSystemEntryMetadataArray> sink;
    340 
    341  IgnoredErrorResult error;
    342  GetFileSystemRequestHandler()->GetEntries(mManager, mEntry.entryId(),
    343                                            /* aPage */ 0, GetSimplePromise(),
    344                                            sink, error);
    345 
    346  ASSERT_TRUE(error.Failed());
    347  ASSERT_TRUE(error.ErrorCodeIs(NS_ERROR_ILLEGAL_DURING_SHUTDOWN));
    348 }
    349 
    350 TEST_F(TestFileSystemRequestHandler, isRemoveEntrySuccessful) {
    351  AllowStorageAccess();
    352 
    353  auto fakeResponse = [](const auto& /* aRequest */, auto&& aResolve,
    354                         auto&& /* aReject */) {
    355    FileSystemRemoveEntryResponse response(mozilla::void_t{});
    356    aResolve(std::move(response));
    357  };
    358 
    359  EXPECT_CALL(mListener->GetSuccessHandler(), InvokeMe());
    360  EXPECT_CALL(*mFileSystemManagerChild, SendRemoveEntry(_, _, _))
    361      .WillOnce(Invoke(fakeResponse));
    362 
    363  auto testable = GetFileSystemRequestHandler();
    364  RefPtr<Promise> promise = GetDefaultPromise();
    365  testable->RemoveEntry(mManager, mChild, /* recursive */ true, promise,
    366                        IgnoredErrorResult());
    367  SpinEventLoopUntil("Promise is fulfilled or timeout"_ns,
    368                     [this]() { return mListener->IsDone(); });
    369 }
    370 
    371 TEST_F(TestFileSystemRequestHandler, isRemoveEntryBlockedAfterShutdown) {
    372  ASSERT_NO_FATAL_FAILURE(ShutdownFileSystemManager());
    373 
    374  IgnoredErrorResult error;
    375  GetFileSystemRequestHandler()->RemoveEntry(
    376      mManager, mChild, /* aRecursive */ true, GetSimplePromise(), error);
    377 
    378  ASSERT_TRUE(error.Failed());
    379  ASSERT_TRUE(error.ErrorCodeIs(NS_ERROR_ILLEGAL_DURING_SHUTDOWN));
    380 }
    381 
    382 }  // namespace mozilla::dom::fs::test