CacheTest.php 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829
  1. <?php
  2. /**
  3. * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
  4. * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
  5. * SPDX-License-Identifier: AGPL-3.0-or-later
  6. */
  7. namespace Test\Files\Cache;
  8. use OC\Files\Cache\Cache;
  9. use OC\Files\Cache\CacheEntry;
  10. use OC\Files\Search\SearchComparison;
  11. use OC\Files\Search\SearchQuery;
  12. use OCP\EventDispatcher\IEventDispatcher;
  13. use OCP\Files\Cache\ICacheEntry;
  14. use OCP\Files\Search\ISearchComparison;
  15. use OCP\IDBConnection;
  16. use OCP\IUser;
  17. class LongId extends \OC\Files\Storage\Temporary {
  18. public function getId(): string {
  19. return 'long:' . str_repeat('foo', 50) . parent::getId();
  20. }
  21. }
  22. /**
  23. * Class CacheTest
  24. *
  25. * @group DB
  26. *
  27. * @package Test\Files\Cache
  28. */
  29. class CacheTest extends \Test\TestCase {
  30. /**
  31. * @var \OC\Files\Storage\Temporary $storage ;
  32. */
  33. protected $storage;
  34. /**
  35. * @var \OC\Files\Storage\Temporary $storage2 ;
  36. */
  37. protected $storage2;
  38. /**
  39. * @var \OC\Files\Cache\Cache $cache
  40. */
  41. protected $cache;
  42. /**
  43. * @var \OC\Files\Cache\Cache $cache2
  44. */
  45. protected $cache2;
  46. public function testGetNumericId(): void {
  47. $this->assertNotNull($this->cache->getNumericStorageId());
  48. }
  49. public function testSimple(): void {
  50. $file1 = 'foo';
  51. $file2 = 'foo/bar';
  52. $data1 = ['size' => 100, 'mtime' => 50, 'mimetype' => 'foo/folder'];
  53. $data2 = ['size' => 1000, 'mtime' => 20, 'mimetype' => 'foo/file'];
  54. $this->assertFalse($this->cache->inCache($file1));
  55. $this->assertEquals($this->cache->get($file1), null);
  56. $id1 = $this->cache->put($file1, $data1);
  57. $this->assertTrue($this->cache->inCache($file1));
  58. $cacheData1 = $this->cache->get($file1);
  59. foreach ($data1 as $key => $value) {
  60. $this->assertEquals($value, $cacheData1[$key]);
  61. }
  62. $this->assertEquals($cacheData1['mimepart'], 'foo');
  63. $this->assertEquals($cacheData1['fileid'], $id1);
  64. $this->assertEquals($id1, $this->cache->getId($file1));
  65. $this->assertFalse($this->cache->inCache($file2));
  66. $id2 = $this->cache->put($file2, $data2);
  67. $this->assertTrue($this->cache->inCache($file2));
  68. $cacheData2 = $this->cache->get($file2);
  69. foreach ($data2 as $key => $value) {
  70. $this->assertEquals($value, $cacheData2[$key]);
  71. }
  72. $this->assertEquals($cacheData1['fileid'], $cacheData2['parent']);
  73. $this->assertEquals($cacheData2['fileid'], $id2);
  74. $this->assertEquals($id2, $this->cache->getId($file2));
  75. $this->assertEquals($id1, $this->cache->getParentId($file2));
  76. $newSize = 1050;
  77. $newId2 = $this->cache->put($file2, ['size' => $newSize]);
  78. $cacheData2 = $this->cache->get($file2);
  79. $this->assertEquals($newId2, $id2);
  80. $this->assertEquals($cacheData2['size'], $newSize);
  81. $this->assertEquals($cacheData1, $this->cache->get($file1));
  82. $this->cache->remove($file2);
  83. $this->assertFalse($this->cache->inCache($file2));
  84. $this->assertEquals($this->cache->get($file2), null);
  85. $this->assertTrue($this->cache->inCache($file1));
  86. $this->assertEquals($cacheData1, $this->cache->get($id1));
  87. }
  88. public function testCacheEntryGetters(): void {
  89. $file1 = 'foo';
  90. $data1 = ['size' => 100, 'mtime' => 50, 'mimetype' => 'foo/file'];
  91. $id1 = $this->cache->put($file1, $data1);
  92. $entry = $this->cache->get($file1);
  93. $this->assertEquals($entry->getId(), $id1);
  94. $this->assertEquals($entry->getStorageId(), $this->cache->getNumericStorageId());
  95. $this->assertEquals($entry->getPath(), 'foo');
  96. $this->assertEquals($entry->getName(), 'foo');
  97. $this->assertEquals($entry->getMimeType(), 'foo/file');
  98. $this->assertEquals($entry->getMimePart(), 'foo');
  99. $this->assertEquals($entry->getSize(), 100);
  100. $this->assertEquals($entry->getMTime(), 50);
  101. $this->assertEquals($entry->getStorageMTime(), 50);
  102. $this->assertEquals($entry->getEtag(), null);
  103. $this->assertEquals($entry->getPermissions(), 0);
  104. $this->assertEquals($entry->isEncrypted(), false);
  105. $this->assertEquals($entry->getMetadataEtag(), null);
  106. $this->assertEquals($entry->getCreationTime(), null);
  107. $this->assertEquals($entry->getUploadTime(), null);
  108. $this->assertEquals($entry->getUnencryptedSize(), 100);
  109. }
  110. public function testPartial(): void {
  111. $file1 = 'foo';
  112. $this->cache->put($file1, ['size' => 10]);
  113. $this->assertEquals(new CacheEntry(['size' => 10]), $this->cache->get($file1));
  114. $this->cache->put($file1, ['mtime' => 15]);
  115. $this->assertEquals(new CacheEntry(['size' => 10, 'mtime' => 15]), $this->cache->get($file1));
  116. $this->cache->put($file1, ['size' => 12]);
  117. $this->assertEquals(new CacheEntry(['size' => 12, 'mtime' => 15]), $this->cache->get($file1));
  118. }
  119. /**
  120. * @dataProvider folderDataProvider
  121. */
  122. public function testFolder($folder): void {
  123. if (strpos($folder, 'F09F9890')) {
  124. // 4 byte UTF doesn't work on mysql
  125. $params = \OC::$server->get(\OC\DB\Connection::class)->getParams();
  126. if (\OC::$server->getDatabaseConnection()->getDatabaseProvider() === IDBConnection::PLATFORM_MYSQL && $params['charset'] !== 'utf8mb4') {
  127. $this->markTestSkipped('MySQL doesn\'t support 4 byte UTF-8');
  128. }
  129. }
  130. $file2 = $folder . '/bar';
  131. $file3 = $folder . '/foo';
  132. $data1 = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE];
  133. $fileData = [];
  134. $fileData['bar'] = ['size' => 1000, 'mtime' => 20, 'mimetype' => 'foo/file'];
  135. $fileData['foo'] = ['size' => 20, 'mtime' => 25, 'mimetype' => 'foo/file'];
  136. $this->cache->put($folder, $data1);
  137. $this->cache->put($file2, $fileData['bar']);
  138. $this->cache->put($file3, $fileData['foo']);
  139. $content = $this->cache->getFolderContents($folder);
  140. $this->assertEquals(count($content), 2);
  141. foreach ($content as $cachedData) {
  142. $data = $fileData[$cachedData['name']];
  143. foreach ($data as $name => $value) {
  144. $this->assertEquals($value, $cachedData[$name]);
  145. }
  146. }
  147. $file4 = $folder . '/unkownSize';
  148. $fileData['unkownSize'] = ['size' => -1, 'mtime' => 25, 'mimetype' => 'foo/file'];
  149. $this->cache->put($file4, $fileData['unkownSize']);
  150. $this->assertEquals(-1, $this->cache->calculateFolderSize($folder));
  151. $fileData['unkownSize'] = ['size' => 5, 'mtime' => 25, 'mimetype' => 'foo/file'];
  152. $this->cache->put($file4, $fileData['unkownSize']);
  153. $this->assertEquals(1025, $this->cache->calculateFolderSize($folder));
  154. $this->cache->remove($file2);
  155. $this->cache->remove($file3);
  156. $this->cache->remove($file4);
  157. $this->assertEquals(0, $this->cache->calculateFolderSize($folder));
  158. $this->cache->remove($folder);
  159. $this->assertFalse($this->cache->inCache($folder . '/foo'));
  160. $this->assertFalse($this->cache->inCache($folder . '/bar'));
  161. }
  162. public function testRemoveRecursive(): void {
  163. $folderData = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE];
  164. $fileData = ['size' => 1000, 'mtime' => 20, 'mimetype' => 'text/plain'];
  165. $folders = ['folder', 'folder/subfolder', 'folder/sub2', 'folder/sub2/sub3'];
  166. $files = ['folder/foo.txt', 'folder/bar.txt', 'folder/subfolder/asd.txt', 'folder/sub2/qwerty.txt', 'folder/sub2/sub3/foo.txt'];
  167. foreach ($folders as $folder) {
  168. $this->cache->put($folder, $folderData);
  169. }
  170. foreach ($files as $file) {
  171. $this->cache->put($file, $fileData);
  172. }
  173. $this->cache->remove('folder');
  174. foreach ($files as $file) {
  175. $this->assertFalse($this->cache->inCache($file));
  176. }
  177. }
  178. public function folderDataProvider() {
  179. return [
  180. ['folder'],
  181. // that was too easy, try something harder
  182. ['☺, WHITE SMILING FACE, UTF-8 hex E298BA'],
  183. // what about 4 byte utf-8
  184. ['😐, NEUTRAL_FACE, UTF-8 hex F09F9890'],
  185. // now the crazy stuff
  186. [', UNASSIGNED PRIVATE USE, UTF-8 hex EF9890'],
  187. // and my favorite
  188. ['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͢͏o̷͏d̸͢e̡͝']
  189. ];
  190. }
  191. public function testEncryptedFolder(): void {
  192. $file1 = 'folder';
  193. $file2 = 'folder/bar';
  194. $file3 = 'folder/foo';
  195. $data1 = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE];
  196. $fileData = [];
  197. $fileData['bar'] = ['size' => 1000, 'encrypted' => 1, 'mtime' => 20, 'mimetype' => 'foo/file'];
  198. $fileData['foo'] = ['size' => 20, 'encrypted' => 1, 'mtime' => 25, 'mimetype' => 'foo/file'];
  199. $this->cache->put($file1, $data1);
  200. $this->cache->put($file2, $fileData['bar']);
  201. $this->cache->put($file3, $fileData['foo']);
  202. $content = $this->cache->getFolderContents($file1);
  203. $this->assertEquals(count($content), 2);
  204. foreach ($content as $cachedData) {
  205. $data = $fileData[$cachedData['name']];
  206. }
  207. $file4 = 'folder/unkownSize';
  208. $fileData['unkownSize'] = ['size' => -1, 'mtime' => 25, 'mimetype' => 'foo/file'];
  209. $this->cache->put($file4, $fileData['unkownSize']);
  210. $this->assertEquals(-1, $this->cache->calculateFolderSize($file1));
  211. $fileData['unkownSize'] = ['size' => 5, 'mtime' => 25, 'mimetype' => 'foo/file'];
  212. $this->cache->put($file4, $fileData['unkownSize']);
  213. $this->assertEquals(1025, $this->cache->calculateFolderSize($file1));
  214. // direct cache entry retrieval returns the original values
  215. $entry = $this->cache->get($file1);
  216. $this->assertEquals(1025, $entry['size']);
  217. $this->cache->remove($file2);
  218. $this->cache->remove($file3);
  219. $this->cache->remove($file4);
  220. $this->assertEquals(0, $this->cache->calculateFolderSize($file1));
  221. $this->cache->remove('folder');
  222. $this->assertFalse($this->cache->inCache('folder/foo'));
  223. $this->assertFalse($this->cache->inCache('folder/bar'));
  224. }
  225. public function testRootFolderSizeForNonHomeStorage(): void {
  226. $dir1 = 'knownsize';
  227. $dir2 = 'unknownsize';
  228. $fileData = [];
  229. $fileData[''] = ['size' => -1, 'mtime' => 20, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE];
  230. $fileData[$dir1] = ['size' => 1000, 'mtime' => 20, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE];
  231. $fileData[$dir2] = ['size' => -1, 'mtime' => 25, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE];
  232. $this->cache->put($dir1, $fileData[$dir1]);
  233. $this->cache->put($dir2, $fileData[$dir2]);
  234. $this->assertTrue($this->cache->inCache($dir1));
  235. $this->assertTrue($this->cache->inCache($dir2));
  236. // check that root size ignored the unknown sizes
  237. $this->assertEquals(-1, $this->cache->calculateFolderSize(''));
  238. // clean up
  239. $this->cache->remove('');
  240. $this->cache->remove($dir1);
  241. $this->cache->remove($dir2);
  242. $this->assertFalse($this->cache->inCache($dir1));
  243. $this->assertFalse($this->cache->inCache($dir2));
  244. }
  245. public function testStatus(): void {
  246. $this->assertEquals(\OC\Files\Cache\Cache::NOT_FOUND, $this->cache->getStatus('foo'));
  247. $this->cache->put('foo', ['size' => -1]);
  248. $this->assertEquals(\OC\Files\Cache\Cache::PARTIAL, $this->cache->getStatus('foo'));
  249. $this->cache->put('foo', ['size' => -1, 'mtime' => 20, 'mimetype' => 'foo/file']);
  250. $this->assertEquals(\OC\Files\Cache\Cache::SHALLOW, $this->cache->getStatus('foo'));
  251. $this->cache->put('foo', ['size' => 10]);
  252. $this->assertEquals(\OC\Files\Cache\Cache::COMPLETE, $this->cache->getStatus('foo'));
  253. }
  254. public function putWithAllKindOfQuotesData() {
  255. return [
  256. ['`backtick`'],
  257. ['´forward´'],
  258. ['\'single\''],
  259. ];
  260. }
  261. /**
  262. * @dataProvider putWithAllKindOfQuotesData
  263. * @param $fileName
  264. */
  265. public function testPutWithAllKindOfQuotes($fileName): void {
  266. $this->assertEquals(\OC\Files\Cache\Cache::NOT_FOUND, $this->cache->get($fileName));
  267. $this->cache->put($fileName, ['size' => 20, 'mtime' => 25, 'mimetype' => 'foo/file', 'etag' => $fileName]);
  268. $cacheEntry = $this->cache->get($fileName);
  269. $this->assertEquals($fileName, $cacheEntry['etag']);
  270. $this->assertEquals($fileName, $cacheEntry['path']);
  271. }
  272. public function testSearch(): void {
  273. $file1 = 'folder';
  274. $file2 = 'folder/foobar';
  275. $file3 = 'folder/foo';
  276. $data1 = ['size' => 100, 'mtime' => 50, 'mimetype' => 'foo/folder'];
  277. $fileData = [];
  278. $fileData['foobar'] = ['size' => 1000, 'mtime' => 20, 'mimetype' => 'foo/file'];
  279. $fileData['foo'] = ['size' => 20, 'mtime' => 25, 'mimetype' => 'foo/file'];
  280. $this->cache->put($file1, $data1);
  281. $this->cache->put($file2, $fileData['foobar']);
  282. $this->cache->put($file3, $fileData['foo']);
  283. $this->assertEquals(2, count($this->cache->search('%foo%')));
  284. $this->assertEquals(1, count($this->cache->search('foo')));
  285. $this->assertEquals(1, count($this->cache->search('%folder%')));
  286. $this->assertEquals(1, count($this->cache->search('folder%')));
  287. // case insensitive search should match the same files
  288. $this->assertEquals(2, count($this->cache->search('%Foo%')));
  289. $this->assertEquals(1, count($this->cache->search('Foo')));
  290. $this->assertEquals(1, count($this->cache->search('%Folder%')));
  291. $this->assertEquals(1, count($this->cache->search('Folder%')));
  292. $this->assertEquals(3, count($this->cache->searchByMime('foo')));
  293. $this->assertEquals(2, count($this->cache->searchByMime('foo/file')));
  294. }
  295. public function testSearchQueryByTag(): void {
  296. $userId = static::getUniqueID('user');
  297. \OC::$server->getUserManager()->createUser($userId, $userId);
  298. static::loginAsUser($userId);
  299. $user = new \OC\User\User($userId, null, \OC::$server->get(IEventDispatcher::class));
  300. $file1 = 'folder';
  301. $file2 = 'folder/foobar';
  302. $file3 = 'folder/foo';
  303. $file4 = 'folder/foo2';
  304. $file5 = 'folder/foo3';
  305. $data1 = ['size' => 100, 'mtime' => 50, 'mimetype' => 'foo/folder'];
  306. $fileData = [];
  307. $fileData['foobar'] = ['size' => 1000, 'mtime' => 20, 'mimetype' => 'foo/file'];
  308. $fileData['foo'] = ['size' => 20, 'mtime' => 25, 'mimetype' => 'foo/file'];
  309. $fileData['foo2'] = ['size' => 25, 'mtime' => 28, 'mimetype' => 'foo/file'];
  310. $fileData['foo3'] = ['size' => 88, 'mtime' => 34, 'mimetype' => 'foo/file'];
  311. $id1 = $this->cache->put($file1, $data1);
  312. $id2 = $this->cache->put($file2, $fileData['foobar']);
  313. $id3 = $this->cache->put($file3, $fileData['foo']);
  314. $id4 = $this->cache->put($file4, $fileData['foo2']);
  315. $id5 = $this->cache->put($file5, $fileData['foo3']);
  316. $tagManager = \OC::$server->getTagManager()->load('files', [], false, $userId);
  317. $this->assertTrue($tagManager->tagAs($id1, 'tag1'));
  318. $this->assertTrue($tagManager->tagAs($id1, 'tag2'));
  319. $this->assertTrue($tagManager->tagAs($id2, 'tag2'));
  320. $this->assertTrue($tagManager->tagAs($id3, 'tag1'));
  321. $this->assertTrue($tagManager->tagAs($id4, 'tag2'));
  322. $results = $this->cache->searchQuery(new SearchQuery(
  323. new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'tagname', 'tag2'),
  324. 0, 0, [], $user
  325. ));
  326. $this->assertEquals(3, count($results));
  327. usort($results, function ($value1, $value2) {
  328. return $value1['name'] <=> $value2['name'];
  329. });
  330. $this->assertEquals('folder', $results[0]['name']);
  331. $this->assertEquals('foo2', $results[1]['name']);
  332. $this->assertEquals('foobar', $results[2]['name']);
  333. $tagManager->delete('tag1');
  334. $tagManager->delete('tag2');
  335. static::logout();
  336. $user = \OC::$server->getUserManager()->get($userId);
  337. if ($user !== null) {
  338. try {
  339. $user->delete();
  340. } catch (\Exception $e) {
  341. }
  342. }
  343. }
  344. public function testSearchByQuery(): void {
  345. $file1 = 'folder';
  346. $file2 = 'folder/foobar';
  347. $file3 = 'folder/foo';
  348. $data1 = ['size' => 100, 'mtime' => 50, 'mimetype' => 'foo/folder'];
  349. $fileData = [];
  350. $fileData['foobar'] = ['size' => 1000, 'mtime' => 20, 'mimetype' => 'foo/file'];
  351. $fileData['foo'] = ['size' => 20, 'mtime' => 25, 'mimetype' => 'foo/file'];
  352. $this->cache->put($file1, $data1);
  353. $this->cache->put($file2, $fileData['foobar']);
  354. $this->cache->put($file3, $fileData['foo']);
  355. /** @var IUser $user */
  356. $user = $this->createMock(IUser::class);
  357. $this->assertCount(1, $this->cache->searchQuery(new SearchQuery(
  358. new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'name', 'foo'), 10, 0, [], $user)));
  359. $this->assertCount(2, $this->cache->searchQuery(new SearchQuery(
  360. new SearchComparison(ISearchComparison::COMPARE_LIKE, 'name', 'foo%'), 10, 0, [], $user)));
  361. $this->assertCount(2, $this->cache->searchQuery(new SearchQuery(
  362. new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'mimetype', 'foo/file'), 10, 0, [], $user)));
  363. $this->assertCount(3, $this->cache->searchQuery(new SearchQuery(
  364. new SearchComparison(ISearchComparison::COMPARE_LIKE, 'mimetype', 'foo/%'), 10, 0, [], $user)));
  365. $this->assertCount(1, $this->cache->searchQuery(new SearchQuery(
  366. new SearchComparison(ISearchComparison::COMPARE_GREATER_THAN, 'size', 100), 10, 0, [], $user)));
  367. $this->assertCount(2, $this->cache->searchQuery(new SearchQuery(
  368. new SearchComparison(ISearchComparison::COMPARE_GREATER_THAN_EQUAL, 'size', 100), 10, 0, [], $user)));
  369. }
  370. public function movePathProvider() {
  371. return [
  372. ['folder/foo', 'folder/foobar', ['1', '2']],
  373. ['folder/foo', 'foo', ['1', '2']],
  374. ['files/Индустрия_Инженерные системы ЦОД', 'files/Индустрия_Инженерные системы ЦОД1', ['1', '2']],
  375. ];
  376. }
  377. /**
  378. * @dataProvider movePathProvider
  379. */
  380. public function testMove($sourceFolder, $targetFolder, $children): void {
  381. $data = ['size' => 100, 'mtime' => 50, 'mimetype' => 'foo/bar'];
  382. $folderData = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE];
  383. // create folders
  384. foreach ([$sourceFolder, $targetFolder] as $current) {
  385. while (strpos($current, '/') > 0) {
  386. $current = dirname($current);
  387. $this->cache->put($current, $folderData);
  388. $this->cache2->put($current, $folderData);
  389. }
  390. }
  391. $this->cache->put($sourceFolder, $folderData);
  392. $this->cache2->put($sourceFolder, $folderData);
  393. foreach ($children as $child) {
  394. $this->cache->put($sourceFolder . '/' . $child, $data);
  395. $this->cache2->put($sourceFolder . '/' . $child, $data);
  396. }
  397. $this->cache->move($sourceFolder, $targetFolder);
  398. $this->assertFalse($this->cache->inCache($sourceFolder));
  399. $this->assertTrue($this->cache2->inCache($sourceFolder));
  400. $this->assertTrue($this->cache->inCache($targetFolder));
  401. $this->assertFalse($this->cache2->inCache($targetFolder));
  402. foreach ($children as $child) {
  403. $this->assertFalse($this->cache->inCache($sourceFolder . '/' . $child));
  404. $this->assertTrue($this->cache2->inCache($sourceFolder . '/' . $child));
  405. $this->assertTrue($this->cache->inCache($targetFolder . '/' . $child));
  406. $this->assertFalse($this->cache2->inCache($targetFolder . '/' . $child));
  407. }
  408. }
  409. public function testMoveFromCache(): void {
  410. $data = ['size' => 100, 'mtime' => 50, 'mimetype' => 'foo/bar'];
  411. $folderData = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE];
  412. $this->cache2->put('folder', $folderData);
  413. $this->cache2->put('folder/sub', $data);
  414. $this->cache->moveFromCache($this->cache2, 'folder', 'targetfolder');
  415. $this->assertFalse($this->cache2->inCache('folder'));
  416. $this->assertFalse($this->cache2->inCache('folder/sub'));
  417. $this->assertTrue($this->cache->inCache('targetfolder'));
  418. $this->assertTrue($this->cache->inCache('targetfolder/sub'));
  419. }
  420. public function testGetIncomplete(): void {
  421. $file1 = 'folder1';
  422. $file2 = 'folder2';
  423. $file3 = 'folder3';
  424. $file4 = 'folder4';
  425. $data = ['size' => 10, 'mtime' => 50, 'mimetype' => 'foo/bar'];
  426. $this->cache->put($file1, $data);
  427. $data['size'] = -1;
  428. $this->cache->put($file2, $data);
  429. $this->cache->put($file3, $data);
  430. $data['size'] = 12;
  431. $this->cache->put($file4, $data);
  432. $this->assertEquals($file3, $this->cache->getIncomplete());
  433. }
  434. public function testNonExisting(): void {
  435. $this->assertFalse($this->cache->get('foo.txt'));
  436. $this->assertFalse($this->cache->get(-1));
  437. $this->assertEquals([], $this->cache->getFolderContents('foo'));
  438. }
  439. public function testGetById(): void {
  440. $storageId = $this->storage->getId();
  441. $data = ['size' => 1000, 'mtime' => 20, 'mimetype' => 'foo/file'];
  442. $id = $this->cache->put('foo', $data);
  443. if (strlen($storageId) > 64) {
  444. $storageId = md5($storageId);
  445. }
  446. $this->assertEquals([$storageId, 'foo'], \OC\Files\Cache\Cache::getById($id));
  447. }
  448. public function testStorageMTime(): void {
  449. $data = ['size' => 1000, 'mtime' => 20, 'mimetype' => 'foo/file'];
  450. $this->cache->put('foo', $data);
  451. $cachedData = $this->cache->get('foo');
  452. $this->assertEquals($data['mtime'], $cachedData['storage_mtime']); //if no storage_mtime is saved, mtime should be used
  453. $this->cache->put('foo', ['storage_mtime' => 30]); //when setting storage_mtime, mtime is also set
  454. $cachedData = $this->cache->get('foo');
  455. $this->assertEquals(30, $cachedData['storage_mtime']);
  456. $this->assertEquals(30, $cachedData['mtime']);
  457. $this->cache->put('foo', ['mtime' => 25]); //setting mtime does not change storage_mtime
  458. $cachedData = $this->cache->get('foo');
  459. $this->assertEquals(30, $cachedData['storage_mtime']);
  460. $this->assertEquals(25, $cachedData['mtime']);
  461. }
  462. public function testLongId(): void {
  463. $storage = new LongId([]);
  464. $cache = $storage->getCache();
  465. $cache->insert('', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]);
  466. $storageId = $storage->getId();
  467. $data = ['size' => 1000, 'mtime' => 20, 'mimetype' => 'foo/file'];
  468. $id = $cache->put('foo', $data);
  469. $this->assertEquals([md5($storageId), 'foo'], \OC\Files\Cache\Cache::getById($id));
  470. }
  471. /**
  472. * this test show the bug resulting if we have no normalizer installed
  473. */
  474. public function testWithoutNormalizer(): void {
  475. // folder name "Schön" with U+00F6 (normalized)
  476. $folderWith00F6 = "\x53\x63\x68\xc3\xb6\x6e";
  477. // folder name "Schön" with U+0308 (un-normalized)
  478. $folderWith0308 = "\x53\x63\x68\x6f\xcc\x88\x6e";
  479. /**
  480. * @var \OC\Files\Cache\Cache | \PHPUnit\Framework\MockObject\MockObject $cacheMock
  481. */
  482. $cacheMock = $this->getMockBuilder(Cache::class)
  483. ->setMethods(['normalize'])
  484. ->setConstructorArgs([$this->storage])
  485. ->getMock();
  486. $cacheMock->expects($this->any())
  487. ->method('normalize')
  488. ->willReturnArgument(0);
  489. $data = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE];
  490. // put root folder
  491. $this->assertFalse($cacheMock->get('folder'));
  492. $this->assertGreaterThan(0, $cacheMock->put('folder', $data));
  493. // put un-normalized folder
  494. $this->assertFalse($cacheMock->get('folder/' . $folderWith0308));
  495. $this->assertGreaterThan(0, $cacheMock->put('folder/' . $folderWith0308, $data));
  496. // get un-normalized folder by name
  497. $unNormalizedFolderName = $cacheMock->get('folder/' . $folderWith0308);
  498. // check if database layer normalized the folder name (this should not happen)
  499. $this->assertEquals($folderWith0308, $unNormalizedFolderName['name']);
  500. // put normalized folder
  501. $this->assertFalse($cacheMock->get('folder/' . $folderWith00F6));
  502. $this->assertGreaterThan(0, $cacheMock->put('folder/' . $folderWith00F6, $data));
  503. // this is our bug, we have two different hashes with the same name (Schön)
  504. $this->assertEquals(2, count($cacheMock->getFolderContents('folder')));
  505. }
  506. /**
  507. * this test shows that there is no bug if we use the normalizer
  508. */
  509. public function testWithNormalizer(): void {
  510. if (!class_exists('Patchwork\PHP\Shim\Normalizer')) {
  511. $this->markTestSkipped('The 3rdparty Normalizer extension is not available.');
  512. return;
  513. }
  514. // folder name "Schön" with U+00F6 (normalized)
  515. $folderWith00F6 = "\x53\x63\x68\xc3\xb6\x6e";
  516. // folder name "Schön" with U+0308 (un-normalized)
  517. $folderWith0308 = "\x53\x63\x68\x6f\xcc\x88\x6e";
  518. $data = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE];
  519. // put root folder
  520. $this->assertFalse($this->cache->get('folder'));
  521. $this->assertGreaterThan(0, $this->cache->put('folder', $data));
  522. // put un-normalized folder
  523. $this->assertFalse($this->cache->get('folder/' . $folderWith0308));
  524. $this->assertGreaterThan(0, $this->cache->put('folder/' . $folderWith0308, $data));
  525. // get un-normalized folder by name
  526. $unNormalizedFolderName = $this->cache->get('folder/' . $folderWith0308);
  527. // check if folder name was normalized
  528. $this->assertEquals($folderWith00F6, $unNormalizedFolderName['name']);
  529. // put normalized folder
  530. $this->assertInstanceOf('\OCP\Files\Cache\ICacheEntry', $this->cache->get('folder/' . $folderWith00F6));
  531. $this->assertGreaterThan(0, $this->cache->put('folder/' . $folderWith00F6, $data));
  532. // at this point we should have only one folder named "Schön"
  533. $this->assertEquals(1, count($this->cache->getFolderContents('folder')));
  534. }
  535. public function bogusPathNamesProvider() {
  536. return [
  537. ['/bogus.txt', 'bogus.txt'],
  538. ['//bogus.txt', 'bogus.txt'],
  539. ['bogus/', 'bogus'],
  540. ['bogus//', 'bogus'],
  541. ];
  542. }
  543. /**
  544. * Test bogus paths with leading or doubled slashes
  545. *
  546. * @dataProvider bogusPathNamesProvider
  547. */
  548. public function testBogusPaths($bogusPath, $fixedBogusPath): void {
  549. $data = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE];
  550. $parentId = $this->cache->getId('');
  551. $this->assertGreaterThan(0, $this->cache->put($bogusPath, $data));
  552. $newData = $this->cache->get($fixedBogusPath);
  553. $this->assertNotFalse($newData);
  554. $this->assertEquals($fixedBogusPath, $newData['path']);
  555. // parent is the correct one, resolved properly (they used to not be)
  556. $this->assertEquals($parentId, $newData['parent']);
  557. $newDataFromBogus = $this->cache->get($bogusPath);
  558. // same entry
  559. $this->assertEquals($newData, $newDataFromBogus);
  560. }
  561. public function testNoReuseOfFileId(): void {
  562. $data1 = ['size' => 100, 'mtime' => 50, 'mimetype' => 'text/plain'];
  563. $this->cache->put('somefile.txt', $data1);
  564. $info = $this->cache->get('somefile.txt');
  565. $fileId = $info['fileid'];
  566. $this->cache->remove('somefile.txt');
  567. $data2 = ['size' => 200, 'mtime' => 100, 'mimetype' => 'text/plain'];
  568. $this->cache->put('anotherfile.txt', $data2);
  569. $info2 = $this->cache->get('anotherfile.txt');
  570. $fileId2 = $info2['fileid'];
  571. $this->assertNotEquals($fileId, $fileId2);
  572. }
  573. public function escapingProvider() {
  574. return [
  575. ['foo'],
  576. ['o%'],
  577. ['oth_r'],
  578. ];
  579. }
  580. /**
  581. * @param string $name
  582. * @dataProvider escapingProvider
  583. */
  584. public function testEscaping($name): void {
  585. $data = ['size' => 100, 'mtime' => 50, 'mimetype' => 'text/plain'];
  586. $this->cache->put($name, $data);
  587. $this->assertTrue($this->cache->inCache($name));
  588. $retrievedData = $this->cache->get($name);
  589. foreach ($data as $key => $value) {
  590. $this->assertEquals($value, $retrievedData[$key]);
  591. }
  592. $this->cache->move($name, $name . 'asd');
  593. $this->assertFalse($this->cache->inCache($name));
  594. $this->assertTrue($this->cache->inCache($name . 'asd'));
  595. $this->cache->remove($name . 'asd');
  596. $this->assertFalse($this->cache->inCache($name . 'asd'));
  597. $folderData = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE];
  598. $this->cache->put($name, $folderData);
  599. $this->cache->put('other', $folderData);
  600. $childs = ['asd', 'bar', 'foo', 'sub/folder'];
  601. $this->cache->put($name . '/sub', $folderData);
  602. $this->cache->put('other/sub', $folderData);
  603. foreach ($childs as $child) {
  604. $this->cache->put($name . '/' . $child, $data);
  605. $this->cache->put('other/' . $child, $data);
  606. $this->assertTrue($this->cache->inCache($name . '/' . $child));
  607. }
  608. $this->cache->move($name, $name . 'asd');
  609. foreach ($childs as $child) {
  610. $this->assertTrue($this->cache->inCache($name . 'asd/' . $child));
  611. $this->assertTrue($this->cache->inCache('other/' . $child));
  612. }
  613. foreach ($childs as $child) {
  614. $this->cache->remove($name . 'asd/' . $child);
  615. $this->assertFalse($this->cache->inCache($name . 'asd/' . $child));
  616. $this->assertTrue($this->cache->inCache('other/' . $child));
  617. }
  618. }
  619. public function testExtended(): void {
  620. $folderData = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE];
  621. $data = ['size' => 100, 'mtime' => 50, 'mimetype' => 'text/plain', 'creation_time' => 20];
  622. $id1 = $this->cache->put('foo1', $data);
  623. $data = ['size' => 100, 'mtime' => 50, 'mimetype' => 'text/plain', 'upload_time' => 30];
  624. $this->cache->put('foo2', $data);
  625. $data = ['size' => 100, 'mtime' => 50, 'mimetype' => 'text/plain', 'metadata_etag' => 'foo'];
  626. $this->cache->put('foo3', $data);
  627. $data = ['size' => 100, 'mtime' => 50, 'mimetype' => 'text/plain'];
  628. $id4 = $this->cache->put('foo4', $data);
  629. $entry = $this->cache->get($id1);
  630. $this->assertEquals(20, $entry->getCreationTime());
  631. $this->assertEquals(0, $entry->getUploadTime());
  632. $this->assertEquals(null, $entry->getMetadataEtag());
  633. $entries = $this->cache->getFolderContents('');
  634. $this->assertCount(4, $entries);
  635. $this->assertEquals('foo1', $entries[0]->getName());
  636. $this->assertEquals('foo2', $entries[1]->getName());
  637. $this->assertEquals('foo3', $entries[2]->getName());
  638. $this->assertEquals('foo4', $entries[3]->getName());
  639. $this->assertEquals(20, $entries[0]->getCreationTime());
  640. $this->assertEquals(0, $entries[0]->getUploadTime());
  641. $this->assertEquals(null, $entries[0]->getMetadataEtag());
  642. $this->assertEquals(0, $entries[1]->getCreationTime());
  643. $this->assertEquals(30, $entries[1]->getUploadTime());
  644. $this->assertEquals(null, $entries[1]->getMetadataEtag());
  645. $this->assertEquals(0, $entries[2]->getCreationTime());
  646. $this->assertEquals(0, $entries[2]->getUploadTime());
  647. $this->assertEquals('foo', $entries[2]->getMetadataEtag());
  648. $this->assertEquals(0, $entries[3]->getCreationTime());
  649. $this->assertEquals(0, $entries[3]->getUploadTime());
  650. $this->assertEquals(null, $entries[3]->getMetadataEtag());
  651. $this->cache->update($id1, ['upload_time' => 25]);
  652. $entry = $this->cache->get($id1);
  653. $this->assertEquals(20, $entry->getCreationTime());
  654. $this->assertEquals(25, $entry->getUploadTime());
  655. $this->assertEquals(null, $entry->getMetadataEtag());
  656. $this->cache->put('sub', $folderData);
  657. $this->cache->move('foo1', 'sub/foo1');
  658. $entries = $this->cache->getFolderContents('sub');
  659. $this->assertCount(1, $entries);
  660. $this->assertEquals(20, $entries[0]->getCreationTime());
  661. $this->assertEquals(25, $entries[0]->getUploadTime());
  662. $this->assertEquals(null, $entries[0]->getMetadataEtag());
  663. $this->cache->update($id4, ['upload_time' => 25]);
  664. $entry = $this->cache->get($id4);
  665. $this->assertEquals(0, $entry->getCreationTime());
  666. $this->assertEquals(25, $entry->getUploadTime());
  667. $this->assertEquals(null, $entry->getMetadataEtag());
  668. $this->cache->remove('sub');
  669. }
  670. protected function tearDown(): void {
  671. if ($this->cache) {
  672. $this->cache->clear();
  673. }
  674. parent::tearDown();
  675. }
  676. protected function setUp(): void {
  677. parent::setUp();
  678. $this->storage = new \OC\Files\Storage\Temporary([]);
  679. $this->storage2 = new \OC\Files\Storage\Temporary([]);
  680. $this->cache = new \OC\Files\Cache\Cache($this->storage);
  681. $this->cache2 = new \OC\Files\Cache\Cache($this->storage2);
  682. $this->cache->insert('', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]);
  683. $this->cache2->insert('', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]);
  684. }
  685. }