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