TestFileSystemDataManagerVersions.cpp (41291B)
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 <algorithm> 8 9 #include "ErrorList.h" 10 #include "FileSystemDataManager.h" 11 #include "FileSystemDatabaseManagerVersion001.h" 12 #include "FileSystemDatabaseManagerVersion002.h" 13 #include "FileSystemFileManager.h" 14 #include "FileSystemHashSource.h" 15 #include "ResultStatement.h" 16 #include "SchemaVersion001.h" 17 #include "SchemaVersion002.h" 18 #include "TestHelpers.h" 19 #include "gtest/gtest.h" 20 #include "mozIStorageService.h" 21 #include "mozStorageCID.h" 22 #include "mozStorageHelper.h" 23 #include "mozilla/ErrorNames.h" 24 #include "mozilla/dom/FileSystemTypes.h" 25 #include "mozilla/dom/PFileSystemManager.h" 26 #include "mozilla/dom/quota/CommonMetadata.h" 27 #include "mozilla/dom/quota/test/QuotaManagerDependencyFixture.h" 28 #include "nsContentUtils.h" 29 #include "nsIFile.h" 30 #include "nsLiteralString.h" 31 #include "nsNetCID.h" 32 #include "nsReadableUtils.h" 33 #include "nsString.h" 34 #include "nsStringFwd.h" 35 #include "nsTArray.h" 36 #include "nsTHashSet.h" 37 38 namespace mozilla::dom::fs::test { 39 40 using data::FileSystemDatabaseManagerVersion001; 41 using data::FileSystemDatabaseManagerVersion002; 42 using data::FileSystemFileManager; 43 44 quota::OriginMetadata GetOriginMetadataSample() { 45 return quota::OriginMetadata{""_ns, 46 "firefox.com"_ns, 47 "http://firefox.com"_ns, 48 "http://firefox.com"_ns, 49 /* aIsPrivate */ false, 50 quota::PERSISTENCE_TYPE_DEFAULT}; 51 } 52 53 class TestFileSystemDatabaseManagerVersionsBase 54 : public quota::test::QuotaManagerDependencyFixture { 55 public: 56 void SetUp() override { ASSERT_NO_FATAL_FAILURE(InitializeFixture()); } 57 58 void TearDown() override { 59 EXPECT_NO_FATAL_FAILURE(ClearStoragesForOrigin(GetOriginMetadataSample())); 60 ASSERT_NO_FATAL_FAILURE(ShutdownFixture()); 61 } 62 }; 63 64 class TestFileSystemDatabaseManagerVersions 65 : public TestFileSystemDatabaseManagerVersionsBase, 66 public ::testing::WithParamInterface<DatabaseVersion> { 67 public: 68 static void AssertEntryIdMoved(const EntryId& aOriginal, 69 const EntryId& aMoved) { 70 switch (sVersion) { 71 case 1: { 72 ASSERT_EQ(aOriginal, aMoved); 73 break; 74 } 75 case 2: { 76 ASSERT_NE(aOriginal, aMoved); 77 break; 78 } 79 default: { 80 ASSERT_FALSE(false) 81 << "Unknown database version"; 82 } 83 } 84 } 85 86 static void AssertEntryIdCollision(const EntryId& aOriginal, 87 const EntryId& aMoved) { 88 switch (sVersion) { 89 case 1: { 90 // We generated a new entryId 91 ASSERT_NE(aOriginal, aMoved); 92 break; 93 } 94 case 2: { 95 // We get the same entryId for the same input 96 ASSERT_EQ(aOriginal, aMoved); 97 break; 98 } 99 default: { 100 ASSERT_FALSE(false) 101 << "Unknown database version"; 102 } 103 } 104 } 105 106 static DatabaseVersion sVersion; 107 }; 108 109 // This is a minimal mock to allow us to safely call the lock methods 110 // while avoiding assertions 111 class MockFileSystemDataManager final : public data::FileSystemDataManager { 112 public: 113 MockFileSystemDataManager(const quota::OriginMetadata& aOriginMetadata, 114 MovingNotNull<nsCOMPtr<nsIEventTarget>> aIOTarget, 115 MovingNotNull<RefPtr<TaskQueue>> aIOTaskQueue) 116 : FileSystemDataManager(aOriginMetadata, nullptr, std::move(aIOTarget), 117 std::move(aIOTaskQueue)) {} 118 119 void SetDatabaseManager(data::FileSystemDatabaseManager* aDatabaseManager) { 120 mDatabaseManager = 121 UniquePtr<data::FileSystemDatabaseManager>(aDatabaseManager); 122 } 123 124 virtual ~MockFileSystemDataManager() { 125 mDatabaseManager->Close(); 126 mDatabaseManager = nullptr; 127 128 // Need to avoid assertions 129 mState = State::Closed; 130 } 131 }; 132 133 static void MakeDatabaseManagerVersions( 134 const DatabaseVersion aVersion, 135 RefPtr<MockFileSystemDataManager>& aDataManager, 136 FileSystemDatabaseManagerVersion001*& aDatabaseManager) { 137 TEST_TRY_UNWRAP(auto storageService, 138 MOZ_TO_RESULT_GET_TYPED(nsCOMPtr<mozIStorageService>, 139 MOZ_SELECT_OVERLOAD(do_GetService), 140 MOZ_STORAGE_SERVICE_CONTRACTID)); 141 142 const auto flags = mozIStorageService::CONNECTION_DEFAULT; 143 ResultConnection connection; 144 145 nsresult rv = storageService->OpenSpecialDatabase(kMozStorageMemoryStorageKey, 146 VoidCString(), flags, 147 getter_AddRefs(connection)); 148 ASSERT_NSEQ(NS_OK, rv); 149 150 auto fmRes = FileSystemFileManager::CreateFileSystemFileManager( 151 GetOriginMetadataSample()); 152 ASSERT_FALSE(fmRes.isErr()); 153 154 const Origin& testOrigin = GetTestOrigin(); 155 156 if (1 == aVersion) { 157 TEST_TRY_UNWRAP( 158 TestFileSystemDatabaseManagerVersions::sVersion, 159 SchemaVersion001::InitializeConnection(connection, testOrigin)); 160 } else { 161 ASSERT_EQ(2, aVersion); 162 163 TEST_TRY_UNWRAP(TestFileSystemDatabaseManagerVersions::sVersion, 164 SchemaVersion002::InitializeConnection( 165 connection, *fmRes.inspect(), testOrigin)); 166 } 167 ASSERT_NE(0, TestFileSystemDatabaseManagerVersions::sVersion); 168 169 TEST_TRY_UNWRAP(EntryId rootId, data::GetRootHandle(GetTestOrigin())); 170 171 QM_TRY_UNWRAP(auto streamTransportService, 172 MOZ_TO_RESULT_GET_TYPED(nsCOMPtr<nsIEventTarget>, 173 MOZ_SELECT_OVERLOAD(do_GetService), 174 NS_STREAMTRANSPORTSERVICE_CONTRACTID), 175 QM_VOID); 176 177 quota::OriginMetadata originMetadata = GetOriginMetadataSample(); 178 179 nsCString taskQueueName("OPFS "_ns + originMetadata.mOrigin); 180 181 RefPtr<TaskQueue> ioTaskQueue = 182 TaskQueue::Create(do_AddRef(streamTransportService), taskQueueName.get()); 183 184 aDataManager = MakeRefPtr<MockFileSystemDataManager>( 185 originMetadata, WrapMovingNotNull(streamTransportService), 186 WrapMovingNotNull(ioTaskQueue)); 187 188 if (1 == aVersion) { 189 aDatabaseManager = new FileSystemDatabaseManagerVersion001( 190 aDataManager, std::move(connection), fmRes.unwrap(), rootId); 191 } else { 192 ASSERT_EQ(2, aVersion); 193 194 aDatabaseManager = new FileSystemDatabaseManagerVersion002( 195 aDataManager, std::move(connection), fmRes.unwrap(), rootId); 196 } 197 198 aDataManager->SetDatabaseManager(aDatabaseManager); 199 } 200 201 DatabaseVersion TestFileSystemDatabaseManagerVersions::sVersion = 0; 202 203 TEST_P(TestFileSystemDatabaseManagerVersions, 204 smokeTestCreateRemoveDirectories) { 205 const DatabaseVersion version = GetParam(); 206 207 auto ioTask = [version]() { 208 nsresult rv = NS_OK; 209 // Ensure that FileSystemDataManager lives for the lifetime of the test 210 RefPtr<MockFileSystemDataManager> dataManager; 211 FileSystemDatabaseManagerVersion001* dm = nullptr; 212 ASSERT_NO_FATAL_FAILURE( 213 MakeDatabaseManagerVersions(version, dataManager, dm)); 214 ASSERT_TRUE(dm); 215 // if any of these exit early, we have to close 216 auto autoClose = MakeScopeExit([dm] { dm->Close(); }); 217 218 TEST_TRY_UNWRAP(EntryId rootId, data::GetRootHandle(GetTestOrigin())); 219 220 FileSystemChildMetadata firstChildMeta(rootId, u"First"_ns); 221 TEST_TRY_UNWRAP_ERR( 222 rv, dm->GetOrCreateDirectory(firstChildMeta, /* create */ false)); 223 ASSERT_NSEQ(NS_ERROR_DOM_NOT_FOUND_ERR, rv); 224 225 TEST_TRY_UNWRAP(EntryId firstChild, dm->GetOrCreateDirectory( 226 firstChildMeta, /* create */ true)); 227 228 int32_t dbVersion = 0; 229 TEST_TRY_UNWRAP(FileSystemDirectoryListing entries, 230 dm->GetDirectoryEntries(rootId, dbVersion)); 231 ASSERT_EQ(1u, entries.directories().Length()); 232 ASSERT_EQ(0u, entries.files().Length()); 233 234 const auto& firstItemRef = entries.directories()[0]; 235 ASSERT_TRUE(u"First"_ns == firstItemRef.entryName()) 236 << firstItemRef.entryName(); 237 ASSERT_EQ(firstChild, firstItemRef.entryId()); 238 239 TEST_TRY_UNWRAP( 240 EntryId firstChildClone, 241 dm->GetOrCreateDirectory(firstChildMeta, /* create */ true)); 242 ASSERT_EQ(firstChild, firstChildClone); 243 244 FileSystemChildMetadata secondChildMeta(firstChild, u"Second"_ns); 245 TEST_TRY_UNWRAP( 246 EntryId secondChild, 247 dm->GetOrCreateDirectory(secondChildMeta, /* create */ true)); 248 249 FileSystemEntryPair shortPair(firstChild, secondChild); 250 TEST_TRY_UNWRAP(Path shortPath, dm->Resolve(shortPair)); 251 ASSERT_EQ(1u, shortPath.Length()); 252 ASSERT_EQ(u"Second"_ns, shortPath[0]); 253 254 FileSystemEntryPair longPair(rootId, secondChild); 255 TEST_TRY_UNWRAP(Path longPath, dm->Resolve(longPair)); 256 ASSERT_EQ(2u, longPath.Length()); 257 ASSERT_EQ(u"First"_ns, longPath[0]); 258 ASSERT_EQ(u"Second"_ns, longPath[1]); 259 260 FileSystemEntryPair wrongPair(secondChild, rootId); 261 TEST_TRY_UNWRAP(Path emptyPath, dm->Resolve(wrongPair)); 262 ASSERT_TRUE(emptyPath.IsEmpty()); 263 264 PageNumber page = 0; 265 TEST_TRY_UNWRAP(FileSystemDirectoryListing fEntries, 266 dm->GetDirectoryEntries(firstChild, page)); 267 ASSERT_EQ(1u, fEntries.directories().Length()); 268 ASSERT_EQ(0u, fEntries.files().Length()); 269 270 const auto& secItemRef = fEntries.directories()[0]; 271 ASSERT_TRUE(u"Second"_ns == secItemRef.entryName()) 272 << secItemRef.entryName(); 273 ASSERT_EQ(secondChild, secItemRef.entryId()); 274 275 TEST_TRY_UNWRAP_ERR( 276 rv, dm->RemoveDirectory(firstChildMeta, /* recursive */ false)); 277 ASSERT_NSEQ(NS_ERROR_DOM_INVALID_MODIFICATION_ERR, rv); 278 279 TEST_TRY_UNWRAP(bool isDeleted, 280 dm->RemoveDirectory(firstChildMeta, /* recursive */ true)); 281 ASSERT_TRUE(isDeleted); 282 283 FileSystemChildMetadata thirdChildMeta(secondChild, u"Second"_ns); 284 TEST_TRY_UNWRAP_ERR( 285 rv, dm->GetOrCreateDirectory(thirdChildMeta, /* create */ true)); 286 ASSERT_NSEQ(NS_ERROR_STORAGE_CONSTRAINT, rv); // Is this a good error? 287 288 dm->Close(); 289 }; 290 291 PerformOnIOThread(std::move(ioTask)); 292 } 293 294 TEST_P(TestFileSystemDatabaseManagerVersions, smokeTestCreateRemoveFiles) { 295 const DatabaseVersion version = GetParam(); 296 297 auto ioTask = [version]() { 298 nsresult rv = NS_OK; 299 // Ensure that FileSystemDataManager lives for the lifetime of the test 300 RefPtr<MockFileSystemDataManager> datamanager; 301 FileSystemDatabaseManagerVersion001* dm = nullptr; 302 ASSERT_NO_FATAL_FAILURE( 303 MakeDatabaseManagerVersions(version, datamanager, dm)); 304 305 TEST_TRY_UNWRAP(EntryId rootId, data::GetRootHandle(GetTestOrigin())); 306 307 FileSystemChildMetadata firstChildMeta(rootId, u"First"_ns); 308 // If creating is not allowed, getting a file from empty root fails 309 TEST_TRY_UNWRAP_ERR( 310 rv, dm->GetOrCreateFile(firstChildMeta, /* create */ false)); 311 ASSERT_NSEQ(NS_ERROR_DOM_NOT_FOUND_ERR, rv); 312 313 // Creating a file under empty root succeeds 314 TEST_TRY_UNWRAP(EntryId firstChild, 315 dm->GetOrCreateFile(firstChildMeta, /* create */ true)); 316 317 // Second time, the same file is returned 318 TEST_TRY_UNWRAP(EntryId firstChildClone, 319 dm->GetOrCreateFile(firstChildMeta, /* create */ true)); 320 ASSERT_EQ(firstChild, firstChildClone); 321 322 // Directory listing returns the created file 323 PageNumber page = 0; 324 TEST_TRY_UNWRAP(FileSystemDirectoryListing entries, 325 dm->GetDirectoryEntries(rootId, page)); 326 ASSERT_EQ(0u, entries.directories().Length()); 327 ASSERT_EQ(1u, entries.files().Length()); 328 329 auto& firstItemRef = entries.files()[0]; 330 ASSERT_TRUE(u"First"_ns == firstItemRef.entryName()) 331 << firstItemRef.entryName(); 332 ASSERT_EQ(firstChild, firstItemRef.entryId()); 333 334 FileId fileId = FileId(firstItemRef.entryId()); // Default 335 336 ContentType type; 337 TimeStamp lastModifiedMilliSeconds; 338 Path path; 339 nsCOMPtr<nsIFile> file; 340 rv = dm->GetFile(firstItemRef.entryId(), fileId, FileMode::EXCLUSIVE, type, 341 lastModifiedMilliSeconds, path, file); 342 ASSERT_NSEQ(NS_OK, rv); 343 344 const int64_t nowMilliSeconds = PR_Now() / 1000; 345 ASSERT_GE(nowMilliSeconds, lastModifiedMilliSeconds); 346 const int64_t expectedMaxDelayMilliSeconds = 100; 347 const int64_t actualDelay = nowMilliSeconds - lastModifiedMilliSeconds; 348 ASSERT_LT(actualDelay, expectedMaxDelayMilliSeconds); 349 350 ASSERT_EQ(1u, path.Length()); 351 ASSERT_STREQ(u"First"_ns, path[0]); 352 353 ASSERT_NE(nullptr, file); 354 355 // Getting the file entry as directory fails 356 TEST_TRY_UNWRAP_ERR( 357 rv, dm->GetOrCreateDirectory(firstChildMeta, /* create */ false)); 358 ASSERT_NSEQ(NS_ERROR_DOM_TYPE_MISMATCH_ERR, rv); 359 360 // Getting or creating the file entry as directory also fails 361 TEST_TRY_UNWRAP_ERR( 362 rv, dm->GetOrCreateDirectory(firstChildMeta, /* create */ true)); 363 ASSERT_NSEQ(NS_ERROR_DOM_TYPE_MISMATCH_ERR, rv); 364 365 // Creating a file with non existing parent hash fails 366 367 EntryId notAChildHash = "0123456789abcdef0123456789abcdef"_ns; 368 FileSystemChildMetadata notAChildMeta(notAChildHash, u"Dummy"_ns); 369 TEST_TRY_UNWRAP_ERR(rv, 370 dm->GetOrCreateFile(notAChildMeta, /* create */ true)); 371 ASSERT_NSEQ(NS_ERROR_STORAGE_CONSTRAINT, rv); // Is this a good error? 372 373 // We create a directory under root 374 FileSystemChildMetadata secondChildMeta(rootId, u"Second"_ns); 375 TEST_TRY_UNWRAP( 376 EntryId secondChild, 377 dm->GetOrCreateDirectory(secondChildMeta, /* create */ true)); 378 379 // The root should now contain the existing file and the new directory 380 TEST_TRY_UNWRAP(FileSystemDirectoryListing fEntries, 381 dm->GetDirectoryEntries(rootId, page)); 382 ASSERT_EQ(1u, fEntries.directories().Length()); 383 ASSERT_EQ(1u, fEntries.files().Length()); 384 385 const auto& secItemRef = fEntries.directories()[0]; 386 ASSERT_TRUE(u"Second"_ns == secItemRef.entryName()) 387 << secItemRef.entryName(); 388 ASSERT_EQ(secondChild, secItemRef.entryId()); 389 390 // Create a file under the new directory 391 FileSystemChildMetadata thirdChildMeta(secondChild, u"Third"_ns); 392 TEST_TRY_UNWRAP(EntryId thirdChild, 393 dm->GetOrCreateFile(thirdChildMeta, /* create */ true)); 394 395 FileSystemEntryPair entryPair(rootId, thirdChild); 396 TEST_TRY_UNWRAP(Path entryPath, dm->Resolve(entryPair)); 397 ASSERT_EQ(2u, entryPath.Length()); 398 ASSERT_EQ(u"Second"_ns, entryPath[0]); 399 ASSERT_EQ(u"Third"_ns, entryPath[1]); 400 401 // If recursion is not allowed, the non-empty new directory may not be 402 // removed 403 TEST_TRY_UNWRAP_ERR( 404 rv, dm->RemoveDirectory(secondChildMeta, /* recursive */ false)); 405 ASSERT_NSEQ(NS_ERROR_DOM_INVALID_MODIFICATION_ERR, rv); 406 407 // If recursion is allowed, the new directory goes away. 408 TEST_TRY_UNWRAP(bool isDeleted, 409 dm->RemoveDirectory(secondChildMeta, /* recursive */ true)); 410 ASSERT_TRUE(isDeleted); 411 412 // The file under the removed directory is no longer accessible. 413 TEST_TRY_UNWRAP_ERR(rv, 414 dm->GetOrCreateFile(thirdChildMeta, /* create */ true)); 415 ASSERT_NSEQ(NS_ERROR_STORAGE_CONSTRAINT, rv); // Is this a good error? 416 417 // The deletion is reflected by the root directory listing 418 TEST_TRY_UNWRAP(FileSystemDirectoryListing nEntries, 419 dm->GetDirectoryEntries(rootId, 0)); 420 ASSERT_EQ(0u, nEntries.directories().Length()); 421 ASSERT_EQ(1u, nEntries.files().Length()); 422 423 const auto& fileItemRef = nEntries.files()[0]; 424 ASSERT_TRUE(u"First"_ns == fileItemRef.entryName()) 425 << fileItemRef.entryName(); 426 ASSERT_EQ(firstChild, fileItemRef.entryId()); 427 428 dm->Close(); 429 }; 430 431 PerformOnIOThread(std::move(ioTask)); 432 } 433 434 TEST_P(TestFileSystemDatabaseManagerVersions, smokeTestCreateMoveDirectories) { 435 const DatabaseVersion version = GetParam(); 436 437 auto ioTask = [version]() { 438 // Ensure that FileSystemDataManager lives for the lifetime of the test 439 RefPtr<MockFileSystemDataManager> datamanager; 440 FileSystemDatabaseManagerVersion001* dm = nullptr; 441 ASSERT_NO_FATAL_FAILURE( 442 MakeDatabaseManagerVersions(version, datamanager, dm)); 443 auto closeAtExit = MakeScopeExit([&dm]() { dm->Close(); }); 444 445 TEST_TRY_UNWRAP(EntryId rootId, data::GetRootHandle(GetTestOrigin())); 446 447 FileSystemEntryMetadata rootMeta{rootId, u"root"_ns, 448 /* is directory */ true}; 449 450 { 451 // Sanity check: no existing items should be found 452 TEST_TRY_UNWRAP(FileSystemDirectoryListing contents, 453 dm->GetDirectoryEntries(rootId, /* page */ 0u)); 454 ASSERT_TRUE(contents.directories().IsEmpty()); 455 ASSERT_TRUE(contents.files().IsEmpty()); 456 } 457 458 // Create subdirectory 459 FileSystemChildMetadata firstChildMeta(rootId, u"First"_ns); 460 TEST_TRY_UNWRAP( 461 EntryId firstChildDir, 462 dm->GetOrCreateDirectory(firstChildMeta, /* create */ true)); 463 464 { 465 // Check that directory listing is as expected 466 TEST_TRY_UNWRAP(FileSystemDirectoryListing contents, 467 dm->GetDirectoryEntries(rootId, /* page */ 0u)); 468 ASSERT_TRUE(contents.files().IsEmpty()); 469 ASSERT_EQ(1u, contents.directories().Length()); 470 ASSERT_STREQ(firstChildMeta.childName(), 471 contents.directories()[0].entryName()); 472 } 473 474 { 475 // Try to move subdirectory to its current location 476 FileSystemEntryMetadata src{firstChildDir, firstChildMeta.childName(), 477 /* is directory */ true}; 478 FileSystemChildMetadata dest{rootId, src.entryName()}; 479 TEST_TRY_UNWRAP(EntryId moved, dm->MoveEntry(src, dest)); 480 ASSERT_FALSE(moved.IsEmpty()); 481 firstChildDir = moved; 482 } 483 484 { 485 // Try to move subdirectory under itself 486 FileSystemEntryMetadata src{firstChildDir, firstChildMeta.childName(), 487 /* is directory */ true}; 488 FileSystemChildMetadata dest{src.entryId(), src.entryName()}; 489 TEST_TRY_UNWRAP_ERR(nsresult rv, dm->MoveEntry(src, dest)); 490 ASSERT_NSEQ(NS_ERROR_DOM_INVALID_MODIFICATION_ERR, rv); 491 } 492 493 { 494 // Try to move root under its subdirectory 495 FileSystemEntryMetadata src{rootId, rootMeta.entryName(), 496 /* is directory */ true}; 497 FileSystemChildMetadata dest{firstChildDir, src.entryName()}; 498 TEST_TRY_UNWRAP_ERR(nsresult rv, dm->MoveEntry(src, dest)); 499 ASSERT_NSEQ(NS_ERROR_DOM_NOT_FOUND_ERR, rv); 500 } 501 502 // Create subsubdirectory 503 FileSystemChildMetadata firstChildDescendantMeta(firstChildDir, 504 u"Descendant"_ns); 505 TEST_TRY_UNWRAP(EntryId firstChildDescendant, 506 dm->GetOrCreateDirectory(firstChildDescendantMeta, 507 /* create */ true)); 508 509 { 510 TEST_TRY_UNWRAP(FileSystemDirectoryListing contents, 511 dm->GetDirectoryEntries(firstChildDir, /* page */ 0u)); 512 ASSERT_TRUE(contents.files().IsEmpty()); 513 ASSERT_EQ(1u, contents.directories().Length()); 514 ASSERT_STREQ(firstChildDescendantMeta.childName(), 515 contents.directories()[0].entryName()); 516 517 TEST_TRY_UNWRAP( 518 Path subSubDirPath, 519 dm->Resolve({rootId, contents.directories()[0].entryId()})); 520 ASSERT_EQ(2u, subSubDirPath.Length()); 521 ASSERT_STREQ(firstChildMeta.childName(), subSubDirPath[0]); 522 ASSERT_STREQ(firstChildDescendantMeta.childName(), subSubDirPath[1]); 523 } 524 525 { 526 // Try to move subsubdirectory to its current location 527 FileSystemEntryMetadata src{firstChildDescendant, 528 firstChildDescendantMeta.childName(), 529 /* is directory */ true}; 530 FileSystemChildMetadata dest{firstChildDir, src.entryName()}; 531 TEST_TRY_UNWRAP(EntryId moved, dm->MoveEntry(src, dest)); 532 ASSERT_FALSE(moved.IsEmpty()); 533 firstChildDescendant = moved; 534 } 535 536 { 537 // Try to move subsubdirectory under itself 538 FileSystemEntryMetadata src{firstChildDescendant, 539 firstChildDescendantMeta.childName(), 540 /* is directory */ true}; 541 FileSystemChildMetadata dest{src.entryId(), src.entryName()}; 542 TEST_TRY_UNWRAP_ERR(nsresult rv, dm->MoveEntry(src, dest)); 543 ASSERT_NSEQ(NS_ERROR_DOM_INVALID_MODIFICATION_ERR, rv); 544 } 545 546 { 547 // Try to move subdirectory under its descendant 548 FileSystemEntryMetadata src{firstChildDir, firstChildMeta.childName(), 549 /* is directory */ true}; 550 FileSystemChildMetadata dest{firstChildDescendant, src.entryName()}; 551 TEST_TRY_UNWRAP_ERR(nsresult rv, dm->MoveEntry(src, dest)); 552 ASSERT_NSEQ(NS_ERROR_DOM_INVALID_MODIFICATION_ERR, rv); 553 } 554 555 { 556 // Try to move root under its subsubdirectory 557 FileSystemEntryMetadata src{rootId, rootMeta.entryName(), 558 /* is directory */ true}; 559 FileSystemChildMetadata dest{firstChildDescendant, src.entryName()}; 560 TEST_TRY_UNWRAP_ERR(nsresult rv, dm->MoveEntry(src, dest)); 561 ASSERT_NSEQ(NS_ERROR_DOM_NOT_FOUND_ERR, rv); 562 } 563 564 // Create file in the subdirectory with already existing subsubdirectory 565 FileSystemChildMetadata testFileMeta(firstChildDir, u"Subfile"_ns); 566 TEST_TRY_UNWRAP(EntryId testFile, 567 dm->GetOrCreateFile(testFileMeta, /* create */ true)); 568 569 // Get handles to the original locations of the entries 570 FileSystemEntryMetadata subSubDir; 571 FileSystemEntryMetadata subSubFile; 572 573 { 574 TEST_TRY_UNWRAP(FileSystemDirectoryListing contents, 575 dm->GetDirectoryEntries(firstChildDir, /* page */ 0u)); 576 ASSERT_EQ(1u, contents.files().Length()); 577 ASSERT_EQ(1u, contents.directories().Length()); 578 579 subSubDir = contents.directories()[0]; 580 ASSERT_STREQ(firstChildDescendantMeta.childName(), subSubDir.entryName()); 581 582 subSubFile = contents.files()[0]; 583 ASSERT_STREQ(testFileMeta.childName(), subSubFile.entryName()); 584 ASSERT_EQ(testFile, subSubFile.entryId()); 585 } 586 587 { 588 TEST_TRY_UNWRAP(Path entryPath, 589 dm->Resolve({rootId, subSubFile.entryId()})); 590 ASSERT_EQ(2u, entryPath.Length()); 591 ASSERT_STREQ(firstChildMeta.childName(), entryPath[0]); 592 ASSERT_STREQ(testFileMeta.childName(), entryPath[1]); 593 } 594 595 { 596 // Try to move file to its current location 597 FileSystemEntryMetadata src{testFile, testFileMeta.childName(), 598 /* is directory */ false}; 599 FileSystemChildMetadata dest{firstChildDir, src.entryName()}; 600 TEST_TRY_UNWRAP(EntryId moved, dm->MoveEntry(src, dest)); 601 ASSERT_FALSE(moved.IsEmpty()); 602 testFile = moved; 603 } 604 605 { 606 // Try to move subsubdirectory under a file 607 FileSystemEntryMetadata src{firstChildDescendant, 608 firstChildDescendantMeta.childName(), 609 /* is directory */ true}; 610 FileSystemChildMetadata dest{testFile, 611 firstChildDescendantMeta.childName()}; 612 TEST_TRY_UNWRAP_ERR(nsresult rv, dm->MoveEntry(src, dest)); 613 ASSERT_NSEQ(NS_ERROR_STORAGE_CONSTRAINT, rv); 614 } 615 616 { 617 // Silently overwrite a directory with a file using rename 618 FileSystemEntryMetadata src{testFile, testFileMeta.childName(), 619 /* is directory */ false}; 620 const FileSystemChildMetadata& dest = firstChildDescendantMeta; 621 TEST_TRY_UNWRAP(EntryId moved, dm->MoveEntry(src, dest)); 622 ASSERT_FALSE(moved.IsEmpty()); 623 testFile = moved; 624 } 625 626 { 627 // Move file back and recreate the directory 628 FileSystemEntryMetadata src{testFile, 629 firstChildDescendantMeta.childName(), 630 /* is directory */ false}; 631 632 TEST_TRY_UNWRAP(EntryId moved, dm->MoveEntry(src, testFileMeta)); 633 ASSERT_FALSE(moved.IsEmpty()); 634 testFile = moved; 635 636 TEST_TRY_UNWRAP(EntryId firstChildDescendantCheck, 637 dm->GetOrCreateDirectory(firstChildDescendantMeta, 638 /* create */ true)); 639 ASSERT_EQ(firstChildDescendant, firstChildDescendantCheck); 640 } 641 642 { 643 // Try to rename directory to quietly overwrite a file 644 FileSystemEntryMetadata src{firstChildDescendant, 645 firstChildDescendantMeta.childName(), 646 /* is directory */ true}; 647 const FileSystemChildMetadata& dest = testFileMeta; 648 TEST_TRY_UNWRAP(EntryId moved, dm->MoveEntry(src, dest)); 649 ASSERT_FALSE(moved.IsEmpty()); 650 firstChildDescendant = moved; 651 } 652 653 { 654 // Move directory back and recreate the file 655 FileSystemEntryMetadata src{firstChildDescendant, 656 testFileMeta.childName(), 657 /* is directory */ true}; 658 659 FileSystemChildMetadata dest{firstChildDir, 660 firstChildDescendantMeta.childName()}; 661 662 TEST_TRY_UNWRAP(EntryId moved, dm->MoveEntry(src, dest)); 663 ASSERT_FALSE(moved.IsEmpty()); 664 firstChildDescendant = moved; 665 666 TEST_TRY_UNWRAP(EntryId testFileCheck, 667 dm->GetOrCreateFile(testFileMeta, /* create */ true)); 668 ASSERT_EQ(testFile, testFileCheck); 669 } 670 671 { 672 // Move file one level up 673 FileSystemEntryMetadata src{testFile, testFileMeta.childName(), 674 /* is directory */ false}; 675 FileSystemChildMetadata dest{rootId, src.entryName()}; 676 TEST_TRY_UNWRAP(EntryId moved, dm->MoveEntry(src, dest)); 677 ASSERT_FALSE(moved.IsEmpty()); 678 testFile = moved; 679 } 680 681 { 682 // Check that listings are as expected 683 TEST_TRY_UNWRAP(FileSystemDirectoryListing contents, 684 dm->GetDirectoryEntries(firstChildDir, 0u)); 685 ASSERT_TRUE(contents.files().IsEmpty()); 686 ASSERT_EQ(1u, contents.directories().Length()); 687 ASSERT_STREQ(firstChildDescendantMeta.childName(), 688 contents.directories()[0].entryName()); 689 } 690 691 { 692 TEST_TRY_UNWRAP(FileSystemDirectoryListing contents, 693 dm->GetDirectoryEntries(rootId, 0u)); 694 ASSERT_EQ(1u, contents.files().Length()); 695 ASSERT_EQ(1u, contents.directories().Length()); 696 ASSERT_STREQ(testFileMeta.childName(), contents.files()[0].entryName()); 697 ASSERT_STREQ(firstChildMeta.childName(), 698 contents.directories()[0].entryName()); 699 } 700 701 { 702 ASSERT_NO_FATAL_FAILURE( 703 AssertEntryIdMoved(subSubFile.entryId(), testFile)); 704 TEST_TRY_UNWRAP(Path entryPath, 705 dm->Resolve({rootId, subSubFile.entryId()})); 706 if (1 == sVersion) { 707 ASSERT_EQ(1u, entryPath.Length()); 708 ASSERT_STREQ(testFileMeta.childName(), entryPath[0]); 709 } else { 710 ASSERT_EQ(2, sVersion); 711 // Per spec, path result is empty when no path exists. 712 ASSERT_TRUE(entryPath.IsEmpty()); 713 } 714 } 715 716 { 717 // Try to get a handle to the old item 718 TEST_TRY_UNWRAP_ERR( 719 nsresult rv, dm->GetOrCreateFile(testFileMeta, /* create */ false)); 720 ASSERT_NSEQ(NS_ERROR_DOM_NOT_FOUND_ERR, rv); 721 } 722 723 { 724 // Try to move + rename file one level down to collide with a 725 // subSubDirectory, silently overwriting it 726 FileSystemEntryMetadata src{testFile, testFileMeta.childName(), 727 /* is directory */ false}; 728 FileSystemChildMetadata dest{firstChildDir, 729 firstChildDescendantMeta.childName()}; 730 TEST_TRY_UNWRAP(EntryId moved, dm->MoveEntry(src, dest)); 731 ASSERT_FALSE(moved.IsEmpty()); 732 testFile = moved; 733 } 734 735 { 736 // Restore filename, move file to its original location and recreate 737 // the overwritten directory 738 FileSystemEntryMetadata src{testFile, 739 firstChildDescendantMeta.childName(), 740 /* is directory */ false}; 741 FileSystemChildMetadata dest{rootId, testFileMeta.childName()}; 742 TEST_TRY_UNWRAP(EntryId moved, dm->MoveEntry(src, dest)); 743 ASSERT_FALSE(moved.IsEmpty()); 744 testFile = moved; 745 746 FileSystemChildMetadata oldLocation{firstChildDir, 747 firstChildDescendantMeta.childName()}; 748 749 // Is there still something out there? 750 TEST_TRY_UNWRAP_ERR(nsresult rv, 751 dm->GetOrCreateFile(oldLocation, /* create */ false)); 752 ASSERT_NSEQ(NS_ERROR_DOM_NOT_FOUND_ERR, rv); 753 754 TEST_TRY_UNWRAP(EntryId firstChildDescendantCheck, 755 dm->GetOrCreateDirectory(oldLocation, /* create */ true)); 756 ASSERT_EQ(firstChildDescendant, firstChildDescendantCheck); 757 } 758 759 // Rename file first and then try to move it to collide with 760 // subSubDirectory, silently overwriting it 761 { 762 // Rename 763 FileSystemEntryMetadata src{testFile, testFileMeta.childName(), 764 /* is directory */ false}; 765 FileSystemChildMetadata dest{rootId, 766 firstChildDescendantMeta.childName()}; 767 TEST_TRY_UNWRAP(EntryId moved, dm->MoveEntry(src, dest)); 768 ASSERT_FALSE(moved.IsEmpty()); 769 testFile = moved; 770 } 771 772 { 773 // Try to move one level down 774 FileSystemEntryMetadata src{testFile, 775 firstChildDescendantMeta.childName(), 776 /* is directory */ false}; 777 FileSystemChildMetadata dest{firstChildDir, 778 firstChildDescendantMeta.childName()}; 779 TEST_TRY_UNWRAP(EntryId moved, dm->MoveEntry(src, dest)); 780 ASSERT_FALSE(moved.IsEmpty()); 781 testFile = moved; 782 } 783 784 { 785 // Move the file back and recreate the directory 786 FileSystemEntryMetadata src{testFile, 787 firstChildDescendantMeta.childName(), 788 /* is directory */ false}; 789 FileSystemChildMetadata dest{rootId, 790 firstChildDescendantMeta.childName()}; 791 TEST_TRY_UNWRAP(EntryId moved, dm->MoveEntry(src, dest)); 792 ASSERT_FALSE(moved.IsEmpty()); 793 testFile = moved; 794 795 FileSystemChildMetadata oldLocation{firstChildDir, 796 firstChildDescendantMeta.childName()}; 797 798 // Is there still something out there? 799 TEST_TRY_UNWRAP_ERR(nsresult rv, 800 dm->GetOrCreateFile(oldLocation, /* create */ false)); 801 ASSERT_NSEQ(NS_ERROR_DOM_NOT_FOUND_ERR, rv); 802 803 TEST_TRY_UNWRAP(EntryId firstChildDescendantCheck, 804 dm->GetOrCreateDirectory(oldLocation, /* create */ true)); 805 ASSERT_EQ(firstChildDescendant, firstChildDescendantCheck); 806 } 807 808 { 809 // Try to move subSubDirectory one level up to quietly overwrite a file 810 FileSystemEntryMetadata src{firstChildDescendant, 811 firstChildDescendantMeta.childName(), 812 /* is directory */ true}; 813 FileSystemChildMetadata dest{rootId, 814 firstChildDescendantMeta.childName()}; 815 TEST_TRY_UNWRAP(EntryId moved, dm->MoveEntry(src, dest)); 816 ASSERT_FALSE(moved.IsEmpty()); 817 firstChildDescendant = moved; 818 } 819 820 { 821 // Move subSubDirectory back one level down and recreate the file 822 FileSystemEntryMetadata src{firstChildDescendant, 823 firstChildDescendantMeta.childName(), 824 /* is directory */ true}; 825 FileSystemChildMetadata dest{firstChildDir, 826 firstChildDescendantMeta.childName()}; 827 TEST_TRY_UNWRAP(EntryId moved, dm->MoveEntry(src, dest)); 828 ASSERT_FALSE(moved.IsEmpty()); 829 firstChildDescendant = moved; 830 831 FileSystemChildMetadata oldLocation{rootId, 832 firstChildDescendantMeta.childName()}; 833 834 // We should no longer find anything there 835 TEST_TRY_UNWRAP_ERR(nsresult rv, dm->GetOrCreateDirectory( 836 oldLocation, /* create */ false)); 837 ASSERT_NSEQ(NS_ERROR_DOM_NOT_FOUND_ERR, rv); 838 839 TEST_TRY_UNWRAP(EntryId testFileCheck, 840 dm->GetOrCreateFile(oldLocation, /* create */ true)); 841 ASSERT_NO_FATAL_FAILURE(AssertEntryIdCollision(testFile, testFileCheck)); 842 testFile = testFileCheck; 843 } 844 845 // Create a new file in the subsubdirectory 846 FileSystemChildMetadata newFileMeta{firstChildDescendant, 847 testFileMeta.childName()}; 848 EntryId oldFirstChildDescendant = firstChildDescendant; 849 850 TEST_TRY_UNWRAP(EntryId newFile, 851 dm->GetOrCreateFile(newFileMeta, /* create */ true)); 852 853 { 854 TEST_TRY_UNWRAP(Path entryPath, dm->Resolve({rootId, newFile})); 855 ASSERT_EQ(3u, entryPath.Length()); 856 ASSERT_STREQ(firstChildMeta.childName(), entryPath[0]); 857 ASSERT_STREQ(firstChildDescendantMeta.childName(), entryPath[1]); 858 ASSERT_STREQ(testFileMeta.childName(), entryPath[2]); 859 } 860 861 { 862 // Move subSubDirectory one level up and rename it to testFile's old name 863 FileSystemEntryMetadata src{firstChildDescendant, 864 firstChildDescendantMeta.childName(), 865 /* is directory */ true}; 866 FileSystemChildMetadata dest{rootId, testFileMeta.childName()}; 867 TEST_TRY_UNWRAP(EntryId moved, dm->MoveEntry(src, dest)); 868 ASSERT_FALSE(moved.IsEmpty()); 869 firstChildDescendant = moved; 870 } 871 872 { 873 // Try to get handles to the moved items 874 TEST_TRY_UNWRAP_ERR(nsresult rv, 875 dm->GetOrCreateDirectory(firstChildDescendantMeta, 876 /* create */ false)); 877 ASSERT_NSEQ(NS_ERROR_DOM_NOT_FOUND_ERR, rv); 878 879 // Still under the same parent which was moved 880 if (1 == sVersion) { 881 TEST_TRY_UNWRAP(EntryId handle, 882 dm->GetOrCreateFile(newFileMeta, /* create */ false)); 883 ASSERT_EQ(handle, newFile); 884 885 TEST_TRY_UNWRAP( 886 handle, dm->GetOrCreateDirectory({rootId, testFileMeta.childName()}, 887 /* create */ false)); 888 ASSERT_EQ(handle, firstChildDescendant); 889 } else if (2 == sVersion) { 890 TEST_TRY_UNWRAP_ERR( 891 rv, dm->GetOrCreateFile(newFileMeta, /* create */ false)); 892 ASSERT_NSEQ(NS_ERROR_DOM_NOT_FOUND_ERR, rv); 893 894 TEST_TRY_UNWRAP( 895 EntryId newFileCheck, 896 dm->GetOrCreateFile({firstChildDescendant, newFileMeta.childName()}, 897 /* create */ false)); 898 ASSERT_FALSE(newFileCheck.IsEmpty()); 899 } else { 900 ASSERT_FALSE(false) 901 << "Unknown database version"; 902 } 903 } 904 905 { 906 // Check that new file path is as expected 907 TEST_TRY_UNWRAP( 908 EntryId newFileCheck, 909 dm->GetOrCreateFile({firstChildDescendant, newFileMeta.childName()}, 910 /* create */ false)); 911 ASSERT_NO_FATAL_FAILURE(AssertEntryIdMoved(newFileCheck, newFile)); 912 newFile = newFileCheck; 913 914 TEST_TRY_UNWRAP(Path entryPath, dm->Resolve({rootId, newFile})); 915 ASSERT_EQ(2u, entryPath.Length()); 916 ASSERT_STREQ(testFileMeta.childName(), entryPath[0]); 917 ASSERT_STREQ(testFileMeta.childName(), entryPath[1]); 918 } 919 920 // Move first file and subSubDirectory back one level down keeping the names 921 { 922 FileSystemEntryMetadata src{testFile, 923 firstChildDescendantMeta.childName(), 924 /* is directory */ false}; 925 FileSystemChildMetadata dest{firstChildDir, 926 firstChildDescendantMeta.childName()}; 927 928 // Flag is ignored 929 TEST_TRY_UNWRAP(EntryId moved, dm->MoveEntry(src, dest)); 930 ASSERT_FALSE(moved.IsEmpty()); 931 testFile = moved; 932 } 933 934 { 935 // Then move the directory 936 FileSystemEntryMetadata src{firstChildDescendant, 937 testFileMeta.childName(), 938 /* is directory */ true}; 939 FileSystemChildMetadata dest{firstChildDir, testFileMeta.childName()}; 940 941 // Flag is ignored 942 TEST_TRY_UNWRAP(EntryId moved, dm->MoveEntry(src, dest)); 943 ASSERT_FALSE(moved.IsEmpty()); 944 firstChildDescendant = moved; 945 } 946 947 // Check that listings are as expected 948 { 949 TEST_TRY_UNWRAP(FileSystemDirectoryListing contents, 950 dm->GetDirectoryEntries(rootId, 0u)); 951 ASSERT_TRUE(contents.files().IsEmpty()); 952 ASSERT_EQ(1u, contents.directories().Length()); 953 ASSERT_STREQ(firstChildMeta.childName(), 954 contents.directories()[0].entryName()); 955 } 956 957 { 958 TEST_TRY_UNWRAP(FileSystemDirectoryListing contents, 959 dm->GetDirectoryEntries(firstChildDir, 0u)); 960 ASSERT_EQ(1u, contents.files().Length()); 961 ASSERT_EQ(1u, contents.directories().Length()); 962 ASSERT_STREQ(firstChildDescendantMeta.childName(), 963 contents.files()[0].entryName()); 964 ASSERT_STREQ(testFileMeta.childName(), 965 contents.directories()[0].entryName()); 966 } 967 968 { 969 TEST_TRY_UNWRAP(FileSystemDirectoryListing contents, 970 dm->GetDirectoryEntries(firstChildDescendant, 0u)); 971 ASSERT_EQ(1u, contents.files().Length()); 972 ASSERT_TRUE(contents.directories().IsEmpty()); 973 ASSERT_STREQ(testFileMeta.childName(), contents.files()[0].entryName()); 974 } 975 976 // Names are swapped 977 { 978 TEST_TRY_UNWRAP(Path entryPath, dm->Resolve({rootId, testFile})); 979 ASSERT_EQ(2u, entryPath.Length()); 980 ASSERT_STREQ(firstChildMeta.childName(), entryPath[0]); 981 ASSERT_STREQ(firstChildDescendantMeta.childName(), entryPath[1]); 982 } 983 984 { 985 TEST_TRY_UNWRAP(Path entryPath, 986 dm->Resolve({rootId, firstChildDescendant})); 987 ASSERT_EQ(2u, entryPath.Length()); 988 ASSERT_STREQ(firstChildMeta.childName(), entryPath[0]); 989 ASSERT_STREQ(testFileMeta.childName(), entryPath[1]); 990 } 991 992 { 993 // Check that new file path is also as expected 994 TEST_TRY_UNWRAP( 995 EntryId newFileCheck, 996 dm->GetOrCreateFile({firstChildDescendant, newFileMeta.childName()}, 997 /* create */ false)); 998 ASSERT_NO_FATAL_FAILURE(AssertEntryIdMoved(newFileCheck, newFile)); 999 newFile = newFileCheck; 1000 1001 TEST_TRY_UNWRAP(Path entryPath, dm->Resolve({rootId, newFile})); 1002 ASSERT_EQ(3u, entryPath.Length()); 1003 ASSERT_STREQ(firstChildMeta.childName(), entryPath[0]); 1004 ASSERT_STREQ(testFileMeta.childName(), entryPath[1]); 1005 ASSERT_STREQ(testFileMeta.childName(), entryPath[2]); 1006 } 1007 1008 { 1009 // Try to get handles to the old items 1010 TEST_TRY_UNWRAP_ERR( 1011 nsresult rv, dm->GetOrCreateFile({rootId, testFileMeta.childName()}, 1012 /* create */ false)); 1013 ASSERT_NSEQ(NS_ERROR_DOM_NOT_FOUND_ERR, rv); 1014 1015 TEST_TRY_UNWRAP_ERR( 1016 rv, 1017 dm->GetOrCreateFile({rootId, firstChildDescendantMeta.childName()}, 1018 /* create */ false)); 1019 ASSERT_NSEQ(NS_ERROR_DOM_NOT_FOUND_ERR, rv); 1020 1021 TEST_TRY_UNWRAP_ERR( 1022 rv, dm->GetOrCreateDirectory({rootId, testFileMeta.childName()}, 1023 /* create */ false)); 1024 ASSERT_NSEQ(NS_ERROR_DOM_NOT_FOUND_ERR, rv); 1025 1026 TEST_TRY_UNWRAP_ERR(rv, 1027 dm->GetOrCreateDirectory( 1028 {rootId, firstChildDescendantMeta.childName()}, 1029 /* create */ false)); 1030 ASSERT_NSEQ(NS_ERROR_DOM_NOT_FOUND_ERR, rv); 1031 1032 TEST_TRY_UNWRAP_ERR( 1033 rv, dm->GetOrCreateFile({firstChildDir, testFileMeta.childName()}, 1034 /* create */ false)); 1035 ASSERT_NSEQ(NS_ERROR_DOM_TYPE_MISMATCH_ERR, rv); 1036 1037 TEST_TRY_UNWRAP_ERR( 1038 rv, dm->GetOrCreateDirectory( 1039 {firstChildDir, firstChildDescendantMeta.childName()}, 1040 /* create */ false)); 1041 ASSERT_NSEQ(NS_ERROR_DOM_TYPE_MISMATCH_ERR, rv); 1042 1043 TEST_TRY_UNWRAP_ERR( 1044 rv, dm->GetOrCreateFile({testFile, newFileMeta.childName()}, 1045 /* create */ false)); 1046 ASSERT_NSEQ(NS_ERROR_DOM_NOT_FOUND_ERR, rv); 1047 } 1048 }; 1049 1050 PerformOnIOThread(std::move(ioTask)); 1051 } 1052 1053 INSTANTIATE_TEST_SUITE_P(TestDatabaseManagerVersions, 1054 TestFileSystemDatabaseManagerVersions, 1055 testing::Values(1, 2)); 1056 1057 } // namespace mozilla::dom::fs::test