0
0
Fork 0
mirror of https://github.com/nextcloud/server.git synced 2025-01-31 06:43:12 +00:00
nextcloud_server/tests/lib/Files/Cache/CacheTest.php
Louis Chemineau f9cda54cbf
fix: Wrap partial cache entry in CacheEntry
Because it is returned here: 7b7d07c575/lib/private/Files/Cache/Cache.php (L136-L137)

And some implementation got stricter: df95bf6ba8/lib/Mount/RootEntryCache.php (L23-L28)

Signed-off-by: Louis Chemineau <louis@chmn.me>
2024-11-21 17:13:24 +01:00

829 lines
30 KiB
PHP
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace Test\Files\Cache;
use OC\Files\Cache\Cache;
use OC\Files\Cache\CacheEntry;
use OC\Files\Search\SearchComparison;
use OC\Files\Search\SearchQuery;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\Cache\ICacheEntry;
use OCP\Files\Search\ISearchComparison;
use OCP\IDBConnection;
use OCP\IUser;
class LongId extends \OC\Files\Storage\Temporary {
public function getId(): string {
return 'long:' . str_repeat('foo', 50) . parent::getId();
}
}
/**
* Class CacheTest
*
* @group DB
*
* @package Test\Files\Cache
*/
class CacheTest extends \Test\TestCase {
/**
* @var \OC\Files\Storage\Temporary $storage ;
*/
protected $storage;
/**
* @var \OC\Files\Storage\Temporary $storage2 ;
*/
protected $storage2;
/**
* @var \OC\Files\Cache\Cache $cache
*/
protected $cache;
/**
* @var \OC\Files\Cache\Cache $cache2
*/
protected $cache2;
public function testGetNumericId(): void {
$this->assertNotNull($this->cache->getNumericStorageId());
}
public function testSimple(): void {
$file1 = 'foo';
$file2 = 'foo/bar';
$data1 = ['size' => 100, 'mtime' => 50, 'mimetype' => 'foo/folder'];
$data2 = ['size' => 1000, 'mtime' => 20, 'mimetype' => 'foo/file'];
$this->assertFalse($this->cache->inCache($file1));
$this->assertEquals($this->cache->get($file1), null);
$id1 = $this->cache->put($file1, $data1);
$this->assertTrue($this->cache->inCache($file1));
$cacheData1 = $this->cache->get($file1);
foreach ($data1 as $key => $value) {
$this->assertEquals($value, $cacheData1[$key]);
}
$this->assertEquals($cacheData1['mimepart'], 'foo');
$this->assertEquals($cacheData1['fileid'], $id1);
$this->assertEquals($id1, $this->cache->getId($file1));
$this->assertFalse($this->cache->inCache($file2));
$id2 = $this->cache->put($file2, $data2);
$this->assertTrue($this->cache->inCache($file2));
$cacheData2 = $this->cache->get($file2);
foreach ($data2 as $key => $value) {
$this->assertEquals($value, $cacheData2[$key]);
}
$this->assertEquals($cacheData1['fileid'], $cacheData2['parent']);
$this->assertEquals($cacheData2['fileid'], $id2);
$this->assertEquals($id2, $this->cache->getId($file2));
$this->assertEquals($id1, $this->cache->getParentId($file2));
$newSize = 1050;
$newId2 = $this->cache->put($file2, ['size' => $newSize]);
$cacheData2 = $this->cache->get($file2);
$this->assertEquals($newId2, $id2);
$this->assertEquals($cacheData2['size'], $newSize);
$this->assertEquals($cacheData1, $this->cache->get($file1));
$this->cache->remove($file2);
$this->assertFalse($this->cache->inCache($file2));
$this->assertEquals($this->cache->get($file2), null);
$this->assertTrue($this->cache->inCache($file1));
$this->assertEquals($cacheData1, $this->cache->get($id1));
}
public function testCacheEntryGetters(): void {
$file1 = 'foo';
$data1 = ['size' => 100, 'mtime' => 50, 'mimetype' => 'foo/file'];
$id1 = $this->cache->put($file1, $data1);
$entry = $this->cache->get($file1);
$this->assertEquals($entry->getId(), $id1);
$this->assertEquals($entry->getStorageId(), $this->cache->getNumericStorageId());
$this->assertEquals($entry->getPath(), 'foo');
$this->assertEquals($entry->getName(), 'foo');
$this->assertEquals($entry->getMimeType(), 'foo/file');
$this->assertEquals($entry->getMimePart(), 'foo');
$this->assertEquals($entry->getSize(), 100);
$this->assertEquals($entry->getMTime(), 50);
$this->assertEquals($entry->getStorageMTime(), 50);
$this->assertEquals($entry->getEtag(), null);
$this->assertEquals($entry->getPermissions(), 0);
$this->assertEquals($entry->isEncrypted(), false);
$this->assertEquals($entry->getMetadataEtag(), null);
$this->assertEquals($entry->getCreationTime(), null);
$this->assertEquals($entry->getUploadTime(), null);
$this->assertEquals($entry->getUnencryptedSize(), 100);
}
public function testPartial(): void {
$file1 = 'foo';
$this->cache->put($file1, ['size' => 10]);
$this->assertEquals(new CacheEntry(['size' => 10]), $this->cache->get($file1));
$this->cache->put($file1, ['mtime' => 15]);
$this->assertEquals(new CacheEntry(['size' => 10, 'mtime' => 15]), $this->cache->get($file1));
$this->cache->put($file1, ['size' => 12]);
$this->assertEquals(new CacheEntry(['size' => 12, 'mtime' => 15]), $this->cache->get($file1));
}
/**
* @dataProvider folderDataProvider
*/
public function testFolder($folder): void {
if (strpos($folder, 'F09F9890')) {
// 4 byte UTF doesn't work on mysql
$params = \OC::$server->get(\OC\DB\Connection::class)->getParams();
if (\OC::$server->getDatabaseConnection()->getDatabaseProvider() === IDBConnection::PLATFORM_MYSQL && $params['charset'] !== 'utf8mb4') {
$this->markTestSkipped('MySQL doesn\'t support 4 byte UTF-8');
}
}
$file2 = $folder . '/bar';
$file3 = $folder . '/foo';
$data1 = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE];
$fileData = [];
$fileData['bar'] = ['size' => 1000, 'mtime' => 20, 'mimetype' => 'foo/file'];
$fileData['foo'] = ['size' => 20, 'mtime' => 25, 'mimetype' => 'foo/file'];
$this->cache->put($folder, $data1);
$this->cache->put($file2, $fileData['bar']);
$this->cache->put($file3, $fileData['foo']);
$content = $this->cache->getFolderContents($folder);
$this->assertEquals(count($content), 2);
foreach ($content as $cachedData) {
$data = $fileData[$cachedData['name']];
foreach ($data as $name => $value) {
$this->assertEquals($value, $cachedData[$name]);
}
}
$file4 = $folder . '/unkownSize';
$fileData['unkownSize'] = ['size' => -1, 'mtime' => 25, 'mimetype' => 'foo/file'];
$this->cache->put($file4, $fileData['unkownSize']);
$this->assertEquals(-1, $this->cache->calculateFolderSize($folder));
$fileData['unkownSize'] = ['size' => 5, 'mtime' => 25, 'mimetype' => 'foo/file'];
$this->cache->put($file4, $fileData['unkownSize']);
$this->assertEquals(1025, $this->cache->calculateFolderSize($folder));
$this->cache->remove($file2);
$this->cache->remove($file3);
$this->cache->remove($file4);
$this->assertEquals(0, $this->cache->calculateFolderSize($folder));
$this->cache->remove($folder);
$this->assertFalse($this->cache->inCache($folder . '/foo'));
$this->assertFalse($this->cache->inCache($folder . '/bar'));
}
public function testRemoveRecursive(): void {
$folderData = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE];
$fileData = ['size' => 1000, 'mtime' => 20, 'mimetype' => 'text/plain'];
$folders = ['folder', 'folder/subfolder', 'folder/sub2', 'folder/sub2/sub3'];
$files = ['folder/foo.txt', 'folder/bar.txt', 'folder/subfolder/asd.txt', 'folder/sub2/qwerty.txt', 'folder/sub2/sub3/foo.txt'];
foreach ($folders as $folder) {
$this->cache->put($folder, $folderData);
}
foreach ($files as $file) {
$this->cache->put($file, $fileData);
}
$this->cache->remove('folder');
foreach ($files as $file) {
$this->assertFalse($this->cache->inCache($file));
}
}
public function folderDataProvider() {
return [
['folder'],
// that was too easy, try something harder
['☺, WHITE SMILING FACE, UTF-8 hex E298BA'],
// what about 4 byte utf-8
['😐, NEUTRAL_FACE, UTF-8 hex F09F9890'],
// now the crazy stuff
[', UNASSIGNED PRIVATE USE, UTF-8 hex EF9890'],
// and my favorite
['w͢͢͝h͡o͢͡ ̸͢k̵͟n̴͘ǫw̸̛s͘ ̀́w͘͢ḩ̵a҉̡͢t ̧̕h́o̵r͏̵rors̡ ̶͡͠lį̶e͟͟ ̶͝in͢ ͏t̕h̷̡͟e ͟͟d̛a͜r̕͡k̢̨ ͡h̴e͏a̷̢̡rt́͏ ̴̷͠ò̵̶f̸ u̧͘ní̛͜c͢͏͏d̸͢e̡͝']
];
}
public function testEncryptedFolder(): void {
$file1 = 'folder';
$file2 = 'folder/bar';
$file3 = 'folder/foo';
$data1 = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE];
$fileData = [];
$fileData['bar'] = ['size' => 1000, 'encrypted' => 1, 'mtime' => 20, 'mimetype' => 'foo/file'];
$fileData['foo'] = ['size' => 20, 'encrypted' => 1, 'mtime' => 25, 'mimetype' => 'foo/file'];
$this->cache->put($file1, $data1);
$this->cache->put($file2, $fileData['bar']);
$this->cache->put($file3, $fileData['foo']);
$content = $this->cache->getFolderContents($file1);
$this->assertEquals(count($content), 2);
foreach ($content as $cachedData) {
$data = $fileData[$cachedData['name']];
}
$file4 = 'folder/unkownSize';
$fileData['unkownSize'] = ['size' => -1, 'mtime' => 25, 'mimetype' => 'foo/file'];
$this->cache->put($file4, $fileData['unkownSize']);
$this->assertEquals(-1, $this->cache->calculateFolderSize($file1));
$fileData['unkownSize'] = ['size' => 5, 'mtime' => 25, 'mimetype' => 'foo/file'];
$this->cache->put($file4, $fileData['unkownSize']);
$this->assertEquals(1025, $this->cache->calculateFolderSize($file1));
// direct cache entry retrieval returns the original values
$entry = $this->cache->get($file1);
$this->assertEquals(1025, $entry['size']);
$this->cache->remove($file2);
$this->cache->remove($file3);
$this->cache->remove($file4);
$this->assertEquals(0, $this->cache->calculateFolderSize($file1));
$this->cache->remove('folder');
$this->assertFalse($this->cache->inCache('folder/foo'));
$this->assertFalse($this->cache->inCache('folder/bar'));
}
public function testRootFolderSizeForNonHomeStorage(): void {
$dir1 = 'knownsize';
$dir2 = 'unknownsize';
$fileData = [];
$fileData[''] = ['size' => -1, 'mtime' => 20, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE];
$fileData[$dir1] = ['size' => 1000, 'mtime' => 20, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE];
$fileData[$dir2] = ['size' => -1, 'mtime' => 25, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE];
$this->cache->put($dir1, $fileData[$dir1]);
$this->cache->put($dir2, $fileData[$dir2]);
$this->assertTrue($this->cache->inCache($dir1));
$this->assertTrue($this->cache->inCache($dir2));
// check that root size ignored the unknown sizes
$this->assertEquals(-1, $this->cache->calculateFolderSize(''));
// clean up
$this->cache->remove('');
$this->cache->remove($dir1);
$this->cache->remove($dir2);
$this->assertFalse($this->cache->inCache($dir1));
$this->assertFalse($this->cache->inCache($dir2));
}
public function testStatus(): void {
$this->assertEquals(\OC\Files\Cache\Cache::NOT_FOUND, $this->cache->getStatus('foo'));
$this->cache->put('foo', ['size' => -1]);
$this->assertEquals(\OC\Files\Cache\Cache::PARTIAL, $this->cache->getStatus('foo'));
$this->cache->put('foo', ['size' => -1, 'mtime' => 20, 'mimetype' => 'foo/file']);
$this->assertEquals(\OC\Files\Cache\Cache::SHALLOW, $this->cache->getStatus('foo'));
$this->cache->put('foo', ['size' => 10]);
$this->assertEquals(\OC\Files\Cache\Cache::COMPLETE, $this->cache->getStatus('foo'));
}
public function putWithAllKindOfQuotesData() {
return [
['`backtick`'],
['´forward´'],
['\'single\''],
];
}
/**
* @dataProvider putWithAllKindOfQuotesData
* @param $fileName
*/
public function testPutWithAllKindOfQuotes($fileName): void {
$this->assertEquals(\OC\Files\Cache\Cache::NOT_FOUND, $this->cache->get($fileName));
$this->cache->put($fileName, ['size' => 20, 'mtime' => 25, 'mimetype' => 'foo/file', 'etag' => $fileName]);
$cacheEntry = $this->cache->get($fileName);
$this->assertEquals($fileName, $cacheEntry['etag']);
$this->assertEquals($fileName, $cacheEntry['path']);
}
public function testSearch(): void {
$file1 = 'folder';
$file2 = 'folder/foobar';
$file3 = 'folder/foo';
$data1 = ['size' => 100, 'mtime' => 50, 'mimetype' => 'foo/folder'];
$fileData = [];
$fileData['foobar'] = ['size' => 1000, 'mtime' => 20, 'mimetype' => 'foo/file'];
$fileData['foo'] = ['size' => 20, 'mtime' => 25, 'mimetype' => 'foo/file'];
$this->cache->put($file1, $data1);
$this->cache->put($file2, $fileData['foobar']);
$this->cache->put($file3, $fileData['foo']);
$this->assertEquals(2, count($this->cache->search('%foo%')));
$this->assertEquals(1, count($this->cache->search('foo')));
$this->assertEquals(1, count($this->cache->search('%folder%')));
$this->assertEquals(1, count($this->cache->search('folder%')));
// case insensitive search should match the same files
$this->assertEquals(2, count($this->cache->search('%Foo%')));
$this->assertEquals(1, count($this->cache->search('Foo')));
$this->assertEquals(1, count($this->cache->search('%Folder%')));
$this->assertEquals(1, count($this->cache->search('Folder%')));
$this->assertEquals(3, count($this->cache->searchByMime('foo')));
$this->assertEquals(2, count($this->cache->searchByMime('foo/file')));
}
public function testSearchQueryByTag(): void {
$userId = static::getUniqueID('user');
\OC::$server->getUserManager()->createUser($userId, $userId);
static::loginAsUser($userId);
$user = new \OC\User\User($userId, null, \OC::$server->get(IEventDispatcher::class));
$file1 = 'folder';
$file2 = 'folder/foobar';
$file3 = 'folder/foo';
$file4 = 'folder/foo2';
$file5 = 'folder/foo3';
$data1 = ['size' => 100, 'mtime' => 50, 'mimetype' => 'foo/folder'];
$fileData = [];
$fileData['foobar'] = ['size' => 1000, 'mtime' => 20, 'mimetype' => 'foo/file'];
$fileData['foo'] = ['size' => 20, 'mtime' => 25, 'mimetype' => 'foo/file'];
$fileData['foo2'] = ['size' => 25, 'mtime' => 28, 'mimetype' => 'foo/file'];
$fileData['foo3'] = ['size' => 88, 'mtime' => 34, 'mimetype' => 'foo/file'];
$id1 = $this->cache->put($file1, $data1);
$id2 = $this->cache->put($file2, $fileData['foobar']);
$id3 = $this->cache->put($file3, $fileData['foo']);
$id4 = $this->cache->put($file4, $fileData['foo2']);
$id5 = $this->cache->put($file5, $fileData['foo3']);
$tagManager = \OC::$server->getTagManager()->load('files', [], false, $userId);
$this->assertTrue($tagManager->tagAs($id1, 'tag1'));
$this->assertTrue($tagManager->tagAs($id1, 'tag2'));
$this->assertTrue($tagManager->tagAs($id2, 'tag2'));
$this->assertTrue($tagManager->tagAs($id3, 'tag1'));
$this->assertTrue($tagManager->tagAs($id4, 'tag2'));
$results = $this->cache->searchQuery(new SearchQuery(
new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'tagname', 'tag2'),
0, 0, [], $user
));
$this->assertEquals(3, count($results));
usort($results, function ($value1, $value2) {
return $value1['name'] <=> $value2['name'];
});
$this->assertEquals('folder', $results[0]['name']);
$this->assertEquals('foo2', $results[1]['name']);
$this->assertEquals('foobar', $results[2]['name']);
$tagManager->delete('tag1');
$tagManager->delete('tag2');
static::logout();
$user = \OC::$server->getUserManager()->get($userId);
if ($user !== null) {
try {
$user->delete();
} catch (\Exception $e) {
}
}
}
public function testSearchByQuery(): void {
$file1 = 'folder';
$file2 = 'folder/foobar';
$file3 = 'folder/foo';
$data1 = ['size' => 100, 'mtime' => 50, 'mimetype' => 'foo/folder'];
$fileData = [];
$fileData['foobar'] = ['size' => 1000, 'mtime' => 20, 'mimetype' => 'foo/file'];
$fileData['foo'] = ['size' => 20, 'mtime' => 25, 'mimetype' => 'foo/file'];
$this->cache->put($file1, $data1);
$this->cache->put($file2, $fileData['foobar']);
$this->cache->put($file3, $fileData['foo']);
/** @var IUser $user */
$user = $this->createMock(IUser::class);
$this->assertCount(1, $this->cache->searchQuery(new SearchQuery(
new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'name', 'foo'), 10, 0, [], $user)));
$this->assertCount(2, $this->cache->searchQuery(new SearchQuery(
new SearchComparison(ISearchComparison::COMPARE_LIKE, 'name', 'foo%'), 10, 0, [], $user)));
$this->assertCount(2, $this->cache->searchQuery(new SearchQuery(
new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'mimetype', 'foo/file'), 10, 0, [], $user)));
$this->assertCount(3, $this->cache->searchQuery(new SearchQuery(
new SearchComparison(ISearchComparison::COMPARE_LIKE, 'mimetype', 'foo/%'), 10, 0, [], $user)));
$this->assertCount(1, $this->cache->searchQuery(new SearchQuery(
new SearchComparison(ISearchComparison::COMPARE_GREATER_THAN, 'size', 100), 10, 0, [], $user)));
$this->assertCount(2, $this->cache->searchQuery(new SearchQuery(
new SearchComparison(ISearchComparison::COMPARE_GREATER_THAN_EQUAL, 'size', 100), 10, 0, [], $user)));
}
public function movePathProvider() {
return [
['folder/foo', 'folder/foobar', ['1', '2']],
['folder/foo', 'foo', ['1', '2']],
['files/Индустрия_Инженерные системы ЦОД', 'files/Индустрия_Инженерные системы ЦОД1', ['1', '2']],
];
}
/**
* @dataProvider movePathProvider
*/
public function testMove($sourceFolder, $targetFolder, $children): void {
$data = ['size' => 100, 'mtime' => 50, 'mimetype' => 'foo/bar'];
$folderData = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE];
// create folders
foreach ([$sourceFolder, $targetFolder] as $current) {
while (strpos($current, '/') > 0) {
$current = dirname($current);
$this->cache->put($current, $folderData);
$this->cache2->put($current, $folderData);
}
}
$this->cache->put($sourceFolder, $folderData);
$this->cache2->put($sourceFolder, $folderData);
foreach ($children as $child) {
$this->cache->put($sourceFolder . '/' . $child, $data);
$this->cache2->put($sourceFolder . '/' . $child, $data);
}
$this->cache->move($sourceFolder, $targetFolder);
$this->assertFalse($this->cache->inCache($sourceFolder));
$this->assertTrue($this->cache2->inCache($sourceFolder));
$this->assertTrue($this->cache->inCache($targetFolder));
$this->assertFalse($this->cache2->inCache($targetFolder));
foreach ($children as $child) {
$this->assertFalse($this->cache->inCache($sourceFolder . '/' . $child));
$this->assertTrue($this->cache2->inCache($sourceFolder . '/' . $child));
$this->assertTrue($this->cache->inCache($targetFolder . '/' . $child));
$this->assertFalse($this->cache2->inCache($targetFolder . '/' . $child));
}
}
public function testMoveFromCache(): void {
$data = ['size' => 100, 'mtime' => 50, 'mimetype' => 'foo/bar'];
$folderData = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE];
$this->cache2->put('folder', $folderData);
$this->cache2->put('folder/sub', $data);
$this->cache->moveFromCache($this->cache2, 'folder', 'targetfolder');
$this->assertFalse($this->cache2->inCache('folder'));
$this->assertFalse($this->cache2->inCache('folder/sub'));
$this->assertTrue($this->cache->inCache('targetfolder'));
$this->assertTrue($this->cache->inCache('targetfolder/sub'));
}
public function testGetIncomplete(): void {
$file1 = 'folder1';
$file2 = 'folder2';
$file3 = 'folder3';
$file4 = 'folder4';
$data = ['size' => 10, 'mtime' => 50, 'mimetype' => 'foo/bar'];
$this->cache->put($file1, $data);
$data['size'] = -1;
$this->cache->put($file2, $data);
$this->cache->put($file3, $data);
$data['size'] = 12;
$this->cache->put($file4, $data);
$this->assertEquals($file3, $this->cache->getIncomplete());
}
public function testNonExisting(): void {
$this->assertFalse($this->cache->get('foo.txt'));
$this->assertFalse($this->cache->get(-1));
$this->assertEquals([], $this->cache->getFolderContents('foo'));
}
public function testGetById(): void {
$storageId = $this->storage->getId();
$data = ['size' => 1000, 'mtime' => 20, 'mimetype' => 'foo/file'];
$id = $this->cache->put('foo', $data);
if (strlen($storageId) > 64) {
$storageId = md5($storageId);
}
$this->assertEquals([$storageId, 'foo'], \OC\Files\Cache\Cache::getById($id));
}
public function testStorageMTime(): void {
$data = ['size' => 1000, 'mtime' => 20, 'mimetype' => 'foo/file'];
$this->cache->put('foo', $data);
$cachedData = $this->cache->get('foo');
$this->assertEquals($data['mtime'], $cachedData['storage_mtime']); //if no storage_mtime is saved, mtime should be used
$this->cache->put('foo', ['storage_mtime' => 30]); //when setting storage_mtime, mtime is also set
$cachedData = $this->cache->get('foo');
$this->assertEquals(30, $cachedData['storage_mtime']);
$this->assertEquals(30, $cachedData['mtime']);
$this->cache->put('foo', ['mtime' => 25]); //setting mtime does not change storage_mtime
$cachedData = $this->cache->get('foo');
$this->assertEquals(30, $cachedData['storage_mtime']);
$this->assertEquals(25, $cachedData['mtime']);
}
public function testLongId(): void {
$storage = new LongId([]);
$cache = $storage->getCache();
$cache->insert('', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]);
$storageId = $storage->getId();
$data = ['size' => 1000, 'mtime' => 20, 'mimetype' => 'foo/file'];
$id = $cache->put('foo', $data);
$this->assertEquals([md5($storageId), 'foo'], \OC\Files\Cache\Cache::getById($id));
}
/**
* this test show the bug resulting if we have no normalizer installed
*/
public function testWithoutNormalizer(): void {
// folder name "Schön" with U+00F6 (normalized)
$folderWith00F6 = "\x53\x63\x68\xc3\xb6\x6e";
// folder name "Schön" with U+0308 (un-normalized)
$folderWith0308 = "\x53\x63\x68\x6f\xcc\x88\x6e";
/**
* @var \OC\Files\Cache\Cache | \PHPUnit\Framework\MockObject\MockObject $cacheMock
*/
$cacheMock = $this->getMockBuilder(Cache::class)
->setMethods(['normalize'])
->setConstructorArgs([$this->storage])
->getMock();
$cacheMock->expects($this->any())
->method('normalize')
->willReturnArgument(0);
$data = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE];
// put root folder
$this->assertFalse($cacheMock->get('folder'));
$this->assertGreaterThan(0, $cacheMock->put('folder', $data));
// put un-normalized folder
$this->assertFalse($cacheMock->get('folder/' . $folderWith0308));
$this->assertGreaterThan(0, $cacheMock->put('folder/' . $folderWith0308, $data));
// get un-normalized folder by name
$unNormalizedFolderName = $cacheMock->get('folder/' . $folderWith0308);
// check if database layer normalized the folder name (this should not happen)
$this->assertEquals($folderWith0308, $unNormalizedFolderName['name']);
// put normalized folder
$this->assertFalse($cacheMock->get('folder/' . $folderWith00F6));
$this->assertGreaterThan(0, $cacheMock->put('folder/' . $folderWith00F6, $data));
// this is our bug, we have two different hashes with the same name (Schön)
$this->assertEquals(2, count($cacheMock->getFolderContents('folder')));
}
/**
* this test shows that there is no bug if we use the normalizer
*/
public function testWithNormalizer(): void {
if (!class_exists('Patchwork\PHP\Shim\Normalizer')) {
$this->markTestSkipped('The 3rdparty Normalizer extension is not available.');
return;
}
// folder name "Schön" with U+00F6 (normalized)
$folderWith00F6 = "\x53\x63\x68\xc3\xb6\x6e";
// folder name "Schön" with U+0308 (un-normalized)
$folderWith0308 = "\x53\x63\x68\x6f\xcc\x88\x6e";
$data = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE];
// put root folder
$this->assertFalse($this->cache->get('folder'));
$this->assertGreaterThan(0, $this->cache->put('folder', $data));
// put un-normalized folder
$this->assertFalse($this->cache->get('folder/' . $folderWith0308));
$this->assertGreaterThan(0, $this->cache->put('folder/' . $folderWith0308, $data));
// get un-normalized folder by name
$unNormalizedFolderName = $this->cache->get('folder/' . $folderWith0308);
// check if folder name was normalized
$this->assertEquals($folderWith00F6, $unNormalizedFolderName['name']);
// put normalized folder
$this->assertInstanceOf('\OCP\Files\Cache\ICacheEntry', $this->cache->get('folder/' . $folderWith00F6));
$this->assertGreaterThan(0, $this->cache->put('folder/' . $folderWith00F6, $data));
// at this point we should have only one folder named "Schön"
$this->assertEquals(1, count($this->cache->getFolderContents('folder')));
}
public function bogusPathNamesProvider() {
return [
['/bogus.txt', 'bogus.txt'],
['//bogus.txt', 'bogus.txt'],
['bogus/', 'bogus'],
['bogus//', 'bogus'],
];
}
/**
* Test bogus paths with leading or doubled slashes
*
* @dataProvider bogusPathNamesProvider
*/
public function testBogusPaths($bogusPath, $fixedBogusPath): void {
$data = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE];
$parentId = $this->cache->getId('');
$this->assertGreaterThan(0, $this->cache->put($bogusPath, $data));
$newData = $this->cache->get($fixedBogusPath);
$this->assertNotFalse($newData);
$this->assertEquals($fixedBogusPath, $newData['path']);
// parent is the correct one, resolved properly (they used to not be)
$this->assertEquals($parentId, $newData['parent']);
$newDataFromBogus = $this->cache->get($bogusPath);
// same entry
$this->assertEquals($newData, $newDataFromBogus);
}
public function testNoReuseOfFileId(): void {
$data1 = ['size' => 100, 'mtime' => 50, 'mimetype' => 'text/plain'];
$this->cache->put('somefile.txt', $data1);
$info = $this->cache->get('somefile.txt');
$fileId = $info['fileid'];
$this->cache->remove('somefile.txt');
$data2 = ['size' => 200, 'mtime' => 100, 'mimetype' => 'text/plain'];
$this->cache->put('anotherfile.txt', $data2);
$info2 = $this->cache->get('anotherfile.txt');
$fileId2 = $info2['fileid'];
$this->assertNotEquals($fileId, $fileId2);
}
public function escapingProvider() {
return [
['foo'],
['o%'],
['oth_r'],
];
}
/**
* @param string $name
* @dataProvider escapingProvider
*/
public function testEscaping($name): void {
$data = ['size' => 100, 'mtime' => 50, 'mimetype' => 'text/plain'];
$this->cache->put($name, $data);
$this->assertTrue($this->cache->inCache($name));
$retrievedData = $this->cache->get($name);
foreach ($data as $key => $value) {
$this->assertEquals($value, $retrievedData[$key]);
}
$this->cache->move($name, $name . 'asd');
$this->assertFalse($this->cache->inCache($name));
$this->assertTrue($this->cache->inCache($name . 'asd'));
$this->cache->remove($name . 'asd');
$this->assertFalse($this->cache->inCache($name . 'asd'));
$folderData = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE];
$this->cache->put($name, $folderData);
$this->cache->put('other', $folderData);
$childs = ['asd', 'bar', 'foo', 'sub/folder'];
$this->cache->put($name . '/sub', $folderData);
$this->cache->put('other/sub', $folderData);
foreach ($childs as $child) {
$this->cache->put($name . '/' . $child, $data);
$this->cache->put('other/' . $child, $data);
$this->assertTrue($this->cache->inCache($name . '/' . $child));
}
$this->cache->move($name, $name . 'asd');
foreach ($childs as $child) {
$this->assertTrue($this->cache->inCache($name . 'asd/' . $child));
$this->assertTrue($this->cache->inCache('other/' . $child));
}
foreach ($childs as $child) {
$this->cache->remove($name . 'asd/' . $child);
$this->assertFalse($this->cache->inCache($name . 'asd/' . $child));
$this->assertTrue($this->cache->inCache('other/' . $child));
}
}
public function testExtended(): void {
$folderData = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE];
$data = ['size' => 100, 'mtime' => 50, 'mimetype' => 'text/plain', 'creation_time' => 20];
$id1 = $this->cache->put('foo1', $data);
$data = ['size' => 100, 'mtime' => 50, 'mimetype' => 'text/plain', 'upload_time' => 30];
$this->cache->put('foo2', $data);
$data = ['size' => 100, 'mtime' => 50, 'mimetype' => 'text/plain', 'metadata_etag' => 'foo'];
$this->cache->put('foo3', $data);
$data = ['size' => 100, 'mtime' => 50, 'mimetype' => 'text/plain'];
$id4 = $this->cache->put('foo4', $data);
$entry = $this->cache->get($id1);
$this->assertEquals(20, $entry->getCreationTime());
$this->assertEquals(0, $entry->getUploadTime());
$this->assertEquals(null, $entry->getMetadataEtag());
$entries = $this->cache->getFolderContents('');
$this->assertCount(4, $entries);
$this->assertEquals('foo1', $entries[0]->getName());
$this->assertEquals('foo2', $entries[1]->getName());
$this->assertEquals('foo3', $entries[2]->getName());
$this->assertEquals('foo4', $entries[3]->getName());
$this->assertEquals(20, $entries[0]->getCreationTime());
$this->assertEquals(0, $entries[0]->getUploadTime());
$this->assertEquals(null, $entries[0]->getMetadataEtag());
$this->assertEquals(0, $entries[1]->getCreationTime());
$this->assertEquals(30, $entries[1]->getUploadTime());
$this->assertEquals(null, $entries[1]->getMetadataEtag());
$this->assertEquals(0, $entries[2]->getCreationTime());
$this->assertEquals(0, $entries[2]->getUploadTime());
$this->assertEquals('foo', $entries[2]->getMetadataEtag());
$this->assertEquals(0, $entries[3]->getCreationTime());
$this->assertEquals(0, $entries[3]->getUploadTime());
$this->assertEquals(null, $entries[3]->getMetadataEtag());
$this->cache->update($id1, ['upload_time' => 25]);
$entry = $this->cache->get($id1);
$this->assertEquals(20, $entry->getCreationTime());
$this->assertEquals(25, $entry->getUploadTime());
$this->assertEquals(null, $entry->getMetadataEtag());
$this->cache->put('sub', $folderData);
$this->cache->move('foo1', 'sub/foo1');
$entries = $this->cache->getFolderContents('sub');
$this->assertCount(1, $entries);
$this->assertEquals(20, $entries[0]->getCreationTime());
$this->assertEquals(25, $entries[0]->getUploadTime());
$this->assertEquals(null, $entries[0]->getMetadataEtag());
$this->cache->update($id4, ['upload_time' => 25]);
$entry = $this->cache->get($id4);
$this->assertEquals(0, $entry->getCreationTime());
$this->assertEquals(25, $entry->getUploadTime());
$this->assertEquals(null, $entry->getMetadataEtag());
$this->cache->remove('sub');
}
protected function tearDown(): void {
if ($this->cache) {
$this->cache->clear();
}
parent::tearDown();
}
protected function setUp(): void {
parent::setUp();
$this->storage = new \OC\Files\Storage\Temporary([]);
$this->storage2 = new \OC\Files\Storage\Temporary([]);
$this->cache = new \OC\Files\Cache\Cache($this->storage);
$this->cache2 = new \OC\Files\Cache\Cache($this->storage2);
$this->cache->insert('', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]);
$this->cache2->insert('', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]);
}
}