diff options
Diffstat (limited to 'tests/lib/Files/ViewTest.php')
-rw-r--r-- | tests/lib/Files/ViewTest.php | 685 |
1 files changed, 424 insertions, 261 deletions
diff --git a/tests/lib/Files/ViewTest.php b/tests/lib/Files/ViewTest.php index 2189e7c09f4..c490cd08dae 100644 --- a/tests/lib/Files/ViewTest.php +++ b/tests/lib/Files/ViewTest.php @@ -1,13 +1,14 @@ <?php + /** - * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. */ + * 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; -use OCP\Cache\CappedMemoryCache; +use OC\Files\Cache\Scanner; use OC\Files\Cache\Watcher; use OC\Files\Filesystem; use OC\Files\Mount\MountPoint; @@ -16,46 +17,75 @@ use OC\Files\Storage\Common; use OC\Files\Storage\Storage; use OC\Files\Storage\Temporary; use OC\Files\View; +use OC\Share20\ShareDisableChecker; +use OCA\Files_Trashbin\Trash\ITrashManager; +use OCP\Cache\CappedMemoryCache; use OCP\Constants; use OCP\Files\Config\IMountProvider; +use OCP\Files\Config\IMountProviderCollection; use OCP\Files\FileInfo; +use OCP\Files\ForbiddenException; use OCP\Files\GenericFileException; +use OCP\Files\InvalidPathException; use OCP\Files\Mount\IMountManager; +use OCP\Files\NotFoundException; use OCP\Files\Storage\IStorage; +use OCP\Files\Storage\IStorageFactory; +use OCP\IConfig; +use OCP\IDBConnection; +use OCP\IGroup; +use OCP\IGroupManager; +use OCP\ITempManager; +use OCP\IUser; +use OCP\IUserManager; use OCP\Lock\ILockingProvider; use OCP\Lock\LockedException; +use OCP\Server; +use OCP\Share\IManager as IShareManager; use OCP\Share\IShare; use OCP\Util; +use PHPUnit\Framework\MockObject\MockObject; use Test\HookHelper; use Test\TestMoveableMountPoint; use Test\Traits\UserTrait; class TemporaryNoTouch extends Temporary { - public function touch($path, $mtime = null) { + public function touch(string $path, ?int $mtime = null): bool { return false; } } class TemporaryNoCross extends Temporary { - public function copyFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime = null) { + public function copyFromStorage(IStorage $sourceStorage, string $sourceInternalPath, string $targetInternalPath, bool $preserveMtime = false): bool { return Common::copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime); } - public function moveFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath) { + public function moveFromStorage(IStorage $sourceStorage, string $sourceInternalPath, string $targetInternalPath): bool { return Common::moveFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath); } } class TemporaryNoLocal extends Temporary { - public function instanceOfStorage($className) { - if ($className === '\OC\Files\Storage\Local') { + public function instanceOfStorage(string $class): bool { + if ($class === '\OC\Files\Storage\Local') { return false; } else { - return parent::instanceOfStorage($className); + return parent::instanceOfStorage($class); } } } +class TestEventHandler { + public function umount() { + } + public function post_umount() { + } + public function preCallback() { + } + public function postCallback() { + } +} + /** * Class ViewTest * @@ -67,7 +97,7 @@ class ViewTest extends \Test\TestCase { use UserTrait; /** - * @var \OC\Files\Storage\Storage[] $storages + * @var Storage[] $storages */ private $storages = []; @@ -77,28 +107,28 @@ class ViewTest extends \Test\TestCase { private $user; /** - * @var \OCP\IUser + * @var IUser */ private $userObject; /** - * @var \OCP\IGroup + * @var IGroup */ private $groupObject; - /** @var \OC\Files\Storage\Storage */ + /** @var Storage */ private $tempStorage; protected function setUp(): void { parent::setUp(); \OC_Hook::clear(); - \OC_User::clearBackends(); - \OC_User::useBackend(new \Test\Util\User\Dummy()); + Server::get(IUserManager::class)->clearBackends(); + Server::get(IUserManager::class)->registerBackend(new \Test\Util\User\Dummy()); //login - $userManager = \OC::$server->getUserManager(); - $groupManager = \OC::$server->getGroupManager(); + $userManager = Server::get(IUserManager::class); + $groupManager = Server::get(IGroupManager::class); $this->user = 'test'; $this->userObject = $userManager->createUser('test', 'test'); @@ -108,7 +138,7 @@ class ViewTest extends \Test\TestCase { self::loginAsUser($this->user); /** @var IMountManager $manager */ - $manager = \OC::$server->get(IMountManager::class); + $manager = Server::get(IMountManager::class); $manager->removeMount('/test'); $this->tempStorage = null; @@ -129,13 +159,13 @@ class ViewTest extends \Test\TestCase { self::logout(); /** @var SetupManager $setupManager */ - $setupManager = \OC::$server->get(SetupManager::class); + $setupManager = Server::get(SetupManager::class); $setupManager->setupRoot(); $this->userObject->delete(); $this->groupObject->delete(); - $mountProviderCollection = \OC::$server->getMountProviderCollection(); + $mountProviderCollection = Server::get(IMountProviderCollection::class); self::invokePrivate($mountProviderCollection, 'providers', [[]]); parent::tearDown(); @@ -144,7 +174,7 @@ class ViewTest extends \Test\TestCase { /** * @medium */ - public function testCacheAPI() { + public function testCacheAPI(): void { $storage1 = $this->getTestStorage(); $storage2 = $this->getTestStorage(); $storage3 = $this->getTestStorage(); @@ -227,7 +257,7 @@ class ViewTest extends \Test\TestCase { /** * @medium */ - public function testGetPath() { + public function testGetPath(): void { $storage1 = $this->getTestStorage(); $storage2 = $this->getTestStorage(); $storage3 = $this->getTestStorage(); @@ -254,8 +284,8 @@ class ViewTest extends \Test\TestCase { } - public function testGetPathNotExisting() { - $this->expectException(\OCP\Files\NotFoundException::class); + public function testGetPathNotExisting(): void { + $this->expectException(NotFoundException::class); $storage1 = $this->getTestStorage(); Filesystem::mount($storage1, [], '/'); @@ -271,7 +301,7 @@ class ViewTest extends \Test\TestCase { /** * @medium */ - public function testMountPointOverwrite() { + public function testMountPointOverwrite(): void { $storage1 = $this->getTestStorage(false); $storage2 = $this->getTestStorage(); $storage1->mkdir('substorage'); @@ -283,21 +313,19 @@ class ViewTest extends \Test\TestCase { $this->assertCount(4, $folderContent); } - public function sharingDisabledPermissionProvider() { + public static function sharingDisabledPermissionProvider(): array { return [ ['no', '', true], ['yes', 'group1', false], ]; } - /** - * @dataProvider sharingDisabledPermissionProvider - */ - public function testRemoveSharePermissionWhenSharingDisabledForUser($excludeGroups, $excludeGroupsList, $expectedShareable) { + #[\PHPUnit\Framework\Attributes\DataProvider('sharingDisabledPermissionProvider')] + public function testRemoveSharePermissionWhenSharingDisabledForUser($excludeGroups, $excludeGroupsList, $expectedShareable): void { // Reset sharing disabled for users cache - self::invokePrivate(\OC::$server->getShareManager(), 'sharingDisabledForUsersCache', [new CappedMemoryCache()]); + self::invokePrivate(Server::get(ShareDisableChecker::class), 'sharingDisabledForUsersCache', [new CappedMemoryCache()]); - $config = \OC::$server->getConfig(); + $config = Server::get(IConfig::class); $oldExcludeGroupsFlag = $config->getAppValue('core', 'shareapi_exclude_groups', 'no'); $oldExcludeGroupsList = $config->getAppValue('core', 'shareapi_exclude_groups_list', ''); $config->setAppValue('core', 'shareapi_exclude_groups', $excludeGroups); @@ -320,10 +348,10 @@ class ViewTest extends \Test\TestCase { $config->setAppValue('core', 'shareapi_exclude_groups_list', $oldExcludeGroupsList); // Reset sharing disabled for users cache - self::invokePrivate(\OC::$server->getShareManager(), 'sharingDisabledForUsersCache', [new CappedMemoryCache()]); + self::invokePrivate(Server::get(ShareDisableChecker::class), 'sharingDisabledForUsersCache', [new CappedMemoryCache()]); } - public function testCacheIncompleteFolder() { + public function testCacheIncompleteFolder(): void { $storage1 = $this->getTestStorage(false); Filesystem::mount($storage1, [], '/incomplete'); $rootView = new View('/incomplete'); @@ -336,7 +364,7 @@ class ViewTest extends \Test\TestCase { $this->assertCount(1, $entries); } - public function testAutoScan() { + public function testAutoScan(): void { $storage1 = $this->getTestStorage(false); $storage2 = $this->getTestStorage(false); Filesystem::mount($storage1, [], '/'); @@ -357,7 +385,7 @@ class ViewTest extends \Test\TestCase { /** * @medium */ - public function testSearch() { + public function testSearch(): void { $storage1 = $this->getTestStorage(); $storage2 = $this->getTestStorage(); $storage3 = $this->getTestStorage(); @@ -407,7 +435,7 @@ class ViewTest extends \Test\TestCase { /** * @medium */ - public function testWatcher() { + public function testWatcher(): void { $storage1 = $this->getTestStorage(); Filesystem::mount($storage1, [], '/'); $storage1->getWatcher()->setPolicy(Watcher::CHECK_ALWAYS); @@ -428,7 +456,7 @@ class ViewTest extends \Test\TestCase { /** * @medium */ - public function testCopyBetweenStorageNoCross() { + public function testCopyBetweenStorageNoCross(): void { $storage1 = $this->getTestStorage(true, TemporaryNoCross::class); $storage2 = $this->getTestStorage(true, TemporaryNoCross::class); $this->copyBetweenStorages($storage1, $storage2); @@ -437,7 +465,7 @@ class ViewTest extends \Test\TestCase { /** * @medium */ - public function testCopyBetweenStorageCross() { + public function testCopyBetweenStorageCross(): void { $storage1 = $this->getTestStorage(); $storage2 = $this->getTestStorage(); $this->copyBetweenStorages($storage1, $storage2); @@ -446,7 +474,7 @@ class ViewTest extends \Test\TestCase { /** * @medium */ - public function testCopyBetweenStorageCrossNonLocal() { + public function testCopyBetweenStorageCrossNonLocal(): void { $storage1 = $this->getTestStorage(true, TemporaryNoLocal::class); $storage2 = $this->getTestStorage(true, TemporaryNoLocal::class); $this->copyBetweenStorages($storage1, $storage2); @@ -474,7 +502,7 @@ class ViewTest extends \Test\TestCase { /** * @medium */ - public function testMoveBetweenStorageNoCross() { + public function testMoveBetweenStorageNoCross(): void { $storage1 = $this->getTestStorage(true, TemporaryNoCross::class); $storage2 = $this->getTestStorage(true, TemporaryNoCross::class); $this->moveBetweenStorages($storage1, $storage2); @@ -483,7 +511,7 @@ class ViewTest extends \Test\TestCase { /** * @medium */ - public function testMoveBetweenStorageCross() { + public function testMoveBetweenStorageCross(): void { $storage1 = $this->getTestStorage(); $storage2 = $this->getTestStorage(); $this->moveBetweenStorages($storage1, $storage2); @@ -492,17 +520,17 @@ class ViewTest extends \Test\TestCase { /** * @medium */ - public function testMoveBetweenStorageCrossNonLocal() { + public function testMoveBetweenStorageCrossNonLocal(): void { $storage1 = $this->getTestStorage(true, TemporaryNoLocal::class); $storage2 = $this->getTestStorage(true, TemporaryNoLocal::class); $this->moveBetweenStorages($storage1, $storage2); } public function moveBetweenStorages($storage1, $storage2) { - Filesystem::mount($storage1, [], '/'); - Filesystem::mount($storage2, [], '/substorage'); + Filesystem::mount($storage1, [], '/' . $this->user . '/'); + Filesystem::mount($storage2, [], '/' . $this->user . '/substorage'); - $rootView = new View(''); + $rootView = new View('/' . $this->user); $rootView->rename('foo.txt', 'substorage/folder/foo.txt'); $this->assertFalse($rootView->file_exists('foo.txt')); $this->assertTrue($rootView->file_exists('substorage/folder/foo.txt')); @@ -515,7 +543,7 @@ class ViewTest extends \Test\TestCase { /** * @medium */ - public function testUnlink() { + public function testUnlink(): void { $storage1 = $this->getTestStorage(); $storage2 = $this->getTestStorage(); Filesystem::mount($storage1, [], '/'); @@ -535,14 +563,12 @@ class ViewTest extends \Test\TestCase { $this->assertFalse($rootView->file_exists('substorage/bar.txt')); } - public function rmdirOrUnlinkDataProvider() { + public static function rmdirOrUnlinkDataProvider(): array { return [['rmdir'], ['unlink']]; } - /** - * @dataProvider rmdirOrUnlinkDataProvider - */ - public function testRmdir($method) { + #[\PHPUnit\Framework\Attributes\DataProvider('rmdirOrUnlinkDataProvider')] + public function testRmdir($method): void { $storage1 = $this->getTestStorage(); Filesystem::mount($storage1, [], '/'); @@ -561,7 +587,7 @@ class ViewTest extends \Test\TestCase { /** * @medium */ - public function testUnlinkRootMustFail() { + public function testUnlinkRootMustFail(): void { $storage1 = $this->getTestStorage(); $storage2 = $this->getTestStorage(); Filesystem::mount($storage1, [], '/'); @@ -580,7 +606,7 @@ class ViewTest extends \Test\TestCase { /** * @medium */ - public function testTouch() { + public function testTouch(): void { $storage = $this->getTestStorage(true, TemporaryNoTouch::class); Filesystem::mount($storage, [], '/'); @@ -604,7 +630,7 @@ class ViewTest extends \Test\TestCase { /** * @medium */ - public function testTouchFloat() { + public function testTouchFloat(): void { $storage = $this->getTestStorage(true, TemporaryNoTouch::class); Filesystem::mount($storage, [], '/'); @@ -621,7 +647,7 @@ class ViewTest extends \Test\TestCase { /** * @medium */ - public function testViewHooks() { + public function testViewHooks(): void { $storage1 = $this->getTestStorage(); $storage2 = $this->getTestStorage(); $defaultRoot = Filesystem::getRoot(); @@ -646,7 +672,7 @@ class ViewTest extends \Test\TestCase { $this->hookPath = $params['path']; } - public function testSearchNotOutsideView() { + public function testSearchNotOutsideView(): void { $storage1 = $this->getTestStorage(); Filesystem::mount($storage1, [], '/'); $storage1->rename('folder', 'foo'); @@ -662,11 +688,11 @@ class ViewTest extends \Test\TestCase { /** * @param bool $scan * @param string $class - * @return \OC\Files\Storage\Storage + * @return Storage */ private function getTestStorage($scan = true, $class = Temporary::class) { /** - * @var \OC\Files\Storage\Storage $storage + * @var Storage $storage */ $storage = new $class([]); $textData = "dummy file data\n"; @@ -687,7 +713,7 @@ class ViewTest extends \Test\TestCase { /** * @medium */ - public function testViewHooksIfRootStartsTheSame() { + public function testViewHooksIfRootStartsTheSame(): void { $storage1 = $this->getTestStorage(); $storage2 = $this->getTestStorage(); $defaultRoot = Filesystem::getRoot(); @@ -718,7 +744,7 @@ class ViewTest extends \Test\TestCase { $this->hookCreatePath = $params['path']; } - public function testEditNoCreateHook() { + public function testEditNoCreateHook(): void { $storage1 = $this->getTestStorage(); $storage2 = $this->getTestStorage(); $defaultRoot = Filesystem::getRoot(); @@ -748,10 +774,8 @@ class ViewTest extends \Test\TestCase { \OC_Hook::clear('OC_Filesystem', 'post_write'); } - /** - * @dataProvider resolvePathTestProvider - */ - public function testResolvePath($expected, $pathToTest) { + #[\PHPUnit\Framework\Attributes\DataProvider('resolvePathTestProvider')] + public function testResolvePath($expected, $pathToTest): void { $storage1 = $this->getTestStorage(); Filesystem::mount($storage1, [], '/'); @@ -767,7 +791,7 @@ class ViewTest extends \Test\TestCase { $this->assertTrue($exists); } - public function resolvePathTestProvider() { + public static function resolvePathTestProvider(): array { return [ ['foo.txt', 'foo.txt'], ['foo.txt', '/foo.txt'], @@ -782,7 +806,7 @@ class ViewTest extends \Test\TestCase { ]; } - public function testUTF8Names() { + public function testUTF8Names(): void { $names = ['虚', '和知しゃ和で', 'regular ascii', 'sɨˈrɪlɪk', 'ѨѬ', 'أنا أحب القراءة كثيرا']; $storage = new Temporary([]); @@ -825,7 +849,7 @@ class ViewTest extends \Test\TestCase { * 1024 is the max path length in mac */ $folderName = 'abcdefghijklmnopqrstuvwxyz012345678901234567890123456789'; - $tmpdirLength = strlen(\OC::$server->getTempManager()->getTemporaryFolder()); + $tmpdirLength = strlen(Server::get(ITempManager::class)->getTemporaryFolder()); if (\OC_Util::runningOnMac()) { $depth = ((1024 - $tmpdirLength) / 57); } else { @@ -861,7 +885,7 @@ class ViewTest extends \Test\TestCase { } } - public function testTouchNotSupported() { + public function testTouchNotSupported(): void { $storage = new TemporaryNoTouch([]); $scanner = $storage->getScanner(); Filesystem::mount($storage, [], '/test/'); @@ -872,13 +896,13 @@ class ViewTest extends \Test\TestCase { $info = $view->getFileInfo('/test/test'); $view->touch('/test/test', $past); - $scanner->scanFile('test', \OC\Files\Cache\Scanner::REUSE_ETAG); + $scanner->scanFile('test', Scanner::REUSE_ETAG); $info2 = $view->getFileInfo('/test/test'); $this->assertSame($info['etag'], $info2['etag']); } - public function testWatcherEtagCrossStorage() { + public function testWatcherEtagCrossStorage(): void { $storage1 = new Temporary([]); $storage2 = new Temporary([]); $scanner1 = $storage1->getScanner(); @@ -910,29 +934,29 @@ class ViewTest extends \Test\TestCase { $this->assertNotEquals($newFolderInfo->getEtag(), $oldEtag); } - /** - * @dataProvider absolutePathProvider - */ - public function testGetAbsolutePath($expectedPath, $relativePath) { + #[\PHPUnit\Framework\Attributes\DataProvider('absolutePathProvider')] + public function testGetAbsolutePath($expectedPath, $relativePath): void { $view = new View('/files'); $this->assertEquals($expectedPath, $view->getAbsolutePath($relativePath)); } - public function testPartFileInfo() { + public function testPartFileInfo(): void { $storage = new Temporary([]); $scanner = $storage->getScanner(); Filesystem::mount($storage, [], '/test/'); - $storage->file_put_contents('test.part', 'foobar'); + $sizeWritten = $storage->file_put_contents('test.part', 'foobar'); $scanner->scan(''); $view = new View('/test'); $info = $view->getFileInfo('test.part'); $this->assertInstanceOf('\OCP\Files\FileInfo', $info); $this->assertNull($info->getId()); + $this->assertEquals(6, $sizeWritten); $this->assertEquals(6, $info->getSize()); + $this->assertEquals('foobar', $view->file_get_contents('test.part')); } - public function absolutePathProvider() { + public static function absolutePathProvider(): array { return [ ['/files/', ''], ['/files/0', '0'], @@ -944,32 +968,28 @@ class ViewTest extends \Test\TestCase { ]; } - /** - * @dataProvider chrootRelativePathProvider - */ - public function testChrootGetRelativePath($root, $absolutePath, $expectedPath) { + #[\PHPUnit\Framework\Attributes\DataProvider('chrootRelativePathProvider')] + public function testChrootGetRelativePath($root, $absolutePath, $expectedPath): void { $view = new View('/files'); $view->chroot($root); $this->assertEquals($expectedPath, $view->getRelativePath($absolutePath)); } - public function chrootRelativePathProvider() { - return $this->relativePathProvider('/'); + public static function chrootRelativePathProvider(): array { + return self::relativePathProvider('/'); } - /** - * @dataProvider initRelativePathProvider - */ - public function testInitGetRelativePath($root, $absolutePath, $expectedPath) { + #[\PHPUnit\Framework\Attributes\DataProvider('initRelativePathProvider')] + public function testInitGetRelativePath($root, $absolutePath, $expectedPath): void { $view = new View($root); $this->assertEquals($expectedPath, $view->getRelativePath($absolutePath)); } - public function initRelativePathProvider() { - return $this->relativePathProvider(null); + public static function initRelativePathProvider(): array { + return self::relativePathProvider(null); } - public function relativePathProvider($missingRootExpectedPath) { + public static function relativePathProvider($missingRootExpectedPath): array { return [ // No root - returns the path ['', '/files', '/files'], @@ -1040,7 +1060,7 @@ class ViewTest extends \Test\TestCase { ]; } - public function testFileView() { + public function testFileView(): void { $storage = new Temporary([]); $scanner = $storage->getScanner(); $storage->file_put_contents('foo.txt', 'bar'); @@ -1056,11 +1076,9 @@ class ViewTest extends \Test\TestCase { $this->assertEquals('foo', $view->file_get_contents('')); } - /** - * @dataProvider tooLongPathDataProvider - */ - public function testTooLongPath($operation, $param0 = null) { - $this->expectException(\OCP\Files\InvalidPathException::class); + #[\PHPUnit\Framework\Attributes\DataProvider('tooLongPathDataProvider')] + public function testTooLongPath($operation, $param0 = null): void { + $this->expectException(InvalidPathException::class); $longPath = ''; @@ -1089,14 +1107,13 @@ class ViewTest extends \Test\TestCase { call_user_func([$rootView, $operation], $longPath, $param0); } - public function tooLongPathDataProvider() { + public static function tooLongPathDataProvider(): array { return [ ['getAbsolutePath'], ['getRelativePath'], ['getMountPoint'], ['resolvePath'], ['getLocalFile'], - ['getLocalFolder'], ['mkdir'], ['rmdir'], ['opendir'], @@ -1135,7 +1152,7 @@ class ViewTest extends \Test\TestCase { ]; } - public function testRenameCrossStoragePreserveMtime() { + public function testRenameCrossStoragePreserveMtime(): void { $storage1 = new Temporary([]); $storage2 = new Temporary([]); $storage1->mkdir('sub'); @@ -1160,11 +1177,11 @@ class ViewTest extends \Test\TestCase { $this->assertEquals($time, $view->filemtime('/test/sub/storage/foo/bar.txt')); } - public function testRenameFailDeleteTargetKeepSource() { + public function testRenameFailDeleteTargetKeepSource(): void { $this->doTestCopyRenameFail('rename'); } - public function testCopyFailDeleteTargetKeepSource() { + public function testCopyFailDeleteTargetKeepSource(): void { $this->doTestCopyRenameFail('copy'); } @@ -1173,11 +1190,11 @@ class ViewTest extends \Test\TestCase { /** @var \PHPUnit\Framework\MockObject\MockObject|Temporary $storage2 */ $storage2 = $this->getMockBuilder(TemporaryNoCross::class) ->setConstructorArgs([[]]) - ->setMethods(['fopen', 'writeStream']) + ->onlyMethods(['fopen', 'writeStream']) ->getMock(); $storage2->method('writeStream') - ->willThrowException(new GenericFileException("Failed to copy stream")); + ->willThrowException(new GenericFileException('Failed to copy stream')); $storage1->mkdir('sub'); $storage1->file_put_contents('foo.txt', '0123456789ABCDEFGH'); @@ -1215,11 +1232,11 @@ class ViewTest extends \Test\TestCase { $this->assertFalse($storage2->getCache()->get('dirtomove/indir2.txt')); } - public function testDeleteFailKeepCache() { + public function testDeleteFailKeepCache(): void { /** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */ $storage = $this->getMockBuilder(Temporary::class) ->setConstructorArgs([[]]) - ->setMethods(['unlink']) + ->onlyMethods(['unlink']) ->getMock(); $storage->expects($this->once()) ->method('unlink') @@ -1236,7 +1253,7 @@ class ViewTest extends \Test\TestCase { $this->assertTrue($cache->inCache('foo.txt')); } - public function directoryTraversalProvider() { + public static function directoryTraversalProvider(): array { return [ ['../test/'], ['..\\test\\my/../folder'], @@ -1245,16 +1262,16 @@ class ViewTest extends \Test\TestCase { } /** - * @dataProvider directoryTraversalProvider * @param string $root */ - public function testConstructDirectoryTraversalException($root) { + #[\PHPUnit\Framework\Attributes\DataProvider('directoryTraversalProvider')] + public function testConstructDirectoryTraversalException($root): void { $this->expectException(\Exception::class); new View($root); } - public function testRenameOverWrite() { + public function testRenameOverWrite(): void { $storage = new Temporary([]); $scanner = $storage->getScanner(); $storage->mkdir('sub'); @@ -1267,36 +1284,36 @@ class ViewTest extends \Test\TestCase { $this->assertTrue($view->rename('/test/foo.txt', '/test/foo/bar.txt')); } - public function testSetMountOptionsInStorage() { + public function testSetMountOptionsInStorage(): void { $mount = new MountPoint(Temporary::class, '/asd/', [[]], Filesystem::getLoader(), ['foo' => 'bar']); Filesystem::getMountManager()->addMount($mount); - /** @var \OC\Files\Storage\Common $storage */ + /** @var Common $storage */ $storage = $mount->getStorage(); $this->assertEquals($storage->getMountOption('foo'), 'bar'); } - public function testSetMountOptionsWatcherPolicy() { + public function testSetMountOptionsWatcherPolicy(): void { $mount = new MountPoint(Temporary::class, '/asd/', [[]], Filesystem::getLoader(), ['filesystem_check_changes' => Watcher::CHECK_NEVER]); Filesystem::getMountManager()->addMount($mount); - /** @var \OC\Files\Storage\Common $storage */ + /** @var Common $storage */ $storage = $mount->getStorage(); $watcher = $storage->getWatcher(); $this->assertEquals(Watcher::CHECK_NEVER, $watcher->getPolicy()); } - public function testGetAbsolutePathOnNull() { + public function testGetAbsolutePathOnNull(): void { $view = new View(); $this->assertNull($view->getAbsolutePath(null)); } - public function testGetRelativePathOnNull() { + public function testGetRelativePathOnNull(): void { $view = new View(); $this->assertNull($view->getRelativePath(null)); } - public function testNullAsRoot() { - $this->expectException(\InvalidArgumentException::class); + public function testNullAsRoot(): void { + $this->expectException(\TypeError::class); new View(null); } @@ -1305,13 +1322,13 @@ class ViewTest extends \Test\TestCase { * e.g. reading from a folder that's being renamed * * - * @dataProvider dataLockPaths * * @param string $rootPath * @param string $pathPrefix */ - public function testReadFromWriteLockedPath($rootPath, $pathPrefix) { - $this->expectException(\OCP\Lock\LockedException::class); + #[\PHPUnit\Framework\Attributes\DataProvider('dataLockPaths')] + public function testReadFromWriteLockedPath($rootPath, $pathPrefix): void { + $this->expectException(LockedException::class); $rootPath = str_replace('{folder}', 'files', $rootPath); $pathPrefix = str_replace('{folder}', 'files', $pathPrefix); @@ -1326,12 +1343,12 @@ class ViewTest extends \Test\TestCase { /** * Reading from a files_encryption folder that's being renamed * - * @dataProvider dataLockPaths * * @param string $rootPath * @param string $pathPrefix */ - public function testReadFromWriteUnlockablePath($rootPath, $pathPrefix) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataLockPaths')] + public function testReadFromWriteUnlockablePath($rootPath, $pathPrefix): void { $rootPath = str_replace('{folder}', 'files_encryption', $rootPath); $pathPrefix = str_replace('{folder}', 'files_encryption', $pathPrefix); @@ -1346,13 +1363,13 @@ class ViewTest extends \Test\TestCase { * e.g. writing a file that's being downloaded * * - * @dataProvider dataLockPaths * * @param string $rootPath * @param string $pathPrefix */ - public function testWriteToReadLockedFile($rootPath, $pathPrefix) { - $this->expectException(\OCP\Lock\LockedException::class); + #[\PHPUnit\Framework\Attributes\DataProvider('dataLockPaths')] + public function testWriteToReadLockedFile($rootPath, $pathPrefix): void { + $this->expectException(LockedException::class); $rootPath = str_replace('{folder}', 'files', $rootPath); $pathPrefix = str_replace('{folder}', 'files', $pathPrefix); @@ -1367,12 +1384,12 @@ class ViewTest extends \Test\TestCase { /** * Writing a file that's being downloaded * - * @dataProvider dataLockPaths * * @param string $rootPath * @param string $pathPrefix */ - public function testWriteToReadUnlockableFile($rootPath, $pathPrefix) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataLockPaths')] + public function testWriteToReadUnlockableFile($rootPath, $pathPrefix): void { $rootPath = str_replace('{folder}', 'files_encryption', $rootPath); $pathPrefix = str_replace('{folder}', 'files_encryption', $pathPrefix); @@ -1386,8 +1403,8 @@ class ViewTest extends \Test\TestCase { /** * Test that locks are on mount point paths instead of mount root */ - public function testLockLocalMountPointPathInsteadOfStorageRoot() { - $lockingProvider = \OC::$server->getLockingProvider(); + public function testLockLocalMountPointPathInsteadOfStorageRoot(): void { + $lockingProvider = Server::get(ILockingProvider::class); $view = new View('/testuser/files/'); $storage = new Temporary([]); Filesystem::mount($storage, [], '/'); @@ -1416,8 +1433,8 @@ class ViewTest extends \Test\TestCase { /** * Test that locks are on mount point paths and also mount root when requested */ - public function testLockStorageRootButNotLocalMountPoint() { - $lockingProvider = \OC::$server->getLockingProvider(); + public function testLockStorageRootButNotLocalMountPoint(): void { + $lockingProvider = Server::get(ILockingProvider::class); $view = new View('/testuser/files/'); $storage = new Temporary([]); Filesystem::mount($storage, [], '/'); @@ -1446,8 +1463,8 @@ class ViewTest extends \Test\TestCase { /** * Test that locks are on mount point paths and also mount root when requested */ - public function testLockMountPointPathFailReleasesBoth() { - $lockingProvider = \OC::$server->getLockingProvider(); + public function testLockMountPointPathFailReleasesBoth(): void { + $lockingProvider = Server::get(ILockingProvider::class); $view = new View('/testuser/files/'); $storage = new Temporary([]); Filesystem::mount($storage, [], '/'); @@ -1474,7 +1491,7 @@ class ViewTest extends \Test\TestCase { $lockingProvider->releaseAll(); } - public function dataLockPaths() { + public static function dataLockPaths(): array { return [ ['/testuser/{folder}', ''], ['/testuser', '/{folder}'], @@ -1482,7 +1499,7 @@ class ViewTest extends \Test\TestCase { ]; } - public function pathRelativeToFilesProvider() { + public static function pathRelativeToFilesProvider(): array { return [ ['admin/files', ''], ['admin/files/x', 'x'], @@ -1494,15 +1511,13 @@ class ViewTest extends \Test\TestCase { ]; } - /** - * @dataProvider pathRelativeToFilesProvider - */ - public function testGetPathRelativeToFiles($path, $expectedPath) { + #[\PHPUnit\Framework\Attributes\DataProvider('pathRelativeToFilesProvider')] + public function testGetPathRelativeToFiles($path, $expectedPath): void { $view = new View(); $this->assertEquals($expectedPath, $view->getPathRelativeToFiles($path)); } - public function pathRelativeToFilesProviderExceptionCases() { + public static function pathRelativeToFilesProviderExceptionCases(): array { return [ [''], ['x'], @@ -1513,10 +1528,10 @@ class ViewTest extends \Test\TestCase { } /** - * @dataProvider pathRelativeToFilesProviderExceptionCases * @param string $path */ - public function testGetPathRelativeToFilesWithInvalidArgument($path) { + #[\PHPUnit\Framework\Attributes\DataProvider('pathRelativeToFilesProviderExceptionCases')] + public function testGetPathRelativeToFilesWithInvalidArgument($path): void { $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('$absolutePath must be relative to "files"'); @@ -1524,7 +1539,7 @@ class ViewTest extends \Test\TestCase { $view->getPathRelativeToFiles($path); } - public function testChangeLock() { + public function testChangeLock(): void { $view = new View('/testuser/files/'); $storage = new Temporary([]); Filesystem::mount($storage, [], '/'); @@ -1545,7 +1560,7 @@ class ViewTest extends \Test\TestCase { $this->assertFalse($this->isFileLocked($view, '/test//sub', ILockingProvider::LOCK_EXCLUSIVE)); } - public function hookPathProvider() { + public static function hookPathProvider(): array { return [ ['/foo/files', '/foo', true], ['/foo/files/bar', '/foo', true], @@ -1558,21 +1573,21 @@ class ViewTest extends \Test\TestCase { } /** - * @dataProvider hookPathProvider * @param $root * @param $path * @param $shouldEmit */ - public function testHookPaths($root, $path, $shouldEmit) { + #[\PHPUnit\Framework\Attributes\DataProvider('hookPathProvider')] + public function testHookPaths($root, $path, $shouldEmit): void { $filesystemReflection = new \ReflectionClass(Filesystem::class); $defaultRootValue = $filesystemReflection->getProperty('defaultInstance'); $defaultRootValue->setAccessible(true); $oldRoot = $defaultRootValue->getValue(); $defaultView = new View('/foo/files'); - $defaultRootValue->setValue($defaultView); + $defaultRootValue->setValue(null, $defaultView); $view = new View($root); $result = self::invokePrivate($view, 'shouldEmitHooks', [$path]); - $defaultRootValue->setValue($oldRoot); + $defaultRootValue->setValue(null, $oldRoot); $this->assertEquals($shouldEmit, $result); } @@ -1586,13 +1601,15 @@ class ViewTest extends \Test\TestCase { $mounts = []; foreach ($mountPoints as $mountPoint) { $storage = $this->getMockBuilder(Storage::class) - ->setMethods([]) - ->setConstructorArgs([[]]) + ->onlyMethods([]) ->getMock(); $storage->method('getId')->willReturn('non-null-id'); + $storage->method('getStorageCache')->willReturnCallback(function () use ($storage) { + return new \OC\Files\Cache\Storage($storage, true, Server::get(IDBConnection::class)); + }); $mounts[] = $this->getMockBuilder(TestMoveableMountPoint::class) - ->setMethods(['moveMount']) + ->onlyMethods(['moveMount']) ->setConstructorArgs([$storage, $mountPoint]) ->getMock(); } @@ -1603,7 +1620,7 @@ class ViewTest extends \Test\TestCase { ->method('getMountsForUser') ->willReturn($mounts); - $mountProviderCollection = \OC::$server->getMountProviderCollection(); + $mountProviderCollection = Server::get(IMountProviderCollection::class); $mountProviderCollection->registerProvider($mountProvider); return $mounts; @@ -1612,7 +1629,7 @@ class ViewTest extends \Test\TestCase { /** * Test mount point move */ - public function testMountPointMove() { + public function testMountPointMove(): void { self::loginAsUser($this->user); [$mount1, $mount2] = $this->createTestMovableMountPoints([ @@ -1634,10 +1651,7 @@ class ViewTest extends \Test\TestCase { $this->assertTrue($view->rename('mount2', 'sub/moved_mount'), 'Can move a mount point into a subdirectory'); } - /** - * Test that moving a mount point into another is forbidden - */ - public function testMoveMountPointIntoAnother() { + public function testMoveMountPointOverwrite(): void { self::loginAsUser($this->user); [$mount1, $mount2] = $this->createTestMovableMountPoints([ @@ -1653,53 +1667,94 @@ class ViewTest extends \Test\TestCase { $view = new View('/' . $this->user . '/files/'); - $this->assertFalse($view->rename('mount1', 'mount2'), 'Cannot overwrite another mount point'); - $this->assertFalse($view->rename('mount1', 'mount2/sub'), 'Cannot move a mount point into another'); + $this->expectException(ForbiddenException::class); + $view->rename('mount1', 'mount2'); + } + + public function testMoveMountPointIntoMount(): void { + self::loginAsUser($this->user); + + [$mount1, $mount2] = $this->createTestMovableMountPoints([ + $this->user . '/files/mount1', + $this->user . '/files/mount2', + ]); + + $mount1->expects($this->never()) + ->method('moveMount'); + + $mount2->expects($this->never()) + ->method('moveMount'); + + $view = new View('/' . $this->user . '/files/'); + + $this->expectException(ForbiddenException::class); + $view->rename('mount1', 'mount2/sub'); } /** * Test that moving a mount point into a shared folder is forbidden */ - public function testMoveMountPointIntoSharedFolder() { + public function testMoveMountPointIntoSharedFolder(): void { self::loginAsUser($this->user); - [$mount1] = $this->createTestMovableMountPoints([ + [$mount1, $mount2] = $this->createTestMovableMountPoints([ $this->user . '/files/mount1', + $this->user . '/files/mount2', ]); $mount1->expects($this->never()) ->method('moveMount'); + $mount2->expects($this->once()) + ->method('moveMount') + ->willReturn(true); + $view = new View('/' . $this->user . '/files/'); $view->mkdir('shareddir'); $view->mkdir('shareddir/sub'); $view->mkdir('shareddir/sub2'); + // Create a similar named but non-shared folder + $view->mkdir('shareddir notshared'); $fileId = $view->getFileInfo('shareddir')->getId(); - $userObject = \OC::$server->getUserManager()->createUser('test2', 'IHateNonMockableStaticClasses'); + $userObject = Server::get(IUserManager::class)->createUser('test2', 'IHateNonMockableStaticClasses'); $userFolder = \OC::$server->getUserFolder($this->user); $shareDir = $userFolder->get('shareddir'); - $shareManager = \OC::$server->getShareManager(); + $shareManager = Server::get(IShareManager::class); $share = $shareManager->newShare(); $share->setSharedWith('test2') ->setSharedBy($this->user) ->setShareType(IShare::TYPE_USER) - ->setPermissions(\OCP\Constants::PERMISSION_READ) - ->setId(42) - ->setProviderId('foo') + ->setPermissions(Constants::PERMISSION_READ) ->setNode($shareDir); $shareManager->createShare($share); - $this->assertFalse($view->rename('mount1', 'shareddir'), 'Cannot overwrite shared folder'); - $this->assertFalse($view->rename('mount1', 'shareddir/sub'), 'Cannot move mount point into shared folder'); - $this->assertFalse($view->rename('mount1', 'shareddir/sub/sub2'), 'Cannot move mount point into shared subfolder'); + try { + $view->rename('mount1', 'shareddir'); + $this->fail('Cannot overwrite shared folder'); + } catch (ForbiddenException $e) { + + } + try { + $view->rename('mount1', 'shareddir/sub'); + $this->fail('Cannot move mount point into shared folder'); + } catch (ForbiddenException $e) { + + } + try { + $view->rename('mount1', 'shareddir/sub/sub2'); + $this->fail('Cannot move mount point into shared subfolder'); + } catch (ForbiddenException $e) { + + } + $this->assertTrue($view->rename('mount2', 'shareddir notshared/sub'), 'Can move mount point into a similarly named but non-shared folder'); $shareManager->deleteShare($share); $userObject->delete(); } - public function basicOperationProviderForLocks() { + public static function basicOperationProviderForLocks(): array { return [ // --- write hook ---- [ @@ -1739,6 +1794,8 @@ class ViewTest extends \Test\TestCase { ILockingProvider::LOCK_SHARED, ILockingProvider::LOCK_EXCLUSIVE, ILockingProvider::LOCK_SHARED, + null, + 0, ], // ---- delete hook ---- @@ -1770,6 +1827,8 @@ class ViewTest extends \Test\TestCase { ILockingProvider::LOCK_SHARED, ILockingProvider::LOCK_SHARED, null, + null, + false, ], [ 'fopen', @@ -1796,23 +1855,63 @@ class ViewTest extends \Test\TestCase { // ---- no hooks, no locks --- ['is_dir', ['dir'], 'dir', null], ['is_file', ['dir'], 'dir', null], - ['stat', ['dir'], 'dir', null], - ['filetype', ['dir'], 'dir', null], - ['filesize', ['dir'], 'dir', null], + [ + 'stat', + ['dir'], + 'dir', + null, + ILockingProvider::LOCK_SHARED, + ILockingProvider::LOCK_SHARED, + ILockingProvider::LOCK_SHARED, + null, + false, + ], + [ + 'filetype', + ['dir'], + 'dir', + null, + ILockingProvider::LOCK_SHARED, + ILockingProvider::LOCK_SHARED, + ILockingProvider::LOCK_SHARED, + null, + false, + ], + [ + 'filesize', + ['dir'], + 'dir', + null, + ILockingProvider::LOCK_SHARED, + ILockingProvider::LOCK_SHARED, + ILockingProvider::LOCK_SHARED, + null, + /* Return an int */ + 100 + ], ['isCreatable', ['dir'], 'dir', null], ['isReadable', ['dir'], 'dir', null], ['isUpdatable', ['dir'], 'dir', null], ['isDeletable', ['dir'], 'dir', null], ['isSharable', ['dir'], 'dir', null], ['file_exists', ['dir'], 'dir', null], - ['filemtime', ['dir'], 'dir', null], + [ + 'filemtime', + ['dir'], + 'dir', + null, + ILockingProvider::LOCK_SHARED, + ILockingProvider::LOCK_SHARED, + ILockingProvider::LOCK_SHARED, + null, + false, + ], ]; } /** * Test whether locks are set before and after the operation * - * @dataProvider basicOperationProviderForLocks * * @param string $operation operation name on the view * @param array $operationArgs arguments for the operation @@ -1822,8 +1921,9 @@ class ViewTest extends \Test\TestCase { * @param int $expectedLockDuring expected lock during operation * @param int $expectedLockAfter expected lock during post hooks * @param int $expectedStrayLock expected lock after returning, should - * be null (unlock) for most operations + * be null (unlock) for most operations */ + #[\PHPUnit\Framework\Attributes\DataProvider('basicOperationProviderForLocks')] public function testLockBasicOperation( $operation, $operationArgs, @@ -1832,15 +1932,21 @@ class ViewTest extends \Test\TestCase { $expectedLockBefore = ILockingProvider::LOCK_SHARED, $expectedLockDuring = ILockingProvider::LOCK_SHARED, $expectedLockAfter = ILockingProvider::LOCK_SHARED, - $expectedStrayLock = null - ) { + $expectedStrayLock = null, + $returnValue = true, + ): void { $view = new View('/' . $this->user . '/files/'); - /** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */ + /** @var Temporary&MockObject $storage */ $storage = $this->getMockBuilder(Temporary::class) - ->setMethods([$operation]) + ->onlyMethods([$operation]) ->getMock(); + /* Pause trash to avoid the trashbin intercepting rmdir and unlink calls */ + Server::get(ITrashManager::class)->pauseTrash(); + /* Same thing with encryption wrapper */ + Server::get(IStorageFactory::class)->removeStorageWrapper('oc_encryption'); + Filesystem::mount($storage, [], $this->user . '/'); // work directly on disk because mkdir might be mocked @@ -1853,10 +1959,10 @@ class ViewTest extends \Test\TestCase { $storage->expects($this->once()) ->method($operation) ->willReturnCallback( - function () use ($view, $lockedPath, &$lockTypeDuring) { + function () use ($view, $lockedPath, &$lockTypeDuring, $returnValue) { $lockTypeDuring = $this->getFileLockType($view, $lockedPath); - return true; + return $returnValue; } ); @@ -1876,19 +1982,22 @@ class ViewTest extends \Test\TestCase { } $this->assertEquals($expectedStrayLock, $this->getFileLockType($view, $lockedPath)); + + /* Resume trash to avoid side effects */ + Server::get(ITrashManager::class)->resumeTrash(); } /** * Test locks for file_put_content with stream. * This code path uses $storage->fopen instead */ - public function testLockFilePutContentWithStream() { + public function testLockFilePutContentWithStream(): void { $view = new View('/' . $this->user . '/files/'); $path = 'test_file_put_contents.txt'; /** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */ $storage = $this->getMockBuilder(Temporary::class) - ->setMethods(['fopen']) + ->onlyMethods(['fopen']) ->getMock(); Filesystem::mount($storage, [], $this->user . '/'); @@ -1921,13 +2030,13 @@ class ViewTest extends \Test\TestCase { /** * Test locks for fopen with fclose at the end */ - public function testLockFopen() { + public function testLockFopen(): void { $view = new View('/' . $this->user . '/files/'); $path = 'test_file_put_contents.txt'; /** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */ $storage = $this->getMockBuilder(Temporary::class) - ->setMethods(['fopen']) + ->onlyMethods(['fopen']) ->getMock(); Filesystem::mount($storage, [], $this->user . '/'); @@ -1964,27 +2073,32 @@ class ViewTest extends \Test\TestCase { /** * Test locks for fopen with fclose at the end * - * @dataProvider basicOperationProviderForLocks * * @param string $operation operation name on the view * @param array $operationArgs arguments for the operation * @param string $path path of the locked item to check */ + #[\PHPUnit\Framework\Attributes\DataProvider('basicOperationProviderForLocks')] public function testLockBasicOperationUnlocksAfterException( $operation, $operationArgs, - $path - ) { + $path, + ): void { if ($operation === 'touch') { - $this->markTestSkipped("touch handles storage exceptions internally"); + $this->markTestSkipped('touch handles storage exceptions internally'); } $view = new View('/' . $this->user . '/files/'); /** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */ $storage = $this->getMockBuilder(Temporary::class) - ->setMethods([$operation]) + ->onlyMethods([$operation]) ->getMock(); + /* Pause trash to avoid the trashbin intercepting rmdir and unlink calls */ + Server::get(ITrashManager::class)->pauseTrash(); + /* Same thing with encryption wrapper */ + Server::get(IStorageFactory::class)->removeStorageWrapper('oc_encryption'); + Filesystem::mount($storage, [], $this->user . '/'); // work directly on disk because mkdir might be mocked @@ -1997,7 +2111,7 @@ class ViewTest extends \Test\TestCase { $storage->expects($this->once()) ->method($operation) ->willReturnCallback( - function () { + function (): void { throw new \Exception('Simulated exception'); } ); @@ -2011,9 +2125,12 @@ class ViewTest extends \Test\TestCase { } $this->assertTrue($thrown, 'Exception was rethrown'); $this->assertNull($this->getFileLockType($view, $path), 'File got unlocked after exception'); + + /* Resume trash to avoid side effects */ + Server::get(ITrashManager::class)->resumeTrash(); } - public function testLockBasicOperationUnlocksAfterLockException() { + public function testLockBasicOperationUnlocksAfterLockException(): void { $view = new View('/' . $this->user . '/files/'); $storage = new Temporary([]); @@ -2047,24 +2164,24 @@ class ViewTest extends \Test\TestCase { /** * Test locks for fopen with fclose at the end * - * @dataProvider basicOperationProviderForLocks * * @param string $operation operation name on the view * @param array $operationArgs arguments for the operation * @param string $path path of the locked item to check * @param string $hookType hook type */ + #[\PHPUnit\Framework\Attributes\DataProvider('basicOperationProviderForLocks')] public function testLockBasicOperationUnlocksAfterCancelledHook( $operation, $operationArgs, $path, - $hookType - ) { + $hookType, + ): void { $view = new View('/' . $this->user . '/files/'); /** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */ $storage = $this->getMockBuilder(Temporary::class) - ->setMethods([$operation]) + ->onlyMethods([$operation]) ->getMock(); Filesystem::mount($storage, [], $this->user . '/'); @@ -2082,7 +2199,7 @@ class ViewTest extends \Test\TestCase { $this->assertNull($this->getFileLockType($view, $path), 'File got unlocked after exception'); } - public function lockFileRenameOrCopyDataProvider() { + public static function lockFileRenameOrCopyDataProvider(): array { return [ ['rename', ILockingProvider::LOCK_EXCLUSIVE], ['copy', ILockingProvider::LOCK_SHARED], @@ -2092,29 +2209,29 @@ class ViewTest extends \Test\TestCase { /** * Test locks for rename or copy operation * - * @dataProvider lockFileRenameOrCopyDataProvider * * @param string $operation operation to be done on the view * @param int $expectedLockTypeSourceDuring expected lock type on source file during - * the operation + * the operation */ - public function testLockFileRename($operation, $expectedLockTypeSourceDuring) { + #[\PHPUnit\Framework\Attributes\DataProvider('lockFileRenameOrCopyDataProvider')] + public function testLockFileRename($operation, $expectedLockTypeSourceDuring): void { $view = new View('/' . $this->user . '/files/'); /** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */ $storage = $this->getMockBuilder(Temporary::class) - ->setMethods([$operation, 'getMetaData', 'filemtime']) + ->onlyMethods([$operation, 'getMetaData', 'filemtime']) ->getMock(); $storage->expects($this->any()) ->method('getMetaData') - ->will($this->returnValue([ + ->willReturn([ 'mtime' => 1885434487, 'etag' => '', 'mimetype' => 'text/plain', 'permissions' => Constants::PERMISSION_ALL, 'size' => 3 - ])); + ]); $storage->expects($this->any()) ->method('filemtime') ->willReturn(123456789); @@ -2122,6 +2239,9 @@ class ViewTest extends \Test\TestCase { $sourcePath = 'original.txt'; $targetPath = 'target.txt'; + /* Disable encryption wrapper to avoid it intercepting mocked call */ + Server::get(IStorageFactory::class)->removeStorageWrapper('oc_encryption'); + Filesystem::mount($storage, [], $this->user . '/'); $storage->mkdir('files'); $view->file_put_contents($sourcePath, 'meh'); @@ -2162,19 +2282,22 @@ class ViewTest extends \Test\TestCase { * We expect that we catch the exception, free the lock and re-throw it. * */ - public function testLockFileCopyException() { + public function testLockFileCopyException(): void { $this->expectException(\Exception::class); $view = new View('/' . $this->user . '/files/'); /** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */ $storage = $this->getMockBuilder(Temporary::class) - ->setMethods(['copy']) + ->onlyMethods(['copy']) ->getMock(); $sourcePath = 'original.txt'; $targetPath = 'target.txt'; + /* Disable encryption wrapper to avoid it intercepting mocked call */ + Server::get(IStorageFactory::class)->removeStorageWrapper('oc_encryption'); + Filesystem::mount($storage, [], $this->user . '/'); $storage->mkdir('files'); $view->file_put_contents($sourcePath, 'meh'); @@ -2182,7 +2305,7 @@ class ViewTest extends \Test\TestCase { $storage->expects($this->once()) ->method('copy') ->willReturnCallback( - function () { + function (): void { throw new \Exception(); } ); @@ -2205,7 +2328,7 @@ class ViewTest extends \Test\TestCase { /** * Test rename operation: unlock first path when second path was locked */ - public function testLockFileRenameUnlockOnException() { + public function testLockFileRenameUnlockOnException(): void { self::loginAsUser('test'); $view = new View('/' . $this->user . '/files/'); @@ -2238,7 +2361,7 @@ class ViewTest extends \Test\TestCase { /** * Test rename operation: unlock first path when second path was locked */ - public function testGetOwner() { + public function testGetOwner(): void { self::loginAsUser('test'); $view = new View('/test/files/'); @@ -2266,7 +2389,7 @@ class ViewTest extends \Test\TestCase { $this->assertEquals('test', $folderInfo[0]->getOwner()->getUID()); } - public function lockFileRenameOrCopyCrossStorageDataProvider() { + public static function lockFileRenameOrCopyCrossStorageDataProvider(): array { return [ ['rename', 'moveFromStorage', ILockingProvider::LOCK_EXCLUSIVE], ['copy', 'copyFromStorage', ILockingProvider::LOCK_SHARED], @@ -2276,34 +2399,34 @@ class ViewTest extends \Test\TestCase { /** * Test locks for rename or copy operation cross-storage * - * @dataProvider lockFileRenameOrCopyCrossStorageDataProvider * * @param string $viewOperation operation to be done on the view * @param string $storageOperation operation to be mocked on the storage * @param int $expectedLockTypeSourceDuring expected lock type on source file during - * the operation + * the operation */ - public function testLockFileRenameCrossStorage($viewOperation, $storageOperation, $expectedLockTypeSourceDuring) { + #[\PHPUnit\Framework\Attributes\DataProvider('lockFileRenameOrCopyCrossStorageDataProvider')] + public function testLockFileRenameCrossStorage($viewOperation, $storageOperation, $expectedLockTypeSourceDuring): void { $view = new View('/' . $this->user . '/files/'); /** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */ $storage = $this->getMockBuilder(Temporary::class) - ->setMethods([$storageOperation]) + ->onlyMethods([$storageOperation]) ->getMock(); /** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage2 */ $storage2 = $this->getMockBuilder(Temporary::class) - ->setMethods([$storageOperation, 'getMetaData', 'filemtime']) + ->onlyMethods([$storageOperation, 'getMetaData', 'filemtime']) ->getMock(); $storage2->expects($this->any()) ->method('getMetaData') - ->will($this->returnValue([ + ->willReturn([ 'mtime' => 1885434487, 'etag' => '', 'mimetype' => 'text/plain', 'permissions' => Constants::PERMISSION_ALL, 'size' => 3 - ])); + ]); $storage2->expects($this->any()) ->method('filemtime') ->willReturn(123456789); @@ -2311,10 +2434,14 @@ class ViewTest extends \Test\TestCase { $sourcePath = 'original.txt'; $targetPath = 'substorage/target.txt'; + /* Disable encryption wrapper to avoid it intercepting mocked call */ + Server::get(IStorageFactory::class)->removeStorageWrapper('oc_encryption'); + Filesystem::mount($storage, [], $this->user . '/'); Filesystem::mount($storage2, [], $this->user . '/files/substorage'); $storage->mkdir('files'); $view->file_put_contents($sourcePath, 'meh'); + $storage2->getUpdater()->update(''); $storage->expects($this->never()) ->method($storageOperation); @@ -2352,7 +2479,7 @@ class ViewTest extends \Test\TestCase { /** * Test locks when moving a mount point */ - public function testLockMoveMountPoint() { + public function testLockMoveMountPoint(): void { self::loginAsUser('test'); [$mount] = $this->createTestMovableMountPoints([ @@ -2419,28 +2546,28 @@ class ViewTest extends \Test\TestCase { * @param int $lockTypePre variable to receive lock type that was active in the pre-hook * @param int $lockTypePost variable to receive lock type that was active in the post-hook * @param bool $onMountPoint true to check the mount point instead of the - * mounted storage + * mounted storage */ private function connectMockHooks($hookType, $view, $path, &$lockTypePre, &$lockTypePost, $onMountPoint = false) { if ($hookType === null) { return; } - $eventHandler = $this->getMockBuilder(\stdclass::class) - ->setMethods(['preCallback', 'postCallback']) + $eventHandler = $this->getMockBuilder(TestEventHandler::class) + ->onlyMethods(['preCallback', 'postCallback']) ->getMock(); $eventHandler->expects($this->any()) ->method('preCallback') ->willReturnCallback( - function () use ($view, $path, $onMountPoint, &$lockTypePre) { + function () use ($view, $path, $onMountPoint, &$lockTypePre): void { $lockTypePre = $this->getFileLockType($view, $path, $onMountPoint); } ); $eventHandler->expects($this->any()) ->method('postCallback') ->willReturnCallback( - function () use ($view, $path, $onMountPoint, &$lockTypePost) { + function () use ($view, $path, $onMountPoint, &$lockTypePost): void { $lockTypePost = $this->getFileLockType($view, $path, $onMountPoint); } ); @@ -2467,7 +2594,7 @@ class ViewTest extends \Test\TestCase { * @param View $view view * @param string $path path * @param bool $onMountPoint true to check the mount point instead of the - * mounted storage + * mounted storage * * @return int lock type or null if file was not locked */ @@ -2481,7 +2608,7 @@ class ViewTest extends \Test\TestCase { } - public function testRemoveMoveableMountPoint() { + public function testRemoveMoveableMountPoint(): void { $mountPoint = '/' . $this->user . '/files/mount/'; // Mock the mount point @@ -2501,8 +2628,8 @@ class ViewTest extends \Test\TestCase { Filesystem::getMountManager()->addMount($mount); // Listen for events - $eventHandler = $this->getMockBuilder(\stdclass::class) - ->setMethods(['umount', 'post_umount']) + $eventHandler = $this->getMockBuilder(TestEventHandler::class) + ->onlyMethods(['umount', 'post_umount']) ->getMock(); $eventHandler->expects($this->once()) ->method('umount') @@ -2528,7 +2655,7 @@ class ViewTest extends \Test\TestCase { $this->assertEquals('foo', $view->rmdir('mount')); } - public function mimeFilterProvider() { + public static function mimeFilterProvider(): array { return [ [null, ['test1.txt', 'test2.txt', 'test3.md', 'test4.png']], ['text/plain', ['test1.txt', 'test2.txt']], @@ -2540,9 +2667,9 @@ class ViewTest extends \Test\TestCase { /** * @param string $filter * @param string[] $expected - * @dataProvider mimeFilterProvider */ - public function testGetDirectoryContentMimeFilter($filter, $expected) { + #[\PHPUnit\Framework\Attributes\DataProvider('mimeFilterProvider')] + public function testGetDirectoryContentMimeFilter($filter, $expected): void { $storage1 = new Temporary(); $root = self::getUniqueID('/'); Filesystem::mount($storage1, [], $root . '/'); @@ -2563,7 +2690,7 @@ class ViewTest extends \Test\TestCase { $this->assertEquals($expected, $files); } - public function testFilePutContentsClearsChecksum() { + public function testFilePutContentsClearsChecksum(): void { $storage = new Temporary([]); $scanner = $storage->getScanner(); $storage->file_put_contents('foo.txt', 'bar'); @@ -2584,7 +2711,7 @@ class ViewTest extends \Test\TestCase { $this->assertEquals('', $data->getChecksum()); } - public function testDeleteGhostFile() { + public function testDeleteGhostFile(): void { $storage = new Temporary([]); $scanner = $storage->getScanner(); $cache = $storage->getCache(); @@ -2607,7 +2734,7 @@ class ViewTest extends \Test\TestCase { $this->assertEquals(0, $newInfo->getSize()); } - public function testDeleteGhostFolder() { + public function testDeleteGhostFolder(): void { $storage = new Temporary([]); $scanner = $storage->getScanner(); $cache = $storage->getCache(); @@ -2633,10 +2760,10 @@ class ViewTest extends \Test\TestCase { $this->assertEquals(0, $newInfo->getSize()); } - public function testCreateParentDirectories() { + public function testCreateParentDirectories(): void { $view = $this->getMockBuilder(View::class) ->disableOriginalConstructor() - ->setMethods([ + ->onlyMethods([ 'is_file', 'file_exists', 'mkdir', @@ -2645,37 +2772,34 @@ class ViewTest extends \Test\TestCase { $view->expects($this->exactly(3)) ->method('is_file') - ->withConsecutive( - ['/new'], - ['/new/folder'], - ['/new/folder/structure'], - ) - ->willReturn(false); + ->willReturnMap([ + ['/new', false], + ['/new/folder', false], + ['/new/folder/structure', false], + ]); $view->expects($this->exactly(3)) ->method('file_exists') - ->withConsecutive( - ['/new'], - ['/new/folder'], - ['/new/folder/structure'], - )->willReturnOnConsecutiveCalls( - true, - false, - false, - ); + ->willReturnMap([ + ['/new', true], + ['/new/folder', false], + ['/new/folder/structure', false], + ]); + + $calls = ['/new/folder', '/new/folder/structure']; $view->expects($this->exactly(2)) ->method('mkdir') - ->withConsecutive( - ['/new/folder'], - ['/new/folder/structure'], - ); + ->willReturnCallback(function ($dir) use (&$calls): void { + $expected = array_shift($calls); + $this->assertEquals($expected, $dir); + }); $this->assertTrue(self::invokePrivate($view, 'createParentDirectories', ['/new/folder/structure'])); } - public function testCreateParentDirectoriesWithExistingFile() { + public function testCreateParentDirectoriesWithExistingFile(): void { $view = $this->getMockBuilder(View::class) ->disableOriginalConstructor() - ->setMethods([ + ->onlyMethods([ 'is_file', 'file_exists', 'mkdir', @@ -2690,7 +2814,7 @@ class ViewTest extends \Test\TestCase { $this->assertFalse(self::invokePrivate($view, 'createParentDirectories', ['/file.txt/folder/structure'])); } - public function testCacheExtension() { + public function testCacheExtension(): void { $storage = new Temporary([]); $scanner = $storage->getScanner(); $storage->file_put_contents('foo.txt', 'bar'); @@ -2710,7 +2834,7 @@ class ViewTest extends \Test\TestCase { $this->assertEquals(0, $info->getCreationTime()); } - public function testFopenGone() { + public function testFopenGone(): void { $storage = new Temporary([]); $scanner = $storage->getScanner(); $storage->file_put_contents('foo.txt', 'bar'); @@ -2728,4 +2852,43 @@ class ViewTest extends \Test\TestCase { $this->assertFalse($cache->inCache('foo.txt')); } + + public function testMountpointParentsCreated(): void { + $storage1 = $this->getTestStorage(); + Filesystem::mount($storage1, [], '/'); + + $storage2 = $this->getTestStorage(); + Filesystem::mount($storage2, [], '/A/B/C'); + + $rootView = new View(''); + + $folderData = $rootView->getDirectoryContent('/'); + $this->assertCount(4, $folderData); + $this->assertEquals('folder', $folderData[0]['name']); + $this->assertEquals('foo.png', $folderData[1]['name']); + $this->assertEquals('foo.txt', $folderData[2]['name']); + $this->assertEquals('A', $folderData[3]['name']); + + $folderData = $rootView->getDirectoryContent('/A'); + $this->assertCount(1, $folderData); + $this->assertEquals('B', $folderData[0]['name']); + + $folderData = $rootView->getDirectoryContent('/A/B'); + $this->assertCount(1, $folderData); + $this->assertEquals('C', $folderData[0]['name']); + + $folderData = $rootView->getDirectoryContent('/A/B/C'); + $this->assertCount(3, $folderData); + $this->assertEquals('folder', $folderData[0]['name']); + $this->assertEquals('foo.png', $folderData[1]['name']); + $this->assertEquals('foo.txt', $folderData[2]['name']); + } + + public function testCopyPreservesContent() { + $viewUser1 = new View('/' . 'userId' . '/files'); + $viewUser1->mkdir(''); + $viewUser1->file_put_contents('foo.txt', 'foo'); + $viewUser1->copy('foo.txt', 'bar.txt'); + $this->assertEquals('foo', $viewUser1->file_get_contents('bar.txt')); + } } |