From 49ad3fba0259f65261ecebda4fcd35aff04dbad6 Mon Sep 17 00:00:00 2001 From: Josh Date: Sun, 4 Oct 2026 19:09:25 -0400 Subject: [PATCH 1/3] test(files): fix ETag scanner test coverage When a folder is marked unscanned `(size = -1)`, rescanning must not reuse its cached ETag. Changes to a nested folder's ETag must also propagate to its ancestors. The tests didn't verify that newly added files were discovered. The nested-folder test also checked the wrong path and restored the subfolder's mtime from its parent instead of its own cached mtime. - Assert newly added files are cached - Check the nested file at its correct path - Preserve the subfolder's own mtime during the scan Signed-off-by: Josh --- tests/lib/Files/Cache/ScannerTest.php | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/tests/lib/Files/Cache/ScannerTest.php b/tests/lib/Files/Cache/ScannerTest.php index 90c059238e3bb..e3e43a5980a24 100644 --- a/tests/lib/Files/Cache/ScannerTest.php +++ b/tests/lib/Files/Cache/ScannerTest.php @@ -314,7 +314,7 @@ public function testETagRecreation(): void { $this->scanner->scan('folder/bar.txt'); - // manipulate etag to simulate an empty etag + // Simulate a cached file with an empty ETag. $this->scanner->scan('', IScanner::SCAN_SHALLOW, IScanner::REUSE_ETAG); /** @var CacheEntry $data0 */ $data0 = $this->cache->get('folder/bar.txt'); @@ -326,10 +326,10 @@ public function testETagRecreation(): void { $data0['etag'] = ''; $this->cache->put('folder/bar.txt', $data0->getData()); - // rescan + // Rescanning must recreate the empty ETag. $this->scanner->scan('folder/bar.txt', IScanner::SCAN_SHALLOW, IScanner::REUSE_ETAG); - // verify cache content + // The recreated ETag must be non-empty. $newData0 = $this->cache->get('folder/bar.txt'); $this->assertIsString($newData0['etag']); $this->assertNotEmpty($newData0['etag']); @@ -415,14 +415,14 @@ public function testNoETagUnscannedFolder(): void { $this->scanner->scan(''); $oldFolderEntry = $this->cache->get('folder'); - // create a new file in a folder by keeping the mtime unchanged, but mark the folder as unscanned + // Add a file without changing the folder mtime, then mark the folder unscanned. $this->storage->file_put_contents('folder/new.txt', 'foo'); $this->storage->touch('folder', $oldFolderEntry->getMTime()); $this->cache->update($oldFolderEntry->getId(), ['size' => -1]); $this->scanner->scan(''); - $this->cache->inCache('folder/new.txt'); + $this->assertTrue($this->cache->inCache('folder/new.txt')); $newFolderEntry = $this->cache->get('folder'); $this->assertNotEquals($newFolderEntry->getEtag(), $oldFolderEntry->getEtag()); @@ -436,17 +436,18 @@ public function testNoETagUnscannedSubFolder(): void { $oldFolderEntry1 = $this->cache->get('folder'); $oldFolderEntry2 = $this->cache->get('folder/sub'); - // create a new file in a folder by keeping the mtime unchanged, but mark the folder as unscanned + // Add a file without changing its parent folder's mtime, then mark that folder unscanned. $this->storage->file_put_contents('folder/sub/new.txt', 'foo'); - $this->storage->touch('folder/sub', $oldFolderEntry1->getMTime()); + $this->storage->touch('folder/sub', $oldFolderEntry2->getMTime()); - // we only mark the direct parent as unscanned, which is the current "notify" behavior + // Notifications mark the new file's direct parent as unscanned, not its ancestors. $this->cache->update($oldFolderEntry2->getId(), ['size' => -1]); $this->scanner->scan(''); - $this->cache->inCache('folder/new.txt'); + $this->assertTrue($this->cache->inCache('folder/sub/new.txt')); + // The changed subfolder ETag must also be reflected in its ancestor. $newFolderEntry1 = $this->cache->get('folder'); $this->assertNotEquals($newFolderEntry1->getEtag(), $oldFolderEntry1->getEtag()); $newFolderEntry2 = $this->cache->get('folder/sub'); From 14101c06517894a8f012bd0f1b3f2ed2e2f749fe Mon Sep 17 00:00:00 2001 From: Josh Date: Sun, 4 Oct 2026 19:35:15 -0400 Subject: [PATCH 2/3] test(files): verify background scans across both folders The tests create files under `folder` and `folder2` but repeat an assertion for `folder/bar.txt`; the pre-scan check also uses a path that the fixture never creates. The broader behavior still had some coverage, but a narrow cache-entry regression could go unnoticed. - Check the actual `folder2/bar.txt` path - Clarify why the tests mark folders complete or incomplete Signed-off-by: Josh --- tests/lib/Files/Cache/ScannerTest.php | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/tests/lib/Files/Cache/ScannerTest.php b/tests/lib/Files/Cache/ScannerTest.php index e3e43a5980a24..2928353fdef27 100644 --- a/tests/lib/Files/Cache/ScannerTest.php +++ b/tests/lib/Files/Cache/ScannerTest.php @@ -160,14 +160,14 @@ public function testBackgroundScan(): void { $this->scanner->scan('', IScanner::SCAN_SHALLOW); $this->assertFalse($this->cache->inCache('folder/bar.txt')); - $this->assertFalse($this->cache->inCache('folder/2bar.txt')); + $this->assertFalse($this->cache->inCache('folder2/bar.txt')); $cachedData = $this->cache->get(''); $this->assertEquals(-1, $cachedData['size']); $this->scanner->backgroundScan(); $this->assertTrue($this->cache->inCache('folder/bar.txt')); - $this->assertTrue($this->cache->inCache('folder/bar.txt')); + $this->assertTrue($this->cache->inCache('folder2/bar.txt')); $cachedData = $this->cache->get(''); $this->assertnotEquals(-1, $cachedData['size']); @@ -182,16 +182,15 @@ public function testBackgroundScanOnlyRecurseIncomplete(): void { $this->scanner->scan('', IScanner::SCAN_SHALLOW); $this->assertFalse($this->cache->inCache('folder/bar.txt')); - $this->assertFalse($this->cache->inCache('folder/2bar.txt')); $this->assertFalse($this->cache->inCache('folder2/bar.txt')); - $this->cache->put('folder2', ['size' => 1]); // mark as complete + // Mark folder2 complete so the incomplete-only scan skips its contents. + $this->cache->put('folder2', ['size' => 1]); $cachedData = $this->cache->get(''); $this->assertEquals(-1, $cachedData['size']); $this->scanner->scan('', IScanner::SCAN_RECURSIVE_INCOMPLETE, IScanner::REUSE_ETAG | IScanner::REUSE_SIZE); - $this->assertTrue($this->cache->inCache('folder/bar.txt')); $this->assertTrue($this->cache->inCache('folder/bar.txt')); $this->assertFalse($this->cache->inCache('folder2/bar.txt')); @@ -212,7 +211,7 @@ public function testBackgroundScanNestedIncompleteFolders(): void { $this->cache->put('folder', ['size' => -1]); $this->cache->put('folder/subfolder1', ['size' => -1]); - // do a scan to get the folders into the cache. + // Cache the newly created directory tree before adding files to it. $this->scanner->backgroundScan(); $this->assertTrue($this->cache->inCache('folder/subfolder1/subfolder3')); @@ -221,7 +220,7 @@ public function testBackgroundScanNestedIncompleteFolders(): void { $this->storage->file_put_contents('folder/subfolder1/subfolder3/bar3.txt', 'foobar'); $this->storage->file_put_contents('folder/subfolder2/bar2.txt', 'foobar'); - //mark folders as incomplete. + // Mark the new files' parent folders incomplete to simulate notifications. $this->cache->put('folder/subfolder1', ['size' => -1]); $this->cache->put('folder/subfolder2', ['size' => -1]); $this->cache->put('folder/subfolder1/subfolder3', ['size' => -1]); @@ -232,7 +231,6 @@ public function testBackgroundScanNestedIncompleteFolders(): void { $this->assertTrue($this->cache->inCache('folder/subfolder2/bar2.txt')); $this->assertTrue($this->cache->inCache('folder/subfolder1/subfolder3/bar3.txt')); - //check if folder sizes are correct. $this->assertEquals(18, $this->cache->get('folder')['size']); $this->assertEquals(12, $this->cache->get('folder/subfolder1')['size']); $this->assertEquals(6, $this->cache->get('folder/subfolder1/subfolder3')['size']); From 15829ac9cc7c485a3446c3769a6c0949b3b31f1e Mon Sep 17 00:00:00 2001 From: Josh Date: Mon, 5 Oct 2026 08:32:34 -0400 Subject: [PATCH 3/3] test(files): clarify ScannerTest etag rescanning comment Signed-off-by: Josh --- tests/lib/Files/Cache/ScannerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/lib/Files/Cache/ScannerTest.php b/tests/lib/Files/Cache/ScannerTest.php index 2928353fdef27..0ec56213e3738 100644 --- a/tests/lib/Files/Cache/ScannerTest.php +++ b/tests/lib/Files/Cache/ScannerTest.php @@ -324,7 +324,7 @@ public function testETagRecreation(): void { $data0['etag'] = ''; $this->cache->put('folder/bar.txt', $data0->getData()); - // Rescanning must recreate the empty ETag. + // Rescanning must replace the empty cached ETag. $this->scanner->scan('folder/bar.txt', IScanner::SCAN_SHALLOW, IScanner::REUSE_ETAG); // The recreated ETag must be non-empty.