Completed
Push — master ( 42f450...2cd491 )
by
unknown
66:17 queued 24:43
created
tests/lib/Encryption/UpdateTest.php 2 patches
Indentation   +172 added lines, -172 removed lines patch added patch discarded remove patch
@@ -23,176 +23,176 @@
 block discarded – undo
23 23
 use Test\TestCase;
24 24
 
25 25
 class UpdateTest extends TestCase {
26
-	private string $uid;
27
-	private View&MockObject $view;
28
-	private Util&MockObject $util;
29
-	private \OC\Encryption\Manager&MockObject $encryptionManager;
30
-	private IEncryptionModule&MockObject $encryptionModule;
31
-	private File&MockObject $fileHelper;
32
-	private LoggerInterface&MockObject $logger;
33
-
34
-	protected function setUp(): void {
35
-		parent::setUp();
36
-
37
-		$this->view = $this->createMock(View::class);
38
-		$this->util = $this->createMock(Util::class);
39
-		$this->encryptionManager = $this->createMock(\OC\Encryption\Manager::class);
40
-		$this->fileHelper = $this->createMock(File::class);
41
-		$this->encryptionModule = $this->createMock(IEncryptionModule::class);
42
-		$this->logger = $this->createMock(LoggerInterface::class);
43
-
44
-		$this->uid = 'testUser1';
45
-	}
46
-
47
-	private function getUserMock(string $uid): IUser&MockObject {
48
-		$user = $this->createMock(IUser::class);
49
-		$user->expects(self::any())
50
-			->method('getUID')
51
-			->willReturn($uid);
52
-		return $user;
53
-	}
54
-
55
-	private function getFileMock(string $path, string $owner): OCPFile&MockObject {
56
-		$node = $this->createMock(OCPFile::class);
57
-		$node->expects(self::atLeastOnce())
58
-			->method('getPath')
59
-			->willReturn($path);
60
-		$node->expects(self::any())
61
-			->method('getOwner')
62
-			->willReturn($this->getUserMock($owner));
63
-
64
-		return $node;
65
-	}
66
-
67
-	private function getFolderMock(string $path, string $owner): Folder&MockObject {
68
-		$node = $this->createMock(Folder::class);
69
-		$node->expects(self::atLeastOnce())
70
-			->method('getPath')
71
-			->willReturn($path);
72
-		$node->expects(self::any())
73
-			->method('getOwner')
74
-			->willReturn($this->getUserMock($owner));
75
-
76
-		return $node;
77
-	}
78
-
79
-	/**
80
-	 * @dataProvider dataTestUpdate
81
-	 *
82
-	 * @param string $path
83
-	 * @param boolean $isDir
84
-	 * @param array $allFiles
85
-	 * @param integer $numberOfFiles
86
-	 */
87
-	public function testUpdate($path, $isDir, $allFiles, $numberOfFiles): void {
88
-		$updateMock = $this->getUpdateMock(['getOwnerPath']);
89
-		$updateMock->expects($this->once())->method('getOwnerPath')
90
-			->willReturnCallback(fn (OCPFile|Folder $node) => '/user/' . $node->getPath());
91
-
92
-		$this->encryptionManager->expects($this->once())
93
-			->method('getEncryptionModule')
94
-			->willReturn($this->encryptionModule);
95
-
96
-		if ($isDir) {
97
-			$this->util->expects($this->once())
98
-				->method('getAllFiles')
99
-				->willReturn($allFiles);
100
-			$node = $this->getFolderMock($path, 'user');
101
-		} else {
102
-			$node = $this->getFileMock($path, 'user');
103
-		}
104
-
105
-		$this->fileHelper->expects($this->exactly($numberOfFiles))
106
-			->method('getAccessList')
107
-			->willReturn(['users' => [], 'public' => false]);
108
-
109
-		$this->encryptionModule->expects($this->exactly($numberOfFiles))
110
-			->method('update')
111
-			->willReturn(true);
112
-
113
-		$updateMock->update($node);
114
-	}
115
-
116
-	/**
117
-	 * data provider for testUpdate()
118
-	 *
119
-	 * @return array
120
-	 */
121
-	public function dataTestUpdate() {
122
-		return [
123
-			['/user/files/foo', true, ['/user/files/foo/file1.txt', '/user/files/foo/file1.txt'], 2],
124
-			['/user/files/test.txt', false, [], 1],
125
-		];
126
-	}
127
-
128
-	/**
129
-	 * @dataProvider dataTestPostRename
130
-	 *
131
-	 * @param string $source
132
-	 * @param string $target
133
-	 */
134
-	public function testPostRename($source, $target): void {
135
-		$updateMock = $this->getUpdateMock(['update','getOwnerPath']);
136
-
137
-		$sourceNode = $this->getFileMock($source, 'user');
138
-		$targetNode = $this->getFileMock($target, 'user');
139
-
140
-		if (dirname($source) === dirname($target)) {
141
-			$updateMock->expects($this->never())->method('getOwnerPath');
142
-			$updateMock->expects($this->never())->method('update');
143
-		} else {
144
-			$updateMock->expects($this->once())->method('update')
145
-				->willReturnCallback(fn (OCPFile|Folder $node) => $this->assertSame(
146
-					$target,
147
-					$node->getPath(),
148
-					'update needs to be executed for the target destination'
149
-				));
150
-		}
151
-
152
-		$updateMock->postRename($sourceNode, $targetNode);
153
-	}
154
-
155
-	/**
156
-	 * test data for testPostRename()
157
-	 *
158
-	 * @return array
159
-	 */
160
-	public function dataTestPostRename() {
161
-		return [
162
-			['/test.txt', '/testNew.txt'],
163
-			['/folder/test.txt', '/testNew.txt'],
164
-			['/test.txt', '/folder/testNew.txt'],
165
-		];
166
-	}
167
-
168
-	public function testPostRestore(): void {
169
-		$updateMock = $this->getUpdateMock(['update']);
170
-
171
-		$updateMock->expects($this->once())->method('update')
172
-			->willReturnCallback(fn (OCPFile|Folder $node) => $this->assertSame(
173
-				'/folder/test.txt',
174
-				$node->getPath(),
175
-				'update needs to be executed for the target destination'
176
-			));
177
-
178
-		$updateMock->postRestore($this->getFileMock('/folder/test.txt', 'user'));
179
-	}
180
-
181
-	/**
182
-	 * create mock of the update method
183
-	 *
184
-	 * @param array $methods methods which should be set
185
-	 */
186
-	protected function getUpdateMock(array $methods): Update&MockObject {
187
-		return  $this->getMockBuilder(Update::class)
188
-			->setConstructorArgs(
189
-				[
190
-					$this->util,
191
-					$this->encryptionManager,
192
-					$this->fileHelper,
193
-					$this->logger,
194
-					$this->uid
195
-				]
196
-			)->setMethods($methods)->getMock();
197
-	}
26
+    private string $uid;
27
+    private View&MockObject $view;
28
+    private Util&MockObject $util;
29
+    private \OC\Encryption\Manager&MockObject $encryptionManager;
30
+    private IEncryptionModule&MockObject $encryptionModule;
31
+    private File&MockObject $fileHelper;
32
+    private LoggerInterface&MockObject $logger;
33
+
34
+    protected function setUp(): void {
35
+        parent::setUp();
36
+
37
+        $this->view = $this->createMock(View::class);
38
+        $this->util = $this->createMock(Util::class);
39
+        $this->encryptionManager = $this->createMock(\OC\Encryption\Manager::class);
40
+        $this->fileHelper = $this->createMock(File::class);
41
+        $this->encryptionModule = $this->createMock(IEncryptionModule::class);
42
+        $this->logger = $this->createMock(LoggerInterface::class);
43
+
44
+        $this->uid = 'testUser1';
45
+    }
46
+
47
+    private function getUserMock(string $uid): IUser&MockObject {
48
+        $user = $this->createMock(IUser::class);
49
+        $user->expects(self::any())
50
+            ->method('getUID')
51
+            ->willReturn($uid);
52
+        return $user;
53
+    }
54
+
55
+    private function getFileMock(string $path, string $owner): OCPFile&MockObject {
56
+        $node = $this->createMock(OCPFile::class);
57
+        $node->expects(self::atLeastOnce())
58
+            ->method('getPath')
59
+            ->willReturn($path);
60
+        $node->expects(self::any())
61
+            ->method('getOwner')
62
+            ->willReturn($this->getUserMock($owner));
63
+
64
+        return $node;
65
+    }
66
+
67
+    private function getFolderMock(string $path, string $owner): Folder&MockObject {
68
+        $node = $this->createMock(Folder::class);
69
+        $node->expects(self::atLeastOnce())
70
+            ->method('getPath')
71
+            ->willReturn($path);
72
+        $node->expects(self::any())
73
+            ->method('getOwner')
74
+            ->willReturn($this->getUserMock($owner));
75
+
76
+        return $node;
77
+    }
78
+
79
+    /**
80
+     * @dataProvider dataTestUpdate
81
+     *
82
+     * @param string $path
83
+     * @param boolean $isDir
84
+     * @param array $allFiles
85
+     * @param integer $numberOfFiles
86
+     */
87
+    public function testUpdate($path, $isDir, $allFiles, $numberOfFiles): void {
88
+        $updateMock = $this->getUpdateMock(['getOwnerPath']);
89
+        $updateMock->expects($this->once())->method('getOwnerPath')
90
+            ->willReturnCallback(fn (OCPFile|Folder $node) => '/user/' . $node->getPath());
91
+
92
+        $this->encryptionManager->expects($this->once())
93
+            ->method('getEncryptionModule')
94
+            ->willReturn($this->encryptionModule);
95
+
96
+        if ($isDir) {
97
+            $this->util->expects($this->once())
98
+                ->method('getAllFiles')
99
+                ->willReturn($allFiles);
100
+            $node = $this->getFolderMock($path, 'user');
101
+        } else {
102
+            $node = $this->getFileMock($path, 'user');
103
+        }
104
+
105
+        $this->fileHelper->expects($this->exactly($numberOfFiles))
106
+            ->method('getAccessList')
107
+            ->willReturn(['users' => [], 'public' => false]);
108
+
109
+        $this->encryptionModule->expects($this->exactly($numberOfFiles))
110
+            ->method('update')
111
+            ->willReturn(true);
112
+
113
+        $updateMock->update($node);
114
+    }
115
+
116
+    /**
117
+     * data provider for testUpdate()
118
+     *
119
+     * @return array
120
+     */
121
+    public function dataTestUpdate() {
122
+        return [
123
+            ['/user/files/foo', true, ['/user/files/foo/file1.txt', '/user/files/foo/file1.txt'], 2],
124
+            ['/user/files/test.txt', false, [], 1],
125
+        ];
126
+    }
127
+
128
+    /**
129
+     * @dataProvider dataTestPostRename
130
+     *
131
+     * @param string $source
132
+     * @param string $target
133
+     */
134
+    public function testPostRename($source, $target): void {
135
+        $updateMock = $this->getUpdateMock(['update','getOwnerPath']);
136
+
137
+        $sourceNode = $this->getFileMock($source, 'user');
138
+        $targetNode = $this->getFileMock($target, 'user');
139
+
140
+        if (dirname($source) === dirname($target)) {
141
+            $updateMock->expects($this->never())->method('getOwnerPath');
142
+            $updateMock->expects($this->never())->method('update');
143
+        } else {
144
+            $updateMock->expects($this->once())->method('update')
145
+                ->willReturnCallback(fn (OCPFile|Folder $node) => $this->assertSame(
146
+                    $target,
147
+                    $node->getPath(),
148
+                    'update needs to be executed for the target destination'
149
+                ));
150
+        }
151
+
152
+        $updateMock->postRename($sourceNode, $targetNode);
153
+    }
154
+
155
+    /**
156
+     * test data for testPostRename()
157
+     *
158
+     * @return array
159
+     */
160
+    public function dataTestPostRename() {
161
+        return [
162
+            ['/test.txt', '/testNew.txt'],
163
+            ['/folder/test.txt', '/testNew.txt'],
164
+            ['/test.txt', '/folder/testNew.txt'],
165
+        ];
166
+    }
167
+
168
+    public function testPostRestore(): void {
169
+        $updateMock = $this->getUpdateMock(['update']);
170
+
171
+        $updateMock->expects($this->once())->method('update')
172
+            ->willReturnCallback(fn (OCPFile|Folder $node) => $this->assertSame(
173
+                '/folder/test.txt',
174
+                $node->getPath(),
175
+                'update needs to be executed for the target destination'
176
+            ));
177
+
178
+        $updateMock->postRestore($this->getFileMock('/folder/test.txt', 'user'));
179
+    }
180
+
181
+    /**
182
+     * create mock of the update method
183
+     *
184
+     * @param array $methods methods which should be set
185
+     */
186
+    protected function getUpdateMock(array $methods): Update&MockObject {
187
+        return  $this->getMockBuilder(Update::class)
188
+            ->setConstructorArgs(
189
+                [
190
+                    $this->util,
191
+                    $this->encryptionManager,
192
+                    $this->fileHelper,
193
+                    $this->logger,
194
+                    $this->uid
195
+                ]
196
+            )->setMethods($methods)->getMock();
197
+    }
198 198
 }
Please login to merge, or discard this patch.
Spacing   +4 added lines, -4 removed lines patch added patch discarded remove patch
@@ -87,7 +87,7 @@  discard block
 block discarded – undo
87 87
 	public function testUpdate($path, $isDir, $allFiles, $numberOfFiles): void {
88 88
 		$updateMock = $this->getUpdateMock(['getOwnerPath']);
89 89
 		$updateMock->expects($this->once())->method('getOwnerPath')
90
-			->willReturnCallback(fn (OCPFile|Folder $node) => '/user/' . $node->getPath());
90
+			->willReturnCallback(fn (OCPFile | Folder $node) => '/user/'.$node->getPath());
91 91
 
92 92
 		$this->encryptionManager->expects($this->once())
93 93
 			->method('getEncryptionModule')
@@ -132,7 +132,7 @@  discard block
 block discarded – undo
132 132
 	 * @param string $target
133 133
 	 */
134 134
 	public function testPostRename($source, $target): void {
135
-		$updateMock = $this->getUpdateMock(['update','getOwnerPath']);
135
+		$updateMock = $this->getUpdateMock(['update', 'getOwnerPath']);
136 136
 
137 137
 		$sourceNode = $this->getFileMock($source, 'user');
138 138
 		$targetNode = $this->getFileMock($target, 'user');
@@ -142,7 +142,7 @@  discard block
 block discarded – undo
142 142
 			$updateMock->expects($this->never())->method('update');
143 143
 		} else {
144 144
 			$updateMock->expects($this->once())->method('update')
145
-				->willReturnCallback(fn (OCPFile|Folder $node) => $this->assertSame(
145
+				->willReturnCallback(fn (OCPFile | Folder $node) => $this->assertSame(
146 146
 					$target,
147 147
 					$node->getPath(),
148 148
 					'update needs to be executed for the target destination'
@@ -169,7 +169,7 @@  discard block
 block discarded – undo
169 169
 		$updateMock = $this->getUpdateMock(['update']);
170 170
 
171 171
 		$updateMock->expects($this->once())->method('update')
172
-			->willReturnCallback(fn (OCPFile|Folder $node) => $this->assertSame(
172
+			->willReturnCallback(fn (OCPFile | Folder $node) => $this->assertSame(
173 173
 				'/folder/test.txt',
174 174
 				$node->getPath(),
175 175
 				'update needs to be executed for the target destination'
Please login to merge, or discard this patch.
tests/lib/Files/Storage/Wrapper/EncryptionTest.php 1 patch
Indentation   +994 added lines, -994 removed lines patch added patch discarded remove patch
@@ -33,998 +33,998 @@
 block discarded – undo
33 33
 use Test\Files\Storage\Storage;
34 34
 
35 35
 class EncryptionTest extends Storage {
36
-	/**
37
-	 * block size will always be 8192 for a PHP stream
38
-	 * @see https://bugs.php.net/bug.php?id=21641
39
-	 */
40
-	protected int $headerSize = 8192;
41
-	private Temporary $sourceStorage;
42
-	/** @var Encryption&MockObject */
43
-	protected $instance;
44
-	private \OC\Encryption\Keys\Storage&MockObject $keyStore;
45
-	private Util&MockObject $util;
46
-	private \OC\Encryption\Manager&MockObject $encryptionManager;
47
-	private IEncryptionModule&MockObject $encryptionModule;
48
-	private Cache&MockObject $cache;
49
-	private LoggerInterface&MockObject $logger;
50
-	private File&MockObject $file;
51
-	private MountPoint&MockObject $mount;
52
-	private \OC\Files\Mount\Manager&MockObject $mountManager;
53
-	private \OC\Group\Manager&MockObject $groupManager;
54
-	private IConfig&MockObject $config;
55
-	private ArrayCache&MockObject $arrayCache;
56
-	/** dummy unencrypted size */
57
-	private int $dummySize = -1;
58
-
59
-	protected function setUp(): void {
60
-		parent::setUp();
61
-
62
-		$mockModule = $this->buildMockModule();
63
-		$this->encryptionManager = $this->getMockBuilder('\OC\Encryption\Manager')
64
-			->disableOriginalConstructor()
65
-			->setMethods(['getEncryptionModule', 'isEnabled'])
66
-			->getMock();
67
-		$this->encryptionManager->expects($this->any())
68
-			->method('getEncryptionModule')
69
-			->willReturn($mockModule);
70
-
71
-		$this->arrayCache = $this->createMock(ArrayCache::class);
72
-		$this->config = $this->getMockBuilder(IConfig::class)
73
-			->disableOriginalConstructor()
74
-			->getMock();
75
-		$this->groupManager = $this->getMockBuilder('\OC\Group\Manager')
76
-			->disableOriginalConstructor()
77
-			->getMock();
78
-
79
-		$this->util = $this->getMockBuilder('\OC\Encryption\Util')
80
-			->setMethods(['getUidAndFilename', 'isFile', 'isExcluded', 'stripPartialFileExtension'])
81
-			->setConstructorArgs([new View(), new Manager(
82
-				$this->config,
83
-				$this->createMock(ICacheFactory::class),
84
-				$this->createMock(IEventDispatcher::class),
85
-				$this->createMock(LoggerInterface::class),
86
-			), $this->groupManager, $this->config, $this->arrayCache])
87
-			->getMock();
88
-		$this->util->expects($this->any())
89
-			->method('getUidAndFilename')
90
-			->willReturnCallback(function ($path) {
91
-				return ['user1', $path];
92
-			});
93
-		$this->util->expects($this->any())
94
-			->method('stripPartialFileExtension')
95
-			->willReturnCallback(function ($path) {
96
-				return $path;
97
-			});
98
-
99
-		$this->file = $this->getMockBuilder('\OC\Encryption\File')
100
-			->disableOriginalConstructor()
101
-			->setMethods(['getAccessList'])
102
-			->getMock();
103
-		$this->file->expects($this->any())->method('getAccessList')->willReturn([]);
104
-
105
-		$this->logger = $this->createMock(LoggerInterface::class);
106
-
107
-		$this->sourceStorage = new Temporary([]);
108
-
109
-		$this->keyStore = $this->getMockBuilder('\OC\Encryption\Keys\Storage')
110
-			->disableOriginalConstructor()->getMock();
111
-
112
-		$this->mount = $this->getMockBuilder('\OC\Files\Mount\MountPoint')
113
-			->disableOriginalConstructor()
114
-			->setMethods(['getOption'])
115
-			->getMock();
116
-		$this->mount->expects($this->any())->method('getOption')->willReturnCallback(function ($option, $default) {
117
-			if ($option === 'encrypt' && $default === true) {
118
-				global $mockedMountPointEncryptionEnabled;
119
-				if ($mockedMountPointEncryptionEnabled !== null) {
120
-					return $mockedMountPointEncryptionEnabled;
121
-				}
122
-			}
123
-			return true;
124
-		});
125
-
126
-		$this->cache = $this->getMockBuilder('\OC\Files\Cache\Cache')
127
-			->disableOriginalConstructor()->getMock();
128
-		$this->cache->expects($this->any())
129
-			->method('get')
130
-			->willReturnCallback(function ($path) {
131
-				return ['encrypted' => false, 'path' => $path];
132
-			});
133
-
134
-		$this->mountManager = $this->createMock(\OC\Files\Mount\Manager::class);
135
-		$this->mountManager->method('findByStorageId')
136
-			->willReturn([]);
137
-
138
-		$this->instance = $this->getMockBuilder(Encryption::class)
139
-			->setConstructorArgs(
140
-				[
141
-					[
142
-						'storage' => $this->sourceStorage,
143
-						'root' => 'foo',
144
-						'mountPoint' => '/',
145
-						'mount' => $this->mount
146
-					],
147
-					$this->encryptionManager,
148
-					$this->util,
149
-					$this->logger,
150
-					$this->file,
151
-					null,
152
-					$this->keyStore,
153
-					$this->mountManager,
154
-					$this->arrayCache
155
-				]
156
-			)
157
-			->setMethods(['getMetaData', 'getCache', 'getEncryptionModule'])
158
-			->getMock();
159
-
160
-		$this->instance->expects($this->any())
161
-			->method('getMetaData')
162
-			->willReturnCallback(function ($path) {
163
-				return ['encrypted' => true, 'size' => $this->dummySize, 'path' => $path];
164
-			});
165
-
166
-		$this->instance->expects($this->any())
167
-			->method('getCache')
168
-			->willReturn($this->cache);
169
-
170
-		$this->instance->expects($this->any())
171
-			->method('getEncryptionModule')
172
-			->willReturn($mockModule);
173
-	}
174
-
175
-	protected function buildMockModule(): IEncryptionModule&MockObject {
176
-		$this->encryptionModule = $this->getMockBuilder('\OCP\Encryption\IEncryptionModule')
177
-			->disableOriginalConstructor()
178
-			->setMethods(['getId', 'getDisplayName', 'begin', 'end', 'encrypt', 'decrypt', 'update', 'shouldEncrypt', 'getUnencryptedBlockSize', 'isReadable', 'encryptAll', 'prepareDecryptAll', 'isReadyForUser', 'needDetailedAccessList'])
179
-			->getMock();
180
-
181
-		$this->encryptionModule->expects($this->any())->method('getId')->willReturn('UNIT_TEST_MODULE');
182
-		$this->encryptionModule->expects($this->any())->method('getDisplayName')->willReturn('Unit test module');
183
-		$this->encryptionModule->expects($this->any())->method('begin')->willReturn([]);
184
-		$this->encryptionModule->expects($this->any())->method('end')->willReturn('');
185
-		$this->encryptionModule->expects($this->any())->method('encrypt')->willReturnArgument(0);
186
-		$this->encryptionModule->expects($this->any())->method('decrypt')->willReturnArgument(0);
187
-		$this->encryptionModule->expects($this->any())->method('update')->willReturn(true);
188
-		$this->encryptionModule->expects($this->any())->method('shouldEncrypt')->willReturn(true);
189
-		$this->encryptionModule->expects($this->any())->method('getUnencryptedBlockSize')->willReturn(8192);
190
-		$this->encryptionModule->expects($this->any())->method('isReadable')->willReturn(true);
191
-		$this->encryptionModule->expects($this->any())->method('needDetailedAccessList')->willReturn(false);
192
-		return $this->encryptionModule;
193
-	}
194
-
195
-	/**
196
-	 * @dataProvider dataTestGetMetaData
197
-	 *
198
-	 * @param string $path
199
-	 * @param array $metaData
200
-	 * @param bool $encrypted
201
-	 * @param bool $unencryptedSizeSet
202
-	 * @param int $storedUnencryptedSize
203
-	 * @param array $expected
204
-	 */
205
-	public function testGetMetaData($path, $metaData, $encrypted, $unencryptedSizeSet, $storedUnencryptedSize, $expected): void {
206
-		$sourceStorage = $this->getMockBuilder('\OC\Files\Storage\Storage')
207
-			->disableOriginalConstructor()->getMock();
208
-
209
-		$cache = $this->getMockBuilder('\OC\Files\Cache\Cache')
210
-			->disableOriginalConstructor()->getMock();
211
-		$cache->expects($this->any())
212
-			->method('get')
213
-			->willReturnCallback(
214
-				function ($path) use ($encrypted) {
215
-					return new CacheEntry(['encrypted' => $encrypted, 'path' => $path, 'size' => 0, 'fileid' => 1]);
216
-				}
217
-			);
218
-
219
-		$this->instance = $this->getMockBuilder(Encryption::class)
220
-			->setConstructorArgs(
221
-				[
222
-					[
223
-						'storage' => $sourceStorage,
224
-						'root' => 'foo',
225
-						'mountPoint' => '/',
226
-						'mount' => $this->mount
227
-					],
228
-					$this->encryptionManager,
229
-					$this->util,
230
-					$this->logger,
231
-					$this->file,
232
-					null,
233
-					$this->keyStore,
234
-					$this->mountManager,
235
-					$this->arrayCache,
236
-				]
237
-			)
238
-			->setMethods(['getCache', 'verifyUnencryptedSize'])
239
-			->getMock();
240
-
241
-		if ($unencryptedSizeSet) {
242
-			$this->invokePrivate($this->instance, 'unencryptedSize', [[$path => $storedUnencryptedSize]]);
243
-		}
244
-
245
-		$fileEntry = $this->getMockBuilder('\OC\Files\Cache\Cache')
246
-			->disableOriginalConstructor()->getMock();
247
-		$sourceStorage->expects($this->once())->method('getMetaData')->with($path)
248
-			->willReturn($metaData);
249
-		$sourceStorage->expects($this->any())
250
-			->method('getCache')
251
-			->with($path)
252
-			->willReturn($fileEntry);
253
-		if ($metaData !== null) {
254
-			$fileEntry->expects($this->any())
255
-				->method('get')
256
-				->with($metaData['fileid']);
257
-		}
258
-
259
-		$this->instance->expects($this->any())->method('getCache')->willReturn($cache);
260
-		if ($expected !== null) {
261
-			$this->instance->expects($this->any())->method('verifyUnencryptedSize')
262
-				->with($path, 0)->willReturn($expected['size']);
263
-		}
264
-
265
-		$result = $this->instance->getMetaData($path);
266
-		if (isset($expected['encrypted'])) {
267
-			$this->assertSame($expected['encrypted'], (bool)$result['encrypted']);
268
-
269
-			if (isset($expected['encryptedVersion'])) {
270
-				$this->assertSame($expected['encryptedVersion'], $result['encryptedVersion']);
271
-			}
272
-		}
273
-
274
-		if ($expected !== null) {
275
-			$this->assertSame($expected['size'], $result['size']);
276
-		} else {
277
-			$this->assertSame(null, $result);
278
-		}
279
-	}
280
-
281
-	public function dataTestGetMetaData() {
282
-		return [
283
-			['/test.txt', ['size' => 42, 'encrypted' => 2, 'encryptedVersion' => 2, 'fileid' => 1], true, true, 12, ['size' => 12, 'encrypted' => true, 'encryptedVersion' => 2]],
284
-			['/test.txt', null, true, true, 12, null],
285
-			['/test.txt', ['size' => 42, 'encrypted' => 0, 'fileid' => 1], false, false, 12, ['size' => 42, 'encrypted' => false]],
286
-			['/test.txt', ['size' => 42, 'encrypted' => false, 'fileid' => 1], true, false, 12, ['size' => 12, 'encrypted' => true]]
287
-		];
288
-	}
289
-
290
-	public function testFilesize(): void {
291
-		$cache = $this->getMockBuilder('\OC\Files\Cache\Cache')
292
-			->disableOriginalConstructor()->getMock();
293
-		$cache->expects($this->any())
294
-			->method('get')
295
-			->willReturn(new CacheEntry(['encrypted' => true, 'path' => '/test.txt', 'size' => 0, 'fileid' => 1]));
296
-
297
-		$this->instance = $this->getMockBuilder(Encryption::class)
298
-			->setConstructorArgs(
299
-				[
300
-					[
301
-						'storage' => $this->sourceStorage,
302
-						'root' => 'foo',
303
-						'mountPoint' => '/',
304
-						'mount' => $this->mount
305
-					],
306
-					$this->encryptionManager,
307
-					$this->util,
308
-					$this->logger,
309
-					$this->file,
310
-					null,
311
-					$this->keyStore,
312
-					$this->mountManager,
313
-					$this->arrayCache,
314
-				]
315
-			)
316
-			->setMethods(['getCache', 'verifyUnencryptedSize'])
317
-			->getMock();
318
-
319
-		$this->instance->expects($this->any())->method('getCache')->willReturn($cache);
320
-		$this->instance->expects($this->any())->method('verifyUnencryptedSize')
321
-			->willReturn(42);
322
-
323
-
324
-		$this->assertSame(42,
325
-			$this->instance->filesize('/test.txt')
326
-		);
327
-	}
328
-
329
-	/**
330
-	 * @dataProvider dataTestVerifyUnencryptedSize
331
-	 *
332
-	 * @param int $encryptedSize
333
-	 * @param int $unencryptedSize
334
-	 * @param bool $failure
335
-	 * @param int $expected
336
-	 */
337
-	public function testVerifyUnencryptedSize($encryptedSize, $unencryptedSize, $failure, $expected): void {
338
-		$sourceStorage = $this->getMockBuilder('\OC\Files\Storage\Storage')
339
-			->disableOriginalConstructor()->getMock();
340
-
341
-		$this->instance = $this->getMockBuilder(Encryption::class)
342
-			->setConstructorArgs(
343
-				[
344
-					[
345
-						'storage' => $sourceStorage,
346
-						'root' => 'foo',
347
-						'mountPoint' => '/',
348
-						'mount' => $this->mount
349
-					],
350
-					$this->encryptionManager,
351
-					$this->util,
352
-					$this->logger,
353
-					$this->file,
354
-					null,
355
-					$this->keyStore,
356
-					$this->mountManager,
357
-					$this->arrayCache,
358
-				]
359
-			)
360
-			->setMethods(['fixUnencryptedSize'])
361
-			->getMock();
362
-
363
-		$sourceStorage->expects($this->once())->method('filesize')->willReturn($encryptedSize);
364
-
365
-		$this->instance->expects($this->any())->method('fixUnencryptedSize')
366
-			->with('/test.txt', $encryptedSize, $unencryptedSize)
367
-			->willReturnCallback(
368
-				function () use ($failure, $expected) {
369
-					if ($failure) {
370
-						throw new Exception();
371
-					} else {
372
-						return $expected;
373
-					}
374
-				}
375
-			);
376
-
377
-		$this->assertSame(
378
-			$expected,
379
-			$this->invokePrivate($this->instance, 'verifyUnencryptedSize', ['/test.txt', $unencryptedSize])
380
-		);
381
-	}
382
-
383
-	public function dataTestVerifyUnencryptedSize() {
384
-		return [
385
-			[120, 80, false, 80],
386
-			[120, 120, false, 80],
387
-			[120, -1, false, 80],
388
-			[120, -1, true, -1]
389
-		];
390
-	}
391
-
392
-	/**
393
-	 * @dataProvider dataTestCopyAndRename
394
-	 *
395
-	 * @param string $source
396
-	 * @param string $target
397
-	 * @param $encryptionEnabled
398
-	 * @param boolean $renameKeysReturn
399
-	 */
400
-	public function testRename($source,
401
-		$target,
402
-		$encryptionEnabled,
403
-		$renameKeysReturn): void {
404
-		if ($encryptionEnabled) {
405
-			$this->keyStore
406
-				->expects($this->once())
407
-				->method('renameKeys')
408
-				->willReturn($renameKeysReturn);
409
-		} else {
410
-			$this->keyStore
411
-				->expects($this->never())->method('renameKeys');
412
-		}
413
-		$this->util->expects($this->any())
414
-			->method('isFile')->willReturn(true);
415
-		$this->encryptionManager->expects($this->once())
416
-			->method('isEnabled')->willReturn($encryptionEnabled);
417
-
418
-		$this->instance->mkdir($source);
419
-		$this->instance->mkdir(dirname($target));
420
-		$this->instance->rename($source, $target);
421
-	}
422
-
423
-	public function testCopyEncryption(): void {
424
-		$this->instance->file_put_contents('source.txt', 'bar');
425
-		$this->instance->copy('source.txt', 'target.txt');
426
-		$this->assertSame('bar', $this->instance->file_get_contents('target.txt'));
427
-		$targetMeta = $this->instance->getMetaData('target.txt');
428
-		$sourceMeta = $this->instance->getMetaData('source.txt');
429
-		$this->assertSame($sourceMeta['encrypted'], $targetMeta['encrypted']);
430
-		$this->assertSame($sourceMeta['size'], $targetMeta['size']);
431
-	}
432
-
433
-	/**
434
-	 * data provider for testCopyTesting() and dataTestCopyAndRename()
435
-	 *
436
-	 * @return array
437
-	 */
438
-	public function dataTestCopyAndRename() {
439
-		return [
440
-			['source', 'target', true, false, false],
441
-			['source', 'target', true, true, false],
442
-			['source', '/subFolder/target', true, false, false],
443
-			['source', '/subFolder/target', true, true, true],
444
-			['source', '/subFolder/target', false, true, false],
445
-		];
446
-	}
447
-
448
-	public function testIsLocal(): void {
449
-		$this->encryptionManager->expects($this->once())
450
-			->method('isEnabled')->willReturn(true);
451
-		$this->assertFalse($this->instance->isLocal());
452
-	}
453
-
454
-	/**
455
-	 * @dataProvider dataTestRmdir
456
-	 *
457
-	 * @param string $path
458
-	 * @param boolean $rmdirResult
459
-	 * @param boolean $isExcluded
460
-	 * @param boolean $encryptionEnabled
461
-	 */
462
-	public function testRmdir($path, $rmdirResult, $isExcluded, $encryptionEnabled): void {
463
-		$sourceStorage = $this->getMockBuilder('\OC\Files\Storage\Storage')
464
-			->disableOriginalConstructor()->getMock();
465
-
466
-		$util = $this->getMockBuilder('\OC\Encryption\Util')->disableOriginalConstructor()->getMock();
467
-
468
-		$sourceStorage->expects($this->once())->method('rmdir')->willReturn($rmdirResult);
469
-		$util->expects($this->any())->method('isExcluded')->willReturn($isExcluded);
470
-		$this->encryptionManager->expects($this->any())->method('isEnabled')->willReturn($encryptionEnabled);
471
-
472
-		$encryptionStorage = new Encryption(
473
-			[
474
-				'storage' => $sourceStorage,
475
-				'root' => 'foo',
476
-				'mountPoint' => '/mountPoint',
477
-				'mount' => $this->mount
478
-			],
479
-			$this->encryptionManager,
480
-			$util,
481
-			$this->logger,
482
-			$this->file,
483
-			null,
484
-			$this->keyStore,
485
-			$this->mountManager,
486
-			$this->arrayCache,
487
-		);
488
-
489
-
490
-		if ($rmdirResult === true && $isExcluded === false && $encryptionEnabled === true) {
491
-			$this->keyStore->expects($this->once())->method('deleteAllFileKeys')->with('/mountPoint' . $path);
492
-		} else {
493
-			$this->keyStore->expects($this->never())->method('deleteAllFileKeys');
494
-		}
495
-
496
-		$encryptionStorage->rmdir($path);
497
-	}
498
-
499
-	public function dataTestRmdir() {
500
-		return [
501
-			['/file.txt', true, true, true],
502
-			['/file.txt', false, true, true],
503
-			['/file.txt', true, false, true],
504
-			['/file.txt', false, false, true],
505
-			['/file.txt', true, true, false],
506
-			['/file.txt', false, true, false],
507
-			['/file.txt', true, false, false],
508
-			['/file.txt', false, false, false],
509
-		];
510
-	}
511
-
512
-	/**
513
-	 * @dataProvider dataTestCopyKeys
514
-	 *
515
-	 * @param boolean $excluded
516
-	 * @param boolean $expected
517
-	 */
518
-	public function testCopyKeys($excluded, $expected): void {
519
-		$this->util->expects($this->once())
520
-			->method('isExcluded')
521
-			->willReturn($excluded);
522
-
523
-		if ($excluded) {
524
-			$this->keyStore->expects($this->never())->method('copyKeys');
525
-		} else {
526
-			$this->keyStore->expects($this->once())->method('copyKeys')->willReturn(true);
527
-		}
528
-
529
-		$this->assertSame($expected,
530
-			self::invokePrivate($this->instance, 'copyKeys', ['/source', '/target'])
531
-		);
532
-	}
533
-
534
-	public function dataTestCopyKeys() {
535
-		return [
536
-			[true, false],
537
-			[false, true],
538
-		];
539
-	}
540
-
541
-	/**
542
-	 * @dataProvider dataTestGetHeader
543
-	 *
544
-	 * @param string $path
545
-	 * @param bool $strippedPathExists
546
-	 * @param string $strippedPath
547
-	 */
548
-	public function testGetHeader($path, $strippedPathExists, $strippedPath): void {
549
-		$sourceStorage = $this->getMockBuilder('\OC\Files\Storage\Storage')
550
-			->disableOriginalConstructor()->getMock();
551
-
552
-		$util = $this->getMockBuilder('\OC\Encryption\Util')
553
-			->setConstructorArgs(
554
-				[
555
-					new View(),
556
-					new Manager(
557
-						$this->config,
558
-						$this->createMock(ICacheFactory::class),
559
-						$this->createMock(IEventDispatcher::class),
560
-						$this->createMock(LoggerInterface::class),
561
-					),
562
-					$this->groupManager,
563
-					$this->config,
564
-					$this->arrayCache
565
-				]
566
-			)->getMock();
567
-
568
-		$cache = $this->getMockBuilder('\OC\Files\Cache\Cache')
569
-			->disableOriginalConstructor()->getMock();
570
-		$cache->expects($this->any())
571
-			->method('get')
572
-			->willReturnCallback(function ($path) {
573
-				return ['encrypted' => true, 'path' => $path];
574
-			});
575
-
576
-		$instance = $this->getMockBuilder(Encryption::class)
577
-			->setConstructorArgs(
578
-				[
579
-					[
580
-						'storage' => $sourceStorage,
581
-						'root' => 'foo',
582
-						'mountPoint' => '/',
583
-						'mount' => $this->mount
584
-					],
585
-					$this->encryptionManager,
586
-					$util,
587
-					$this->logger,
588
-					$this->file,
589
-					null,
590
-					$this->keyStore,
591
-					$this->mountManager,
592
-					$this->arrayCache,
593
-				]
594
-			)
595
-			->setMethods(['getCache', 'readFirstBlock'])
596
-			->getMock();
597
-
598
-		$instance->method('getCache')->willReturn($cache);
599
-
600
-		$util->method('parseRawHeader')
601
-			->willReturn([Util::HEADER_ENCRYPTION_MODULE_KEY => 'OC_DEFAULT_MODULE']);
602
-
603
-		if ($strippedPathExists) {
604
-			$instance->method('readFirstBlock')
605
-				->with($strippedPath)->willReturn('');
606
-		} else {
607
-			$instance->method('readFirstBlock')
608
-				->with($path)->willReturn('');
609
-		}
610
-
611
-		$util->expects($this->once())->method('stripPartialFileExtension')
612
-			->with($path)->willReturn($strippedPath);
613
-		$sourceStorage->expects($this->once())
614
-			->method('is_file')
615
-			->with($strippedPath)
616
-			->willReturn($strippedPathExists);
617
-
618
-		$this->invokePrivate($instance, 'getHeader', [$path]);
619
-	}
620
-
621
-	public function dataTestGetHeader() {
622
-		return [
623
-			['/foo/bar.txt', false, '/foo/bar.txt'],
624
-			['/foo/bar.txt.part', false, '/foo/bar.txt'],
625
-			['/foo/bar.txt.ocTransferId7437493.part', false, '/foo/bar.txt'],
626
-			['/foo/bar.txt.part', true, '/foo/bar.txt'],
627
-			['/foo/bar.txt.ocTransferId7437493.part', true, '/foo/bar.txt'],
628
-		];
629
-	}
630
-
631
-	/**
632
-	 * test if getHeader adds the default module correctly to the header for
633
-	 * legacy files
634
-	 *
635
-	 * @dataProvider dataTestGetHeaderAddLegacyModule
636
-	 */
637
-	public function testGetHeaderAddLegacyModule($header, $isEncrypted, $strippedPathExists, $expected): void {
638
-		$sourceStorage = $this->getMockBuilder('\OC\Files\Storage\Storage')
639
-			->disableOriginalConstructor()->getMock();
640
-
641
-		$sourceStorage->expects($this->once())
642
-			->method('is_file')
643
-			->with('test.txt')
644
-			->willReturn($strippedPathExists);
645
-
646
-		$util = $this->getMockBuilder('\OC\Encryption\Util')
647
-			->onlyMethods(['stripPartialFileExtension', 'parseRawHeader'])
648
-			->setConstructorArgs([new View(), new Manager(
649
-				$this->config,
650
-				$this->createMock(ICacheFactory::class),
651
-				$this->createMock(IEventDispatcher::class),
652
-				$this->createMock(LoggerInterface::class),
653
-			), $this->groupManager, $this->config, $this->arrayCache])
654
-			->getMock();
655
-		$util->expects($this->any())
656
-			->method('stripPartialFileExtension')
657
-			->willReturnCallback(function ($path) {
658
-				return $path;
659
-			});
660
-
661
-		$cache = $this->getMockBuilder('\OC\Files\Cache\Cache')
662
-			->disableOriginalConstructor()->getMock();
663
-		$cache->expects($this->any())
664
-			->method('get')
665
-			->willReturnCallback(function ($path) use ($isEncrypted) {
666
-				return ['encrypted' => $isEncrypted, 'path' => $path];
667
-			});
668
-
669
-		$instance = $this->getMockBuilder(Encryption::class)
670
-			->setConstructorArgs(
671
-				[
672
-					[
673
-						'storage' => $sourceStorage,
674
-						'root' => 'foo',
675
-						'mountPoint' => '/',
676
-						'mount' => $this->mount
677
-					],
678
-					$this->encryptionManager,
679
-					$util,
680
-					$this->logger,
681
-					$this->file,
682
-					null,
683
-					$this->keyStore,
684
-					$this->mountManager,
685
-					$this->arrayCache,
686
-				]
687
-			)
688
-			->setMethods(['readFirstBlock', 'getCache'])
689
-			->getMock();
690
-
691
-		$instance->method('readFirstBlock')->willReturn('');
692
-
693
-		$util->method(('parseRawHeader'))->willReturn($header);
694
-		$instance->method('getCache')->willReturn($cache);
695
-
696
-		$result = $this->invokePrivate($instance, 'getHeader', ['test.txt']);
697
-		$this->assertSameSize($expected, $result);
698
-		foreach ($result as $key => $value) {
699
-			$this->assertArrayHasKey($key, $expected);
700
-			$this->assertSame($expected[$key], $value);
701
-		}
702
-	}
703
-
704
-	public function dataTestGetHeaderAddLegacyModule() {
705
-		return [
706
-			[['cipher' => 'AES-128'], true, true, ['cipher' => 'AES-128', Util::HEADER_ENCRYPTION_MODULE_KEY => 'OC_DEFAULT_MODULE']],
707
-			[[], true, false, []],
708
-			[[], true, true, [Util::HEADER_ENCRYPTION_MODULE_KEY => 'OC_DEFAULT_MODULE']],
709
-			[[], false, true, []],
710
-		];
711
-	}
712
-
713
-	public function dataCopyBetweenStorage() {
714
-		return [
715
-			[true, true, true],
716
-			[true, false, false],
717
-			[false, true, false],
718
-			[false, false, false],
719
-		];
720
-	}
721
-
722
-	public function testCopyBetweenStorageMinimumEncryptedVersion(): void {
723
-		$storage2 = $this->createMock(\OC\Files\Storage\Storage::class);
724
-
725
-		$sourceInternalPath = $targetInternalPath = 'file.txt';
726
-		$preserveMtime = $isRename = false;
727
-
728
-		$storage2->expects($this->any())
729
-			->method('fopen')
730
-			->willReturnCallback(function ($path, $mode) {
731
-				$temp = OC::$server->getTempManager();
732
-				return fopen($temp->getTemporaryFile(), $mode);
733
-			});
734
-		$storage2->method('getId')
735
-			->willReturn('stroage2');
736
-		$cache = $this->createMock(ICache::class);
737
-		$cache->expects($this->once())
738
-			->method('get')
739
-			->with($sourceInternalPath)
740
-			->willReturn(['encryptedVersion' => 0]);
741
-		$storage2->expects($this->once())
742
-			->method('getCache')
743
-			->willReturn($cache);
744
-		$this->encryptionManager->expects($this->any())
745
-			->method('isEnabled')
746
-			->willReturn(true);
747
-		global $mockedMountPointEncryptionEnabled;
748
-		$mockedMountPointEncryptionEnabled = true;
749
-
750
-		$expectedCachePut = [
751
-			'encrypted' => true,
752
-		];
753
-		$expectedCachePut['encryptedVersion'] = 1;
754
-
755
-		$this->cache->expects($this->once())
756
-			->method('put')
757
-			->with($sourceInternalPath, $expectedCachePut);
758
-
759
-		$this->invokePrivate($this->instance, 'copyBetweenStorage', [$storage2, $sourceInternalPath, $targetInternalPath, $preserveMtime, $isRename]);
760
-
761
-		$this->assertFalse(false);
762
-	}
763
-
764
-	/**
765
-	 * @dataProvider dataCopyBetweenStorage
766
-	 *
767
-	 * @param bool $encryptionEnabled
768
-	 * @param bool $mountPointEncryptionEnabled
769
-	 * @param bool $expectedEncrypted
770
-	 */
771
-	public function testCopyBetweenStorage($encryptionEnabled, $mountPointEncryptionEnabled, $expectedEncrypted): void {
772
-		$storage2 = $this->createMock(\OC\Files\Storage\Storage::class);
773
-
774
-		$sourceInternalPath = $targetInternalPath = 'file.txt';
775
-		$preserveMtime = $isRename = false;
776
-
777
-		$storage2->expects($this->any())
778
-			->method('fopen')
779
-			->willReturnCallback(function ($path, $mode) {
780
-				$temp = OC::$server->getTempManager();
781
-				return fopen($temp->getTemporaryFile(), $mode);
782
-			});
783
-		$storage2->method('getId')
784
-			->willReturn('stroage2');
785
-		if ($expectedEncrypted) {
786
-			$cache = $this->createMock(ICache::class);
787
-			$cache->expects($this->once())
788
-				->method('get')
789
-				->with($sourceInternalPath)
790
-				->willReturn(['encryptedVersion' => 12345]);
791
-			$storage2->expects($this->once())
792
-				->method('getCache')
793
-				->willReturn($cache);
794
-		}
795
-		$this->encryptionManager->expects($this->any())
796
-			->method('isEnabled')
797
-			->willReturn($encryptionEnabled);
798
-		// FIXME can not overwrite the return after definition
799
-		//		$this->mount->expects($this->at(0))
800
-		//			->method('getOption')
801
-		//			->with('encrypt', true)
802
-		//			->willReturn($mountPointEncryptionEnabled);
803
-		global $mockedMountPointEncryptionEnabled;
804
-		$mockedMountPointEncryptionEnabled = $mountPointEncryptionEnabled;
805
-
806
-		$expectedCachePut = [
807
-			'encrypted' => $expectedEncrypted,
808
-		];
809
-		if ($expectedEncrypted === true) {
810
-			$expectedCachePut['encryptedVersion'] = 1;
811
-		}
812
-
813
-		$this->arrayCache->expects($this->never())->method('set');
814
-
815
-		$this->cache->expects($this->once())
816
-			->method('put')
817
-			->with($sourceInternalPath, $expectedCachePut);
818
-
819
-		$this->invokePrivate($this->instance, 'copyBetweenStorage', [$storage2, $sourceInternalPath, $targetInternalPath, $preserveMtime, $isRename]);
820
-
821
-		$this->assertFalse(false);
822
-	}
823
-
824
-	/**
825
-	 * @dataProvider dataTestCopyBetweenStorageVersions
826
-	 *
827
-	 * @param string $sourceInternalPath
828
-	 * @param string $targetInternalPath
829
-	 * @param bool $copyResult
830
-	 * @param bool $encrypted
831
-	 */
832
-	public function testCopyBetweenStorageVersions($sourceInternalPath, $targetInternalPath, $copyResult, $encrypted): void {
833
-		$sourceStorage = $this->createMock(\OC\Files\Storage\Storage::class);
834
-
835
-		$targetStorage = $this->createMock(\OC\Files\Storage\Storage::class);
836
-
837
-		$cache = $this->getMockBuilder('\OC\Files\Cache\Cache')
838
-			->disableOriginalConstructor()->getMock();
839
-
840
-		$mountPoint = '/mountPoint';
841
-
842
-		/** @var Encryption |MockObject $instance */
843
-		$instance = $this->getMockBuilder(Encryption::class)
844
-			->setConstructorArgs(
845
-				[
846
-					[
847
-						'storage' => $targetStorage,
848
-						'root' => 'foo',
849
-						'mountPoint' => $mountPoint,
850
-						'mount' => $this->mount
851
-					],
852
-					$this->encryptionManager,
853
-					$this->util,
854
-					$this->logger,
855
-					$this->file,
856
-					null,
857
-					$this->keyStore,
858
-					$this->mountManager,
859
-					$this->arrayCache
860
-				]
861
-			)
862
-			->setMethods(['updateUnencryptedSize', 'getCache'])
863
-			->getMock();
864
-
865
-		$targetStorage->expects($this->once())->method('copyFromStorage')
866
-			->with($sourceStorage, $sourceInternalPath, $targetInternalPath)
867
-			->willReturn($copyResult);
868
-
869
-		$instance->expects($this->any())->method('getCache')
870
-			->willReturn($cache);
871
-
872
-		$this->arrayCache->expects($this->once())->method('set')
873
-			->with('encryption_copy_version_' . $sourceInternalPath, true);
874
-
875
-		if ($copyResult) {
876
-			$cache->expects($this->once())->method('get')
877
-				->with($sourceInternalPath)
878
-				->willReturn(new CacheEntry(['encrypted' => $encrypted, 'size' => 42]));
879
-			if ($encrypted) {
880
-				$instance->expects($this->once())->method('updateUnencryptedSize')
881
-					->with($mountPoint . $targetInternalPath, 42);
882
-			} else {
883
-				$instance->expects($this->never())->method('updateUnencryptedSize');
884
-			}
885
-		} else {
886
-			$instance->expects($this->never())->method('updateUnencryptedSize');
887
-		}
888
-
889
-		$result = $this->invokePrivate(
890
-			$instance,
891
-			'copyBetweenStorage',
892
-			[
893
-				$sourceStorage,
894
-				$sourceInternalPath,
895
-				$targetInternalPath,
896
-				false,
897
-				false
898
-			]
899
-		);
900
-
901
-		$this->assertSame($copyResult, $result);
902
-	}
903
-
904
-	public function dataTestCopyBetweenStorageVersions() {
905
-		return [
906
-			['/files/foo.txt', '/files_versions/foo.txt.768743', true, true],
907
-			['/files/foo.txt', '/files_versions/foo.txt.768743', true, false],
908
-			['/files/foo.txt', '/files_versions/foo.txt.768743', false, true],
909
-			['/files/foo.txt', '/files_versions/foo.txt.768743', false, false],
910
-			['/files_versions/foo.txt.6487634', '/files/foo.txt', true, true],
911
-			['/files_versions/foo.txt.6487634', '/files/foo.txt', true, false],
912
-			['/files_versions/foo.txt.6487634', '/files/foo.txt', false, true],
913
-			['/files_versions/foo.txt.6487634', '/files/foo.txt', false, false],
914
-
915
-		];
916
-	}
917
-
918
-	/**
919
-	 * @dataProvider dataTestIsVersion
920
-	 * @param string $path
921
-	 * @param bool $expected
922
-	 */
923
-	public function testIsVersion($path, $expected): void {
924
-		$this->assertSame($expected,
925
-			$this->invokePrivate($this->instance, 'isVersion', [$path])
926
-		);
927
-	}
928
-
929
-	public function dataTestIsVersion() {
930
-		return [
931
-			['files_versions/foo', true],
932
-			['/files_versions/foo', true],
933
-			['//files_versions/foo', true],
934
-			['files/versions/foo', false],
935
-			['files/files_versions/foo', false],
936
-			['files_versions_test/foo', false],
937
-		];
938
-	}
939
-
940
-	/**
941
-	 * @dataProvider dataTestShouldEncrypt
942
-	 *
943
-	 * @param bool $encryptMountPoint
944
-	 * @param mixed $encryptionModule
945
-	 * @param bool $encryptionModuleShouldEncrypt
946
-	 * @param bool $expected
947
-	 */
948
-	public function testShouldEncrypt(
949
-		$encryptMountPoint,
950
-		$encryptionModule,
951
-		$encryptionModuleShouldEncrypt,
952
-		$expected,
953
-	): void {
954
-		$encryptionManager = $this->createMock(\OC\Encryption\Manager::class);
955
-		$util = $this->createMock(Util::class);
956
-		$fileHelper = $this->createMock(IFile::class);
957
-		$keyStorage = $this->createMock(IStorage::class);
958
-		$mountManager = $this->createMock(\OC\Files\Mount\Manager::class);
959
-		$mount = $this->createMock(IMountPoint::class);
960
-		$arrayCache = $this->createMock(ArrayCache::class);
961
-		$path = '/welcome.txt';
962
-		$fullPath = 'admin/files/welcome.txt';
963
-		$defaultEncryptionModule = $this->createMock(IEncryptionModule::class);
964
-
965
-		$wrapper = $this->getMockBuilder(Encryption::class)
966
-			->setConstructorArgs(
967
-				[
968
-					['mountPoint' => '', 'mount' => $mount, 'storage' => ''],
969
-					$encryptionManager,
970
-					$util,
971
-					$this->logger,
972
-					$fileHelper,
973
-					null,
974
-					$keyStorage,
975
-					$mountManager,
976
-					$arrayCache
977
-				]
978
-			)
979
-			->setMethods(['getFullPath', 'getEncryptionModule'])
980
-			->getMock();
981
-
982
-		if ($encryptionModule === true) {
983
-			/** @var IEncryptionModule|MockObject $encryptionModule */
984
-			$encryptionModule = $this->createMock(IEncryptionModule::class);
985
-		}
986
-
987
-		$wrapper->method('getFullPath')->with($path)->willReturn($fullPath);
988
-		$wrapper->expects($encryptMountPoint ? $this->once() : $this->never())
989
-			->method('getEncryptionModule')
990
-			->with($fullPath)
991
-			->willReturnCallback(
992
-				function () use ($encryptionModule) {
993
-					if ($encryptionModule === false) {
994
-						throw new ModuleDoesNotExistsException();
995
-					}
996
-					return $encryptionModule;
997
-				}
998
-			);
999
-		$mount->expects($this->once())->method('getOption')->with('encrypt', true)
1000
-			->willReturn($encryptMountPoint);
1001
-
1002
-		if ($encryptionModule !== null && $encryptionModule !== false) {
1003
-			$encryptionModule
1004
-				->method('shouldEncrypt')
1005
-				->with($fullPath)
1006
-				->willReturn($encryptionModuleShouldEncrypt);
1007
-		}
1008
-
1009
-		if ($encryptionModule === null) {
1010
-			$encryptionManager->expects($this->once())
1011
-				->method('getEncryptionModule')
1012
-				->willReturn($defaultEncryptionModule);
1013
-		}
1014
-		$defaultEncryptionModule->method('shouldEncrypt')->willReturn(true);
1015
-
1016
-		$result = $this->invokePrivate($wrapper, 'shouldEncrypt', [$path]);
1017
-
1018
-		$this->assertSame($expected, $result);
1019
-	}
1020
-
1021
-	public function dataTestShouldEncrypt() {
1022
-		return [
1023
-			[false, false, false, false],
1024
-			[true, false, false, false],
1025
-			[true, true, false, false],
1026
-			[true, true, true, true],
1027
-			[true, null, false, true],
1028
-		];
1029
-	}
36
+    /**
37
+     * block size will always be 8192 for a PHP stream
38
+     * @see https://bugs.php.net/bug.php?id=21641
39
+     */
40
+    protected int $headerSize = 8192;
41
+    private Temporary $sourceStorage;
42
+    /** @var Encryption&MockObject */
43
+    protected $instance;
44
+    private \OC\Encryption\Keys\Storage&MockObject $keyStore;
45
+    private Util&MockObject $util;
46
+    private \OC\Encryption\Manager&MockObject $encryptionManager;
47
+    private IEncryptionModule&MockObject $encryptionModule;
48
+    private Cache&MockObject $cache;
49
+    private LoggerInterface&MockObject $logger;
50
+    private File&MockObject $file;
51
+    private MountPoint&MockObject $mount;
52
+    private \OC\Files\Mount\Manager&MockObject $mountManager;
53
+    private \OC\Group\Manager&MockObject $groupManager;
54
+    private IConfig&MockObject $config;
55
+    private ArrayCache&MockObject $arrayCache;
56
+    /** dummy unencrypted size */
57
+    private int $dummySize = -1;
58
+
59
+    protected function setUp(): void {
60
+        parent::setUp();
61
+
62
+        $mockModule = $this->buildMockModule();
63
+        $this->encryptionManager = $this->getMockBuilder('\OC\Encryption\Manager')
64
+            ->disableOriginalConstructor()
65
+            ->setMethods(['getEncryptionModule', 'isEnabled'])
66
+            ->getMock();
67
+        $this->encryptionManager->expects($this->any())
68
+            ->method('getEncryptionModule')
69
+            ->willReturn($mockModule);
70
+
71
+        $this->arrayCache = $this->createMock(ArrayCache::class);
72
+        $this->config = $this->getMockBuilder(IConfig::class)
73
+            ->disableOriginalConstructor()
74
+            ->getMock();
75
+        $this->groupManager = $this->getMockBuilder('\OC\Group\Manager')
76
+            ->disableOriginalConstructor()
77
+            ->getMock();
78
+
79
+        $this->util = $this->getMockBuilder('\OC\Encryption\Util')
80
+            ->setMethods(['getUidAndFilename', 'isFile', 'isExcluded', 'stripPartialFileExtension'])
81
+            ->setConstructorArgs([new View(), new Manager(
82
+                $this->config,
83
+                $this->createMock(ICacheFactory::class),
84
+                $this->createMock(IEventDispatcher::class),
85
+                $this->createMock(LoggerInterface::class),
86
+            ), $this->groupManager, $this->config, $this->arrayCache])
87
+            ->getMock();
88
+        $this->util->expects($this->any())
89
+            ->method('getUidAndFilename')
90
+            ->willReturnCallback(function ($path) {
91
+                return ['user1', $path];
92
+            });
93
+        $this->util->expects($this->any())
94
+            ->method('stripPartialFileExtension')
95
+            ->willReturnCallback(function ($path) {
96
+                return $path;
97
+            });
98
+
99
+        $this->file = $this->getMockBuilder('\OC\Encryption\File')
100
+            ->disableOriginalConstructor()
101
+            ->setMethods(['getAccessList'])
102
+            ->getMock();
103
+        $this->file->expects($this->any())->method('getAccessList')->willReturn([]);
104
+
105
+        $this->logger = $this->createMock(LoggerInterface::class);
106
+
107
+        $this->sourceStorage = new Temporary([]);
108
+
109
+        $this->keyStore = $this->getMockBuilder('\OC\Encryption\Keys\Storage')
110
+            ->disableOriginalConstructor()->getMock();
111
+
112
+        $this->mount = $this->getMockBuilder('\OC\Files\Mount\MountPoint')
113
+            ->disableOriginalConstructor()
114
+            ->setMethods(['getOption'])
115
+            ->getMock();
116
+        $this->mount->expects($this->any())->method('getOption')->willReturnCallback(function ($option, $default) {
117
+            if ($option === 'encrypt' && $default === true) {
118
+                global $mockedMountPointEncryptionEnabled;
119
+                if ($mockedMountPointEncryptionEnabled !== null) {
120
+                    return $mockedMountPointEncryptionEnabled;
121
+                }
122
+            }
123
+            return true;
124
+        });
125
+
126
+        $this->cache = $this->getMockBuilder('\OC\Files\Cache\Cache')
127
+            ->disableOriginalConstructor()->getMock();
128
+        $this->cache->expects($this->any())
129
+            ->method('get')
130
+            ->willReturnCallback(function ($path) {
131
+                return ['encrypted' => false, 'path' => $path];
132
+            });
133
+
134
+        $this->mountManager = $this->createMock(\OC\Files\Mount\Manager::class);
135
+        $this->mountManager->method('findByStorageId')
136
+            ->willReturn([]);
137
+
138
+        $this->instance = $this->getMockBuilder(Encryption::class)
139
+            ->setConstructorArgs(
140
+                [
141
+                    [
142
+                        'storage' => $this->sourceStorage,
143
+                        'root' => 'foo',
144
+                        'mountPoint' => '/',
145
+                        'mount' => $this->mount
146
+                    ],
147
+                    $this->encryptionManager,
148
+                    $this->util,
149
+                    $this->logger,
150
+                    $this->file,
151
+                    null,
152
+                    $this->keyStore,
153
+                    $this->mountManager,
154
+                    $this->arrayCache
155
+                ]
156
+            )
157
+            ->setMethods(['getMetaData', 'getCache', 'getEncryptionModule'])
158
+            ->getMock();
159
+
160
+        $this->instance->expects($this->any())
161
+            ->method('getMetaData')
162
+            ->willReturnCallback(function ($path) {
163
+                return ['encrypted' => true, 'size' => $this->dummySize, 'path' => $path];
164
+            });
165
+
166
+        $this->instance->expects($this->any())
167
+            ->method('getCache')
168
+            ->willReturn($this->cache);
169
+
170
+        $this->instance->expects($this->any())
171
+            ->method('getEncryptionModule')
172
+            ->willReturn($mockModule);
173
+    }
174
+
175
+    protected function buildMockModule(): IEncryptionModule&MockObject {
176
+        $this->encryptionModule = $this->getMockBuilder('\OCP\Encryption\IEncryptionModule')
177
+            ->disableOriginalConstructor()
178
+            ->setMethods(['getId', 'getDisplayName', 'begin', 'end', 'encrypt', 'decrypt', 'update', 'shouldEncrypt', 'getUnencryptedBlockSize', 'isReadable', 'encryptAll', 'prepareDecryptAll', 'isReadyForUser', 'needDetailedAccessList'])
179
+            ->getMock();
180
+
181
+        $this->encryptionModule->expects($this->any())->method('getId')->willReturn('UNIT_TEST_MODULE');
182
+        $this->encryptionModule->expects($this->any())->method('getDisplayName')->willReturn('Unit test module');
183
+        $this->encryptionModule->expects($this->any())->method('begin')->willReturn([]);
184
+        $this->encryptionModule->expects($this->any())->method('end')->willReturn('');
185
+        $this->encryptionModule->expects($this->any())->method('encrypt')->willReturnArgument(0);
186
+        $this->encryptionModule->expects($this->any())->method('decrypt')->willReturnArgument(0);
187
+        $this->encryptionModule->expects($this->any())->method('update')->willReturn(true);
188
+        $this->encryptionModule->expects($this->any())->method('shouldEncrypt')->willReturn(true);
189
+        $this->encryptionModule->expects($this->any())->method('getUnencryptedBlockSize')->willReturn(8192);
190
+        $this->encryptionModule->expects($this->any())->method('isReadable')->willReturn(true);
191
+        $this->encryptionModule->expects($this->any())->method('needDetailedAccessList')->willReturn(false);
192
+        return $this->encryptionModule;
193
+    }
194
+
195
+    /**
196
+     * @dataProvider dataTestGetMetaData
197
+     *
198
+     * @param string $path
199
+     * @param array $metaData
200
+     * @param bool $encrypted
201
+     * @param bool $unencryptedSizeSet
202
+     * @param int $storedUnencryptedSize
203
+     * @param array $expected
204
+     */
205
+    public function testGetMetaData($path, $metaData, $encrypted, $unencryptedSizeSet, $storedUnencryptedSize, $expected): void {
206
+        $sourceStorage = $this->getMockBuilder('\OC\Files\Storage\Storage')
207
+            ->disableOriginalConstructor()->getMock();
208
+
209
+        $cache = $this->getMockBuilder('\OC\Files\Cache\Cache')
210
+            ->disableOriginalConstructor()->getMock();
211
+        $cache->expects($this->any())
212
+            ->method('get')
213
+            ->willReturnCallback(
214
+                function ($path) use ($encrypted) {
215
+                    return new CacheEntry(['encrypted' => $encrypted, 'path' => $path, 'size' => 0, 'fileid' => 1]);
216
+                }
217
+            );
218
+
219
+        $this->instance = $this->getMockBuilder(Encryption::class)
220
+            ->setConstructorArgs(
221
+                [
222
+                    [
223
+                        'storage' => $sourceStorage,
224
+                        'root' => 'foo',
225
+                        'mountPoint' => '/',
226
+                        'mount' => $this->mount
227
+                    ],
228
+                    $this->encryptionManager,
229
+                    $this->util,
230
+                    $this->logger,
231
+                    $this->file,
232
+                    null,
233
+                    $this->keyStore,
234
+                    $this->mountManager,
235
+                    $this->arrayCache,
236
+                ]
237
+            )
238
+            ->setMethods(['getCache', 'verifyUnencryptedSize'])
239
+            ->getMock();
240
+
241
+        if ($unencryptedSizeSet) {
242
+            $this->invokePrivate($this->instance, 'unencryptedSize', [[$path => $storedUnencryptedSize]]);
243
+        }
244
+
245
+        $fileEntry = $this->getMockBuilder('\OC\Files\Cache\Cache')
246
+            ->disableOriginalConstructor()->getMock();
247
+        $sourceStorage->expects($this->once())->method('getMetaData')->with($path)
248
+            ->willReturn($metaData);
249
+        $sourceStorage->expects($this->any())
250
+            ->method('getCache')
251
+            ->with($path)
252
+            ->willReturn($fileEntry);
253
+        if ($metaData !== null) {
254
+            $fileEntry->expects($this->any())
255
+                ->method('get')
256
+                ->with($metaData['fileid']);
257
+        }
258
+
259
+        $this->instance->expects($this->any())->method('getCache')->willReturn($cache);
260
+        if ($expected !== null) {
261
+            $this->instance->expects($this->any())->method('verifyUnencryptedSize')
262
+                ->with($path, 0)->willReturn($expected['size']);
263
+        }
264
+
265
+        $result = $this->instance->getMetaData($path);
266
+        if (isset($expected['encrypted'])) {
267
+            $this->assertSame($expected['encrypted'], (bool)$result['encrypted']);
268
+
269
+            if (isset($expected['encryptedVersion'])) {
270
+                $this->assertSame($expected['encryptedVersion'], $result['encryptedVersion']);
271
+            }
272
+        }
273
+
274
+        if ($expected !== null) {
275
+            $this->assertSame($expected['size'], $result['size']);
276
+        } else {
277
+            $this->assertSame(null, $result);
278
+        }
279
+    }
280
+
281
+    public function dataTestGetMetaData() {
282
+        return [
283
+            ['/test.txt', ['size' => 42, 'encrypted' => 2, 'encryptedVersion' => 2, 'fileid' => 1], true, true, 12, ['size' => 12, 'encrypted' => true, 'encryptedVersion' => 2]],
284
+            ['/test.txt', null, true, true, 12, null],
285
+            ['/test.txt', ['size' => 42, 'encrypted' => 0, 'fileid' => 1], false, false, 12, ['size' => 42, 'encrypted' => false]],
286
+            ['/test.txt', ['size' => 42, 'encrypted' => false, 'fileid' => 1], true, false, 12, ['size' => 12, 'encrypted' => true]]
287
+        ];
288
+    }
289
+
290
+    public function testFilesize(): void {
291
+        $cache = $this->getMockBuilder('\OC\Files\Cache\Cache')
292
+            ->disableOriginalConstructor()->getMock();
293
+        $cache->expects($this->any())
294
+            ->method('get')
295
+            ->willReturn(new CacheEntry(['encrypted' => true, 'path' => '/test.txt', 'size' => 0, 'fileid' => 1]));
296
+
297
+        $this->instance = $this->getMockBuilder(Encryption::class)
298
+            ->setConstructorArgs(
299
+                [
300
+                    [
301
+                        'storage' => $this->sourceStorage,
302
+                        'root' => 'foo',
303
+                        'mountPoint' => '/',
304
+                        'mount' => $this->mount
305
+                    ],
306
+                    $this->encryptionManager,
307
+                    $this->util,
308
+                    $this->logger,
309
+                    $this->file,
310
+                    null,
311
+                    $this->keyStore,
312
+                    $this->mountManager,
313
+                    $this->arrayCache,
314
+                ]
315
+            )
316
+            ->setMethods(['getCache', 'verifyUnencryptedSize'])
317
+            ->getMock();
318
+
319
+        $this->instance->expects($this->any())->method('getCache')->willReturn($cache);
320
+        $this->instance->expects($this->any())->method('verifyUnencryptedSize')
321
+            ->willReturn(42);
322
+
323
+
324
+        $this->assertSame(42,
325
+            $this->instance->filesize('/test.txt')
326
+        );
327
+    }
328
+
329
+    /**
330
+     * @dataProvider dataTestVerifyUnencryptedSize
331
+     *
332
+     * @param int $encryptedSize
333
+     * @param int $unencryptedSize
334
+     * @param bool $failure
335
+     * @param int $expected
336
+     */
337
+    public function testVerifyUnencryptedSize($encryptedSize, $unencryptedSize, $failure, $expected): void {
338
+        $sourceStorage = $this->getMockBuilder('\OC\Files\Storage\Storage')
339
+            ->disableOriginalConstructor()->getMock();
340
+
341
+        $this->instance = $this->getMockBuilder(Encryption::class)
342
+            ->setConstructorArgs(
343
+                [
344
+                    [
345
+                        'storage' => $sourceStorage,
346
+                        'root' => 'foo',
347
+                        'mountPoint' => '/',
348
+                        'mount' => $this->mount
349
+                    ],
350
+                    $this->encryptionManager,
351
+                    $this->util,
352
+                    $this->logger,
353
+                    $this->file,
354
+                    null,
355
+                    $this->keyStore,
356
+                    $this->mountManager,
357
+                    $this->arrayCache,
358
+                ]
359
+            )
360
+            ->setMethods(['fixUnencryptedSize'])
361
+            ->getMock();
362
+
363
+        $sourceStorage->expects($this->once())->method('filesize')->willReturn($encryptedSize);
364
+
365
+        $this->instance->expects($this->any())->method('fixUnencryptedSize')
366
+            ->with('/test.txt', $encryptedSize, $unencryptedSize)
367
+            ->willReturnCallback(
368
+                function () use ($failure, $expected) {
369
+                    if ($failure) {
370
+                        throw new Exception();
371
+                    } else {
372
+                        return $expected;
373
+                    }
374
+                }
375
+            );
376
+
377
+        $this->assertSame(
378
+            $expected,
379
+            $this->invokePrivate($this->instance, 'verifyUnencryptedSize', ['/test.txt', $unencryptedSize])
380
+        );
381
+    }
382
+
383
+    public function dataTestVerifyUnencryptedSize() {
384
+        return [
385
+            [120, 80, false, 80],
386
+            [120, 120, false, 80],
387
+            [120, -1, false, 80],
388
+            [120, -1, true, -1]
389
+        ];
390
+    }
391
+
392
+    /**
393
+     * @dataProvider dataTestCopyAndRename
394
+     *
395
+     * @param string $source
396
+     * @param string $target
397
+     * @param $encryptionEnabled
398
+     * @param boolean $renameKeysReturn
399
+     */
400
+    public function testRename($source,
401
+        $target,
402
+        $encryptionEnabled,
403
+        $renameKeysReturn): void {
404
+        if ($encryptionEnabled) {
405
+            $this->keyStore
406
+                ->expects($this->once())
407
+                ->method('renameKeys')
408
+                ->willReturn($renameKeysReturn);
409
+        } else {
410
+            $this->keyStore
411
+                ->expects($this->never())->method('renameKeys');
412
+        }
413
+        $this->util->expects($this->any())
414
+            ->method('isFile')->willReturn(true);
415
+        $this->encryptionManager->expects($this->once())
416
+            ->method('isEnabled')->willReturn($encryptionEnabled);
417
+
418
+        $this->instance->mkdir($source);
419
+        $this->instance->mkdir(dirname($target));
420
+        $this->instance->rename($source, $target);
421
+    }
422
+
423
+    public function testCopyEncryption(): void {
424
+        $this->instance->file_put_contents('source.txt', 'bar');
425
+        $this->instance->copy('source.txt', 'target.txt');
426
+        $this->assertSame('bar', $this->instance->file_get_contents('target.txt'));
427
+        $targetMeta = $this->instance->getMetaData('target.txt');
428
+        $sourceMeta = $this->instance->getMetaData('source.txt');
429
+        $this->assertSame($sourceMeta['encrypted'], $targetMeta['encrypted']);
430
+        $this->assertSame($sourceMeta['size'], $targetMeta['size']);
431
+    }
432
+
433
+    /**
434
+     * data provider for testCopyTesting() and dataTestCopyAndRename()
435
+     *
436
+     * @return array
437
+     */
438
+    public function dataTestCopyAndRename() {
439
+        return [
440
+            ['source', 'target', true, false, false],
441
+            ['source', 'target', true, true, false],
442
+            ['source', '/subFolder/target', true, false, false],
443
+            ['source', '/subFolder/target', true, true, true],
444
+            ['source', '/subFolder/target', false, true, false],
445
+        ];
446
+    }
447
+
448
+    public function testIsLocal(): void {
449
+        $this->encryptionManager->expects($this->once())
450
+            ->method('isEnabled')->willReturn(true);
451
+        $this->assertFalse($this->instance->isLocal());
452
+    }
453
+
454
+    /**
455
+     * @dataProvider dataTestRmdir
456
+     *
457
+     * @param string $path
458
+     * @param boolean $rmdirResult
459
+     * @param boolean $isExcluded
460
+     * @param boolean $encryptionEnabled
461
+     */
462
+    public function testRmdir($path, $rmdirResult, $isExcluded, $encryptionEnabled): void {
463
+        $sourceStorage = $this->getMockBuilder('\OC\Files\Storage\Storage')
464
+            ->disableOriginalConstructor()->getMock();
465
+
466
+        $util = $this->getMockBuilder('\OC\Encryption\Util')->disableOriginalConstructor()->getMock();
467
+
468
+        $sourceStorage->expects($this->once())->method('rmdir')->willReturn($rmdirResult);
469
+        $util->expects($this->any())->method('isExcluded')->willReturn($isExcluded);
470
+        $this->encryptionManager->expects($this->any())->method('isEnabled')->willReturn($encryptionEnabled);
471
+
472
+        $encryptionStorage = new Encryption(
473
+            [
474
+                'storage' => $sourceStorage,
475
+                'root' => 'foo',
476
+                'mountPoint' => '/mountPoint',
477
+                'mount' => $this->mount
478
+            ],
479
+            $this->encryptionManager,
480
+            $util,
481
+            $this->logger,
482
+            $this->file,
483
+            null,
484
+            $this->keyStore,
485
+            $this->mountManager,
486
+            $this->arrayCache,
487
+        );
488
+
489
+
490
+        if ($rmdirResult === true && $isExcluded === false && $encryptionEnabled === true) {
491
+            $this->keyStore->expects($this->once())->method('deleteAllFileKeys')->with('/mountPoint' . $path);
492
+        } else {
493
+            $this->keyStore->expects($this->never())->method('deleteAllFileKeys');
494
+        }
495
+
496
+        $encryptionStorage->rmdir($path);
497
+    }
498
+
499
+    public function dataTestRmdir() {
500
+        return [
501
+            ['/file.txt', true, true, true],
502
+            ['/file.txt', false, true, true],
503
+            ['/file.txt', true, false, true],
504
+            ['/file.txt', false, false, true],
505
+            ['/file.txt', true, true, false],
506
+            ['/file.txt', false, true, false],
507
+            ['/file.txt', true, false, false],
508
+            ['/file.txt', false, false, false],
509
+        ];
510
+    }
511
+
512
+    /**
513
+     * @dataProvider dataTestCopyKeys
514
+     *
515
+     * @param boolean $excluded
516
+     * @param boolean $expected
517
+     */
518
+    public function testCopyKeys($excluded, $expected): void {
519
+        $this->util->expects($this->once())
520
+            ->method('isExcluded')
521
+            ->willReturn($excluded);
522
+
523
+        if ($excluded) {
524
+            $this->keyStore->expects($this->never())->method('copyKeys');
525
+        } else {
526
+            $this->keyStore->expects($this->once())->method('copyKeys')->willReturn(true);
527
+        }
528
+
529
+        $this->assertSame($expected,
530
+            self::invokePrivate($this->instance, 'copyKeys', ['/source', '/target'])
531
+        );
532
+    }
533
+
534
+    public function dataTestCopyKeys() {
535
+        return [
536
+            [true, false],
537
+            [false, true],
538
+        ];
539
+    }
540
+
541
+    /**
542
+     * @dataProvider dataTestGetHeader
543
+     *
544
+     * @param string $path
545
+     * @param bool $strippedPathExists
546
+     * @param string $strippedPath
547
+     */
548
+    public function testGetHeader($path, $strippedPathExists, $strippedPath): void {
549
+        $sourceStorage = $this->getMockBuilder('\OC\Files\Storage\Storage')
550
+            ->disableOriginalConstructor()->getMock();
551
+
552
+        $util = $this->getMockBuilder('\OC\Encryption\Util')
553
+            ->setConstructorArgs(
554
+                [
555
+                    new View(),
556
+                    new Manager(
557
+                        $this->config,
558
+                        $this->createMock(ICacheFactory::class),
559
+                        $this->createMock(IEventDispatcher::class),
560
+                        $this->createMock(LoggerInterface::class),
561
+                    ),
562
+                    $this->groupManager,
563
+                    $this->config,
564
+                    $this->arrayCache
565
+                ]
566
+            )->getMock();
567
+
568
+        $cache = $this->getMockBuilder('\OC\Files\Cache\Cache')
569
+            ->disableOriginalConstructor()->getMock();
570
+        $cache->expects($this->any())
571
+            ->method('get')
572
+            ->willReturnCallback(function ($path) {
573
+                return ['encrypted' => true, 'path' => $path];
574
+            });
575
+
576
+        $instance = $this->getMockBuilder(Encryption::class)
577
+            ->setConstructorArgs(
578
+                [
579
+                    [
580
+                        'storage' => $sourceStorage,
581
+                        'root' => 'foo',
582
+                        'mountPoint' => '/',
583
+                        'mount' => $this->mount
584
+                    ],
585
+                    $this->encryptionManager,
586
+                    $util,
587
+                    $this->logger,
588
+                    $this->file,
589
+                    null,
590
+                    $this->keyStore,
591
+                    $this->mountManager,
592
+                    $this->arrayCache,
593
+                ]
594
+            )
595
+            ->setMethods(['getCache', 'readFirstBlock'])
596
+            ->getMock();
597
+
598
+        $instance->method('getCache')->willReturn($cache);
599
+
600
+        $util->method('parseRawHeader')
601
+            ->willReturn([Util::HEADER_ENCRYPTION_MODULE_KEY => 'OC_DEFAULT_MODULE']);
602
+
603
+        if ($strippedPathExists) {
604
+            $instance->method('readFirstBlock')
605
+                ->with($strippedPath)->willReturn('');
606
+        } else {
607
+            $instance->method('readFirstBlock')
608
+                ->with($path)->willReturn('');
609
+        }
610
+
611
+        $util->expects($this->once())->method('stripPartialFileExtension')
612
+            ->with($path)->willReturn($strippedPath);
613
+        $sourceStorage->expects($this->once())
614
+            ->method('is_file')
615
+            ->with($strippedPath)
616
+            ->willReturn($strippedPathExists);
617
+
618
+        $this->invokePrivate($instance, 'getHeader', [$path]);
619
+    }
620
+
621
+    public function dataTestGetHeader() {
622
+        return [
623
+            ['/foo/bar.txt', false, '/foo/bar.txt'],
624
+            ['/foo/bar.txt.part', false, '/foo/bar.txt'],
625
+            ['/foo/bar.txt.ocTransferId7437493.part', false, '/foo/bar.txt'],
626
+            ['/foo/bar.txt.part', true, '/foo/bar.txt'],
627
+            ['/foo/bar.txt.ocTransferId7437493.part', true, '/foo/bar.txt'],
628
+        ];
629
+    }
630
+
631
+    /**
632
+     * test if getHeader adds the default module correctly to the header for
633
+     * legacy files
634
+     *
635
+     * @dataProvider dataTestGetHeaderAddLegacyModule
636
+     */
637
+    public function testGetHeaderAddLegacyModule($header, $isEncrypted, $strippedPathExists, $expected): void {
638
+        $sourceStorage = $this->getMockBuilder('\OC\Files\Storage\Storage')
639
+            ->disableOriginalConstructor()->getMock();
640
+
641
+        $sourceStorage->expects($this->once())
642
+            ->method('is_file')
643
+            ->with('test.txt')
644
+            ->willReturn($strippedPathExists);
645
+
646
+        $util = $this->getMockBuilder('\OC\Encryption\Util')
647
+            ->onlyMethods(['stripPartialFileExtension', 'parseRawHeader'])
648
+            ->setConstructorArgs([new View(), new Manager(
649
+                $this->config,
650
+                $this->createMock(ICacheFactory::class),
651
+                $this->createMock(IEventDispatcher::class),
652
+                $this->createMock(LoggerInterface::class),
653
+            ), $this->groupManager, $this->config, $this->arrayCache])
654
+            ->getMock();
655
+        $util->expects($this->any())
656
+            ->method('stripPartialFileExtension')
657
+            ->willReturnCallback(function ($path) {
658
+                return $path;
659
+            });
660
+
661
+        $cache = $this->getMockBuilder('\OC\Files\Cache\Cache')
662
+            ->disableOriginalConstructor()->getMock();
663
+        $cache->expects($this->any())
664
+            ->method('get')
665
+            ->willReturnCallback(function ($path) use ($isEncrypted) {
666
+                return ['encrypted' => $isEncrypted, 'path' => $path];
667
+            });
668
+
669
+        $instance = $this->getMockBuilder(Encryption::class)
670
+            ->setConstructorArgs(
671
+                [
672
+                    [
673
+                        'storage' => $sourceStorage,
674
+                        'root' => 'foo',
675
+                        'mountPoint' => '/',
676
+                        'mount' => $this->mount
677
+                    ],
678
+                    $this->encryptionManager,
679
+                    $util,
680
+                    $this->logger,
681
+                    $this->file,
682
+                    null,
683
+                    $this->keyStore,
684
+                    $this->mountManager,
685
+                    $this->arrayCache,
686
+                ]
687
+            )
688
+            ->setMethods(['readFirstBlock', 'getCache'])
689
+            ->getMock();
690
+
691
+        $instance->method('readFirstBlock')->willReturn('');
692
+
693
+        $util->method(('parseRawHeader'))->willReturn($header);
694
+        $instance->method('getCache')->willReturn($cache);
695
+
696
+        $result = $this->invokePrivate($instance, 'getHeader', ['test.txt']);
697
+        $this->assertSameSize($expected, $result);
698
+        foreach ($result as $key => $value) {
699
+            $this->assertArrayHasKey($key, $expected);
700
+            $this->assertSame($expected[$key], $value);
701
+        }
702
+    }
703
+
704
+    public function dataTestGetHeaderAddLegacyModule() {
705
+        return [
706
+            [['cipher' => 'AES-128'], true, true, ['cipher' => 'AES-128', Util::HEADER_ENCRYPTION_MODULE_KEY => 'OC_DEFAULT_MODULE']],
707
+            [[], true, false, []],
708
+            [[], true, true, [Util::HEADER_ENCRYPTION_MODULE_KEY => 'OC_DEFAULT_MODULE']],
709
+            [[], false, true, []],
710
+        ];
711
+    }
712
+
713
+    public function dataCopyBetweenStorage() {
714
+        return [
715
+            [true, true, true],
716
+            [true, false, false],
717
+            [false, true, false],
718
+            [false, false, false],
719
+        ];
720
+    }
721
+
722
+    public function testCopyBetweenStorageMinimumEncryptedVersion(): void {
723
+        $storage2 = $this->createMock(\OC\Files\Storage\Storage::class);
724
+
725
+        $sourceInternalPath = $targetInternalPath = 'file.txt';
726
+        $preserveMtime = $isRename = false;
727
+
728
+        $storage2->expects($this->any())
729
+            ->method('fopen')
730
+            ->willReturnCallback(function ($path, $mode) {
731
+                $temp = OC::$server->getTempManager();
732
+                return fopen($temp->getTemporaryFile(), $mode);
733
+            });
734
+        $storage2->method('getId')
735
+            ->willReturn('stroage2');
736
+        $cache = $this->createMock(ICache::class);
737
+        $cache->expects($this->once())
738
+            ->method('get')
739
+            ->with($sourceInternalPath)
740
+            ->willReturn(['encryptedVersion' => 0]);
741
+        $storage2->expects($this->once())
742
+            ->method('getCache')
743
+            ->willReturn($cache);
744
+        $this->encryptionManager->expects($this->any())
745
+            ->method('isEnabled')
746
+            ->willReturn(true);
747
+        global $mockedMountPointEncryptionEnabled;
748
+        $mockedMountPointEncryptionEnabled = true;
749
+
750
+        $expectedCachePut = [
751
+            'encrypted' => true,
752
+        ];
753
+        $expectedCachePut['encryptedVersion'] = 1;
754
+
755
+        $this->cache->expects($this->once())
756
+            ->method('put')
757
+            ->with($sourceInternalPath, $expectedCachePut);
758
+
759
+        $this->invokePrivate($this->instance, 'copyBetweenStorage', [$storage2, $sourceInternalPath, $targetInternalPath, $preserveMtime, $isRename]);
760
+
761
+        $this->assertFalse(false);
762
+    }
763
+
764
+    /**
765
+     * @dataProvider dataCopyBetweenStorage
766
+     *
767
+     * @param bool $encryptionEnabled
768
+     * @param bool $mountPointEncryptionEnabled
769
+     * @param bool $expectedEncrypted
770
+     */
771
+    public function testCopyBetweenStorage($encryptionEnabled, $mountPointEncryptionEnabled, $expectedEncrypted): void {
772
+        $storage2 = $this->createMock(\OC\Files\Storage\Storage::class);
773
+
774
+        $sourceInternalPath = $targetInternalPath = 'file.txt';
775
+        $preserveMtime = $isRename = false;
776
+
777
+        $storage2->expects($this->any())
778
+            ->method('fopen')
779
+            ->willReturnCallback(function ($path, $mode) {
780
+                $temp = OC::$server->getTempManager();
781
+                return fopen($temp->getTemporaryFile(), $mode);
782
+            });
783
+        $storage2->method('getId')
784
+            ->willReturn('stroage2');
785
+        if ($expectedEncrypted) {
786
+            $cache = $this->createMock(ICache::class);
787
+            $cache->expects($this->once())
788
+                ->method('get')
789
+                ->with($sourceInternalPath)
790
+                ->willReturn(['encryptedVersion' => 12345]);
791
+            $storage2->expects($this->once())
792
+                ->method('getCache')
793
+                ->willReturn($cache);
794
+        }
795
+        $this->encryptionManager->expects($this->any())
796
+            ->method('isEnabled')
797
+            ->willReturn($encryptionEnabled);
798
+        // FIXME can not overwrite the return after definition
799
+        //		$this->mount->expects($this->at(0))
800
+        //			->method('getOption')
801
+        //			->with('encrypt', true)
802
+        //			->willReturn($mountPointEncryptionEnabled);
803
+        global $mockedMountPointEncryptionEnabled;
804
+        $mockedMountPointEncryptionEnabled = $mountPointEncryptionEnabled;
805
+
806
+        $expectedCachePut = [
807
+            'encrypted' => $expectedEncrypted,
808
+        ];
809
+        if ($expectedEncrypted === true) {
810
+            $expectedCachePut['encryptedVersion'] = 1;
811
+        }
812
+
813
+        $this->arrayCache->expects($this->never())->method('set');
814
+
815
+        $this->cache->expects($this->once())
816
+            ->method('put')
817
+            ->with($sourceInternalPath, $expectedCachePut);
818
+
819
+        $this->invokePrivate($this->instance, 'copyBetweenStorage', [$storage2, $sourceInternalPath, $targetInternalPath, $preserveMtime, $isRename]);
820
+
821
+        $this->assertFalse(false);
822
+    }
823
+
824
+    /**
825
+     * @dataProvider dataTestCopyBetweenStorageVersions
826
+     *
827
+     * @param string $sourceInternalPath
828
+     * @param string $targetInternalPath
829
+     * @param bool $copyResult
830
+     * @param bool $encrypted
831
+     */
832
+    public function testCopyBetweenStorageVersions($sourceInternalPath, $targetInternalPath, $copyResult, $encrypted): void {
833
+        $sourceStorage = $this->createMock(\OC\Files\Storage\Storage::class);
834
+
835
+        $targetStorage = $this->createMock(\OC\Files\Storage\Storage::class);
836
+
837
+        $cache = $this->getMockBuilder('\OC\Files\Cache\Cache')
838
+            ->disableOriginalConstructor()->getMock();
839
+
840
+        $mountPoint = '/mountPoint';
841
+
842
+        /** @var Encryption |MockObject $instance */
843
+        $instance = $this->getMockBuilder(Encryption::class)
844
+            ->setConstructorArgs(
845
+                [
846
+                    [
847
+                        'storage' => $targetStorage,
848
+                        'root' => 'foo',
849
+                        'mountPoint' => $mountPoint,
850
+                        'mount' => $this->mount
851
+                    ],
852
+                    $this->encryptionManager,
853
+                    $this->util,
854
+                    $this->logger,
855
+                    $this->file,
856
+                    null,
857
+                    $this->keyStore,
858
+                    $this->mountManager,
859
+                    $this->arrayCache
860
+                ]
861
+            )
862
+            ->setMethods(['updateUnencryptedSize', 'getCache'])
863
+            ->getMock();
864
+
865
+        $targetStorage->expects($this->once())->method('copyFromStorage')
866
+            ->with($sourceStorage, $sourceInternalPath, $targetInternalPath)
867
+            ->willReturn($copyResult);
868
+
869
+        $instance->expects($this->any())->method('getCache')
870
+            ->willReturn($cache);
871
+
872
+        $this->arrayCache->expects($this->once())->method('set')
873
+            ->with('encryption_copy_version_' . $sourceInternalPath, true);
874
+
875
+        if ($copyResult) {
876
+            $cache->expects($this->once())->method('get')
877
+                ->with($sourceInternalPath)
878
+                ->willReturn(new CacheEntry(['encrypted' => $encrypted, 'size' => 42]));
879
+            if ($encrypted) {
880
+                $instance->expects($this->once())->method('updateUnencryptedSize')
881
+                    ->with($mountPoint . $targetInternalPath, 42);
882
+            } else {
883
+                $instance->expects($this->never())->method('updateUnencryptedSize');
884
+            }
885
+        } else {
886
+            $instance->expects($this->never())->method('updateUnencryptedSize');
887
+        }
888
+
889
+        $result = $this->invokePrivate(
890
+            $instance,
891
+            'copyBetweenStorage',
892
+            [
893
+                $sourceStorage,
894
+                $sourceInternalPath,
895
+                $targetInternalPath,
896
+                false,
897
+                false
898
+            ]
899
+        );
900
+
901
+        $this->assertSame($copyResult, $result);
902
+    }
903
+
904
+    public function dataTestCopyBetweenStorageVersions() {
905
+        return [
906
+            ['/files/foo.txt', '/files_versions/foo.txt.768743', true, true],
907
+            ['/files/foo.txt', '/files_versions/foo.txt.768743', true, false],
908
+            ['/files/foo.txt', '/files_versions/foo.txt.768743', false, true],
909
+            ['/files/foo.txt', '/files_versions/foo.txt.768743', false, false],
910
+            ['/files_versions/foo.txt.6487634', '/files/foo.txt', true, true],
911
+            ['/files_versions/foo.txt.6487634', '/files/foo.txt', true, false],
912
+            ['/files_versions/foo.txt.6487634', '/files/foo.txt', false, true],
913
+            ['/files_versions/foo.txt.6487634', '/files/foo.txt', false, false],
914
+
915
+        ];
916
+    }
917
+
918
+    /**
919
+     * @dataProvider dataTestIsVersion
920
+     * @param string $path
921
+     * @param bool $expected
922
+     */
923
+    public function testIsVersion($path, $expected): void {
924
+        $this->assertSame($expected,
925
+            $this->invokePrivate($this->instance, 'isVersion', [$path])
926
+        );
927
+    }
928
+
929
+    public function dataTestIsVersion() {
930
+        return [
931
+            ['files_versions/foo', true],
932
+            ['/files_versions/foo', true],
933
+            ['//files_versions/foo', true],
934
+            ['files/versions/foo', false],
935
+            ['files/files_versions/foo', false],
936
+            ['files_versions_test/foo', false],
937
+        ];
938
+    }
939
+
940
+    /**
941
+     * @dataProvider dataTestShouldEncrypt
942
+     *
943
+     * @param bool $encryptMountPoint
944
+     * @param mixed $encryptionModule
945
+     * @param bool $encryptionModuleShouldEncrypt
946
+     * @param bool $expected
947
+     */
948
+    public function testShouldEncrypt(
949
+        $encryptMountPoint,
950
+        $encryptionModule,
951
+        $encryptionModuleShouldEncrypt,
952
+        $expected,
953
+    ): void {
954
+        $encryptionManager = $this->createMock(\OC\Encryption\Manager::class);
955
+        $util = $this->createMock(Util::class);
956
+        $fileHelper = $this->createMock(IFile::class);
957
+        $keyStorage = $this->createMock(IStorage::class);
958
+        $mountManager = $this->createMock(\OC\Files\Mount\Manager::class);
959
+        $mount = $this->createMock(IMountPoint::class);
960
+        $arrayCache = $this->createMock(ArrayCache::class);
961
+        $path = '/welcome.txt';
962
+        $fullPath = 'admin/files/welcome.txt';
963
+        $defaultEncryptionModule = $this->createMock(IEncryptionModule::class);
964
+
965
+        $wrapper = $this->getMockBuilder(Encryption::class)
966
+            ->setConstructorArgs(
967
+                [
968
+                    ['mountPoint' => '', 'mount' => $mount, 'storage' => ''],
969
+                    $encryptionManager,
970
+                    $util,
971
+                    $this->logger,
972
+                    $fileHelper,
973
+                    null,
974
+                    $keyStorage,
975
+                    $mountManager,
976
+                    $arrayCache
977
+                ]
978
+            )
979
+            ->setMethods(['getFullPath', 'getEncryptionModule'])
980
+            ->getMock();
981
+
982
+        if ($encryptionModule === true) {
983
+            /** @var IEncryptionModule|MockObject $encryptionModule */
984
+            $encryptionModule = $this->createMock(IEncryptionModule::class);
985
+        }
986
+
987
+        $wrapper->method('getFullPath')->with($path)->willReturn($fullPath);
988
+        $wrapper->expects($encryptMountPoint ? $this->once() : $this->never())
989
+            ->method('getEncryptionModule')
990
+            ->with($fullPath)
991
+            ->willReturnCallback(
992
+                function () use ($encryptionModule) {
993
+                    if ($encryptionModule === false) {
994
+                        throw new ModuleDoesNotExistsException();
995
+                    }
996
+                    return $encryptionModule;
997
+                }
998
+            );
999
+        $mount->expects($this->once())->method('getOption')->with('encrypt', true)
1000
+            ->willReturn($encryptMountPoint);
1001
+
1002
+        if ($encryptionModule !== null && $encryptionModule !== false) {
1003
+            $encryptionModule
1004
+                ->method('shouldEncrypt')
1005
+                ->with($fullPath)
1006
+                ->willReturn($encryptionModuleShouldEncrypt);
1007
+        }
1008
+
1009
+        if ($encryptionModule === null) {
1010
+            $encryptionManager->expects($this->once())
1011
+                ->method('getEncryptionModule')
1012
+                ->willReturn($defaultEncryptionModule);
1013
+        }
1014
+        $defaultEncryptionModule->method('shouldEncrypt')->willReturn(true);
1015
+
1016
+        $result = $this->invokePrivate($wrapper, 'shouldEncrypt', [$path]);
1017
+
1018
+        $this->assertSame($expected, $result);
1019
+    }
1020
+
1021
+    public function dataTestShouldEncrypt() {
1022
+        return [
1023
+            [false, false, false, false],
1024
+            [true, false, false, false],
1025
+            [true, true, false, false],
1026
+            [true, true, true, true],
1027
+            [true, null, false, true],
1028
+        ];
1029
+    }
1030 1030
 }
Please login to merge, or discard this patch.
tests/lib/Files/ViewTest.php 2 patches
Indentation   +2834 added lines, -2834 removed lines patch added patch discarded remove patch
@@ -40,40 +40,40 @@  discard block
 block discarded – undo
40 40
 use Test\Traits\UserTrait;
41 41
 
42 42
 class TemporaryNoTouch extends Temporary {
43
-	public function touch(string $path, ?int $mtime = null): bool {
44
-		return false;
45
-	}
43
+    public function touch(string $path, ?int $mtime = null): bool {
44
+        return false;
45
+    }
46 46
 }
47 47
 
48 48
 class TemporaryNoCross extends Temporary {
49
-	public function copyFromStorage(IStorage $sourceStorage, string $sourceInternalPath, string $targetInternalPath, bool $preserveMtime = false): bool {
50
-		return Common::copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime);
51
-	}
49
+    public function copyFromStorage(IStorage $sourceStorage, string $sourceInternalPath, string $targetInternalPath, bool $preserveMtime = false): bool {
50
+        return Common::copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime);
51
+    }
52 52
 
53
-	public function moveFromStorage(IStorage $sourceStorage, string $sourceInternalPath, string $targetInternalPath): bool {
54
-		return Common::moveFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
55
-	}
53
+    public function moveFromStorage(IStorage $sourceStorage, string $sourceInternalPath, string $targetInternalPath): bool {
54
+        return Common::moveFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
55
+    }
56 56
 }
57 57
 
58 58
 class TemporaryNoLocal extends Temporary {
59
-	public function instanceOfStorage(string $class): bool {
60
-		if ($class === '\OC\Files\Storage\Local') {
61
-			return false;
62
-		} else {
63
-			return parent::instanceOfStorage($class);
64
-		}
65
-	}
59
+    public function instanceOfStorage(string $class): bool {
60
+        if ($class === '\OC\Files\Storage\Local') {
61
+            return false;
62
+        } else {
63
+            return parent::instanceOfStorage($class);
64
+        }
65
+    }
66 66
 }
67 67
 
68 68
 class TestEventHandler {
69
-	public function umount() {
70
-	}
71
-	public function post_umount() {
72
-	}
73
-	public function preCallback() {
74
-	}
75
-	public function postCallback() {
76
-	}
69
+    public function umount() {
70
+    }
71
+    public function post_umount() {
72
+    }
73
+    public function preCallback() {
74
+    }
75
+    public function postCallback() {
76
+    }
77 77
 }
78 78
 
79 79
 /**
@@ -84,2817 +84,2817 @@  discard block
 block discarded – undo
84 84
  * @package Test\Files
85 85
  */
86 86
 class ViewTest extends \Test\TestCase {
87
-	use UserTrait;
88
-
89
-	/**
90
-	 * @var \OC\Files\Storage\Storage[] $storages
91
-	 */
92
-	private $storages = [];
93
-
94
-	/**
95
-	 * @var string
96
-	 */
97
-	private $user;
98
-
99
-	/**
100
-	 * @var \OCP\IUser
101
-	 */
102
-	private $userObject;
103
-
104
-	/**
105
-	 * @var \OCP\IGroup
106
-	 */
107
-	private $groupObject;
108
-
109
-	/** @var \OC\Files\Storage\Storage */
110
-	private $tempStorage;
111
-
112
-	protected function setUp(): void {
113
-		parent::setUp();
114
-		\OC_Hook::clear();
115
-
116
-		Server::get(IUserManager::class)->clearBackends();
117
-		Server::get(IUserManager::class)->registerBackend(new \Test\Util\User\Dummy());
118
-
119
-		//login
120
-		$userManager = \OC::$server->getUserManager();
121
-		$groupManager = \OC::$server->getGroupManager();
122
-		$this->user = 'test';
123
-		$this->userObject = $userManager->createUser('test', 'test');
124
-
125
-		$this->groupObject = $groupManager->createGroup('group1');
126
-		$this->groupObject->addUser($this->userObject);
127
-
128
-		self::loginAsUser($this->user);
129
-
130
-		/** @var IMountManager $manager */
131
-		$manager = \OC::$server->get(IMountManager::class);
132
-		$manager->removeMount('/test');
133
-
134
-		$this->tempStorage = null;
135
-	}
136
-
137
-	protected function tearDown(): void {
138
-		\OC_User::setUserId($this->user);
139
-		foreach ($this->storages as $storage) {
140
-			$cache = $storage->getCache();
141
-			$ids = $cache->getAll();
142
-			$cache->clear();
143
-		}
144
-
145
-		if ($this->tempStorage) {
146
-			system('rm -rf ' . escapeshellarg($this->tempStorage->getDataDir()));
147
-		}
148
-
149
-		self::logout();
150
-
151
-		/** @var SetupManager $setupManager */
152
-		$setupManager = \OC::$server->get(SetupManager::class);
153
-		$setupManager->setupRoot();
154
-
155
-		$this->userObject->delete();
156
-		$this->groupObject->delete();
157
-
158
-		$mountProviderCollection = \OC::$server->getMountProviderCollection();
159
-		self::invokePrivate($mountProviderCollection, 'providers', [[]]);
160
-
161
-		parent::tearDown();
162
-	}
163
-
164
-	/**
165
-	 * @medium
166
-	 */
167
-	public function testCacheAPI(): void {
168
-		$storage1 = $this->getTestStorage();
169
-		$storage2 = $this->getTestStorage();
170
-		$storage3 = $this->getTestStorage();
171
-		$root = self::getUniqueID('/');
172
-		Filesystem::mount($storage1, [], $root . '/');
173
-		Filesystem::mount($storage2, [], $root . '/substorage');
174
-		Filesystem::mount($storage3, [], $root . '/folder/anotherstorage');
175
-		$textSize = strlen("dummy file data\n");
176
-		$imageSize = filesize(\OC::$SERVERROOT . '/core/img/logo/logo.png');
177
-		$storageSize = $textSize * 2 + $imageSize;
178
-
179
-		$storageInfo = $storage3->getCache()->get('');
180
-		$this->assertEquals($storageSize, $storageInfo['size']);
181
-
182
-		$rootView = new View($root);
183
-
184
-		$cachedData = $rootView->getFileInfo('/foo.txt');
185
-		$this->assertEquals($textSize, $cachedData['size']);
186
-		$this->assertEquals('text/plain', $cachedData['mimetype']);
187
-		$this->assertNotEquals(-1, $cachedData['permissions']);
188
-
189
-		$cachedData = $rootView->getFileInfo('/');
190
-		$this->assertEquals($storageSize * 3, $cachedData['size']);
191
-		$this->assertEquals('httpd/unix-directory', $cachedData['mimetype']);
192
-
193
-		// get cached data excluding mount points
194
-		$cachedData = $rootView->getFileInfo('/', false);
195
-		$this->assertEquals($storageSize, $cachedData['size']);
196
-		$this->assertEquals('httpd/unix-directory', $cachedData['mimetype']);
197
-
198
-		$cachedData = $rootView->getFileInfo('/folder');
199
-		$this->assertEquals($storageSize + $textSize, $cachedData['size']);
200
-		$this->assertEquals('httpd/unix-directory', $cachedData['mimetype']);
201
-
202
-		$folderData = $rootView->getDirectoryContent('/');
203
-		/**
204
-		 * expected entries:
205
-		 * folder
206
-		 * foo.png
207
-		 * foo.txt
208
-		 * substorage
209
-		 */
210
-		$this->assertCount(4, $folderData);
211
-		$this->assertEquals('folder', $folderData[0]['name']);
212
-		$this->assertEquals('foo.png', $folderData[1]['name']);
213
-		$this->assertEquals('foo.txt', $folderData[2]['name']);
214
-		$this->assertEquals('substorage', $folderData[3]['name']);
215
-
216
-		$this->assertEquals($storageSize + $textSize, $folderData[0]['size']);
217
-		$this->assertEquals($imageSize, $folderData[1]['size']);
218
-		$this->assertEquals($textSize, $folderData[2]['size']);
219
-		$this->assertEquals($storageSize, $folderData[3]['size']);
220
-
221
-		$folderData = $rootView->getDirectoryContent('/substorage');
222
-		/**
223
-		 * expected entries:
224
-		 * folder
225
-		 * foo.png
226
-		 * foo.txt
227
-		 */
228
-		$this->assertCount(3, $folderData);
229
-		$this->assertEquals('folder', $folderData[0]['name']);
230
-		$this->assertEquals('foo.png', $folderData[1]['name']);
231
-		$this->assertEquals('foo.txt', $folderData[2]['name']);
232
-
233
-		$folderView = new View($root . '/folder');
234
-		$this->assertEquals($rootView->getFileInfo('/folder'), $folderView->getFileInfo('/'));
235
-
236
-		$cachedData = $rootView->getFileInfo('/foo.txt');
237
-		$this->assertFalse($cachedData['encrypted']);
238
-		$id = $rootView->putFileInfo('/foo.txt', ['encrypted' => true]);
239
-		$cachedData = $rootView->getFileInfo('/foo.txt');
240
-		$this->assertTrue($cachedData['encrypted']);
241
-		$this->assertEquals($cachedData['fileid'], $id);
242
-
243
-		$this->assertFalse($rootView->getFileInfo('/non/existing'));
244
-		$this->assertEquals([], $rootView->getDirectoryContent('/non/existing'));
245
-	}
246
-
247
-	/**
248
-	 * @medium
249
-	 */
250
-	public function testGetPath(): void {
251
-		$storage1 = $this->getTestStorage();
252
-		$storage2 = $this->getTestStorage();
253
-		$storage3 = $this->getTestStorage();
254
-
255
-		Filesystem::mount($storage1, [], '/');
256
-		Filesystem::mount($storage2, [], '/substorage');
257
-		Filesystem::mount($storage3, [], '/folder/anotherstorage');
258
-
259
-		$rootView = new View('');
260
-
261
-
262
-		$cachedData = $rootView->getFileInfo('/foo.txt');
263
-		/** @var int $id1 */
264
-		$id1 = $cachedData['fileid'];
265
-		$this->assertEquals('/foo.txt', $rootView->getPath($id1));
266
-
267
-		$cachedData = $rootView->getFileInfo('/substorage/foo.txt');
268
-		/** @var int $id2 */
269
-		$id2 = $cachedData['fileid'];
270
-		$this->assertEquals('/substorage/foo.txt', $rootView->getPath($id2));
271
-
272
-		$folderView = new View('/substorage');
273
-		$this->assertEquals('/foo.txt', $folderView->getPath($id2));
274
-	}
275
-
276
-
277
-	public function testGetPathNotExisting(): void {
278
-		$this->expectException(\OCP\Files\NotFoundException::class);
279
-
280
-		$storage1 = $this->getTestStorage();
281
-		Filesystem::mount($storage1, [], '/');
282
-
283
-		$rootView = new View('');
284
-		$cachedData = $rootView->getFileInfo('/foo.txt');
285
-		/** @var int $id1 */
286
-		$id1 = $cachedData['fileid'];
287
-		$folderView = new View('/substorage');
288
-		$this->assertNull($folderView->getPath($id1));
289
-	}
290
-
291
-	/**
292
-	 * @medium
293
-	 */
294
-	public function testMountPointOverwrite(): void {
295
-		$storage1 = $this->getTestStorage(false);
296
-		$storage2 = $this->getTestStorage();
297
-		$storage1->mkdir('substorage');
298
-		Filesystem::mount($storage1, [], '/');
299
-		Filesystem::mount($storage2, [], '/substorage');
300
-
301
-		$rootView = new View('');
302
-		$folderContent = $rootView->getDirectoryContent('/');
303
-		$this->assertCount(4, $folderContent);
304
-	}
305
-
306
-	public static function sharingDisabledPermissionProvider(): array {
307
-		return [
308
-			['no', '', true],
309
-			['yes', 'group1', false],
310
-		];
311
-	}
312
-
313
-	/**
314
-	 * @dataProvider sharingDisabledPermissionProvider
315
-	 */
316
-	public function testRemoveSharePermissionWhenSharingDisabledForUser($excludeGroups, $excludeGroupsList, $expectedShareable): void {
317
-		// Reset sharing disabled for users cache
318
-		self::invokePrivate(\OC::$server->get(ShareDisableChecker::class), 'sharingDisabledForUsersCache', [new CappedMemoryCache()]);
319
-
320
-		$config = \OC::$server->getConfig();
321
-		$oldExcludeGroupsFlag = $config->getAppValue('core', 'shareapi_exclude_groups', 'no');
322
-		$oldExcludeGroupsList = $config->getAppValue('core', 'shareapi_exclude_groups_list', '');
323
-		$config->setAppValue('core', 'shareapi_exclude_groups', $excludeGroups);
324
-		$config->setAppValue('core', 'shareapi_exclude_groups_list', $excludeGroupsList);
325
-
326
-		$storage1 = $this->getTestStorage();
327
-		$storage2 = $this->getTestStorage();
328
-		Filesystem::mount($storage1, [], '/');
329
-		Filesystem::mount($storage2, [], '/mount');
330
-
331
-		$view = new View('/');
332
-
333
-		$folderContent = $view->getDirectoryContent('');
334
-		$this->assertEquals($expectedShareable, $folderContent[0]->isShareable());
335
-
336
-		$folderContent = $view->getDirectoryContent('mount');
337
-		$this->assertEquals($expectedShareable, $folderContent[0]->isShareable());
338
-
339
-		$config->setAppValue('core', 'shareapi_exclude_groups', $oldExcludeGroupsFlag);
340
-		$config->setAppValue('core', 'shareapi_exclude_groups_list', $oldExcludeGroupsList);
341
-
342
-		// Reset sharing disabled for users cache
343
-		self::invokePrivate(\OC::$server->get(ShareDisableChecker::class), 'sharingDisabledForUsersCache', [new CappedMemoryCache()]);
344
-	}
345
-
346
-	public function testCacheIncompleteFolder(): void {
347
-		$storage1 = $this->getTestStorage(false);
348
-		Filesystem::mount($storage1, [], '/incomplete');
349
-		$rootView = new View('/incomplete');
350
-
351
-		$entries = $rootView->getDirectoryContent('/');
352
-		$this->assertCount(3, $entries);
353
-
354
-		// /folder will already be in the cache but not scanned
355
-		$entries = $rootView->getDirectoryContent('/folder');
356
-		$this->assertCount(1, $entries);
357
-	}
358
-
359
-	public function testAutoScan(): void {
360
-		$storage1 = $this->getTestStorage(false);
361
-		$storage2 = $this->getTestStorage(false);
362
-		Filesystem::mount($storage1, [], '/');
363
-		Filesystem::mount($storage2, [], '/substorage');
364
-		$textSize = strlen("dummy file data\n");
365
-
366
-		$rootView = new View('');
367
-
368
-		$cachedData = $rootView->getFileInfo('/');
369
-		$this->assertEquals('httpd/unix-directory', $cachedData['mimetype']);
370
-		$this->assertEquals(-1, $cachedData['size']);
371
-
372
-		$folderData = $rootView->getDirectoryContent('/substorage/folder');
373
-		$this->assertEquals('text/plain', $folderData[0]['mimetype']);
374
-		$this->assertEquals($textSize, $folderData[0]['size']);
375
-	}
376
-
377
-	/**
378
-	 * @medium
379
-	 */
380
-	public function testSearch(): void {
381
-		$storage1 = $this->getTestStorage();
382
-		$storage2 = $this->getTestStorage();
383
-		$storage3 = $this->getTestStorage();
384
-		Filesystem::mount($storage1, [], '/');
385
-		Filesystem::mount($storage2, [], '/substorage');
386
-		Filesystem::mount($storage3, [], '/folder/anotherstorage');
387
-
388
-		$rootView = new View('');
389
-
390
-		$results = $rootView->search('foo');
391
-		$this->assertCount(6, $results);
392
-		$paths = [];
393
-		foreach ($results as $result) {
394
-			$this->assertEquals($result['path'], Filesystem::normalizePath($result['path']));
395
-			$paths[] = $result['path'];
396
-		}
397
-		$this->assertContains('/foo.txt', $paths);
398
-		$this->assertContains('/foo.png', $paths);
399
-		$this->assertContains('/substorage/foo.txt', $paths);
400
-		$this->assertContains('/substorage/foo.png', $paths);
401
-		$this->assertContains('/folder/anotherstorage/foo.txt', $paths);
402
-		$this->assertContains('/folder/anotherstorage/foo.png', $paths);
403
-
404
-		$folderView = new View('/folder');
405
-		$results = $folderView->search('bar');
406
-		$this->assertCount(2, $results);
407
-		$paths = [];
408
-		foreach ($results as $result) {
409
-			$paths[] = $result['path'];
410
-		}
411
-		$this->assertContains('/anotherstorage/folder/bar.txt', $paths);
412
-		$this->assertContains('/bar.txt', $paths);
413
-
414
-		$results = $folderView->search('foo');
415
-		$this->assertCount(2, $results);
416
-		$paths = [];
417
-		foreach ($results as $result) {
418
-			$paths[] = $result['path'];
419
-		}
420
-		$this->assertContains('/anotherstorage/foo.txt', $paths);
421
-		$this->assertContains('/anotherstorage/foo.png', $paths);
422
-
423
-		$this->assertCount(6, $rootView->searchByMime('text'));
424
-		$this->assertCount(3, $folderView->searchByMime('text'));
425
-	}
426
-
427
-	/**
428
-	 * @medium
429
-	 */
430
-	public function testWatcher(): void {
431
-		$storage1 = $this->getTestStorage();
432
-		Filesystem::mount($storage1, [], '/');
433
-		$storage1->getWatcher()->setPolicy(Watcher::CHECK_ALWAYS);
434
-
435
-		$rootView = new View('');
436
-
437
-		$cachedData = $rootView->getFileInfo('foo.txt');
438
-		$this->assertEquals(16, $cachedData['size']);
439
-
440
-		$rootView->putFileInfo('foo.txt', ['storage_mtime' => 10]);
441
-		$storage1->file_put_contents('foo.txt', 'foo');
442
-		clearstatcache();
443
-
444
-		$cachedData = $rootView->getFileInfo('foo.txt');
445
-		$this->assertEquals(3, $cachedData['size']);
446
-	}
447
-
448
-	/**
449
-	 * @medium
450
-	 */
451
-	public function testCopyBetweenStorageNoCross(): void {
452
-		$storage1 = $this->getTestStorage(true, TemporaryNoCross::class);
453
-		$storage2 = $this->getTestStorage(true, TemporaryNoCross::class);
454
-		$this->copyBetweenStorages($storage1, $storage2);
455
-	}
456
-
457
-	/**
458
-	 * @medium
459
-	 */
460
-	public function testCopyBetweenStorageCross(): void {
461
-		$storage1 = $this->getTestStorage();
462
-		$storage2 = $this->getTestStorage();
463
-		$this->copyBetweenStorages($storage1, $storage2);
464
-	}
465
-
466
-	/**
467
-	 * @medium
468
-	 */
469
-	public function testCopyBetweenStorageCrossNonLocal(): void {
470
-		$storage1 = $this->getTestStorage(true, TemporaryNoLocal::class);
471
-		$storage2 = $this->getTestStorage(true, TemporaryNoLocal::class);
472
-		$this->copyBetweenStorages($storage1, $storage2);
473
-	}
474
-
475
-	public function copyBetweenStorages($storage1, $storage2) {
476
-		Filesystem::mount($storage1, [], '/');
477
-		Filesystem::mount($storage2, [], '/substorage');
478
-
479
-		$rootView = new View('');
480
-		$rootView->mkdir('substorage/emptyfolder');
481
-		$rootView->copy('substorage', 'anotherfolder');
482
-		$this->assertTrue($rootView->is_dir('/anotherfolder'));
483
-		$this->assertTrue($rootView->is_dir('/substorage'));
484
-		$this->assertTrue($rootView->is_dir('/anotherfolder/emptyfolder'));
485
-		$this->assertTrue($rootView->is_dir('/substorage/emptyfolder'));
486
-		$this->assertTrue($rootView->file_exists('/anotherfolder/foo.txt'));
487
-		$this->assertTrue($rootView->file_exists('/anotherfolder/foo.png'));
488
-		$this->assertTrue($rootView->file_exists('/anotherfolder/folder/bar.txt'));
489
-		$this->assertTrue($rootView->file_exists('/substorage/foo.txt'));
490
-		$this->assertTrue($rootView->file_exists('/substorage/foo.png'));
491
-		$this->assertTrue($rootView->file_exists('/substorage/folder/bar.txt'));
492
-	}
493
-
494
-	/**
495
-	 * @medium
496
-	 */
497
-	public function testMoveBetweenStorageNoCross(): void {
498
-		$storage1 = $this->getTestStorage(true, TemporaryNoCross::class);
499
-		$storage2 = $this->getTestStorage(true, TemporaryNoCross::class);
500
-		$this->moveBetweenStorages($storage1, $storage2);
501
-	}
502
-
503
-	/**
504
-	 * @medium
505
-	 */
506
-	public function testMoveBetweenStorageCross(): void {
507
-		$storage1 = $this->getTestStorage();
508
-		$storage2 = $this->getTestStorage();
509
-		$this->moveBetweenStorages($storage1, $storage2);
510
-	}
511
-
512
-	/**
513
-	 * @medium
514
-	 */
515
-	public function testMoveBetweenStorageCrossNonLocal(): void {
516
-		$storage1 = $this->getTestStorage(true, TemporaryNoLocal::class);
517
-		$storage2 = $this->getTestStorage(true, TemporaryNoLocal::class);
518
-		$this->moveBetweenStorages($storage1, $storage2);
519
-	}
520
-
521
-	public function moveBetweenStorages($storage1, $storage2) {
522
-		Filesystem::mount($storage1, [], '/' . $this->user . '/');
523
-		Filesystem::mount($storage2, [], '/' . $this->user . '/substorage');
524
-
525
-		$rootView = new View('/' . $this->user);
526
-		$rootView->rename('foo.txt', 'substorage/folder/foo.txt');
527
-		$this->assertFalse($rootView->file_exists('foo.txt'));
528
-		$this->assertTrue($rootView->file_exists('substorage/folder/foo.txt'));
529
-		$rootView->rename('substorage/folder', 'anotherfolder');
530
-		$this->assertFalse($rootView->is_dir('substorage/folder'));
531
-		$this->assertTrue($rootView->file_exists('anotherfolder/foo.txt'));
532
-		$this->assertTrue($rootView->file_exists('anotherfolder/bar.txt'));
533
-	}
534
-
535
-	/**
536
-	 * @medium
537
-	 */
538
-	public function testUnlink(): void {
539
-		$storage1 = $this->getTestStorage();
540
-		$storage2 = $this->getTestStorage();
541
-		Filesystem::mount($storage1, [], '/');
542
-		Filesystem::mount($storage2, [], '/substorage');
543
-
544
-		$rootView = new View('');
545
-		$rootView->file_put_contents('/foo.txt', 'asd');
546
-		$rootView->file_put_contents('/substorage/bar.txt', 'asd');
547
-
548
-		$this->assertTrue($rootView->file_exists('foo.txt'));
549
-		$this->assertTrue($rootView->file_exists('substorage/bar.txt'));
550
-
551
-		$this->assertTrue($rootView->unlink('foo.txt'));
552
-		$this->assertTrue($rootView->unlink('substorage/bar.txt'));
553
-
554
-		$this->assertFalse($rootView->file_exists('foo.txt'));
555
-		$this->assertFalse($rootView->file_exists('substorage/bar.txt'));
556
-	}
557
-
558
-	public static function rmdirOrUnlinkDataProvider(): array {
559
-		return [['rmdir'], ['unlink']];
560
-	}
561
-
562
-	/**
563
-	 * @dataProvider rmdirOrUnlinkDataProvider
564
-	 */
565
-	public function testRmdir($method): void {
566
-		$storage1 = $this->getTestStorage();
567
-		Filesystem::mount($storage1, [], '/');
568
-
569
-		$rootView = new View('');
570
-		$rootView->mkdir('sub');
571
-		$rootView->mkdir('sub/deep');
572
-		$rootView->file_put_contents('/sub/deep/foo.txt', 'asd');
573
-
574
-		$this->assertTrue($rootView->file_exists('sub/deep/foo.txt'));
575
-
576
-		$this->assertTrue($rootView->$method('sub'));
577
-
578
-		$this->assertFalse($rootView->file_exists('sub'));
579
-	}
580
-
581
-	/**
582
-	 * @medium
583
-	 */
584
-	public function testUnlinkRootMustFail(): void {
585
-		$storage1 = $this->getTestStorage();
586
-		$storage2 = $this->getTestStorage();
587
-		Filesystem::mount($storage1, [], '/');
588
-		Filesystem::mount($storage2, [], '/substorage');
589
-
590
-		$rootView = new View('');
591
-		$rootView->file_put_contents('/foo.txt', 'asd');
592
-		$rootView->file_put_contents('/substorage/bar.txt', 'asd');
593
-
594
-		$this->assertFalse($rootView->unlink(''));
595
-		$this->assertFalse($rootView->unlink('/'));
596
-		$this->assertFalse($rootView->unlink('substorage'));
597
-		$this->assertFalse($rootView->unlink('/substorage'));
598
-	}
599
-
600
-	/**
601
-	 * @medium
602
-	 */
603
-	public function testTouch(): void {
604
-		$storage = $this->getTestStorage(true, TemporaryNoTouch::class);
605
-
606
-		Filesystem::mount($storage, [], '/');
607
-
608
-		$rootView = new View('');
609
-		$oldCachedData = $rootView->getFileInfo('foo.txt');
610
-
611
-		$rootView->touch('foo.txt', 500);
612
-
613
-		$cachedData = $rootView->getFileInfo('foo.txt');
614
-		$this->assertEquals(500, $cachedData['mtime']);
615
-		$this->assertEquals($oldCachedData['storage_mtime'], $cachedData['storage_mtime']);
616
-
617
-		$rootView->putFileInfo('foo.txt', ['storage_mtime' => 1000]); //make sure the watcher detects the change
618
-		$rootView->file_put_contents('foo.txt', 'asd');
619
-		$cachedData = $rootView->getFileInfo('foo.txt');
620
-		$this->assertGreaterThanOrEqual($oldCachedData['mtime'], $cachedData['mtime']);
621
-		$this->assertEquals($cachedData['storage_mtime'], $cachedData['mtime']);
622
-	}
623
-
624
-	/**
625
-	 * @medium
626
-	 */
627
-	public function testTouchFloat(): void {
628
-		$storage = $this->getTestStorage(true, TemporaryNoTouch::class);
629
-
630
-		Filesystem::mount($storage, [], '/');
631
-
632
-		$rootView = new View('');
633
-		$oldCachedData = $rootView->getFileInfo('foo.txt');
634
-
635
-		$rootView->touch('foo.txt', 500.5);
636
-
637
-		$cachedData = $rootView->getFileInfo('foo.txt');
638
-		$this->assertEquals(500, $cachedData['mtime']);
639
-	}
640
-
641
-	/**
642
-	 * @medium
643
-	 */
644
-	public function testViewHooks(): void {
645
-		$storage1 = $this->getTestStorage();
646
-		$storage2 = $this->getTestStorage();
647
-		$defaultRoot = Filesystem::getRoot();
648
-		Filesystem::mount($storage1, [], '/');
649
-		Filesystem::mount($storage2, [], $defaultRoot . '/substorage');
650
-		\OC_Hook::connect('OC_Filesystem', 'post_write', $this, 'dummyHook');
651
-
652
-		$rootView = new View('');
653
-		$subView = new View($defaultRoot . '/substorage');
654
-		$this->hookPath = null;
655
-
656
-		$rootView->file_put_contents('/foo.txt', 'asd');
657
-		$this->assertNull($this->hookPath);
658
-
659
-		$subView->file_put_contents('/foo.txt', 'asd');
660
-		$this->assertEquals('/substorage/foo.txt', $this->hookPath);
661
-	}
662
-
663
-	private $hookPath;
664
-
665
-	public function dummyHook($params) {
666
-		$this->hookPath = $params['path'];
667
-	}
668
-
669
-	public function testSearchNotOutsideView(): void {
670
-		$storage1 = $this->getTestStorage();
671
-		Filesystem::mount($storage1, [], '/');
672
-		$storage1->rename('folder', 'foo');
673
-		$scanner = $storage1->getScanner();
674
-		$scanner->scan('');
675
-
676
-		$view = new View('/foo');
677
-
678
-		$result = $view->search('.txt');
679
-		$this->assertCount(1, $result);
680
-	}
681
-
682
-	/**
683
-	 * @param bool $scan
684
-	 * @param string $class
685
-	 * @return \OC\Files\Storage\Storage
686
-	 */
687
-	private function getTestStorage($scan = true, $class = Temporary::class) {
688
-		/**
689
-		 * @var \OC\Files\Storage\Storage $storage
690
-		 */
691
-		$storage = new $class([]);
692
-		$textData = "dummy file data\n";
693
-		$imgData = file_get_contents(\OC::$SERVERROOT . '/core/img/logo/logo.png');
694
-		$storage->mkdir('folder');
695
-		$storage->file_put_contents('foo.txt', $textData);
696
-		$storage->file_put_contents('foo.png', $imgData);
697
-		$storage->file_put_contents('folder/bar.txt', $textData);
698
-
699
-		if ($scan) {
700
-			$scanner = $storage->getScanner();
701
-			$scanner->scan('');
702
-		}
703
-		$this->storages[] = $storage;
704
-		return $storage;
705
-	}
706
-
707
-	/**
708
-	 * @medium
709
-	 */
710
-	public function testViewHooksIfRootStartsTheSame(): void {
711
-		$storage1 = $this->getTestStorage();
712
-		$storage2 = $this->getTestStorage();
713
-		$defaultRoot = Filesystem::getRoot();
714
-		Filesystem::mount($storage1, [], '/');
715
-		Filesystem::mount($storage2, [], $defaultRoot . '_substorage');
716
-		\OC_Hook::connect('OC_Filesystem', 'post_write', $this, 'dummyHook');
717
-
718
-		$subView = new View($defaultRoot . '_substorage');
719
-		$this->hookPath = null;
720
-
721
-		$subView->file_put_contents('/foo.txt', 'asd');
722
-		$this->assertNull($this->hookPath);
723
-	}
724
-
725
-	private $hookWritePath;
726
-	private $hookCreatePath;
727
-	private $hookUpdatePath;
728
-
729
-	public function dummyHookWrite($params) {
730
-		$this->hookWritePath = $params['path'];
731
-	}
732
-
733
-	public function dummyHookUpdate($params) {
734
-		$this->hookUpdatePath = $params['path'];
735
-	}
736
-
737
-	public function dummyHookCreate($params) {
738
-		$this->hookCreatePath = $params['path'];
739
-	}
740
-
741
-	public function testEditNoCreateHook(): void {
742
-		$storage1 = $this->getTestStorage();
743
-		$storage2 = $this->getTestStorage();
744
-		$defaultRoot = Filesystem::getRoot();
745
-		Filesystem::mount($storage1, [], '/');
746
-		Filesystem::mount($storage2, [], $defaultRoot);
747
-		\OC_Hook::connect('OC_Filesystem', 'post_create', $this, 'dummyHookCreate');
748
-		\OC_Hook::connect('OC_Filesystem', 'post_update', $this, 'dummyHookUpdate');
749
-		\OC_Hook::connect('OC_Filesystem', 'post_write', $this, 'dummyHookWrite');
750
-
751
-		$view = new View($defaultRoot);
752
-		$this->hookWritePath = $this->hookUpdatePath = $this->hookCreatePath = null;
753
-
754
-		$view->file_put_contents('/asd.txt', 'foo');
755
-		$this->assertEquals('/asd.txt', $this->hookCreatePath);
756
-		$this->assertNull($this->hookUpdatePath);
757
-		$this->assertEquals('/asd.txt', $this->hookWritePath);
758
-
759
-		$this->hookWritePath = $this->hookUpdatePath = $this->hookCreatePath = null;
760
-
761
-		$view->file_put_contents('/asd.txt', 'foo');
762
-		$this->assertNull($this->hookCreatePath);
763
-		$this->assertEquals('/asd.txt', $this->hookUpdatePath);
764
-		$this->assertEquals('/asd.txt', $this->hookWritePath);
765
-
766
-		\OC_Hook::clear('OC_Filesystem', 'post_create');
767
-		\OC_Hook::clear('OC_Filesystem', 'post_update');
768
-		\OC_Hook::clear('OC_Filesystem', 'post_write');
769
-	}
770
-
771
-	/**
772
-	 * @dataProvider resolvePathTestProvider
773
-	 */
774
-	public function testResolvePath($expected, $pathToTest): void {
775
-		$storage1 = $this->getTestStorage();
776
-		Filesystem::mount($storage1, [], '/');
777
-
778
-		$view = new View('');
779
-
780
-		$result = $view->resolvePath($pathToTest);
781
-		$this->assertEquals($expected, $result[1]);
782
-
783
-		$exists = $view->file_exists($pathToTest);
784
-		$this->assertTrue($exists);
785
-
786
-		$exists = $view->file_exists($result[1]);
787
-		$this->assertTrue($exists);
788
-	}
789
-
790
-	public static function resolvePathTestProvider(): array {
791
-		return [
792
-			['foo.txt', 'foo.txt'],
793
-			['foo.txt', '/foo.txt'],
794
-			['folder', 'folder'],
795
-			['folder', '/folder'],
796
-			['folder', 'folder/'],
797
-			['folder', '/folder/'],
798
-			['folder/bar.txt', 'folder/bar.txt'],
799
-			['folder/bar.txt', '/folder/bar.txt'],
800
-			['', ''],
801
-			['', '/'],
802
-		];
803
-	}
804
-
805
-	public function testUTF8Names(): void {
806
-		$names = ['虚', '和知しゃ和で', 'regular ascii', 'sɨˈrɪlɪk', 'ѨѬ', 'أنا أحب القراءة كثيرا'];
807
-
808
-		$storage = new Temporary([]);
809
-		Filesystem::mount($storage, [], '/');
810
-
811
-		$rootView = new View('');
812
-		foreach ($names as $name) {
813
-			$rootView->file_put_contents('/' . $name, 'dummy content');
814
-		}
815
-
816
-		$list = $rootView->getDirectoryContent('/');
817
-
818
-		$this->assertCount(count($names), $list);
819
-		foreach ($list as $item) {
820
-			$this->assertContains($item['name'], $names);
821
-		}
822
-
823
-		$cache = $storage->getCache();
824
-		$scanner = $storage->getScanner();
825
-		$scanner->scan('');
826
-
827
-		$list = $cache->getFolderContents('');
828
-
829
-		$this->assertCount(count($names), $list);
830
-		foreach ($list as $item) {
831
-			$this->assertContains($item['name'], $names);
832
-		}
833
-	}
834
-
835
-	public function xtestLongPath() {
836
-		$storage = new Temporary([]);
837
-		Filesystem::mount($storage, [], '/');
838
-
839
-		$rootView = new View('');
840
-
841
-		$longPath = '';
842
-		$ds = DIRECTORY_SEPARATOR;
843
-		/*
87
+    use UserTrait;
88
+
89
+    /**
90
+     * @var \OC\Files\Storage\Storage[] $storages
91
+     */
92
+    private $storages = [];
93
+
94
+    /**
95
+     * @var string
96
+     */
97
+    private $user;
98
+
99
+    /**
100
+     * @var \OCP\IUser
101
+     */
102
+    private $userObject;
103
+
104
+    /**
105
+     * @var \OCP\IGroup
106
+     */
107
+    private $groupObject;
108
+
109
+    /** @var \OC\Files\Storage\Storage */
110
+    private $tempStorage;
111
+
112
+    protected function setUp(): void {
113
+        parent::setUp();
114
+        \OC_Hook::clear();
115
+
116
+        Server::get(IUserManager::class)->clearBackends();
117
+        Server::get(IUserManager::class)->registerBackend(new \Test\Util\User\Dummy());
118
+
119
+        //login
120
+        $userManager = \OC::$server->getUserManager();
121
+        $groupManager = \OC::$server->getGroupManager();
122
+        $this->user = 'test';
123
+        $this->userObject = $userManager->createUser('test', 'test');
124
+
125
+        $this->groupObject = $groupManager->createGroup('group1');
126
+        $this->groupObject->addUser($this->userObject);
127
+
128
+        self::loginAsUser($this->user);
129
+
130
+        /** @var IMountManager $manager */
131
+        $manager = \OC::$server->get(IMountManager::class);
132
+        $manager->removeMount('/test');
133
+
134
+        $this->tempStorage = null;
135
+    }
136
+
137
+    protected function tearDown(): void {
138
+        \OC_User::setUserId($this->user);
139
+        foreach ($this->storages as $storage) {
140
+            $cache = $storage->getCache();
141
+            $ids = $cache->getAll();
142
+            $cache->clear();
143
+        }
144
+
145
+        if ($this->tempStorage) {
146
+            system('rm -rf ' . escapeshellarg($this->tempStorage->getDataDir()));
147
+        }
148
+
149
+        self::logout();
150
+
151
+        /** @var SetupManager $setupManager */
152
+        $setupManager = \OC::$server->get(SetupManager::class);
153
+        $setupManager->setupRoot();
154
+
155
+        $this->userObject->delete();
156
+        $this->groupObject->delete();
157
+
158
+        $mountProviderCollection = \OC::$server->getMountProviderCollection();
159
+        self::invokePrivate($mountProviderCollection, 'providers', [[]]);
160
+
161
+        parent::tearDown();
162
+    }
163
+
164
+    /**
165
+     * @medium
166
+     */
167
+    public function testCacheAPI(): void {
168
+        $storage1 = $this->getTestStorage();
169
+        $storage2 = $this->getTestStorage();
170
+        $storage3 = $this->getTestStorage();
171
+        $root = self::getUniqueID('/');
172
+        Filesystem::mount($storage1, [], $root . '/');
173
+        Filesystem::mount($storage2, [], $root . '/substorage');
174
+        Filesystem::mount($storage3, [], $root . '/folder/anotherstorage');
175
+        $textSize = strlen("dummy file data\n");
176
+        $imageSize = filesize(\OC::$SERVERROOT . '/core/img/logo/logo.png');
177
+        $storageSize = $textSize * 2 + $imageSize;
178
+
179
+        $storageInfo = $storage3->getCache()->get('');
180
+        $this->assertEquals($storageSize, $storageInfo['size']);
181
+
182
+        $rootView = new View($root);
183
+
184
+        $cachedData = $rootView->getFileInfo('/foo.txt');
185
+        $this->assertEquals($textSize, $cachedData['size']);
186
+        $this->assertEquals('text/plain', $cachedData['mimetype']);
187
+        $this->assertNotEquals(-1, $cachedData['permissions']);
188
+
189
+        $cachedData = $rootView->getFileInfo('/');
190
+        $this->assertEquals($storageSize * 3, $cachedData['size']);
191
+        $this->assertEquals('httpd/unix-directory', $cachedData['mimetype']);
192
+
193
+        // get cached data excluding mount points
194
+        $cachedData = $rootView->getFileInfo('/', false);
195
+        $this->assertEquals($storageSize, $cachedData['size']);
196
+        $this->assertEquals('httpd/unix-directory', $cachedData['mimetype']);
197
+
198
+        $cachedData = $rootView->getFileInfo('/folder');
199
+        $this->assertEquals($storageSize + $textSize, $cachedData['size']);
200
+        $this->assertEquals('httpd/unix-directory', $cachedData['mimetype']);
201
+
202
+        $folderData = $rootView->getDirectoryContent('/');
203
+        /**
204
+         * expected entries:
205
+         * folder
206
+         * foo.png
207
+         * foo.txt
208
+         * substorage
209
+         */
210
+        $this->assertCount(4, $folderData);
211
+        $this->assertEquals('folder', $folderData[0]['name']);
212
+        $this->assertEquals('foo.png', $folderData[1]['name']);
213
+        $this->assertEquals('foo.txt', $folderData[2]['name']);
214
+        $this->assertEquals('substorage', $folderData[3]['name']);
215
+
216
+        $this->assertEquals($storageSize + $textSize, $folderData[0]['size']);
217
+        $this->assertEquals($imageSize, $folderData[1]['size']);
218
+        $this->assertEquals($textSize, $folderData[2]['size']);
219
+        $this->assertEquals($storageSize, $folderData[3]['size']);
220
+
221
+        $folderData = $rootView->getDirectoryContent('/substorage');
222
+        /**
223
+         * expected entries:
224
+         * folder
225
+         * foo.png
226
+         * foo.txt
227
+         */
228
+        $this->assertCount(3, $folderData);
229
+        $this->assertEquals('folder', $folderData[0]['name']);
230
+        $this->assertEquals('foo.png', $folderData[1]['name']);
231
+        $this->assertEquals('foo.txt', $folderData[2]['name']);
232
+
233
+        $folderView = new View($root . '/folder');
234
+        $this->assertEquals($rootView->getFileInfo('/folder'), $folderView->getFileInfo('/'));
235
+
236
+        $cachedData = $rootView->getFileInfo('/foo.txt');
237
+        $this->assertFalse($cachedData['encrypted']);
238
+        $id = $rootView->putFileInfo('/foo.txt', ['encrypted' => true]);
239
+        $cachedData = $rootView->getFileInfo('/foo.txt');
240
+        $this->assertTrue($cachedData['encrypted']);
241
+        $this->assertEquals($cachedData['fileid'], $id);
242
+
243
+        $this->assertFalse($rootView->getFileInfo('/non/existing'));
244
+        $this->assertEquals([], $rootView->getDirectoryContent('/non/existing'));
245
+    }
246
+
247
+    /**
248
+     * @medium
249
+     */
250
+    public function testGetPath(): void {
251
+        $storage1 = $this->getTestStorage();
252
+        $storage2 = $this->getTestStorage();
253
+        $storage3 = $this->getTestStorage();
254
+
255
+        Filesystem::mount($storage1, [], '/');
256
+        Filesystem::mount($storage2, [], '/substorage');
257
+        Filesystem::mount($storage3, [], '/folder/anotherstorage');
258
+
259
+        $rootView = new View('');
260
+
261
+
262
+        $cachedData = $rootView->getFileInfo('/foo.txt');
263
+        /** @var int $id1 */
264
+        $id1 = $cachedData['fileid'];
265
+        $this->assertEquals('/foo.txt', $rootView->getPath($id1));
266
+
267
+        $cachedData = $rootView->getFileInfo('/substorage/foo.txt');
268
+        /** @var int $id2 */
269
+        $id2 = $cachedData['fileid'];
270
+        $this->assertEquals('/substorage/foo.txt', $rootView->getPath($id2));
271
+
272
+        $folderView = new View('/substorage');
273
+        $this->assertEquals('/foo.txt', $folderView->getPath($id2));
274
+    }
275
+
276
+
277
+    public function testGetPathNotExisting(): void {
278
+        $this->expectException(\OCP\Files\NotFoundException::class);
279
+
280
+        $storage1 = $this->getTestStorage();
281
+        Filesystem::mount($storage1, [], '/');
282
+
283
+        $rootView = new View('');
284
+        $cachedData = $rootView->getFileInfo('/foo.txt');
285
+        /** @var int $id1 */
286
+        $id1 = $cachedData['fileid'];
287
+        $folderView = new View('/substorage');
288
+        $this->assertNull($folderView->getPath($id1));
289
+    }
290
+
291
+    /**
292
+     * @medium
293
+     */
294
+    public function testMountPointOverwrite(): void {
295
+        $storage1 = $this->getTestStorage(false);
296
+        $storage2 = $this->getTestStorage();
297
+        $storage1->mkdir('substorage');
298
+        Filesystem::mount($storage1, [], '/');
299
+        Filesystem::mount($storage2, [], '/substorage');
300
+
301
+        $rootView = new View('');
302
+        $folderContent = $rootView->getDirectoryContent('/');
303
+        $this->assertCount(4, $folderContent);
304
+    }
305
+
306
+    public static function sharingDisabledPermissionProvider(): array {
307
+        return [
308
+            ['no', '', true],
309
+            ['yes', 'group1', false],
310
+        ];
311
+    }
312
+
313
+    /**
314
+     * @dataProvider sharingDisabledPermissionProvider
315
+     */
316
+    public function testRemoveSharePermissionWhenSharingDisabledForUser($excludeGroups, $excludeGroupsList, $expectedShareable): void {
317
+        // Reset sharing disabled for users cache
318
+        self::invokePrivate(\OC::$server->get(ShareDisableChecker::class), 'sharingDisabledForUsersCache', [new CappedMemoryCache()]);
319
+
320
+        $config = \OC::$server->getConfig();
321
+        $oldExcludeGroupsFlag = $config->getAppValue('core', 'shareapi_exclude_groups', 'no');
322
+        $oldExcludeGroupsList = $config->getAppValue('core', 'shareapi_exclude_groups_list', '');
323
+        $config->setAppValue('core', 'shareapi_exclude_groups', $excludeGroups);
324
+        $config->setAppValue('core', 'shareapi_exclude_groups_list', $excludeGroupsList);
325
+
326
+        $storage1 = $this->getTestStorage();
327
+        $storage2 = $this->getTestStorage();
328
+        Filesystem::mount($storage1, [], '/');
329
+        Filesystem::mount($storage2, [], '/mount');
330
+
331
+        $view = new View('/');
332
+
333
+        $folderContent = $view->getDirectoryContent('');
334
+        $this->assertEquals($expectedShareable, $folderContent[0]->isShareable());
335
+
336
+        $folderContent = $view->getDirectoryContent('mount');
337
+        $this->assertEquals($expectedShareable, $folderContent[0]->isShareable());
338
+
339
+        $config->setAppValue('core', 'shareapi_exclude_groups', $oldExcludeGroupsFlag);
340
+        $config->setAppValue('core', 'shareapi_exclude_groups_list', $oldExcludeGroupsList);
341
+
342
+        // Reset sharing disabled for users cache
343
+        self::invokePrivate(\OC::$server->get(ShareDisableChecker::class), 'sharingDisabledForUsersCache', [new CappedMemoryCache()]);
344
+    }
345
+
346
+    public function testCacheIncompleteFolder(): void {
347
+        $storage1 = $this->getTestStorage(false);
348
+        Filesystem::mount($storage1, [], '/incomplete');
349
+        $rootView = new View('/incomplete');
350
+
351
+        $entries = $rootView->getDirectoryContent('/');
352
+        $this->assertCount(3, $entries);
353
+
354
+        // /folder will already be in the cache but not scanned
355
+        $entries = $rootView->getDirectoryContent('/folder');
356
+        $this->assertCount(1, $entries);
357
+    }
358
+
359
+    public function testAutoScan(): void {
360
+        $storage1 = $this->getTestStorage(false);
361
+        $storage2 = $this->getTestStorage(false);
362
+        Filesystem::mount($storage1, [], '/');
363
+        Filesystem::mount($storage2, [], '/substorage');
364
+        $textSize = strlen("dummy file data\n");
365
+
366
+        $rootView = new View('');
367
+
368
+        $cachedData = $rootView->getFileInfo('/');
369
+        $this->assertEquals('httpd/unix-directory', $cachedData['mimetype']);
370
+        $this->assertEquals(-1, $cachedData['size']);
371
+
372
+        $folderData = $rootView->getDirectoryContent('/substorage/folder');
373
+        $this->assertEquals('text/plain', $folderData[0]['mimetype']);
374
+        $this->assertEquals($textSize, $folderData[0]['size']);
375
+    }
376
+
377
+    /**
378
+     * @medium
379
+     */
380
+    public function testSearch(): void {
381
+        $storage1 = $this->getTestStorage();
382
+        $storage2 = $this->getTestStorage();
383
+        $storage3 = $this->getTestStorage();
384
+        Filesystem::mount($storage1, [], '/');
385
+        Filesystem::mount($storage2, [], '/substorage');
386
+        Filesystem::mount($storage3, [], '/folder/anotherstorage');
387
+
388
+        $rootView = new View('');
389
+
390
+        $results = $rootView->search('foo');
391
+        $this->assertCount(6, $results);
392
+        $paths = [];
393
+        foreach ($results as $result) {
394
+            $this->assertEquals($result['path'], Filesystem::normalizePath($result['path']));
395
+            $paths[] = $result['path'];
396
+        }
397
+        $this->assertContains('/foo.txt', $paths);
398
+        $this->assertContains('/foo.png', $paths);
399
+        $this->assertContains('/substorage/foo.txt', $paths);
400
+        $this->assertContains('/substorage/foo.png', $paths);
401
+        $this->assertContains('/folder/anotherstorage/foo.txt', $paths);
402
+        $this->assertContains('/folder/anotherstorage/foo.png', $paths);
403
+
404
+        $folderView = new View('/folder');
405
+        $results = $folderView->search('bar');
406
+        $this->assertCount(2, $results);
407
+        $paths = [];
408
+        foreach ($results as $result) {
409
+            $paths[] = $result['path'];
410
+        }
411
+        $this->assertContains('/anotherstorage/folder/bar.txt', $paths);
412
+        $this->assertContains('/bar.txt', $paths);
413
+
414
+        $results = $folderView->search('foo');
415
+        $this->assertCount(2, $results);
416
+        $paths = [];
417
+        foreach ($results as $result) {
418
+            $paths[] = $result['path'];
419
+        }
420
+        $this->assertContains('/anotherstorage/foo.txt', $paths);
421
+        $this->assertContains('/anotherstorage/foo.png', $paths);
422
+
423
+        $this->assertCount(6, $rootView->searchByMime('text'));
424
+        $this->assertCount(3, $folderView->searchByMime('text'));
425
+    }
426
+
427
+    /**
428
+     * @medium
429
+     */
430
+    public function testWatcher(): void {
431
+        $storage1 = $this->getTestStorage();
432
+        Filesystem::mount($storage1, [], '/');
433
+        $storage1->getWatcher()->setPolicy(Watcher::CHECK_ALWAYS);
434
+
435
+        $rootView = new View('');
436
+
437
+        $cachedData = $rootView->getFileInfo('foo.txt');
438
+        $this->assertEquals(16, $cachedData['size']);
439
+
440
+        $rootView->putFileInfo('foo.txt', ['storage_mtime' => 10]);
441
+        $storage1->file_put_contents('foo.txt', 'foo');
442
+        clearstatcache();
443
+
444
+        $cachedData = $rootView->getFileInfo('foo.txt');
445
+        $this->assertEquals(3, $cachedData['size']);
446
+    }
447
+
448
+    /**
449
+     * @medium
450
+     */
451
+    public function testCopyBetweenStorageNoCross(): void {
452
+        $storage1 = $this->getTestStorage(true, TemporaryNoCross::class);
453
+        $storage2 = $this->getTestStorage(true, TemporaryNoCross::class);
454
+        $this->copyBetweenStorages($storage1, $storage2);
455
+    }
456
+
457
+    /**
458
+     * @medium
459
+     */
460
+    public function testCopyBetweenStorageCross(): void {
461
+        $storage1 = $this->getTestStorage();
462
+        $storage2 = $this->getTestStorage();
463
+        $this->copyBetweenStorages($storage1, $storage2);
464
+    }
465
+
466
+    /**
467
+     * @medium
468
+     */
469
+    public function testCopyBetweenStorageCrossNonLocal(): void {
470
+        $storage1 = $this->getTestStorage(true, TemporaryNoLocal::class);
471
+        $storage2 = $this->getTestStorage(true, TemporaryNoLocal::class);
472
+        $this->copyBetweenStorages($storage1, $storage2);
473
+    }
474
+
475
+    public function copyBetweenStorages($storage1, $storage2) {
476
+        Filesystem::mount($storage1, [], '/');
477
+        Filesystem::mount($storage2, [], '/substorage');
478
+
479
+        $rootView = new View('');
480
+        $rootView->mkdir('substorage/emptyfolder');
481
+        $rootView->copy('substorage', 'anotherfolder');
482
+        $this->assertTrue($rootView->is_dir('/anotherfolder'));
483
+        $this->assertTrue($rootView->is_dir('/substorage'));
484
+        $this->assertTrue($rootView->is_dir('/anotherfolder/emptyfolder'));
485
+        $this->assertTrue($rootView->is_dir('/substorage/emptyfolder'));
486
+        $this->assertTrue($rootView->file_exists('/anotherfolder/foo.txt'));
487
+        $this->assertTrue($rootView->file_exists('/anotherfolder/foo.png'));
488
+        $this->assertTrue($rootView->file_exists('/anotherfolder/folder/bar.txt'));
489
+        $this->assertTrue($rootView->file_exists('/substorage/foo.txt'));
490
+        $this->assertTrue($rootView->file_exists('/substorage/foo.png'));
491
+        $this->assertTrue($rootView->file_exists('/substorage/folder/bar.txt'));
492
+    }
493
+
494
+    /**
495
+     * @medium
496
+     */
497
+    public function testMoveBetweenStorageNoCross(): void {
498
+        $storage1 = $this->getTestStorage(true, TemporaryNoCross::class);
499
+        $storage2 = $this->getTestStorage(true, TemporaryNoCross::class);
500
+        $this->moveBetweenStorages($storage1, $storage2);
501
+    }
502
+
503
+    /**
504
+     * @medium
505
+     */
506
+    public function testMoveBetweenStorageCross(): void {
507
+        $storage1 = $this->getTestStorage();
508
+        $storage2 = $this->getTestStorage();
509
+        $this->moveBetweenStorages($storage1, $storage2);
510
+    }
511
+
512
+    /**
513
+     * @medium
514
+     */
515
+    public function testMoveBetweenStorageCrossNonLocal(): void {
516
+        $storage1 = $this->getTestStorage(true, TemporaryNoLocal::class);
517
+        $storage2 = $this->getTestStorage(true, TemporaryNoLocal::class);
518
+        $this->moveBetweenStorages($storage1, $storage2);
519
+    }
520
+
521
+    public function moveBetweenStorages($storage1, $storage2) {
522
+        Filesystem::mount($storage1, [], '/' . $this->user . '/');
523
+        Filesystem::mount($storage2, [], '/' . $this->user . '/substorage');
524
+
525
+        $rootView = new View('/' . $this->user);
526
+        $rootView->rename('foo.txt', 'substorage/folder/foo.txt');
527
+        $this->assertFalse($rootView->file_exists('foo.txt'));
528
+        $this->assertTrue($rootView->file_exists('substorage/folder/foo.txt'));
529
+        $rootView->rename('substorage/folder', 'anotherfolder');
530
+        $this->assertFalse($rootView->is_dir('substorage/folder'));
531
+        $this->assertTrue($rootView->file_exists('anotherfolder/foo.txt'));
532
+        $this->assertTrue($rootView->file_exists('anotherfolder/bar.txt'));
533
+    }
534
+
535
+    /**
536
+     * @medium
537
+     */
538
+    public function testUnlink(): void {
539
+        $storage1 = $this->getTestStorage();
540
+        $storage2 = $this->getTestStorage();
541
+        Filesystem::mount($storage1, [], '/');
542
+        Filesystem::mount($storage2, [], '/substorage');
543
+
544
+        $rootView = new View('');
545
+        $rootView->file_put_contents('/foo.txt', 'asd');
546
+        $rootView->file_put_contents('/substorage/bar.txt', 'asd');
547
+
548
+        $this->assertTrue($rootView->file_exists('foo.txt'));
549
+        $this->assertTrue($rootView->file_exists('substorage/bar.txt'));
550
+
551
+        $this->assertTrue($rootView->unlink('foo.txt'));
552
+        $this->assertTrue($rootView->unlink('substorage/bar.txt'));
553
+
554
+        $this->assertFalse($rootView->file_exists('foo.txt'));
555
+        $this->assertFalse($rootView->file_exists('substorage/bar.txt'));
556
+    }
557
+
558
+    public static function rmdirOrUnlinkDataProvider(): array {
559
+        return [['rmdir'], ['unlink']];
560
+    }
561
+
562
+    /**
563
+     * @dataProvider rmdirOrUnlinkDataProvider
564
+     */
565
+    public function testRmdir($method): void {
566
+        $storage1 = $this->getTestStorage();
567
+        Filesystem::mount($storage1, [], '/');
568
+
569
+        $rootView = new View('');
570
+        $rootView->mkdir('sub');
571
+        $rootView->mkdir('sub/deep');
572
+        $rootView->file_put_contents('/sub/deep/foo.txt', 'asd');
573
+
574
+        $this->assertTrue($rootView->file_exists('sub/deep/foo.txt'));
575
+
576
+        $this->assertTrue($rootView->$method('sub'));
577
+
578
+        $this->assertFalse($rootView->file_exists('sub'));
579
+    }
580
+
581
+    /**
582
+     * @medium
583
+     */
584
+    public function testUnlinkRootMustFail(): void {
585
+        $storage1 = $this->getTestStorage();
586
+        $storage2 = $this->getTestStorage();
587
+        Filesystem::mount($storage1, [], '/');
588
+        Filesystem::mount($storage2, [], '/substorage');
589
+
590
+        $rootView = new View('');
591
+        $rootView->file_put_contents('/foo.txt', 'asd');
592
+        $rootView->file_put_contents('/substorage/bar.txt', 'asd');
593
+
594
+        $this->assertFalse($rootView->unlink(''));
595
+        $this->assertFalse($rootView->unlink('/'));
596
+        $this->assertFalse($rootView->unlink('substorage'));
597
+        $this->assertFalse($rootView->unlink('/substorage'));
598
+    }
599
+
600
+    /**
601
+     * @medium
602
+     */
603
+    public function testTouch(): void {
604
+        $storage = $this->getTestStorage(true, TemporaryNoTouch::class);
605
+
606
+        Filesystem::mount($storage, [], '/');
607
+
608
+        $rootView = new View('');
609
+        $oldCachedData = $rootView->getFileInfo('foo.txt');
610
+
611
+        $rootView->touch('foo.txt', 500);
612
+
613
+        $cachedData = $rootView->getFileInfo('foo.txt');
614
+        $this->assertEquals(500, $cachedData['mtime']);
615
+        $this->assertEquals($oldCachedData['storage_mtime'], $cachedData['storage_mtime']);
616
+
617
+        $rootView->putFileInfo('foo.txt', ['storage_mtime' => 1000]); //make sure the watcher detects the change
618
+        $rootView->file_put_contents('foo.txt', 'asd');
619
+        $cachedData = $rootView->getFileInfo('foo.txt');
620
+        $this->assertGreaterThanOrEqual($oldCachedData['mtime'], $cachedData['mtime']);
621
+        $this->assertEquals($cachedData['storage_mtime'], $cachedData['mtime']);
622
+    }
623
+
624
+    /**
625
+     * @medium
626
+     */
627
+    public function testTouchFloat(): void {
628
+        $storage = $this->getTestStorage(true, TemporaryNoTouch::class);
629
+
630
+        Filesystem::mount($storage, [], '/');
631
+
632
+        $rootView = new View('');
633
+        $oldCachedData = $rootView->getFileInfo('foo.txt');
634
+
635
+        $rootView->touch('foo.txt', 500.5);
636
+
637
+        $cachedData = $rootView->getFileInfo('foo.txt');
638
+        $this->assertEquals(500, $cachedData['mtime']);
639
+    }
640
+
641
+    /**
642
+     * @medium
643
+     */
644
+    public function testViewHooks(): void {
645
+        $storage1 = $this->getTestStorage();
646
+        $storage2 = $this->getTestStorage();
647
+        $defaultRoot = Filesystem::getRoot();
648
+        Filesystem::mount($storage1, [], '/');
649
+        Filesystem::mount($storage2, [], $defaultRoot . '/substorage');
650
+        \OC_Hook::connect('OC_Filesystem', 'post_write', $this, 'dummyHook');
651
+
652
+        $rootView = new View('');
653
+        $subView = new View($defaultRoot . '/substorage');
654
+        $this->hookPath = null;
655
+
656
+        $rootView->file_put_contents('/foo.txt', 'asd');
657
+        $this->assertNull($this->hookPath);
658
+
659
+        $subView->file_put_contents('/foo.txt', 'asd');
660
+        $this->assertEquals('/substorage/foo.txt', $this->hookPath);
661
+    }
662
+
663
+    private $hookPath;
664
+
665
+    public function dummyHook($params) {
666
+        $this->hookPath = $params['path'];
667
+    }
668
+
669
+    public function testSearchNotOutsideView(): void {
670
+        $storage1 = $this->getTestStorage();
671
+        Filesystem::mount($storage1, [], '/');
672
+        $storage1->rename('folder', 'foo');
673
+        $scanner = $storage1->getScanner();
674
+        $scanner->scan('');
675
+
676
+        $view = new View('/foo');
677
+
678
+        $result = $view->search('.txt');
679
+        $this->assertCount(1, $result);
680
+    }
681
+
682
+    /**
683
+     * @param bool $scan
684
+     * @param string $class
685
+     * @return \OC\Files\Storage\Storage
686
+     */
687
+    private function getTestStorage($scan = true, $class = Temporary::class) {
688
+        /**
689
+         * @var \OC\Files\Storage\Storage $storage
690
+         */
691
+        $storage = new $class([]);
692
+        $textData = "dummy file data\n";
693
+        $imgData = file_get_contents(\OC::$SERVERROOT . '/core/img/logo/logo.png');
694
+        $storage->mkdir('folder');
695
+        $storage->file_put_contents('foo.txt', $textData);
696
+        $storage->file_put_contents('foo.png', $imgData);
697
+        $storage->file_put_contents('folder/bar.txt', $textData);
698
+
699
+        if ($scan) {
700
+            $scanner = $storage->getScanner();
701
+            $scanner->scan('');
702
+        }
703
+        $this->storages[] = $storage;
704
+        return $storage;
705
+    }
706
+
707
+    /**
708
+     * @medium
709
+     */
710
+    public function testViewHooksIfRootStartsTheSame(): void {
711
+        $storage1 = $this->getTestStorage();
712
+        $storage2 = $this->getTestStorage();
713
+        $defaultRoot = Filesystem::getRoot();
714
+        Filesystem::mount($storage1, [], '/');
715
+        Filesystem::mount($storage2, [], $defaultRoot . '_substorage');
716
+        \OC_Hook::connect('OC_Filesystem', 'post_write', $this, 'dummyHook');
717
+
718
+        $subView = new View($defaultRoot . '_substorage');
719
+        $this->hookPath = null;
720
+
721
+        $subView->file_put_contents('/foo.txt', 'asd');
722
+        $this->assertNull($this->hookPath);
723
+    }
724
+
725
+    private $hookWritePath;
726
+    private $hookCreatePath;
727
+    private $hookUpdatePath;
728
+
729
+    public function dummyHookWrite($params) {
730
+        $this->hookWritePath = $params['path'];
731
+    }
732
+
733
+    public function dummyHookUpdate($params) {
734
+        $this->hookUpdatePath = $params['path'];
735
+    }
736
+
737
+    public function dummyHookCreate($params) {
738
+        $this->hookCreatePath = $params['path'];
739
+    }
740
+
741
+    public function testEditNoCreateHook(): void {
742
+        $storage1 = $this->getTestStorage();
743
+        $storage2 = $this->getTestStorage();
744
+        $defaultRoot = Filesystem::getRoot();
745
+        Filesystem::mount($storage1, [], '/');
746
+        Filesystem::mount($storage2, [], $defaultRoot);
747
+        \OC_Hook::connect('OC_Filesystem', 'post_create', $this, 'dummyHookCreate');
748
+        \OC_Hook::connect('OC_Filesystem', 'post_update', $this, 'dummyHookUpdate');
749
+        \OC_Hook::connect('OC_Filesystem', 'post_write', $this, 'dummyHookWrite');
750
+
751
+        $view = new View($defaultRoot);
752
+        $this->hookWritePath = $this->hookUpdatePath = $this->hookCreatePath = null;
753
+
754
+        $view->file_put_contents('/asd.txt', 'foo');
755
+        $this->assertEquals('/asd.txt', $this->hookCreatePath);
756
+        $this->assertNull($this->hookUpdatePath);
757
+        $this->assertEquals('/asd.txt', $this->hookWritePath);
758
+
759
+        $this->hookWritePath = $this->hookUpdatePath = $this->hookCreatePath = null;
760
+
761
+        $view->file_put_contents('/asd.txt', 'foo');
762
+        $this->assertNull($this->hookCreatePath);
763
+        $this->assertEquals('/asd.txt', $this->hookUpdatePath);
764
+        $this->assertEquals('/asd.txt', $this->hookWritePath);
765
+
766
+        \OC_Hook::clear('OC_Filesystem', 'post_create');
767
+        \OC_Hook::clear('OC_Filesystem', 'post_update');
768
+        \OC_Hook::clear('OC_Filesystem', 'post_write');
769
+    }
770
+
771
+    /**
772
+     * @dataProvider resolvePathTestProvider
773
+     */
774
+    public function testResolvePath($expected, $pathToTest): void {
775
+        $storage1 = $this->getTestStorage();
776
+        Filesystem::mount($storage1, [], '/');
777
+
778
+        $view = new View('');
779
+
780
+        $result = $view->resolvePath($pathToTest);
781
+        $this->assertEquals($expected, $result[1]);
782
+
783
+        $exists = $view->file_exists($pathToTest);
784
+        $this->assertTrue($exists);
785
+
786
+        $exists = $view->file_exists($result[1]);
787
+        $this->assertTrue($exists);
788
+    }
789
+
790
+    public static function resolvePathTestProvider(): array {
791
+        return [
792
+            ['foo.txt', 'foo.txt'],
793
+            ['foo.txt', '/foo.txt'],
794
+            ['folder', 'folder'],
795
+            ['folder', '/folder'],
796
+            ['folder', 'folder/'],
797
+            ['folder', '/folder/'],
798
+            ['folder/bar.txt', 'folder/bar.txt'],
799
+            ['folder/bar.txt', '/folder/bar.txt'],
800
+            ['', ''],
801
+            ['', '/'],
802
+        ];
803
+    }
804
+
805
+    public function testUTF8Names(): void {
806
+        $names = ['虚', '和知しゃ和で', 'regular ascii', 'sɨˈrɪlɪk', 'ѨѬ', 'أنا أحب القراءة كثيرا'];
807
+
808
+        $storage = new Temporary([]);
809
+        Filesystem::mount($storage, [], '/');
810
+
811
+        $rootView = new View('');
812
+        foreach ($names as $name) {
813
+            $rootView->file_put_contents('/' . $name, 'dummy content');
814
+        }
815
+
816
+        $list = $rootView->getDirectoryContent('/');
817
+
818
+        $this->assertCount(count($names), $list);
819
+        foreach ($list as $item) {
820
+            $this->assertContains($item['name'], $names);
821
+        }
822
+
823
+        $cache = $storage->getCache();
824
+        $scanner = $storage->getScanner();
825
+        $scanner->scan('');
826
+
827
+        $list = $cache->getFolderContents('');
828
+
829
+        $this->assertCount(count($names), $list);
830
+        foreach ($list as $item) {
831
+            $this->assertContains($item['name'], $names);
832
+        }
833
+    }
834
+
835
+    public function xtestLongPath() {
836
+        $storage = new Temporary([]);
837
+        Filesystem::mount($storage, [], '/');
838
+
839
+        $rootView = new View('');
840
+
841
+        $longPath = '';
842
+        $ds = DIRECTORY_SEPARATOR;
843
+        /*
844 844
 		 * 4096 is the maximum path length in file_cache.path in *nix
845 845
 		 * 1024 is the max path length in mac
846 846
 		 */
847
-		$folderName = 'abcdefghijklmnopqrstuvwxyz012345678901234567890123456789';
848
-		$tmpdirLength = strlen(\OC::$server->getTempManager()->getTemporaryFolder());
849
-		if (\OC_Util::runningOnMac()) {
850
-			$depth = ((1024 - $tmpdirLength) / 57);
851
-		} else {
852
-			$depth = ((4000 - $tmpdirLength) / 57);
853
-		}
854
-		foreach (range(0, $depth - 1) as $i) {
855
-			$longPath .= $ds . $folderName;
856
-			$result = $rootView->mkdir($longPath);
857
-			$this->assertTrue($result, "mkdir failed on $i - path length: " . strlen($longPath));
858
-
859
-			$result = $rootView->file_put_contents($longPath . "{$ds}test.txt", 'lorem');
860
-			$this->assertEquals(5, $result, "file_put_contents failed on $i");
861
-
862
-			$this->assertTrue($rootView->file_exists($longPath));
863
-			$this->assertTrue($rootView->file_exists($longPath . "{$ds}test.txt"));
864
-		}
865
-
866
-		$cache = $storage->getCache();
867
-		$scanner = $storage->getScanner();
868
-		$scanner->scan('');
869
-
870
-		$longPath = $folderName;
871
-		foreach (range(0, $depth - 1) as $i) {
872
-			$cachedFolder = $cache->get($longPath);
873
-			$this->assertTrue(is_array($cachedFolder), "No cache entry for folder at $i");
874
-			$this->assertEquals($folderName, $cachedFolder['name'], "Wrong cache entry for folder at $i");
875
-
876
-			$cachedFile = $cache->get($longPath . '/test.txt');
877
-			$this->assertTrue(is_array($cachedFile), "No cache entry for file at $i");
878
-			$this->assertEquals('test.txt', $cachedFile['name'], "Wrong cache entry for file at $i");
879
-
880
-			$longPath .= $ds . $folderName;
881
-		}
882
-	}
883
-
884
-	public function testTouchNotSupported(): void {
885
-		$storage = new TemporaryNoTouch([]);
886
-		$scanner = $storage->getScanner();
887
-		Filesystem::mount($storage, [], '/test/');
888
-		$past = time() - 100;
889
-		$storage->file_put_contents('test', 'foobar');
890
-		$scanner->scan('');
891
-		$view = new View('');
892
-		$info = $view->getFileInfo('/test/test');
893
-
894
-		$view->touch('/test/test', $past);
895
-		$scanner->scanFile('test', \OC\Files\Cache\Scanner::REUSE_ETAG);
896
-
897
-		$info2 = $view->getFileInfo('/test/test');
898
-		$this->assertSame($info['etag'], $info2['etag']);
899
-	}
900
-
901
-	public function testWatcherEtagCrossStorage(): void {
902
-		$storage1 = new Temporary([]);
903
-		$storage2 = new Temporary([]);
904
-		$scanner1 = $storage1->getScanner();
905
-		$scanner2 = $storage2->getScanner();
906
-		$storage1->mkdir('sub');
907
-		Filesystem::mount($storage1, [], '/test/');
908
-		Filesystem::mount($storage2, [], '/test/sub/storage');
909
-
910
-		$past = time() - 100;
911
-		$storage2->file_put_contents('test.txt', 'foobar');
912
-		$scanner1->scan('');
913
-		$scanner2->scan('');
914
-		$view = new View('');
915
-
916
-		$storage2->getWatcher('')->setPolicy(Watcher::CHECK_ALWAYS);
917
-
918
-		$oldFileInfo = $view->getFileInfo('/test/sub/storage/test.txt');
919
-		$oldFolderInfo = $view->getFileInfo('/test');
920
-
921
-		$storage2->getCache()->update($oldFileInfo->getId(), [
922
-			'storage_mtime' => $past,
923
-		]);
924
-
925
-		$oldEtag = $oldFolderInfo->getEtag();
926
-
927
-		$view->getFileInfo('/test/sub/storage/test.txt');
928
-		$newFolderInfo = $view->getFileInfo('/test');
929
-
930
-		$this->assertNotEquals($newFolderInfo->getEtag(), $oldEtag);
931
-	}
932
-
933
-	/**
934
-	 * @dataProvider absolutePathProvider
935
-	 */
936
-	public function testGetAbsolutePath($expectedPath, $relativePath): void {
937
-		$view = new View('/files');
938
-		$this->assertEquals($expectedPath, $view->getAbsolutePath($relativePath));
939
-	}
940
-
941
-	public function testPartFileInfo(): void {
942
-		$storage = new Temporary([]);
943
-		$scanner = $storage->getScanner();
944
-		Filesystem::mount($storage, [], '/test/');
945
-		$sizeWritten = $storage->file_put_contents('test.part', 'foobar');
946
-		$scanner->scan('');
947
-		$view = new View('/test');
948
-		$info = $view->getFileInfo('test.part');
949
-
950
-		$this->assertInstanceOf('\OCP\Files\FileInfo', $info);
951
-		$this->assertNull($info->getId());
952
-		$this->assertEquals(6, $sizeWritten);
953
-		$this->assertEquals(6, $info->getSize());
954
-		$this->assertEquals('foobar', $view->file_get_contents('test.part'));
955
-	}
956
-
957
-	public static function absolutePathProvider(): array {
958
-		return [
959
-			['/files/', ''],
960
-			['/files/0', '0'],
961
-			['/files/false', 'false'],
962
-			['/files/true', 'true'],
963
-			['/files/', '/'],
964
-			['/files/test', 'test'],
965
-			['/files/test', '/test'],
966
-		];
967
-	}
968
-
969
-	/**
970
-	 * @dataProvider chrootRelativePathProvider
971
-	 */
972
-	public function testChrootGetRelativePath($root, $absolutePath, $expectedPath): void {
973
-		$view = new View('/files');
974
-		$view->chroot($root);
975
-		$this->assertEquals($expectedPath, $view->getRelativePath($absolutePath));
976
-	}
977
-
978
-	public static function chrootRelativePathProvider(): array {
979
-		return self::relativePathProvider('/');
980
-	}
981
-
982
-	/**
983
-	 * @dataProvider initRelativePathProvider
984
-	 */
985
-	public function testInitGetRelativePath($root, $absolutePath, $expectedPath): void {
986
-		$view = new View($root);
987
-		$this->assertEquals($expectedPath, $view->getRelativePath($absolutePath));
988
-	}
989
-
990
-	public static function initRelativePathProvider(): array {
991
-		return self::relativePathProvider(null);
992
-	}
993
-
994
-	public static function relativePathProvider($missingRootExpectedPath): array {
995
-		return [
996
-			// No root - returns the path
997
-			['', '/files', '/files'],
998
-			['', '/files/', '/files/'],
999
-
1000
-			// Root equals path - /
1001
-			['/files/', '/files/', '/'],
1002
-			['/files/', '/files', '/'],
1003
-			['/files', '/files/', '/'],
1004
-			['/files', '/files', '/'],
1005
-
1006
-			// False negatives: chroot fixes those by adding the leading slash.
1007
-			// But setting them up with this root (instead of chroot($root))
1008
-			// will fail them, although they should be the same.
1009
-			// TODO init should be fixed, so it also adds the leading slash
1010
-			['files/', '/files/', $missingRootExpectedPath],
1011
-			['files', '/files/', $missingRootExpectedPath],
1012
-			['files/', '/files', $missingRootExpectedPath],
1013
-			['files', '/files', $missingRootExpectedPath],
1014
-
1015
-			// False negatives: Paths provided to the method should have a leading slash
1016
-			// TODO input should be checked to have a leading slash
1017
-			['/files/', 'files/', null],
1018
-			['/files', 'files/', null],
1019
-			['/files/', 'files', null],
1020
-			['/files', 'files', null],
1021
-
1022
-			// with trailing slashes
1023
-			['/files/', '/files/0', '0'],
1024
-			['/files/', '/files/false', 'false'],
1025
-			['/files/', '/files/true', 'true'],
1026
-			['/files/', '/files/test', 'test'],
1027
-			['/files/', '/files/test/foo', 'test/foo'],
1028
-
1029
-			// without trailing slashes
1030
-			// TODO false expectation: Should match "with trailing slashes"
1031
-			['/files', '/files/0', '/0'],
1032
-			['/files', '/files/false', '/false'],
1033
-			['/files', '/files/true', '/true'],
1034
-			['/files', '/files/test', '/test'],
1035
-			['/files', '/files/test/foo', '/test/foo'],
1036
-
1037
-			// leading slashes
1038
-			['/files/', '/files_trashbin/', null],
1039
-			['/files', '/files_trashbin/', null],
1040
-			['/files/', '/files_trashbin', null],
1041
-			['/files', '/files_trashbin', null],
1042
-
1043
-			// no leading slashes
1044
-			['files/', 'files_trashbin/', null],
1045
-			['files', 'files_trashbin/', null],
1046
-			['files/', 'files_trashbin', null],
1047
-			['files', 'files_trashbin', null],
1048
-
1049
-			// mixed leading slashes
1050
-			['files/', '/files_trashbin/', null],
1051
-			['/files/', 'files_trashbin/', null],
1052
-			['files', '/files_trashbin/', null],
1053
-			['/files', 'files_trashbin/', null],
1054
-			['files/', '/files_trashbin', null],
1055
-			['/files/', 'files_trashbin', null],
1056
-			['files', '/files_trashbin', null],
1057
-			['/files', 'files_trashbin', null],
1058
-
1059
-			['files', 'files_trashbin/test', null],
1060
-			['/files', '/files_trashbin/test', null],
1061
-			['/files', 'files_trashbin/test', null],
1062
-		];
1063
-	}
1064
-
1065
-	public function testFileView(): void {
1066
-		$storage = new Temporary([]);
1067
-		$scanner = $storage->getScanner();
1068
-		$storage->file_put_contents('foo.txt', 'bar');
1069
-		Filesystem::mount($storage, [], '/test/');
1070
-		$scanner->scan('');
1071
-		$view = new View('/test/foo.txt');
1072
-
1073
-		$this->assertEquals('bar', $view->file_get_contents(''));
1074
-		$fh = tmpfile();
1075
-		fwrite($fh, 'foo');
1076
-		rewind($fh);
1077
-		$view->file_put_contents('', $fh);
1078
-		$this->assertEquals('foo', $view->file_get_contents(''));
1079
-	}
1080
-
1081
-	/**
1082
-	 * @dataProvider tooLongPathDataProvider
1083
-	 */
1084
-	public function testTooLongPath($operation, $param0 = null): void {
1085
-		$this->expectException(\OCP\Files\InvalidPathException::class);
1086
-
1087
-
1088
-		$longPath = '';
1089
-		// 4000 is the maximum path length in file_cache.path
1090
-		$folderName = 'abcdefghijklmnopqrstuvwxyz012345678901234567890123456789';
1091
-		$depth = (4000 / 57);
1092
-		foreach (range(0, $depth + 1) as $i) {
1093
-			$longPath .= '/' . $folderName;
1094
-		}
1095
-
1096
-		$storage = new Temporary([]);
1097
-		$this->tempStorage = $storage; // for later hard cleanup
1098
-		Filesystem::mount($storage, [], '/');
1099
-
1100
-		$rootView = new View('');
1101
-
1102
-		if ($param0 === '@0') {
1103
-			$param0 = $longPath;
1104
-		}
1105
-
1106
-		if ($operation === 'hash') {
1107
-			$param0 = $longPath;
1108
-			$longPath = 'md5';
1109
-		}
1110
-
1111
-		call_user_func([$rootView, $operation], $longPath, $param0);
1112
-	}
1113
-
1114
-	public static function tooLongPathDataProvider(): array {
1115
-		return [
1116
-			['getAbsolutePath'],
1117
-			['getRelativePath'],
1118
-			['getMountPoint'],
1119
-			['resolvePath'],
1120
-			['getLocalFile'],
1121
-			['mkdir'],
1122
-			['rmdir'],
1123
-			['opendir'],
1124
-			['is_dir'],
1125
-			['is_file'],
1126
-			['stat'],
1127
-			['filetype'],
1128
-			['filesize'],
1129
-			['readfile'],
1130
-			['isCreatable'],
1131
-			['isReadable'],
1132
-			['isUpdatable'],
1133
-			['isDeletable'],
1134
-			['isSharable'],
1135
-			['file_exists'],
1136
-			['filemtime'],
1137
-			['touch'],
1138
-			['file_get_contents'],
1139
-			['unlink'],
1140
-			['deleteAll'],
1141
-			['toTmpFile'],
1142
-			['getMimeType'],
1143
-			['free_space'],
1144
-			['getFileInfo'],
1145
-			['getDirectoryContent'],
1146
-			['getOwner'],
1147
-			['getETag'],
1148
-			['file_put_contents', 'ipsum'],
1149
-			['rename', '@0'],
1150
-			['copy', '@0'],
1151
-			['fopen', 'r'],
1152
-			['fromTmpFile', '@0'],
1153
-			['hash'],
1154
-			['hasUpdated', 0],
1155
-			['putFileInfo', []],
1156
-		];
1157
-	}
1158
-
1159
-	public function testRenameCrossStoragePreserveMtime(): void {
1160
-		$storage1 = new Temporary([]);
1161
-		$storage2 = new Temporary([]);
1162
-		$storage1->mkdir('sub');
1163
-		$storage1->mkdir('foo');
1164
-		$storage1->file_put_contents('foo.txt', 'asd');
1165
-		$storage1->file_put_contents('foo/bar.txt', 'asd');
1166
-		Filesystem::mount($storage1, [], '/test/');
1167
-		Filesystem::mount($storage2, [], '/test/sub/storage');
1168
-
1169
-		$view = new View('');
1170
-		$time = time() - 200;
1171
-		$view->touch('/test/foo.txt', $time);
1172
-		$view->touch('/test/foo', $time);
1173
-		$view->touch('/test/foo/bar.txt', $time);
1174
-
1175
-		$view->rename('/test/foo.txt', '/test/sub/storage/foo.txt');
1176
-
1177
-		$this->assertEquals($time, $view->filemtime('/test/sub/storage/foo.txt'));
1178
-
1179
-		$view->rename('/test/foo', '/test/sub/storage/foo');
1180
-
1181
-		$this->assertEquals($time, $view->filemtime('/test/sub/storage/foo/bar.txt'));
1182
-	}
1183
-
1184
-	public function testRenameFailDeleteTargetKeepSource(): void {
1185
-		$this->doTestCopyRenameFail('rename');
1186
-	}
1187
-
1188
-	public function testCopyFailDeleteTargetKeepSource(): void {
1189
-		$this->doTestCopyRenameFail('copy');
1190
-	}
1191
-
1192
-	private function doTestCopyRenameFail($operation) {
1193
-		$storage1 = new Temporary([]);
1194
-		/** @var \PHPUnit\Framework\MockObject\MockObject|Temporary $storage2 */
1195
-		$storage2 = $this->getMockBuilder(TemporaryNoCross::class)
1196
-			->setConstructorArgs([[]])
1197
-			->onlyMethods(['fopen', 'writeStream'])
1198
-			->getMock();
1199
-
1200
-		$storage2->method('writeStream')
1201
-			->willThrowException(new GenericFileException('Failed to copy stream'));
1202
-
1203
-		$storage1->mkdir('sub');
1204
-		$storage1->file_put_contents('foo.txt', '0123456789ABCDEFGH');
1205
-		$storage1->mkdir('dirtomove');
1206
-		$storage1->file_put_contents('dirtomove/indir1.txt', '0123456'); // fits
1207
-		$storage1->file_put_contents('dirtomove/indir2.txt', '0123456789ABCDEFGH'); // doesn't fit
1208
-		$storage2->file_put_contents('existing.txt', '0123');
1209
-		$storage1->getScanner()->scan('');
1210
-		$storage2->getScanner()->scan('');
1211
-		Filesystem::mount($storage1, [], '/test/');
1212
-		Filesystem::mount($storage2, [], '/test/sub/storage');
1213
-
1214
-		// move file
1215
-		$view = new View('');
1216
-		$this->assertTrue($storage1->file_exists('foo.txt'));
1217
-		$this->assertFalse($storage2->file_exists('foo.txt'));
1218
-		$this->assertFalse($view->$operation('/test/foo.txt', '/test/sub/storage/foo.txt'));
1219
-		$this->assertFalse($storage2->file_exists('foo.txt'));
1220
-		$this->assertFalse($storage2->getCache()->get('foo.txt'));
1221
-		$this->assertTrue($storage1->file_exists('foo.txt'));
1222
-
1223
-		// if target exists, it will be deleted too
1224
-		$this->assertFalse($view->$operation('/test/foo.txt', '/test/sub/storage/existing.txt'));
1225
-		$this->assertFalse($storage2->file_exists('existing.txt'));
1226
-		$this->assertFalse($storage2->getCache()->get('existing.txt'));
1227
-		$this->assertTrue($storage1->file_exists('foo.txt'));
1228
-
1229
-		// move folder
1230
-		$this->assertFalse($view->$operation('/test/dirtomove/', '/test/sub/storage/dirtomove/'));
1231
-		// since the move failed, the full source tree is kept
1232
-		$this->assertTrue($storage1->file_exists('dirtomove/indir1.txt'));
1233
-		$this->assertTrue($storage1->file_exists('dirtomove/indir2.txt'));
1234
-		// second file not moved/copied
1235
-		$this->assertFalse($storage2->file_exists('dirtomove/indir2.txt'));
1236
-		$this->assertFalse($storage2->getCache()->get('dirtomove/indir2.txt'));
1237
-	}
1238
-
1239
-	public function testDeleteFailKeepCache(): void {
1240
-		/** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */
1241
-		$storage = $this->getMockBuilder(Temporary::class)
1242
-			->setConstructorArgs([[]])
1243
-			->onlyMethods(['unlink'])
1244
-			->getMock();
1245
-		$storage->expects($this->once())
1246
-			->method('unlink')
1247
-			->willReturn(false);
1248
-		$scanner = $storage->getScanner();
1249
-		$cache = $storage->getCache();
1250
-		$storage->file_put_contents('foo.txt', 'asd');
1251
-		$scanner->scan('');
1252
-		Filesystem::mount($storage, [], '/test/');
1253
-
1254
-		$view = new View('/test');
1255
-
1256
-		$this->assertFalse($view->unlink('foo.txt'));
1257
-		$this->assertTrue($cache->inCache('foo.txt'));
1258
-	}
1259
-
1260
-	public static function directoryTraversalProvider(): array {
1261
-		return [
1262
-			['../test/'],
1263
-			['..\\test\\my/../folder'],
1264
-			['/test/my/../foo\\'],
1265
-		];
1266
-	}
1267
-
1268
-	/**
1269
-	 * @dataProvider directoryTraversalProvider
1270
-	 * @param string $root
1271
-	 */
1272
-	public function testConstructDirectoryTraversalException($root): void {
1273
-		$this->expectException(\Exception::class);
1274
-
1275
-		new View($root);
1276
-	}
1277
-
1278
-	public function testRenameOverWrite(): void {
1279
-		$storage = new Temporary([]);
1280
-		$scanner = $storage->getScanner();
1281
-		$storage->mkdir('sub');
1282
-		$storage->mkdir('foo');
1283
-		$storage->file_put_contents('foo.txt', 'asd');
1284
-		$storage->file_put_contents('foo/bar.txt', 'asd');
1285
-		$scanner->scan('');
1286
-		Filesystem::mount($storage, [], '/test/');
1287
-		$view = new View('');
1288
-		$this->assertTrue($view->rename('/test/foo.txt', '/test/foo/bar.txt'));
1289
-	}
1290
-
1291
-	public function testSetMountOptionsInStorage(): void {
1292
-		$mount = new MountPoint(Temporary::class, '/asd/', [[]], Filesystem::getLoader(), ['foo' => 'bar']);
1293
-		Filesystem::getMountManager()->addMount($mount);
1294
-		/** @var \OC\Files\Storage\Common $storage */
1295
-		$storage = $mount->getStorage();
1296
-		$this->assertEquals($storage->getMountOption('foo'), 'bar');
1297
-	}
1298
-
1299
-	public function testSetMountOptionsWatcherPolicy(): void {
1300
-		$mount = new MountPoint(Temporary::class, '/asd/', [[]], Filesystem::getLoader(), ['filesystem_check_changes' => Watcher::CHECK_NEVER]);
1301
-		Filesystem::getMountManager()->addMount($mount);
1302
-		/** @var \OC\Files\Storage\Common $storage */
1303
-		$storage = $mount->getStorage();
1304
-		$watcher = $storage->getWatcher();
1305
-		$this->assertEquals(Watcher::CHECK_NEVER, $watcher->getPolicy());
1306
-	}
1307
-
1308
-	public function testGetAbsolutePathOnNull(): void {
1309
-		$view = new View();
1310
-		$this->assertNull($view->getAbsolutePath(null));
1311
-	}
1312
-
1313
-	public function testGetRelativePathOnNull(): void {
1314
-		$view = new View();
1315
-		$this->assertNull($view->getRelativePath(null));
1316
-	}
1317
-
1318
-
1319
-	public function testNullAsRoot(): void {
1320
-		$this->expectException(\TypeError::class);
1321
-
1322
-		new View(null);
1323
-	}
1324
-
1325
-	/**
1326
-	 * e.g. reading from a folder that's being renamed
1327
-	 *
1328
-	 *
1329
-	 * @dataProvider dataLockPaths
1330
-	 *
1331
-	 * @param string $rootPath
1332
-	 * @param string $pathPrefix
1333
-	 */
1334
-	public function testReadFromWriteLockedPath($rootPath, $pathPrefix): void {
1335
-		$this->expectException(\OCP\Lock\LockedException::class);
1336
-
1337
-		$rootPath = str_replace('{folder}', 'files', $rootPath);
1338
-		$pathPrefix = str_replace('{folder}', 'files', $pathPrefix);
1339
-
1340
-		$view = new View($rootPath);
1341
-		$storage = new Temporary([]);
1342
-		Filesystem::mount($storage, [], '/');
1343
-		$this->assertTrue($view->lockFile($pathPrefix . '/foo/bar', ILockingProvider::LOCK_EXCLUSIVE));
1344
-		$view->lockFile($pathPrefix . '/foo/bar/asd', ILockingProvider::LOCK_SHARED);
1345
-	}
1346
-
1347
-	/**
1348
-	 * Reading from a files_encryption folder that's being renamed
1349
-	 *
1350
-	 * @dataProvider dataLockPaths
1351
-	 *
1352
-	 * @param string $rootPath
1353
-	 * @param string $pathPrefix
1354
-	 */
1355
-	public function testReadFromWriteUnlockablePath($rootPath, $pathPrefix): void {
1356
-		$rootPath = str_replace('{folder}', 'files_encryption', $rootPath);
1357
-		$pathPrefix = str_replace('{folder}', 'files_encryption', $pathPrefix);
1358
-
1359
-		$view = new View($rootPath);
1360
-		$storage = new Temporary([]);
1361
-		Filesystem::mount($storage, [], '/');
1362
-		$this->assertFalse($view->lockFile($pathPrefix . '/foo/bar', ILockingProvider::LOCK_EXCLUSIVE));
1363
-		$this->assertFalse($view->lockFile($pathPrefix . '/foo/bar/asd', ILockingProvider::LOCK_SHARED));
1364
-	}
1365
-
1366
-	/**
1367
-	 * e.g. writing a file that's being downloaded
1368
-	 *
1369
-	 *
1370
-	 * @dataProvider dataLockPaths
1371
-	 *
1372
-	 * @param string $rootPath
1373
-	 * @param string $pathPrefix
1374
-	 */
1375
-	public function testWriteToReadLockedFile($rootPath, $pathPrefix): void {
1376
-		$this->expectException(\OCP\Lock\LockedException::class);
1377
-
1378
-		$rootPath = str_replace('{folder}', 'files', $rootPath);
1379
-		$pathPrefix = str_replace('{folder}', 'files', $pathPrefix);
1380
-
1381
-		$view = new View($rootPath);
1382
-		$storage = new Temporary([]);
1383
-		Filesystem::mount($storage, [], '/');
1384
-		$this->assertTrue($view->lockFile($pathPrefix . '/foo/bar', ILockingProvider::LOCK_SHARED));
1385
-		$view->lockFile($pathPrefix . '/foo/bar', ILockingProvider::LOCK_EXCLUSIVE);
1386
-	}
1387
-
1388
-	/**
1389
-	 * Writing a file that's being downloaded
1390
-	 *
1391
-	 * @dataProvider dataLockPaths
1392
-	 *
1393
-	 * @param string $rootPath
1394
-	 * @param string $pathPrefix
1395
-	 */
1396
-	public function testWriteToReadUnlockableFile($rootPath, $pathPrefix): void {
1397
-		$rootPath = str_replace('{folder}', 'files_encryption', $rootPath);
1398
-		$pathPrefix = str_replace('{folder}', 'files_encryption', $pathPrefix);
1399
-
1400
-		$view = new View($rootPath);
1401
-		$storage = new Temporary([]);
1402
-		Filesystem::mount($storage, [], '/');
1403
-		$this->assertFalse($view->lockFile($pathPrefix . '/foo/bar', ILockingProvider::LOCK_SHARED));
1404
-		$this->assertFalse($view->lockFile($pathPrefix . '/foo/bar', ILockingProvider::LOCK_EXCLUSIVE));
1405
-	}
1406
-
1407
-	/**
1408
-	 * Test that locks are on mount point paths instead of mount root
1409
-	 */
1410
-	public function testLockLocalMountPointPathInsteadOfStorageRoot(): void {
1411
-		$lockingProvider = \OC::$server->get(ILockingProvider::class);
1412
-		$view = new View('/testuser/files/');
1413
-		$storage = new Temporary([]);
1414
-		Filesystem::mount($storage, [], '/');
1415
-		$mountedStorage = new Temporary([]);
1416
-		Filesystem::mount($mountedStorage, [], '/testuser/files/mountpoint');
1417
-
1418
-		$this->assertTrue(
1419
-			$view->lockFile('/mountpoint', ILockingProvider::LOCK_EXCLUSIVE, true),
1420
-			'Can lock mount point'
1421
-		);
1422
-
1423
-		// no exception here because storage root was not locked
1424
-		$mountedStorage->acquireLock('', ILockingProvider::LOCK_EXCLUSIVE, $lockingProvider);
1425
-
1426
-		$thrown = false;
1427
-		try {
1428
-			$storage->acquireLock('/testuser/files/mountpoint', ILockingProvider::LOCK_EXCLUSIVE, $lockingProvider);
1429
-		} catch (LockedException $e) {
1430
-			$thrown = true;
1431
-		}
1432
-		$this->assertTrue($thrown, 'Mount point path was locked on root storage');
1433
-
1434
-		$lockingProvider->releaseAll();
1435
-	}
1436
-
1437
-	/**
1438
-	 * Test that locks are on mount point paths and also mount root when requested
1439
-	 */
1440
-	public function testLockStorageRootButNotLocalMountPoint(): void {
1441
-		$lockingProvider = \OC::$server->get(ILockingProvider::class);
1442
-		$view = new View('/testuser/files/');
1443
-		$storage = new Temporary([]);
1444
-		Filesystem::mount($storage, [], '/');
1445
-		$mountedStorage = new Temporary([]);
1446
-		Filesystem::mount($mountedStorage, [], '/testuser/files/mountpoint');
1447
-
1448
-		$this->assertTrue(
1449
-			$view->lockFile('/mountpoint', ILockingProvider::LOCK_EXCLUSIVE, false),
1450
-			'Can lock mount point'
1451
-		);
1452
-
1453
-		$thrown = false;
1454
-		try {
1455
-			$mountedStorage->acquireLock('', ILockingProvider::LOCK_EXCLUSIVE, $lockingProvider);
1456
-		} catch (LockedException $e) {
1457
-			$thrown = true;
1458
-		}
1459
-		$this->assertTrue($thrown, 'Mount point storage root was locked on original storage');
1460
-
1461
-		// local mount point was not locked
1462
-		$storage->acquireLock('/testuser/files/mountpoint', ILockingProvider::LOCK_EXCLUSIVE, $lockingProvider);
1463
-
1464
-		$lockingProvider->releaseAll();
1465
-	}
1466
-
1467
-	/**
1468
-	 * Test that locks are on mount point paths and also mount root when requested
1469
-	 */
1470
-	public function testLockMountPointPathFailReleasesBoth(): void {
1471
-		$lockingProvider = \OC::$server->get(ILockingProvider::class);
1472
-		$view = new View('/testuser/files/');
1473
-		$storage = new Temporary([]);
1474
-		Filesystem::mount($storage, [], '/');
1475
-		$mountedStorage = new Temporary([]);
1476
-		Filesystem::mount($mountedStorage, [], '/testuser/files/mountpoint.txt');
1477
-
1478
-		// this would happen if someone is writing on the mount point
1479
-		$mountedStorage->acquireLock('', ILockingProvider::LOCK_EXCLUSIVE, $lockingProvider);
1480
-
1481
-		$thrown = false;
1482
-		try {
1483
-			// this actually acquires two locks, one on the mount point and one on the storage root,
1484
-			// but the one on the storage root will fail
1485
-			$view->lockFile('/mountpoint.txt', ILockingProvider::LOCK_SHARED);
1486
-		} catch (LockedException $e) {
1487
-			$thrown = true;
1488
-		}
1489
-		$this->assertTrue($thrown, 'Cannot acquire shared lock because storage root is already locked');
1490
-
1491
-		// from here we expect that the lock on the local mount point was released properly
1492
-		// so acquiring an exclusive lock will succeed
1493
-		$storage->acquireLock('/testuser/files/mountpoint.txt', ILockingProvider::LOCK_EXCLUSIVE, $lockingProvider);
1494
-
1495
-		$lockingProvider->releaseAll();
1496
-	}
1497
-
1498
-	public static function dataLockPaths(): array {
1499
-		return [
1500
-			['/testuser/{folder}', ''],
1501
-			['/testuser', '/{folder}'],
1502
-			['', '/testuser/{folder}'],
1503
-		];
1504
-	}
1505
-
1506
-	public static function pathRelativeToFilesProvider(): array {
1507
-		return [
1508
-			['admin/files', ''],
1509
-			['admin/files/x', 'x'],
1510
-			['/admin/files', ''],
1511
-			['/admin/files/sub', 'sub'],
1512
-			['/admin/files/sub/', 'sub'],
1513
-			['/admin/files/sub/sub2', 'sub/sub2'],
1514
-			['//admin//files/sub//sub2', 'sub/sub2'],
1515
-		];
1516
-	}
1517
-
1518
-	/**
1519
-	 * @dataProvider pathRelativeToFilesProvider
1520
-	 */
1521
-	public function testGetPathRelativeToFiles($path, $expectedPath): void {
1522
-		$view = new View();
1523
-		$this->assertEquals($expectedPath, $view->getPathRelativeToFiles($path));
1524
-	}
1525
-
1526
-	public static function pathRelativeToFilesProviderExceptionCases(): array {
1527
-		return [
1528
-			[''],
1529
-			['x'],
1530
-			['files'],
1531
-			['/files'],
1532
-			['/admin/files_versions/abc'],
1533
-		];
1534
-	}
1535
-
1536
-	/**
1537
-	 * @dataProvider pathRelativeToFilesProviderExceptionCases
1538
-	 * @param string $path
1539
-	 */
1540
-	public function testGetPathRelativeToFilesWithInvalidArgument($path): void {
1541
-		$this->expectException(\InvalidArgumentException::class);
1542
-		$this->expectExceptionMessage('$absolutePath must be relative to "files"');
1543
-
1544
-		$view = new View();
1545
-		$view->getPathRelativeToFiles($path);
1546
-	}
1547
-
1548
-	public function testChangeLock(): void {
1549
-		$view = new View('/testuser/files/');
1550
-		$storage = new Temporary([]);
1551
-		Filesystem::mount($storage, [], '/');
1552
-
1553
-		$view->lockFile('/test/sub', ILockingProvider::LOCK_SHARED);
1554
-		$this->assertTrue($this->isFileLocked($view, '/test//sub', ILockingProvider::LOCK_SHARED));
1555
-		$this->assertFalse($this->isFileLocked($view, '/test//sub', ILockingProvider::LOCK_EXCLUSIVE));
1556
-
1557
-		$view->changeLock('//test/sub', ILockingProvider::LOCK_EXCLUSIVE);
1558
-		$this->assertTrue($this->isFileLocked($view, '/test//sub', ILockingProvider::LOCK_EXCLUSIVE));
1559
-
1560
-		$view->changeLock('test/sub', ILockingProvider::LOCK_SHARED);
1561
-		$this->assertTrue($this->isFileLocked($view, '/test//sub', ILockingProvider::LOCK_SHARED));
1562
-
1563
-		$view->unlockFile('/test/sub/', ILockingProvider::LOCK_SHARED);
1564
-
1565
-		$this->assertFalse($this->isFileLocked($view, '/test//sub', ILockingProvider::LOCK_SHARED));
1566
-		$this->assertFalse($this->isFileLocked($view, '/test//sub', ILockingProvider::LOCK_EXCLUSIVE));
1567
-	}
1568
-
1569
-	public static function hookPathProvider(): array {
1570
-		return [
1571
-			['/foo/files', '/foo', true],
1572
-			['/foo/files/bar', '/foo', true],
1573
-			['/foo', '/foo', false],
1574
-			['/foo', '/files/foo', true],
1575
-			['/foo', 'filesfoo', false],
1576
-			['', '/foo/files', true],
1577
-			['', '/foo/files/bar.txt', true],
1578
-		];
1579
-	}
1580
-
1581
-	/**
1582
-	 * @dataProvider hookPathProvider
1583
-	 * @param $root
1584
-	 * @param $path
1585
-	 * @param $shouldEmit
1586
-	 */
1587
-	public function testHookPaths($root, $path, $shouldEmit): void {
1588
-		$filesystemReflection = new \ReflectionClass(Filesystem::class);
1589
-		$defaultRootValue = $filesystemReflection->getProperty('defaultInstance');
1590
-		$defaultRootValue->setAccessible(true);
1591
-		$oldRoot = $defaultRootValue->getValue();
1592
-		$defaultView = new View('/foo/files');
1593
-		$defaultRootValue->setValue(null, $defaultView);
1594
-		$view = new View($root);
1595
-		$result = self::invokePrivate($view, 'shouldEmitHooks', [$path]);
1596
-		$defaultRootValue->setValue(null, $oldRoot);
1597
-		$this->assertEquals($shouldEmit, $result);
1598
-	}
1599
-
1600
-	/**
1601
-	 * Create test movable mount points
1602
-	 *
1603
-	 * @param array $mountPoints array of mount point locations
1604
-	 * @return array array of MountPoint objects
1605
-	 */
1606
-	private function createTestMovableMountPoints($mountPoints) {
1607
-		$mounts = [];
1608
-		foreach ($mountPoints as $mountPoint) {
1609
-			$storage = $this->getMockBuilder(Storage::class)
1610
-				->onlyMethods([])
1611
-				->getMock();
1612
-			$storage->method('getId')->willReturn('non-null-id');
1613
-			$storage->method('getStorageCache')->willReturnCallback(function () use ($storage) {
1614
-				return new \OC\Files\Cache\Storage($storage, true, \OC::$server->get(IDBConnection::class));
1615
-			});
1616
-
1617
-			$mounts[] = $this->getMockBuilder(TestMoveableMountPoint::class)
1618
-				->onlyMethods(['moveMount'])
1619
-				->setConstructorArgs([$storage, $mountPoint])
1620
-				->getMock();
1621
-		}
1622
-
1623
-		/** @var IMountProvider|\PHPUnit\Framework\MockObject\MockObject $mountProvider */
1624
-		$mountProvider = $this->createMock(IMountProvider::class);
1625
-		$mountProvider->expects($this->any())
1626
-			->method('getMountsForUser')
1627
-			->willReturn($mounts);
1628
-
1629
-		$mountProviderCollection = \OC::$server->getMountProviderCollection();
1630
-		$mountProviderCollection->registerProvider($mountProvider);
1631
-
1632
-		return $mounts;
1633
-	}
1634
-
1635
-	/**
1636
-	 * Test mount point move
1637
-	 */
1638
-	public function testMountPointMove(): void {
1639
-		self::loginAsUser($this->user);
1640
-
1641
-		[$mount1, $mount2] = $this->createTestMovableMountPoints([
1642
-			$this->user . '/files/mount1',
1643
-			$this->user . '/files/mount2',
1644
-		]);
1645
-		$mount1->expects($this->once())
1646
-			->method('moveMount')
1647
-			->willReturn(true);
1648
-
1649
-		$mount2->expects($this->once())
1650
-			->method('moveMount')
1651
-			->willReturn(true);
1652
-
1653
-		$view = new View('/' . $this->user . '/files/');
1654
-		$view->mkdir('sub');
1655
-
1656
-		$this->assertTrue($view->rename('mount1', 'renamed_mount'), 'Can rename mount point');
1657
-		$this->assertTrue($view->rename('mount2', 'sub/moved_mount'), 'Can move a mount point into a subdirectory');
1658
-	}
1659
-
1660
-	public function testMoveMountPointOverwrite(): void {
1661
-		self::loginAsUser($this->user);
1662
-
1663
-		[$mount1, $mount2] = $this->createTestMovableMountPoints([
1664
-			$this->user . '/files/mount1',
1665
-			$this->user . '/files/mount2',
1666
-		]);
1667
-
1668
-		$mount1->expects($this->never())
1669
-			->method('moveMount');
1670
-
1671
-		$mount2->expects($this->never())
1672
-			->method('moveMount');
1673
-
1674
-		$view = new View('/' . $this->user . '/files/');
1675
-
1676
-		$this->expectException(ForbiddenException::class);
1677
-		$view->rename('mount1', 'mount2');
1678
-	}
1679
-
1680
-	public function testMoveMountPointIntoMount(): void {
1681
-		self::loginAsUser($this->user);
1682
-
1683
-		[$mount1, $mount2] = $this->createTestMovableMountPoints([
1684
-			$this->user . '/files/mount1',
1685
-			$this->user . '/files/mount2',
1686
-		]);
1687
-
1688
-		$mount1->expects($this->never())
1689
-			->method('moveMount');
1690
-
1691
-		$mount2->expects($this->never())
1692
-			->method('moveMount');
1693
-
1694
-		$view = new View('/' . $this->user . '/files/');
1695
-
1696
-		$this->expectException(ForbiddenException::class);
1697
-		$view->rename('mount1', 'mount2/sub');
1698
-	}
1699
-
1700
-	/**
1701
-	 * Test that moving a mount point into a shared folder is forbidden
1702
-	 */
1703
-	public function testMoveMountPointIntoSharedFolder(): void {
1704
-		self::loginAsUser($this->user);
1705
-
1706
-		[$mount1, $mount2] = $this->createTestMovableMountPoints([
1707
-			$this->user . '/files/mount1',
1708
-			$this->user . '/files/mount2',
1709
-		]);
1710
-
1711
-		$mount1->expects($this->never())
1712
-			->method('moveMount');
1713
-
1714
-		$mount2->expects($this->once())
1715
-			->method('moveMount')
1716
-			->willReturn(true);
1717
-
1718
-		$view = new View('/' . $this->user . '/files/');
1719
-		$view->mkdir('shareddir');
1720
-		$view->mkdir('shareddir/sub');
1721
-		$view->mkdir('shareddir/sub2');
1722
-		// Create a similar named but non-shared folder
1723
-		$view->mkdir('shareddir notshared');
1724
-
1725
-		$fileId = $view->getFileInfo('shareddir')->getId();
1726
-		$userObject = \OC::$server->getUserManager()->createUser('test2', 'IHateNonMockableStaticClasses');
1727
-
1728
-		$userFolder = \OC::$server->getUserFolder($this->user);
1729
-		$shareDir = $userFolder->get('shareddir');
1730
-		$shareManager = \OC::$server->get(IShareManager::class);
1731
-		$share = $shareManager->newShare();
1732
-		$share->setSharedWith('test2')
1733
-			->setSharedBy($this->user)
1734
-			->setShareType(IShare::TYPE_USER)
1735
-			->setPermissions(\OCP\Constants::PERMISSION_READ)
1736
-			->setNode($shareDir);
1737
-		$shareManager->createShare($share);
1738
-
1739
-		try {
1740
-			$view->rename('mount1', 'shareddir');
1741
-			$this->fail('Cannot overwrite shared folder');
1742
-		} catch (ForbiddenException $e) {
1743
-
1744
-		}
1745
-		try {
1746
-			$view->rename('mount1', 'shareddir/sub');
1747
-			$this->fail('Cannot move mount point into shared folder');
1748
-		} catch (ForbiddenException $e) {
1749
-
1750
-		}
1751
-		try {
1752
-			$view->rename('mount1', 'shareddir/sub/sub2');
1753
-			$this->fail('Cannot move mount point into shared subfolder');
1754
-		} catch (ForbiddenException $e) {
1755
-
1756
-		}
1757
-		$this->assertTrue($view->rename('mount2', 'shareddir notshared/sub'), 'Can move mount point into a similarly named but non-shared folder');
1758
-
1759
-		$shareManager->deleteShare($share);
1760
-		$userObject->delete();
1761
-	}
1762
-
1763
-	public static function basicOperationProviderForLocks(): array {
1764
-		return [
1765
-			// --- write hook ----
1766
-			[
1767
-				'touch',
1768
-				['touch-create.txt'],
1769
-				'touch-create.txt',
1770
-				'create',
1771
-				ILockingProvider::LOCK_SHARED,
1772
-				ILockingProvider::LOCK_EXCLUSIVE,
1773
-				ILockingProvider::LOCK_SHARED,
1774
-			],
1775
-			[
1776
-				'fopen',
1777
-				['test-write.txt', 'w'],
1778
-				'test-write.txt',
1779
-				'write',
1780
-				ILockingProvider::LOCK_SHARED,
1781
-				ILockingProvider::LOCK_EXCLUSIVE,
1782
-				null,
1783
-				// exclusive lock stays until fclose
1784
-				ILockingProvider::LOCK_EXCLUSIVE,
1785
-			],
1786
-			[
1787
-				'mkdir',
1788
-				['newdir'],
1789
-				'newdir',
1790
-				'write',
1791
-				ILockingProvider::LOCK_SHARED,
1792
-				ILockingProvider::LOCK_EXCLUSIVE,
1793
-				ILockingProvider::LOCK_SHARED,
1794
-			],
1795
-			[
1796
-				'file_put_contents',
1797
-				['file_put_contents.txt', 'blah'],
1798
-				'file_put_contents.txt',
1799
-				'write',
1800
-				ILockingProvider::LOCK_SHARED,
1801
-				ILockingProvider::LOCK_EXCLUSIVE,
1802
-				ILockingProvider::LOCK_SHARED,
1803
-				null,
1804
-				0,
1805
-			],
1806
-
1807
-			// ---- delete hook ----
1808
-			[
1809
-				'rmdir',
1810
-				['dir'],
1811
-				'dir',
1812
-				'delete',
1813
-				ILockingProvider::LOCK_SHARED,
1814
-				ILockingProvider::LOCK_EXCLUSIVE,
1815
-				ILockingProvider::LOCK_SHARED,
1816
-			],
1817
-			[
1818
-				'unlink',
1819
-				['test.txt'],
1820
-				'test.txt',
1821
-				'delete',
1822
-				ILockingProvider::LOCK_SHARED,
1823
-				ILockingProvider::LOCK_EXCLUSIVE,
1824
-				ILockingProvider::LOCK_SHARED,
1825
-			],
1826
-
1827
-			// ---- read hook (no post hooks) ----
1828
-			[
1829
-				'file_get_contents',
1830
-				['test.txt'],
1831
-				'test.txt',
1832
-				'read',
1833
-				ILockingProvider::LOCK_SHARED,
1834
-				ILockingProvider::LOCK_SHARED,
1835
-				null,
1836
-				null,
1837
-				false,
1838
-			],
1839
-			[
1840
-				'fopen',
1841
-				['test.txt', 'r'],
1842
-				'test.txt',
1843
-				'read',
1844
-				ILockingProvider::LOCK_SHARED,
1845
-				ILockingProvider::LOCK_SHARED,
1846
-				null,
1847
-			],
1848
-			[
1849
-				'opendir',
1850
-				['dir'],
1851
-				'dir',
1852
-				'read',
1853
-				ILockingProvider::LOCK_SHARED,
1854
-				ILockingProvider::LOCK_SHARED,
1855
-				null,
1856
-			],
1857
-
1858
-			// ---- no lock, touch hook ---
1859
-			['touch', ['test.txt'], 'test.txt', 'touch', null, null, null],
1860
-
1861
-			// ---- no hooks, no locks ---
1862
-			['is_dir', ['dir'], 'dir', null],
1863
-			['is_file', ['dir'], 'dir', null],
1864
-			[
1865
-				'stat',
1866
-				['dir'],
1867
-				'dir',
1868
-				null,
1869
-				ILockingProvider::LOCK_SHARED,
1870
-				ILockingProvider::LOCK_SHARED,
1871
-				ILockingProvider::LOCK_SHARED,
1872
-				null,
1873
-				false,
1874
-			],
1875
-			[
1876
-				'filetype',
1877
-				['dir'],
1878
-				'dir',
1879
-				null,
1880
-				ILockingProvider::LOCK_SHARED,
1881
-				ILockingProvider::LOCK_SHARED,
1882
-				ILockingProvider::LOCK_SHARED,
1883
-				null,
1884
-				false,
1885
-			],
1886
-			[
1887
-				'filesize',
1888
-				['dir'],
1889
-				'dir',
1890
-				null,
1891
-				ILockingProvider::LOCK_SHARED,
1892
-				ILockingProvider::LOCK_SHARED,
1893
-				ILockingProvider::LOCK_SHARED,
1894
-				null,
1895
-				/* Return an int */
1896
-				100
1897
-			],
1898
-			['isCreatable', ['dir'], 'dir', null],
1899
-			['isReadable', ['dir'], 'dir', null],
1900
-			['isUpdatable', ['dir'], 'dir', null],
1901
-			['isDeletable', ['dir'], 'dir', null],
1902
-			['isSharable', ['dir'], 'dir', null],
1903
-			['file_exists', ['dir'], 'dir', null],
1904
-			[
1905
-				'filemtime',
1906
-				['dir'],
1907
-				'dir',
1908
-				null,
1909
-				ILockingProvider::LOCK_SHARED,
1910
-				ILockingProvider::LOCK_SHARED,
1911
-				ILockingProvider::LOCK_SHARED,
1912
-				null,
1913
-				false,
1914
-			],
1915
-		];
1916
-	}
1917
-
1918
-	/**
1919
-	 * Test whether locks are set before and after the operation
1920
-	 *
1921
-	 * @dataProvider basicOperationProviderForLocks
1922
-	 *
1923
-	 * @param string $operation operation name on the view
1924
-	 * @param array $operationArgs arguments for the operation
1925
-	 * @param string $lockedPath path of the locked item to check
1926
-	 * @param string $hookType hook type
1927
-	 * @param int $expectedLockBefore expected lock during pre hooks
1928
-	 * @param int $expectedLockDuring expected lock during operation
1929
-	 * @param int $expectedLockAfter expected lock during post hooks
1930
-	 * @param int $expectedStrayLock expected lock after returning, should
1931
-	 *                               be null (unlock) for most operations
1932
-	 */
1933
-	public function testLockBasicOperation(
1934
-		$operation,
1935
-		$operationArgs,
1936
-		$lockedPath,
1937
-		$hookType,
1938
-		$expectedLockBefore = ILockingProvider::LOCK_SHARED,
1939
-		$expectedLockDuring = ILockingProvider::LOCK_SHARED,
1940
-		$expectedLockAfter = ILockingProvider::LOCK_SHARED,
1941
-		$expectedStrayLock = null,
1942
-		$returnValue = true,
1943
-	): void {
1944
-		$view = new View('/' . $this->user . '/files/');
1945
-
1946
-		/** @var Temporary&MockObject $storage */
1947
-		$storage = $this->getMockBuilder(Temporary::class)
1948
-			->onlyMethods([$operation])
1949
-			->getMock();
1950
-
1951
-		/* Pause trash to avoid the trashbin intercepting rmdir and unlink calls */
1952
-		Server::get(ITrashManager::class)->pauseTrash();
1953
-		/* Same thing with encryption wrapper */
1954
-		Server::get(IStorageFactory::class)->removeStorageWrapper('oc_encryption');
1955
-
1956
-		Filesystem::mount($storage, [], $this->user . '/');
1957
-
1958
-		// work directly on disk because mkdir might be mocked
1959
-		$realPath = $storage->getSourcePath('');
1960
-		mkdir($realPath . '/files');
1961
-		mkdir($realPath . '/files/dir');
1962
-		file_put_contents($realPath . '/files/test.txt', 'blah');
1963
-		$storage->getScanner()->scan('files');
1964
-
1965
-		$storage->expects($this->once())
1966
-			->method($operation)
1967
-			->willReturnCallback(
1968
-				function () use ($view, $lockedPath, &$lockTypeDuring, $returnValue) {
1969
-					$lockTypeDuring = $this->getFileLockType($view, $lockedPath);
1970
-
1971
-					return $returnValue;
1972
-				}
1973
-			);
1974
-
1975
-		$this->assertNull($this->getFileLockType($view, $lockedPath), 'File not locked before operation');
1976
-
1977
-		$this->connectMockHooks($hookType, $view, $lockedPath, $lockTypePre, $lockTypePost);
1978
-
1979
-		// do operation
1980
-		call_user_func_array([$view, $operation], $operationArgs);
1981
-
1982
-		if ($hookType !== null) {
1983
-			$this->assertEquals($expectedLockBefore, $lockTypePre, 'File locked properly during pre-hook');
1984
-			$this->assertEquals($expectedLockAfter, $lockTypePost, 'File locked properly during post-hook');
1985
-			$this->assertEquals($expectedLockDuring, $lockTypeDuring, 'File locked properly during operation');
1986
-		} else {
1987
-			$this->assertNull($lockTypeDuring, 'File not locked during operation');
1988
-		}
1989
-
1990
-		$this->assertEquals($expectedStrayLock, $this->getFileLockType($view, $lockedPath));
1991
-
1992
-		/* Resume trash to avoid side effects */
1993
-		Server::get(ITrashManager::class)->resumeTrash();
1994
-	}
1995
-
1996
-	/**
1997
-	 * Test locks for file_put_content with stream.
1998
-	 * This code path uses $storage->fopen instead
1999
-	 */
2000
-	public function testLockFilePutContentWithStream(): void {
2001
-		$view = new View('/' . $this->user . '/files/');
2002
-
2003
-		$path = 'test_file_put_contents.txt';
2004
-		/** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */
2005
-		$storage = $this->getMockBuilder(Temporary::class)
2006
-			->onlyMethods(['fopen'])
2007
-			->getMock();
2008
-
2009
-		Filesystem::mount($storage, [], $this->user . '/');
2010
-		$storage->mkdir('files');
2011
-
2012
-		$storage->expects($this->once())
2013
-			->method('fopen')
2014
-			->willReturnCallback(
2015
-				function () use ($view, $path, &$lockTypeDuring) {
2016
-					$lockTypeDuring = $this->getFileLockType($view, $path);
2017
-
2018
-					return fopen('php://temp', 'r+');
2019
-				}
2020
-			);
2021
-
2022
-		$this->connectMockHooks('write', $view, $path, $lockTypePre, $lockTypePost);
2023
-
2024
-		$this->assertNull($this->getFileLockType($view, $path), 'File not locked before operation');
2025
-
2026
-		// do operation
2027
-		$view->file_put_contents($path, fopen('php://temp', 'r+'));
2028
-
2029
-		$this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypePre, 'File locked properly during pre-hook');
2030
-		$this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypePost, 'File locked properly during post-hook');
2031
-		$this->assertEquals(ILockingProvider::LOCK_EXCLUSIVE, $lockTypeDuring, 'File locked properly during operation');
2032
-
2033
-		$this->assertNull($this->getFileLockType($view, $path));
2034
-	}
2035
-
2036
-	/**
2037
-	 * Test locks for fopen with fclose at the end
2038
-	 */
2039
-	public function testLockFopen(): void {
2040
-		$view = new View('/' . $this->user . '/files/');
2041
-
2042
-		$path = 'test_file_put_contents.txt';
2043
-		/** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */
2044
-		$storage = $this->getMockBuilder(Temporary::class)
2045
-			->onlyMethods(['fopen'])
2046
-			->getMock();
2047
-
2048
-		Filesystem::mount($storage, [], $this->user . '/');
2049
-		$storage->mkdir('files');
2050
-
2051
-		$storage->expects($this->once())
2052
-			->method('fopen')
2053
-			->willReturnCallback(
2054
-				function () use ($view, $path, &$lockTypeDuring) {
2055
-					$lockTypeDuring = $this->getFileLockType($view, $path);
2056
-
2057
-					return fopen('php://temp', 'r+');
2058
-				}
2059
-			);
2060
-
2061
-		$this->connectMockHooks('write', $view, $path, $lockTypePre, $lockTypePost);
2062
-
2063
-		$this->assertNull($this->getFileLockType($view, $path), 'File not locked before operation');
2064
-
2065
-		// do operation
2066
-		$res = $view->fopen($path, 'w');
2067
-
2068
-		$this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypePre, 'File locked properly during pre-hook');
2069
-		$this->assertEquals(ILockingProvider::LOCK_EXCLUSIVE, $lockTypeDuring, 'File locked properly during operation');
2070
-		$this->assertNull($lockTypePost, 'No post hook, no lock check possible');
2071
-
2072
-		$this->assertEquals(ILockingProvider::LOCK_EXCLUSIVE, $lockTypeDuring, 'File still locked after fopen');
2073
-
2074
-		fclose($res);
2075
-
2076
-		$this->assertNull($this->getFileLockType($view, $path), 'File unlocked after fclose');
2077
-	}
2078
-
2079
-	/**
2080
-	 * Test locks for fopen with fclose at the end
2081
-	 *
2082
-	 * @dataProvider basicOperationProviderForLocks
2083
-	 *
2084
-	 * @param string $operation operation name on the view
2085
-	 * @param array $operationArgs arguments for the operation
2086
-	 * @param string $path path of the locked item to check
2087
-	 */
2088
-	public function testLockBasicOperationUnlocksAfterException(
2089
-		$operation,
2090
-		$operationArgs,
2091
-		$path,
2092
-	): void {
2093
-		if ($operation === 'touch') {
2094
-			$this->markTestSkipped('touch handles storage exceptions internally');
2095
-		}
2096
-		$view = new View('/' . $this->user . '/files/');
2097
-
2098
-		/** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */
2099
-		$storage = $this->getMockBuilder(Temporary::class)
2100
-			->onlyMethods([$operation])
2101
-			->getMock();
2102
-
2103
-		/* Pause trash to avoid the trashbin intercepting rmdir and unlink calls */
2104
-		Server::get(ITrashManager::class)->pauseTrash();
2105
-		/* Same thing with encryption wrapper */
2106
-		Server::get(IStorageFactory::class)->removeStorageWrapper('oc_encryption');
2107
-
2108
-		Filesystem::mount($storage, [], $this->user . '/');
2109
-
2110
-		// work directly on disk because mkdir might be mocked
2111
-		$realPath = $storage->getSourcePath('');
2112
-		mkdir($realPath . '/files');
2113
-		mkdir($realPath . '/files/dir');
2114
-		file_put_contents($realPath . '/files/test.txt', 'blah');
2115
-		$storage->getScanner()->scan('files');
2116
-
2117
-		$storage->expects($this->once())
2118
-			->method($operation)
2119
-			->willReturnCallback(
2120
-				function () {
2121
-					throw new \Exception('Simulated exception');
2122
-				}
2123
-			);
2124
-
2125
-		$thrown = false;
2126
-		try {
2127
-			call_user_func_array([$view, $operation], $operationArgs);
2128
-		} catch (\Exception $e) {
2129
-			$thrown = true;
2130
-			$this->assertEquals('Simulated exception', $e->getMessage());
2131
-		}
2132
-		$this->assertTrue($thrown, 'Exception was rethrown');
2133
-		$this->assertNull($this->getFileLockType($view, $path), 'File got unlocked after exception');
2134
-
2135
-		/* Resume trash to avoid side effects */
2136
-		Server::get(ITrashManager::class)->resumeTrash();
2137
-	}
2138
-
2139
-	public function testLockBasicOperationUnlocksAfterLockException(): void {
2140
-		$view = new View('/' . $this->user . '/files/');
2141
-
2142
-		$storage = new Temporary([]);
2143
-
2144
-		Filesystem::mount($storage, [], $this->user . '/');
2145
-
2146
-		$storage->mkdir('files');
2147
-		$storage->mkdir('files/dir');
2148
-		$storage->file_put_contents('files/test.txt', 'blah');
2149
-		$storage->getScanner()->scan('files');
2150
-
2151
-		// get a shared lock
2152
-		$handle = $view->fopen('test.txt', 'r');
2153
-
2154
-		$thrown = false;
2155
-		try {
2156
-			// try (and fail) to get a write lock
2157
-			$view->unlink('test.txt');
2158
-		} catch (\Exception $e) {
2159
-			$thrown = true;
2160
-			$this->assertInstanceOf(LockedException::class, $e);
2161
-		}
2162
-		$this->assertTrue($thrown, 'Exception was rethrown');
2163
-
2164
-		// clean shared lock
2165
-		fclose($handle);
2166
-
2167
-		$this->assertNull($this->getFileLockType($view, 'test.txt'), 'File got unlocked');
2168
-	}
2169
-
2170
-	/**
2171
-	 * Test locks for fopen with fclose at the end
2172
-	 *
2173
-	 * @dataProvider basicOperationProviderForLocks
2174
-	 *
2175
-	 * @param string $operation operation name on the view
2176
-	 * @param array $operationArgs arguments for the operation
2177
-	 * @param string $path path of the locked item to check
2178
-	 * @param string $hookType hook type
2179
-	 */
2180
-	public function testLockBasicOperationUnlocksAfterCancelledHook(
2181
-		$operation,
2182
-		$operationArgs,
2183
-		$path,
2184
-		$hookType,
2185
-	): void {
2186
-		$view = new View('/' . $this->user . '/files/');
2187
-
2188
-		/** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */
2189
-		$storage = $this->getMockBuilder(Temporary::class)
2190
-			->onlyMethods([$operation])
2191
-			->getMock();
2192
-
2193
-		Filesystem::mount($storage, [], $this->user . '/');
2194
-		$storage->mkdir('files');
2195
-
2196
-		Util::connectHook(
2197
-			Filesystem::CLASSNAME,
2198
-			$hookType,
2199
-			HookHelper::class,
2200
-			'cancellingCallback'
2201
-		);
2202
-
2203
-		call_user_func_array([$view, $operation], $operationArgs);
2204
-
2205
-		$this->assertNull($this->getFileLockType($view, $path), 'File got unlocked after exception');
2206
-	}
2207
-
2208
-	public static function lockFileRenameOrCopyDataProvider(): array {
2209
-		return [
2210
-			['rename', ILockingProvider::LOCK_EXCLUSIVE],
2211
-			['copy', ILockingProvider::LOCK_SHARED],
2212
-		];
2213
-	}
2214
-
2215
-	/**
2216
-	 * Test locks for rename or copy operation
2217
-	 *
2218
-	 * @dataProvider lockFileRenameOrCopyDataProvider
2219
-	 *
2220
-	 * @param string $operation operation to be done on the view
2221
-	 * @param int $expectedLockTypeSourceDuring expected lock type on source file during
2222
-	 *                                          the operation
2223
-	 */
2224
-	public function testLockFileRename($operation, $expectedLockTypeSourceDuring): void {
2225
-		$view = new View('/' . $this->user . '/files/');
2226
-
2227
-		/** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */
2228
-		$storage = $this->getMockBuilder(Temporary::class)
2229
-			->onlyMethods([$operation, 'getMetaData', 'filemtime'])
2230
-			->getMock();
2231
-
2232
-		$storage->expects($this->any())
2233
-			->method('getMetaData')
2234
-			->will($this->returnValue([
2235
-				'mtime' => 1885434487,
2236
-				'etag' => '',
2237
-				'mimetype' => 'text/plain',
2238
-				'permissions' => Constants::PERMISSION_ALL,
2239
-				'size' => 3
2240
-			]));
2241
-		$storage->expects($this->any())
2242
-			->method('filemtime')
2243
-			->willReturn(123456789);
2244
-
2245
-		$sourcePath = 'original.txt';
2246
-		$targetPath = 'target.txt';
2247
-
2248
-		/* Disable encryption wrapper to avoid it intercepting mocked call */
2249
-		Server::get(IStorageFactory::class)->removeStorageWrapper('oc_encryption');
2250
-
2251
-		Filesystem::mount($storage, [], $this->user . '/');
2252
-		$storage->mkdir('files');
2253
-		$view->file_put_contents($sourcePath, 'meh');
2254
-
2255
-		$storage->expects($this->once())
2256
-			->method($operation)
2257
-			->willReturnCallback(
2258
-				function () use ($view, $sourcePath, $targetPath, &$lockTypeSourceDuring, &$lockTypeTargetDuring) {
2259
-					$lockTypeSourceDuring = $this->getFileLockType($view, $sourcePath);
2260
-					$lockTypeTargetDuring = $this->getFileLockType($view, $targetPath);
2261
-
2262
-					return true;
2263
-				}
2264
-			);
2265
-
2266
-		$this->connectMockHooks($operation, $view, $sourcePath, $lockTypeSourcePre, $lockTypeSourcePost);
2267
-		$this->connectMockHooks($operation, $view, $targetPath, $lockTypeTargetPre, $lockTypeTargetPost);
2268
-
2269
-		$this->assertNull($this->getFileLockType($view, $sourcePath), 'Source file not locked before operation');
2270
-		$this->assertNull($this->getFileLockType($view, $targetPath), 'Target file not locked before operation');
2271
-
2272
-		$view->$operation($sourcePath, $targetPath);
2273
-
2274
-		$this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeSourcePre, 'Source file locked properly during pre-hook');
2275
-		$this->assertEquals($expectedLockTypeSourceDuring, $lockTypeSourceDuring, 'Source file locked properly during operation');
2276
-		$this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeSourcePost, 'Source file locked properly during post-hook');
2277
-
2278
-		$this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeTargetPre, 'Target file locked properly during pre-hook');
2279
-		$this->assertEquals(ILockingProvider::LOCK_EXCLUSIVE, $lockTypeTargetDuring, 'Target file locked properly during operation');
2280
-		$this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeTargetPost, 'Target file locked properly during post-hook');
2281
-
2282
-		$this->assertNull($this->getFileLockType($view, $sourcePath), 'Source file not locked after operation');
2283
-		$this->assertNull($this->getFileLockType($view, $targetPath), 'Target file not locked after operation');
2284
-	}
2285
-
2286
-	/**
2287
-	 * simulate a failed copy operation.
2288
-	 * We expect that we catch the exception, free the lock and re-throw it.
2289
-	 *
2290
-	 */
2291
-	public function testLockFileCopyException(): void {
2292
-		$this->expectException(\Exception::class);
2293
-
2294
-		$view = new View('/' . $this->user . '/files/');
2295
-
2296
-		/** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */
2297
-		$storage = $this->getMockBuilder(Temporary::class)
2298
-			->onlyMethods(['copy'])
2299
-			->getMock();
2300
-
2301
-		$sourcePath = 'original.txt';
2302
-		$targetPath = 'target.txt';
2303
-
2304
-		/* Disable encryption wrapper to avoid it intercepting mocked call */
2305
-		Server::get(IStorageFactory::class)->removeStorageWrapper('oc_encryption');
2306
-
2307
-		Filesystem::mount($storage, [], $this->user . '/');
2308
-		$storage->mkdir('files');
2309
-		$view->file_put_contents($sourcePath, 'meh');
2310
-
2311
-		$storage->expects($this->once())
2312
-			->method('copy')
2313
-			->willReturnCallback(
2314
-				function () {
2315
-					throw new \Exception();
2316
-				}
2317
-			);
2318
-
2319
-		$this->connectMockHooks('copy', $view, $sourcePath, $lockTypeSourcePre, $lockTypeSourcePost);
2320
-		$this->connectMockHooks('copy', $view, $targetPath, $lockTypeTargetPre, $lockTypeTargetPost);
2321
-
2322
-		$this->assertNull($this->getFileLockType($view, $sourcePath), 'Source file not locked before operation');
2323
-		$this->assertNull($this->getFileLockType($view, $targetPath), 'Target file not locked before operation');
2324
-
2325
-		try {
2326
-			$view->copy($sourcePath, $targetPath);
2327
-		} catch (\Exception $e) {
2328
-			$this->assertNull($this->getFileLockType($view, $sourcePath), 'Source file not locked after operation');
2329
-			$this->assertNull($this->getFileLockType($view, $targetPath), 'Target file not locked after operation');
2330
-			throw $e;
2331
-		}
2332
-	}
2333
-
2334
-	/**
2335
-	 * Test rename operation: unlock first path when second path was locked
2336
-	 */
2337
-	public function testLockFileRenameUnlockOnException(): void {
2338
-		self::loginAsUser('test');
2339
-
2340
-		$view = new View('/' . $this->user . '/files/');
2341
-
2342
-		$sourcePath = 'original.txt';
2343
-		$targetPath = 'target.txt';
2344
-		$view->file_put_contents($sourcePath, 'meh');
2345
-
2346
-		// simulate that the target path is already locked
2347
-		$view->lockFile($targetPath, ILockingProvider::LOCK_EXCLUSIVE);
2348
-
2349
-		$this->assertNull($this->getFileLockType($view, $sourcePath), 'Source file not locked before operation');
2350
-		$this->assertEquals(ILockingProvider::LOCK_EXCLUSIVE, $this->getFileLockType($view, $targetPath), 'Target file is locked before operation');
2351
-
2352
-		$thrown = false;
2353
-		try {
2354
-			$view->rename($sourcePath, $targetPath);
2355
-		} catch (LockedException $e) {
2356
-			$thrown = true;
2357
-		}
2358
-
2359
-		$this->assertTrue($thrown, 'LockedException thrown');
2360
-
2361
-		$this->assertNull($this->getFileLockType($view, $sourcePath), 'Source file not locked after operation');
2362
-		$this->assertEquals(ILockingProvider::LOCK_EXCLUSIVE, $this->getFileLockType($view, $targetPath), 'Target file still locked after operation');
2363
-
2364
-		$view->unlockFile($targetPath, ILockingProvider::LOCK_EXCLUSIVE);
2365
-	}
2366
-
2367
-	/**
2368
-	 * Test rename operation: unlock first path when second path was locked
2369
-	 */
2370
-	public function testGetOwner(): void {
2371
-		self::loginAsUser('test');
2372
-
2373
-		$view = new View('/test/files/');
2374
-
2375
-		$path = 'foo.txt';
2376
-		$view->file_put_contents($path, 'meh');
2377
-
2378
-		$this->assertEquals('test', $view->getFileInfo($path)->getOwner()->getUID());
2379
-
2380
-		$folderInfo = $view->getDirectoryContent('');
2381
-		$folderInfo = array_values(array_filter($folderInfo, function (FileInfo $info) {
2382
-			return $info->getName() === 'foo.txt';
2383
-		}));
2384
-
2385
-		$this->assertEquals('test', $folderInfo[0]->getOwner()->getUID());
2386
-
2387
-		$subStorage = new Temporary();
2388
-		Filesystem::mount($subStorage, [], '/test/files/asd');
2389
-
2390
-		$folderInfo = $view->getDirectoryContent('');
2391
-		$folderInfo = array_values(array_filter($folderInfo, function (FileInfo $info) {
2392
-			return $info->getName() === 'asd';
2393
-		}));
2394
-
2395
-		$this->assertEquals('test', $folderInfo[0]->getOwner()->getUID());
2396
-	}
2397
-
2398
-	public static function lockFileRenameOrCopyCrossStorageDataProvider(): array {
2399
-		return [
2400
-			['rename', 'moveFromStorage', ILockingProvider::LOCK_EXCLUSIVE],
2401
-			['copy', 'copyFromStorage', ILockingProvider::LOCK_SHARED],
2402
-		];
2403
-	}
2404
-
2405
-	/**
2406
-	 * Test locks for rename or copy operation cross-storage
2407
-	 *
2408
-	 * @dataProvider lockFileRenameOrCopyCrossStorageDataProvider
2409
-	 *
2410
-	 * @param string $viewOperation operation to be done on the view
2411
-	 * @param string $storageOperation operation to be mocked on the storage
2412
-	 * @param int $expectedLockTypeSourceDuring expected lock type on source file during
2413
-	 *                                          the operation
2414
-	 */
2415
-	public function testLockFileRenameCrossStorage($viewOperation, $storageOperation, $expectedLockTypeSourceDuring): void {
2416
-		$view = new View('/' . $this->user . '/files/');
2417
-
2418
-		/** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */
2419
-		$storage = $this->getMockBuilder(Temporary::class)
2420
-			->onlyMethods([$storageOperation])
2421
-			->getMock();
2422
-		/** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage2 */
2423
-		$storage2 = $this->getMockBuilder(Temporary::class)
2424
-			->onlyMethods([$storageOperation, 'getMetaData', 'filemtime'])
2425
-			->getMock();
2426
-
2427
-		$storage2->expects($this->any())
2428
-			->method('getMetaData')
2429
-			->will($this->returnValue([
2430
-				'mtime' => 1885434487,
2431
-				'etag' => '',
2432
-				'mimetype' => 'text/plain',
2433
-				'permissions' => Constants::PERMISSION_ALL,
2434
-				'size' => 3
2435
-			]));
2436
-		$storage2->expects($this->any())
2437
-			->method('filemtime')
2438
-			->willReturn(123456789);
2439
-
2440
-		$sourcePath = 'original.txt';
2441
-		$targetPath = 'substorage/target.txt';
2442
-
2443
-		/* Disable encryption wrapper to avoid it intercepting mocked call */
2444
-		Server::get(IStorageFactory::class)->removeStorageWrapper('oc_encryption');
2445
-
2446
-		Filesystem::mount($storage, [], $this->user . '/');
2447
-		Filesystem::mount($storage2, [], $this->user . '/files/substorage');
2448
-		$storage->mkdir('files');
2449
-		$view->file_put_contents($sourcePath, 'meh');
2450
-		$storage2->getUpdater()->update('');
2451
-
2452
-		$storage->expects($this->never())
2453
-			->method($storageOperation);
2454
-		$storage2->expects($this->once())
2455
-			->method($storageOperation)
2456
-			->willReturnCallback(
2457
-				function () use ($view, $sourcePath, $targetPath, &$lockTypeSourceDuring, &$lockTypeTargetDuring) {
2458
-					$lockTypeSourceDuring = $this->getFileLockType($view, $sourcePath);
2459
-					$lockTypeTargetDuring = $this->getFileLockType($view, $targetPath);
2460
-
2461
-					return true;
2462
-				}
2463
-			);
2464
-
2465
-		$this->connectMockHooks($viewOperation, $view, $sourcePath, $lockTypeSourcePre, $lockTypeSourcePost);
2466
-		$this->connectMockHooks($viewOperation, $view, $targetPath, $lockTypeTargetPre, $lockTypeTargetPost);
2467
-
2468
-		$this->assertNull($this->getFileLockType($view, $sourcePath), 'Source file not locked before operation');
2469
-		$this->assertNull($this->getFileLockType($view, $targetPath), 'Target file not locked before operation');
2470
-
2471
-		$view->$viewOperation($sourcePath, $targetPath);
2472
-
2473
-		$this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeSourcePre, 'Source file locked properly during pre-hook');
2474
-		$this->assertEquals($expectedLockTypeSourceDuring, $lockTypeSourceDuring, 'Source file locked properly during operation');
2475
-		$this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeSourcePost, 'Source file locked properly during post-hook');
2476
-
2477
-		$this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeTargetPre, 'Target file locked properly during pre-hook');
2478
-		$this->assertEquals(ILockingProvider::LOCK_EXCLUSIVE, $lockTypeTargetDuring, 'Target file locked properly during operation');
2479
-		$this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeTargetPost, 'Target file locked properly during post-hook');
2480
-
2481
-		$this->assertNull($this->getFileLockType($view, $sourcePath), 'Source file not locked after operation');
2482
-		$this->assertNull($this->getFileLockType($view, $targetPath), 'Target file not locked after operation');
2483
-	}
2484
-
2485
-	/**
2486
-	 * Test locks when moving a mount point
2487
-	 */
2488
-	public function testLockMoveMountPoint(): void {
2489
-		self::loginAsUser('test');
2490
-
2491
-		[$mount] = $this->createTestMovableMountPoints([
2492
-			$this->user . '/files/substorage',
2493
-		]);
2494
-
2495
-		$view = new View('/' . $this->user . '/files/');
2496
-		$view->mkdir('subdir');
2497
-
2498
-		$sourcePath = 'substorage';
2499
-		$targetPath = 'subdir/substorage_moved';
2500
-
2501
-		$mount->expects($this->once())
2502
-			->method('moveMount')
2503
-			->willReturnCallback(
2504
-				function ($target) use ($mount, $view, $sourcePath, $targetPath, &$lockTypeSourceDuring, &$lockTypeTargetDuring, &$lockTypeSharedRootDuring) {
2505
-					$lockTypeSourceDuring = $this->getFileLockType($view, $sourcePath, true);
2506
-					$lockTypeTargetDuring = $this->getFileLockType($view, $targetPath, true);
2507
-
2508
-					$lockTypeSharedRootDuring = $this->getFileLockType($view, $sourcePath, false);
2509
-
2510
-					$mount->setMountPoint($target);
2511
-
2512
-					return true;
2513
-				}
2514
-			);
2515
-
2516
-		$this->connectMockHooks('rename', $view, $sourcePath, $lockTypeSourcePre, $lockTypeSourcePost, true);
2517
-		$this->connectMockHooks('rename', $view, $targetPath, $lockTypeTargetPre, $lockTypeTargetPost, true);
2518
-		// in pre-hook, mount point is still on $sourcePath
2519
-		$this->connectMockHooks('rename', $view, $sourcePath, $lockTypeSharedRootPre, $dummy, false);
2520
-		// in post-hook, mount point is now on $targetPath
2521
-		$this->connectMockHooks('rename', $view, $targetPath, $dummy, $lockTypeSharedRootPost, false);
2522
-
2523
-		$this->assertNull($this->getFileLockType($view, $sourcePath, false), 'Shared storage root not locked before operation');
2524
-		$this->assertNull($this->getFileLockType($view, $sourcePath, true), 'Source path not locked before operation');
2525
-		$this->assertNull($this->getFileLockType($view, $targetPath, true), 'Target path not locked before operation');
2526
-
2527
-		$view->rename($sourcePath, $targetPath);
2528
-
2529
-		$this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeSourcePre, 'Source path locked properly during pre-hook');
2530
-		$this->assertEquals(ILockingProvider::LOCK_EXCLUSIVE, $lockTypeSourceDuring, 'Source path locked properly during operation');
2531
-		$this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeSourcePost, 'Source path locked properly during post-hook');
2532
-
2533
-		$this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeTargetPre, 'Target path locked properly during pre-hook');
2534
-		$this->assertEquals(ILockingProvider::LOCK_EXCLUSIVE, $lockTypeTargetDuring, 'Target path locked properly during operation');
2535
-		$this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeTargetPost, 'Target path locked properly during post-hook');
2536
-
2537
-		$this->assertNull($lockTypeSharedRootPre, 'Shared storage root not locked during pre-hook');
2538
-		$this->assertNull($lockTypeSharedRootDuring, 'Shared storage root not locked during move');
2539
-		$this->assertNull($lockTypeSharedRootPost, 'Shared storage root not locked during post-hook');
2540
-
2541
-		$this->assertNull($this->getFileLockType($view, $sourcePath, false), 'Shared storage root not locked after operation');
2542
-		$this->assertNull($this->getFileLockType($view, $sourcePath, true), 'Source path not locked after operation');
2543
-		$this->assertNull($this->getFileLockType($view, $targetPath, true), 'Target path not locked after operation');
2544
-	}
2545
-
2546
-	/**
2547
-	 * Connect hook callbacks for hook type
2548
-	 *
2549
-	 * @param string $hookType hook type or null for none
2550
-	 * @param View $view view to check the lock on
2551
-	 * @param string $path path for which to check the lock
2552
-	 * @param int $lockTypePre variable to receive lock type that was active in the pre-hook
2553
-	 * @param int $lockTypePost variable to receive lock type that was active in the post-hook
2554
-	 * @param bool $onMountPoint true to check the mount point instead of the
2555
-	 *                           mounted storage
2556
-	 */
2557
-	private function connectMockHooks($hookType, $view, $path, &$lockTypePre, &$lockTypePost, $onMountPoint = false) {
2558
-		if ($hookType === null) {
2559
-			return;
2560
-		}
2561
-
2562
-		$eventHandler = $this->getMockBuilder(TestEventHandler::class)
2563
-			->onlyMethods(['preCallback', 'postCallback'])
2564
-			->getMock();
2565
-
2566
-		$eventHandler->expects($this->any())
2567
-			->method('preCallback')
2568
-			->willReturnCallback(
2569
-				function () use ($view, $path, $onMountPoint, &$lockTypePre) {
2570
-					$lockTypePre = $this->getFileLockType($view, $path, $onMountPoint);
2571
-				}
2572
-			);
2573
-		$eventHandler->expects($this->any())
2574
-			->method('postCallback')
2575
-			->willReturnCallback(
2576
-				function () use ($view, $path, $onMountPoint, &$lockTypePost) {
2577
-					$lockTypePost = $this->getFileLockType($view, $path, $onMountPoint);
2578
-				}
2579
-			);
2580
-
2581
-		if ($hookType !== null) {
2582
-			Util::connectHook(
2583
-				Filesystem::CLASSNAME,
2584
-				$hookType,
2585
-				$eventHandler,
2586
-				'preCallback'
2587
-			);
2588
-			Util::connectHook(
2589
-				Filesystem::CLASSNAME,
2590
-				'post_' . $hookType,
2591
-				$eventHandler,
2592
-				'postCallback'
2593
-			);
2594
-		}
2595
-	}
2596
-
2597
-	/**
2598
-	 * Returns the file lock type
2599
-	 *
2600
-	 * @param View $view view
2601
-	 * @param string $path path
2602
-	 * @param bool $onMountPoint true to check the mount point instead of the
2603
-	 *                           mounted storage
2604
-	 *
2605
-	 * @return int lock type or null if file was not locked
2606
-	 */
2607
-	private function getFileLockType(View $view, $path, $onMountPoint = false) {
2608
-		if ($this->isFileLocked($view, $path, ILockingProvider::LOCK_EXCLUSIVE, $onMountPoint)) {
2609
-			return ILockingProvider::LOCK_EXCLUSIVE;
2610
-		} elseif ($this->isFileLocked($view, $path, ILockingProvider::LOCK_SHARED, $onMountPoint)) {
2611
-			return ILockingProvider::LOCK_SHARED;
2612
-		}
2613
-		return null;
2614
-	}
2615
-
2616
-
2617
-	public function testRemoveMoveableMountPoint(): void {
2618
-		$mountPoint = '/' . $this->user . '/files/mount/';
2619
-
2620
-		// Mock the mount point
2621
-		/** @var TestMoveableMountPoint|\PHPUnit\Framework\MockObject\MockObject $mount */
2622
-		$mount = $this->createMock(TestMoveableMountPoint::class);
2623
-		$mount->expects($this->once())
2624
-			->method('getMountPoint')
2625
-			->willReturn($mountPoint);
2626
-		$mount->expects($this->once())
2627
-			->method('removeMount')
2628
-			->willReturn('foo');
2629
-		$mount->expects($this->any())
2630
-			->method('getInternalPath')
2631
-			->willReturn('');
2632
-
2633
-		// Register mount
2634
-		Filesystem::getMountManager()->addMount($mount);
2635
-
2636
-		// Listen for events
2637
-		$eventHandler = $this->getMockBuilder(TestEventHandler::class)
2638
-			->onlyMethods(['umount', 'post_umount'])
2639
-			->getMock();
2640
-		$eventHandler->expects($this->once())
2641
-			->method('umount')
2642
-			->with([Filesystem::signal_param_path => '/mount']);
2643
-		$eventHandler->expects($this->once())
2644
-			->method('post_umount')
2645
-			->with([Filesystem::signal_param_path => '/mount']);
2646
-		Util::connectHook(
2647
-			Filesystem::CLASSNAME,
2648
-			'umount',
2649
-			$eventHandler,
2650
-			'umount'
2651
-		);
2652
-		Util::connectHook(
2653
-			Filesystem::CLASSNAME,
2654
-			'post_umount',
2655
-			$eventHandler,
2656
-			'post_umount'
2657
-		);
2658
-
2659
-		//Delete the mountpoint
2660
-		$view = new View('/' . $this->user . '/files');
2661
-		$this->assertEquals('foo', $view->rmdir('mount'));
2662
-	}
2663
-
2664
-	public static function mimeFilterProvider(): array {
2665
-		return [
2666
-			[null, ['test1.txt', 'test2.txt', 'test3.md', 'test4.png']],
2667
-			['text/plain', ['test1.txt', 'test2.txt']],
2668
-			['text/markdown', ['test3.md']],
2669
-			['text', ['test1.txt', 'test2.txt', 'test3.md']],
2670
-		];
2671
-	}
2672
-
2673
-	/**
2674
-	 * @param string $filter
2675
-	 * @param string[] $expected
2676
-	 * @dataProvider mimeFilterProvider
2677
-	 */
2678
-	public function testGetDirectoryContentMimeFilter($filter, $expected): void {
2679
-		$storage1 = new Temporary();
2680
-		$root = self::getUniqueID('/');
2681
-		Filesystem::mount($storage1, [], $root . '/');
2682
-		$view = new View($root);
2683
-
2684
-		$view->file_put_contents('test1.txt', 'asd');
2685
-		$view->file_put_contents('test2.txt', 'asd');
2686
-		$view->file_put_contents('test3.md', 'asd');
2687
-		$view->file_put_contents('test4.png', '');
2688
-
2689
-		$content = $view->getDirectoryContent('', $filter);
2690
-
2691
-		$files = array_map(function (FileInfo $info) {
2692
-			return $info->getName();
2693
-		}, $content);
2694
-		sort($files);
2695
-
2696
-		$this->assertEquals($expected, $files);
2697
-	}
2698
-
2699
-	public function testFilePutContentsClearsChecksum(): void {
2700
-		$storage = new Temporary([]);
2701
-		$scanner = $storage->getScanner();
2702
-		$storage->file_put_contents('foo.txt', 'bar');
2703
-		Filesystem::mount($storage, [], '/test/');
2704
-		$scanner->scan('');
2705
-
2706
-		$view = new View('/test/foo.txt');
2707
-		$view->putFileInfo('.', ['checksum' => '42']);
2708
-
2709
-		$this->assertEquals('bar', $view->file_get_contents(''));
2710
-		$fh = tmpfile();
2711
-		fwrite($fh, 'fooo');
2712
-		rewind($fh);
2713
-		clearstatcache();
2714
-		$view->file_put_contents('', $fh);
2715
-		$this->assertEquals('fooo', $view->file_get_contents(''));
2716
-		$data = $view->getFileInfo('.');
2717
-		$this->assertEquals('', $data->getChecksum());
2718
-	}
2719
-
2720
-	public function testDeleteGhostFile(): void {
2721
-		$storage = new Temporary([]);
2722
-		$scanner = $storage->getScanner();
2723
-		$cache = $storage->getCache();
2724
-		$storage->file_put_contents('foo.txt', 'bar');
2725
-		Filesystem::mount($storage, [], '/test/');
2726
-		$scanner->scan('');
2727
-
2728
-		$storage->unlink('foo.txt');
2729
-
2730
-		$this->assertTrue($cache->inCache('foo.txt'));
2731
-
2732
-		$view = new View('/test');
2733
-		$rootInfo = $view->getFileInfo('');
2734
-		$this->assertEquals(3, $rootInfo->getSize());
2735
-		$view->unlink('foo.txt');
2736
-		$newInfo = $view->getFileInfo('');
2737
-
2738
-		$this->assertFalse($cache->inCache('foo.txt'));
2739
-		$this->assertNotEquals($rootInfo->getEtag(), $newInfo->getEtag());
2740
-		$this->assertEquals(0, $newInfo->getSize());
2741
-	}
2742
-
2743
-	public function testDeleteGhostFolder(): void {
2744
-		$storage = new Temporary([]);
2745
-		$scanner = $storage->getScanner();
2746
-		$cache = $storage->getCache();
2747
-		$storage->mkdir('foo');
2748
-		$storage->file_put_contents('foo/foo.txt', 'bar');
2749
-		Filesystem::mount($storage, [], '/test/');
2750
-		$scanner->scan('');
2751
-
2752
-		$storage->rmdir('foo');
2753
-
2754
-		$this->assertTrue($cache->inCache('foo'));
2755
-		$this->assertTrue($cache->inCache('foo/foo.txt'));
2756
-
2757
-		$view = new View('/test');
2758
-		$rootInfo = $view->getFileInfo('');
2759
-		$this->assertEquals(3, $rootInfo->getSize());
2760
-		$view->rmdir('foo');
2761
-		$newInfo = $view->getFileInfo('');
2762
-
2763
-		$this->assertFalse($cache->inCache('foo'));
2764
-		$this->assertFalse($cache->inCache('foo/foo.txt'));
2765
-		$this->assertNotEquals($rootInfo->getEtag(), $newInfo->getEtag());
2766
-		$this->assertEquals(0, $newInfo->getSize());
2767
-	}
2768
-
2769
-	public function testCreateParentDirectories(): void {
2770
-		$view = $this->getMockBuilder(View::class)
2771
-			->disableOriginalConstructor()
2772
-			->onlyMethods([
2773
-				'is_file',
2774
-				'file_exists',
2775
-				'mkdir',
2776
-			])
2777
-			->getMock();
2778
-
2779
-		$view->expects($this->exactly(3))
2780
-			->method('is_file')
2781
-			->willReturnMap([
2782
-				['/new', false],
2783
-				['/new/folder', false],
2784
-				['/new/folder/structure', false],
2785
-			]);
2786
-		$view->expects($this->exactly(3))
2787
-			->method('file_exists')
2788
-			->willReturnMap([
2789
-				['/new', true],
2790
-				['/new/folder', false],
2791
-				['/new/folder/structure', false],
2792
-			]);
2793
-
2794
-		$calls = ['/new/folder', '/new/folder/structure'];
2795
-		$view->expects($this->exactly(2))
2796
-			->method('mkdir')
2797
-			->willReturnCallback(function ($dir) use (&$calls) {
2798
-				$expected = array_shift($calls);
2799
-				$this->assertEquals($expected, $dir);
2800
-			});
2801
-
2802
-		$this->assertTrue(self::invokePrivate($view, 'createParentDirectories', ['/new/folder/structure']));
2803
-	}
2804
-
2805
-	public function testCreateParentDirectoriesWithExistingFile(): void {
2806
-		$view = $this->getMockBuilder(View::class)
2807
-			->disableOriginalConstructor()
2808
-			->onlyMethods([
2809
-				'is_file',
2810
-				'file_exists',
2811
-				'mkdir',
2812
-			])
2813
-			->getMock();
2814
-
2815
-		$view
2816
-			->expects($this->once())
2817
-			->method('is_file')
2818
-			->with('/file.txt')
2819
-			->willReturn(true);
2820
-		$this->assertFalse(self::invokePrivate($view, 'createParentDirectories', ['/file.txt/folder/structure']));
2821
-	}
2822
-
2823
-	public function testCacheExtension(): void {
2824
-		$storage = new Temporary([]);
2825
-		$scanner = $storage->getScanner();
2826
-		$storage->file_put_contents('foo.txt', 'bar');
2827
-		$scanner->scan('');
2828
-
2829
-		Filesystem::mount($storage, [], '/test/');
2830
-		$view = new View('/test');
2831
-
2832
-		$info = $view->getFileInfo('/foo.txt');
2833
-		$this->assertEquals(0, $info->getUploadTime());
2834
-		$this->assertEquals(0, $info->getCreationTime());
2835
-
2836
-		$view->putFileInfo('/foo.txt', ['upload_time' => 25]);
2837
-
2838
-		$info = $view->getFileInfo('/foo.txt');
2839
-		$this->assertEquals(25, $info->getUploadTime());
2840
-		$this->assertEquals(0, $info->getCreationTime());
2841
-	}
2842
-
2843
-	public function testFopenGone(): void {
2844
-		$storage = new Temporary([]);
2845
-		$scanner = $storage->getScanner();
2846
-		$storage->file_put_contents('foo.txt', 'bar');
2847
-		$scanner->scan('');
2848
-		$cache = $storage->getCache();
2849
-
2850
-		Filesystem::mount($storage, [], '/test/');
2851
-		$view = new View('/test');
2852
-
2853
-		$storage->unlink('foo.txt');
2854
-
2855
-		$this->assertTrue($cache->inCache('foo.txt'));
2856
-
2857
-		$this->assertFalse($view->fopen('foo.txt', 'r'));
2858
-
2859
-		$this->assertFalse($cache->inCache('foo.txt'));
2860
-	}
2861
-
2862
-	public function testMountpointParentsCreated(): void {
2863
-		$storage1 = $this->getTestStorage();
2864
-		Filesystem::mount($storage1, [], '/');
2865
-
2866
-		$storage2 = $this->getTestStorage();
2867
-		Filesystem::mount($storage2, [], '/A/B/C');
2868
-
2869
-		$rootView = new View('');
2870
-
2871
-		$folderData = $rootView->getDirectoryContent('/');
2872
-		$this->assertCount(4, $folderData);
2873
-		$this->assertEquals('folder', $folderData[0]['name']);
2874
-		$this->assertEquals('foo.png', $folderData[1]['name']);
2875
-		$this->assertEquals('foo.txt', $folderData[2]['name']);
2876
-		$this->assertEquals('A', $folderData[3]['name']);
2877
-
2878
-		$folderData = $rootView->getDirectoryContent('/A');
2879
-		$this->assertCount(1, $folderData);
2880
-		$this->assertEquals('B', $folderData[0]['name']);
2881
-
2882
-		$folderData = $rootView->getDirectoryContent('/A/B');
2883
-		$this->assertCount(1, $folderData);
2884
-		$this->assertEquals('C', $folderData[0]['name']);
2885
-
2886
-		$folderData = $rootView->getDirectoryContent('/A/B/C');
2887
-		$this->assertCount(3, $folderData);
2888
-		$this->assertEquals('folder', $folderData[0]['name']);
2889
-		$this->assertEquals('foo.png', $folderData[1]['name']);
2890
-		$this->assertEquals('foo.txt', $folderData[2]['name']);
2891
-	}
2892
-
2893
-	public function testCopyPreservesContent() {
2894
-		$viewUser1 = new View('/' . 'userId' . '/files');
2895
-		$viewUser1->mkdir('');
2896
-		$viewUser1->file_put_contents('foo.txt', 'foo');
2897
-		$viewUser1->copy('foo.txt', 'bar.txt');
2898
-		$this->assertEquals('foo', $viewUser1->file_get_contents('bar.txt'));
2899
-	}
847
+        $folderName = 'abcdefghijklmnopqrstuvwxyz012345678901234567890123456789';
848
+        $tmpdirLength = strlen(\OC::$server->getTempManager()->getTemporaryFolder());
849
+        if (\OC_Util::runningOnMac()) {
850
+            $depth = ((1024 - $tmpdirLength) / 57);
851
+        } else {
852
+            $depth = ((4000 - $tmpdirLength) / 57);
853
+        }
854
+        foreach (range(0, $depth - 1) as $i) {
855
+            $longPath .= $ds . $folderName;
856
+            $result = $rootView->mkdir($longPath);
857
+            $this->assertTrue($result, "mkdir failed on $i - path length: " . strlen($longPath));
858
+
859
+            $result = $rootView->file_put_contents($longPath . "{$ds}test.txt", 'lorem');
860
+            $this->assertEquals(5, $result, "file_put_contents failed on $i");
861
+
862
+            $this->assertTrue($rootView->file_exists($longPath));
863
+            $this->assertTrue($rootView->file_exists($longPath . "{$ds}test.txt"));
864
+        }
865
+
866
+        $cache = $storage->getCache();
867
+        $scanner = $storage->getScanner();
868
+        $scanner->scan('');
869
+
870
+        $longPath = $folderName;
871
+        foreach (range(0, $depth - 1) as $i) {
872
+            $cachedFolder = $cache->get($longPath);
873
+            $this->assertTrue(is_array($cachedFolder), "No cache entry for folder at $i");
874
+            $this->assertEquals($folderName, $cachedFolder['name'], "Wrong cache entry for folder at $i");
875
+
876
+            $cachedFile = $cache->get($longPath . '/test.txt');
877
+            $this->assertTrue(is_array($cachedFile), "No cache entry for file at $i");
878
+            $this->assertEquals('test.txt', $cachedFile['name'], "Wrong cache entry for file at $i");
879
+
880
+            $longPath .= $ds . $folderName;
881
+        }
882
+    }
883
+
884
+    public function testTouchNotSupported(): void {
885
+        $storage = new TemporaryNoTouch([]);
886
+        $scanner = $storage->getScanner();
887
+        Filesystem::mount($storage, [], '/test/');
888
+        $past = time() - 100;
889
+        $storage->file_put_contents('test', 'foobar');
890
+        $scanner->scan('');
891
+        $view = new View('');
892
+        $info = $view->getFileInfo('/test/test');
893
+
894
+        $view->touch('/test/test', $past);
895
+        $scanner->scanFile('test', \OC\Files\Cache\Scanner::REUSE_ETAG);
896
+
897
+        $info2 = $view->getFileInfo('/test/test');
898
+        $this->assertSame($info['etag'], $info2['etag']);
899
+    }
900
+
901
+    public function testWatcherEtagCrossStorage(): void {
902
+        $storage1 = new Temporary([]);
903
+        $storage2 = new Temporary([]);
904
+        $scanner1 = $storage1->getScanner();
905
+        $scanner2 = $storage2->getScanner();
906
+        $storage1->mkdir('sub');
907
+        Filesystem::mount($storage1, [], '/test/');
908
+        Filesystem::mount($storage2, [], '/test/sub/storage');
909
+
910
+        $past = time() - 100;
911
+        $storage2->file_put_contents('test.txt', 'foobar');
912
+        $scanner1->scan('');
913
+        $scanner2->scan('');
914
+        $view = new View('');
915
+
916
+        $storage2->getWatcher('')->setPolicy(Watcher::CHECK_ALWAYS);
917
+
918
+        $oldFileInfo = $view->getFileInfo('/test/sub/storage/test.txt');
919
+        $oldFolderInfo = $view->getFileInfo('/test');
920
+
921
+        $storage2->getCache()->update($oldFileInfo->getId(), [
922
+            'storage_mtime' => $past,
923
+        ]);
924
+
925
+        $oldEtag = $oldFolderInfo->getEtag();
926
+
927
+        $view->getFileInfo('/test/sub/storage/test.txt');
928
+        $newFolderInfo = $view->getFileInfo('/test');
929
+
930
+        $this->assertNotEquals($newFolderInfo->getEtag(), $oldEtag);
931
+    }
932
+
933
+    /**
934
+     * @dataProvider absolutePathProvider
935
+     */
936
+    public function testGetAbsolutePath($expectedPath, $relativePath): void {
937
+        $view = new View('/files');
938
+        $this->assertEquals($expectedPath, $view->getAbsolutePath($relativePath));
939
+    }
940
+
941
+    public function testPartFileInfo(): void {
942
+        $storage = new Temporary([]);
943
+        $scanner = $storage->getScanner();
944
+        Filesystem::mount($storage, [], '/test/');
945
+        $sizeWritten = $storage->file_put_contents('test.part', 'foobar');
946
+        $scanner->scan('');
947
+        $view = new View('/test');
948
+        $info = $view->getFileInfo('test.part');
949
+
950
+        $this->assertInstanceOf('\OCP\Files\FileInfo', $info);
951
+        $this->assertNull($info->getId());
952
+        $this->assertEquals(6, $sizeWritten);
953
+        $this->assertEquals(6, $info->getSize());
954
+        $this->assertEquals('foobar', $view->file_get_contents('test.part'));
955
+    }
956
+
957
+    public static function absolutePathProvider(): array {
958
+        return [
959
+            ['/files/', ''],
960
+            ['/files/0', '0'],
961
+            ['/files/false', 'false'],
962
+            ['/files/true', 'true'],
963
+            ['/files/', '/'],
964
+            ['/files/test', 'test'],
965
+            ['/files/test', '/test'],
966
+        ];
967
+    }
968
+
969
+    /**
970
+     * @dataProvider chrootRelativePathProvider
971
+     */
972
+    public function testChrootGetRelativePath($root, $absolutePath, $expectedPath): void {
973
+        $view = new View('/files');
974
+        $view->chroot($root);
975
+        $this->assertEquals($expectedPath, $view->getRelativePath($absolutePath));
976
+    }
977
+
978
+    public static function chrootRelativePathProvider(): array {
979
+        return self::relativePathProvider('/');
980
+    }
981
+
982
+    /**
983
+     * @dataProvider initRelativePathProvider
984
+     */
985
+    public function testInitGetRelativePath($root, $absolutePath, $expectedPath): void {
986
+        $view = new View($root);
987
+        $this->assertEquals($expectedPath, $view->getRelativePath($absolutePath));
988
+    }
989
+
990
+    public static function initRelativePathProvider(): array {
991
+        return self::relativePathProvider(null);
992
+    }
993
+
994
+    public static function relativePathProvider($missingRootExpectedPath): array {
995
+        return [
996
+            // No root - returns the path
997
+            ['', '/files', '/files'],
998
+            ['', '/files/', '/files/'],
999
+
1000
+            // Root equals path - /
1001
+            ['/files/', '/files/', '/'],
1002
+            ['/files/', '/files', '/'],
1003
+            ['/files', '/files/', '/'],
1004
+            ['/files', '/files', '/'],
1005
+
1006
+            // False negatives: chroot fixes those by adding the leading slash.
1007
+            // But setting them up with this root (instead of chroot($root))
1008
+            // will fail them, although they should be the same.
1009
+            // TODO init should be fixed, so it also adds the leading slash
1010
+            ['files/', '/files/', $missingRootExpectedPath],
1011
+            ['files', '/files/', $missingRootExpectedPath],
1012
+            ['files/', '/files', $missingRootExpectedPath],
1013
+            ['files', '/files', $missingRootExpectedPath],
1014
+
1015
+            // False negatives: Paths provided to the method should have a leading slash
1016
+            // TODO input should be checked to have a leading slash
1017
+            ['/files/', 'files/', null],
1018
+            ['/files', 'files/', null],
1019
+            ['/files/', 'files', null],
1020
+            ['/files', 'files', null],
1021
+
1022
+            // with trailing slashes
1023
+            ['/files/', '/files/0', '0'],
1024
+            ['/files/', '/files/false', 'false'],
1025
+            ['/files/', '/files/true', 'true'],
1026
+            ['/files/', '/files/test', 'test'],
1027
+            ['/files/', '/files/test/foo', 'test/foo'],
1028
+
1029
+            // without trailing slashes
1030
+            // TODO false expectation: Should match "with trailing slashes"
1031
+            ['/files', '/files/0', '/0'],
1032
+            ['/files', '/files/false', '/false'],
1033
+            ['/files', '/files/true', '/true'],
1034
+            ['/files', '/files/test', '/test'],
1035
+            ['/files', '/files/test/foo', '/test/foo'],
1036
+
1037
+            // leading slashes
1038
+            ['/files/', '/files_trashbin/', null],
1039
+            ['/files', '/files_trashbin/', null],
1040
+            ['/files/', '/files_trashbin', null],
1041
+            ['/files', '/files_trashbin', null],
1042
+
1043
+            // no leading slashes
1044
+            ['files/', 'files_trashbin/', null],
1045
+            ['files', 'files_trashbin/', null],
1046
+            ['files/', 'files_trashbin', null],
1047
+            ['files', 'files_trashbin', null],
1048
+
1049
+            // mixed leading slashes
1050
+            ['files/', '/files_trashbin/', null],
1051
+            ['/files/', 'files_trashbin/', null],
1052
+            ['files', '/files_trashbin/', null],
1053
+            ['/files', 'files_trashbin/', null],
1054
+            ['files/', '/files_trashbin', null],
1055
+            ['/files/', 'files_trashbin', null],
1056
+            ['files', '/files_trashbin', null],
1057
+            ['/files', 'files_trashbin', null],
1058
+
1059
+            ['files', 'files_trashbin/test', null],
1060
+            ['/files', '/files_trashbin/test', null],
1061
+            ['/files', 'files_trashbin/test', null],
1062
+        ];
1063
+    }
1064
+
1065
+    public function testFileView(): void {
1066
+        $storage = new Temporary([]);
1067
+        $scanner = $storage->getScanner();
1068
+        $storage->file_put_contents('foo.txt', 'bar');
1069
+        Filesystem::mount($storage, [], '/test/');
1070
+        $scanner->scan('');
1071
+        $view = new View('/test/foo.txt');
1072
+
1073
+        $this->assertEquals('bar', $view->file_get_contents(''));
1074
+        $fh = tmpfile();
1075
+        fwrite($fh, 'foo');
1076
+        rewind($fh);
1077
+        $view->file_put_contents('', $fh);
1078
+        $this->assertEquals('foo', $view->file_get_contents(''));
1079
+    }
1080
+
1081
+    /**
1082
+     * @dataProvider tooLongPathDataProvider
1083
+     */
1084
+    public function testTooLongPath($operation, $param0 = null): void {
1085
+        $this->expectException(\OCP\Files\InvalidPathException::class);
1086
+
1087
+
1088
+        $longPath = '';
1089
+        // 4000 is the maximum path length in file_cache.path
1090
+        $folderName = 'abcdefghijklmnopqrstuvwxyz012345678901234567890123456789';
1091
+        $depth = (4000 / 57);
1092
+        foreach (range(0, $depth + 1) as $i) {
1093
+            $longPath .= '/' . $folderName;
1094
+        }
1095
+
1096
+        $storage = new Temporary([]);
1097
+        $this->tempStorage = $storage; // for later hard cleanup
1098
+        Filesystem::mount($storage, [], '/');
1099
+
1100
+        $rootView = new View('');
1101
+
1102
+        if ($param0 === '@0') {
1103
+            $param0 = $longPath;
1104
+        }
1105
+
1106
+        if ($operation === 'hash') {
1107
+            $param0 = $longPath;
1108
+            $longPath = 'md5';
1109
+        }
1110
+
1111
+        call_user_func([$rootView, $operation], $longPath, $param0);
1112
+    }
1113
+
1114
+    public static function tooLongPathDataProvider(): array {
1115
+        return [
1116
+            ['getAbsolutePath'],
1117
+            ['getRelativePath'],
1118
+            ['getMountPoint'],
1119
+            ['resolvePath'],
1120
+            ['getLocalFile'],
1121
+            ['mkdir'],
1122
+            ['rmdir'],
1123
+            ['opendir'],
1124
+            ['is_dir'],
1125
+            ['is_file'],
1126
+            ['stat'],
1127
+            ['filetype'],
1128
+            ['filesize'],
1129
+            ['readfile'],
1130
+            ['isCreatable'],
1131
+            ['isReadable'],
1132
+            ['isUpdatable'],
1133
+            ['isDeletable'],
1134
+            ['isSharable'],
1135
+            ['file_exists'],
1136
+            ['filemtime'],
1137
+            ['touch'],
1138
+            ['file_get_contents'],
1139
+            ['unlink'],
1140
+            ['deleteAll'],
1141
+            ['toTmpFile'],
1142
+            ['getMimeType'],
1143
+            ['free_space'],
1144
+            ['getFileInfo'],
1145
+            ['getDirectoryContent'],
1146
+            ['getOwner'],
1147
+            ['getETag'],
1148
+            ['file_put_contents', 'ipsum'],
1149
+            ['rename', '@0'],
1150
+            ['copy', '@0'],
1151
+            ['fopen', 'r'],
1152
+            ['fromTmpFile', '@0'],
1153
+            ['hash'],
1154
+            ['hasUpdated', 0],
1155
+            ['putFileInfo', []],
1156
+        ];
1157
+    }
1158
+
1159
+    public function testRenameCrossStoragePreserveMtime(): void {
1160
+        $storage1 = new Temporary([]);
1161
+        $storage2 = new Temporary([]);
1162
+        $storage1->mkdir('sub');
1163
+        $storage1->mkdir('foo');
1164
+        $storage1->file_put_contents('foo.txt', 'asd');
1165
+        $storage1->file_put_contents('foo/bar.txt', 'asd');
1166
+        Filesystem::mount($storage1, [], '/test/');
1167
+        Filesystem::mount($storage2, [], '/test/sub/storage');
1168
+
1169
+        $view = new View('');
1170
+        $time = time() - 200;
1171
+        $view->touch('/test/foo.txt', $time);
1172
+        $view->touch('/test/foo', $time);
1173
+        $view->touch('/test/foo/bar.txt', $time);
1174
+
1175
+        $view->rename('/test/foo.txt', '/test/sub/storage/foo.txt');
1176
+
1177
+        $this->assertEquals($time, $view->filemtime('/test/sub/storage/foo.txt'));
1178
+
1179
+        $view->rename('/test/foo', '/test/sub/storage/foo');
1180
+
1181
+        $this->assertEquals($time, $view->filemtime('/test/sub/storage/foo/bar.txt'));
1182
+    }
1183
+
1184
+    public function testRenameFailDeleteTargetKeepSource(): void {
1185
+        $this->doTestCopyRenameFail('rename');
1186
+    }
1187
+
1188
+    public function testCopyFailDeleteTargetKeepSource(): void {
1189
+        $this->doTestCopyRenameFail('copy');
1190
+    }
1191
+
1192
+    private function doTestCopyRenameFail($operation) {
1193
+        $storage1 = new Temporary([]);
1194
+        /** @var \PHPUnit\Framework\MockObject\MockObject|Temporary $storage2 */
1195
+        $storage2 = $this->getMockBuilder(TemporaryNoCross::class)
1196
+            ->setConstructorArgs([[]])
1197
+            ->onlyMethods(['fopen', 'writeStream'])
1198
+            ->getMock();
1199
+
1200
+        $storage2->method('writeStream')
1201
+            ->willThrowException(new GenericFileException('Failed to copy stream'));
1202
+
1203
+        $storage1->mkdir('sub');
1204
+        $storage1->file_put_contents('foo.txt', '0123456789ABCDEFGH');
1205
+        $storage1->mkdir('dirtomove');
1206
+        $storage1->file_put_contents('dirtomove/indir1.txt', '0123456'); // fits
1207
+        $storage1->file_put_contents('dirtomove/indir2.txt', '0123456789ABCDEFGH'); // doesn't fit
1208
+        $storage2->file_put_contents('existing.txt', '0123');
1209
+        $storage1->getScanner()->scan('');
1210
+        $storage2->getScanner()->scan('');
1211
+        Filesystem::mount($storage1, [], '/test/');
1212
+        Filesystem::mount($storage2, [], '/test/sub/storage');
1213
+
1214
+        // move file
1215
+        $view = new View('');
1216
+        $this->assertTrue($storage1->file_exists('foo.txt'));
1217
+        $this->assertFalse($storage2->file_exists('foo.txt'));
1218
+        $this->assertFalse($view->$operation('/test/foo.txt', '/test/sub/storage/foo.txt'));
1219
+        $this->assertFalse($storage2->file_exists('foo.txt'));
1220
+        $this->assertFalse($storage2->getCache()->get('foo.txt'));
1221
+        $this->assertTrue($storage1->file_exists('foo.txt'));
1222
+
1223
+        // if target exists, it will be deleted too
1224
+        $this->assertFalse($view->$operation('/test/foo.txt', '/test/sub/storage/existing.txt'));
1225
+        $this->assertFalse($storage2->file_exists('existing.txt'));
1226
+        $this->assertFalse($storage2->getCache()->get('existing.txt'));
1227
+        $this->assertTrue($storage1->file_exists('foo.txt'));
1228
+
1229
+        // move folder
1230
+        $this->assertFalse($view->$operation('/test/dirtomove/', '/test/sub/storage/dirtomove/'));
1231
+        // since the move failed, the full source tree is kept
1232
+        $this->assertTrue($storage1->file_exists('dirtomove/indir1.txt'));
1233
+        $this->assertTrue($storage1->file_exists('dirtomove/indir2.txt'));
1234
+        // second file not moved/copied
1235
+        $this->assertFalse($storage2->file_exists('dirtomove/indir2.txt'));
1236
+        $this->assertFalse($storage2->getCache()->get('dirtomove/indir2.txt'));
1237
+    }
1238
+
1239
+    public function testDeleteFailKeepCache(): void {
1240
+        /** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */
1241
+        $storage = $this->getMockBuilder(Temporary::class)
1242
+            ->setConstructorArgs([[]])
1243
+            ->onlyMethods(['unlink'])
1244
+            ->getMock();
1245
+        $storage->expects($this->once())
1246
+            ->method('unlink')
1247
+            ->willReturn(false);
1248
+        $scanner = $storage->getScanner();
1249
+        $cache = $storage->getCache();
1250
+        $storage->file_put_contents('foo.txt', 'asd');
1251
+        $scanner->scan('');
1252
+        Filesystem::mount($storage, [], '/test/');
1253
+
1254
+        $view = new View('/test');
1255
+
1256
+        $this->assertFalse($view->unlink('foo.txt'));
1257
+        $this->assertTrue($cache->inCache('foo.txt'));
1258
+    }
1259
+
1260
+    public static function directoryTraversalProvider(): array {
1261
+        return [
1262
+            ['../test/'],
1263
+            ['..\\test\\my/../folder'],
1264
+            ['/test/my/../foo\\'],
1265
+        ];
1266
+    }
1267
+
1268
+    /**
1269
+     * @dataProvider directoryTraversalProvider
1270
+     * @param string $root
1271
+     */
1272
+    public function testConstructDirectoryTraversalException($root): void {
1273
+        $this->expectException(\Exception::class);
1274
+
1275
+        new View($root);
1276
+    }
1277
+
1278
+    public function testRenameOverWrite(): void {
1279
+        $storage = new Temporary([]);
1280
+        $scanner = $storage->getScanner();
1281
+        $storage->mkdir('sub');
1282
+        $storage->mkdir('foo');
1283
+        $storage->file_put_contents('foo.txt', 'asd');
1284
+        $storage->file_put_contents('foo/bar.txt', 'asd');
1285
+        $scanner->scan('');
1286
+        Filesystem::mount($storage, [], '/test/');
1287
+        $view = new View('');
1288
+        $this->assertTrue($view->rename('/test/foo.txt', '/test/foo/bar.txt'));
1289
+    }
1290
+
1291
+    public function testSetMountOptionsInStorage(): void {
1292
+        $mount = new MountPoint(Temporary::class, '/asd/', [[]], Filesystem::getLoader(), ['foo' => 'bar']);
1293
+        Filesystem::getMountManager()->addMount($mount);
1294
+        /** @var \OC\Files\Storage\Common $storage */
1295
+        $storage = $mount->getStorage();
1296
+        $this->assertEquals($storage->getMountOption('foo'), 'bar');
1297
+    }
1298
+
1299
+    public function testSetMountOptionsWatcherPolicy(): void {
1300
+        $mount = new MountPoint(Temporary::class, '/asd/', [[]], Filesystem::getLoader(), ['filesystem_check_changes' => Watcher::CHECK_NEVER]);
1301
+        Filesystem::getMountManager()->addMount($mount);
1302
+        /** @var \OC\Files\Storage\Common $storage */
1303
+        $storage = $mount->getStorage();
1304
+        $watcher = $storage->getWatcher();
1305
+        $this->assertEquals(Watcher::CHECK_NEVER, $watcher->getPolicy());
1306
+    }
1307
+
1308
+    public function testGetAbsolutePathOnNull(): void {
1309
+        $view = new View();
1310
+        $this->assertNull($view->getAbsolutePath(null));
1311
+    }
1312
+
1313
+    public function testGetRelativePathOnNull(): void {
1314
+        $view = new View();
1315
+        $this->assertNull($view->getRelativePath(null));
1316
+    }
1317
+
1318
+
1319
+    public function testNullAsRoot(): void {
1320
+        $this->expectException(\TypeError::class);
1321
+
1322
+        new View(null);
1323
+    }
1324
+
1325
+    /**
1326
+     * e.g. reading from a folder that's being renamed
1327
+     *
1328
+     *
1329
+     * @dataProvider dataLockPaths
1330
+     *
1331
+     * @param string $rootPath
1332
+     * @param string $pathPrefix
1333
+     */
1334
+    public function testReadFromWriteLockedPath($rootPath, $pathPrefix): void {
1335
+        $this->expectException(\OCP\Lock\LockedException::class);
1336
+
1337
+        $rootPath = str_replace('{folder}', 'files', $rootPath);
1338
+        $pathPrefix = str_replace('{folder}', 'files', $pathPrefix);
1339
+
1340
+        $view = new View($rootPath);
1341
+        $storage = new Temporary([]);
1342
+        Filesystem::mount($storage, [], '/');
1343
+        $this->assertTrue($view->lockFile($pathPrefix . '/foo/bar', ILockingProvider::LOCK_EXCLUSIVE));
1344
+        $view->lockFile($pathPrefix . '/foo/bar/asd', ILockingProvider::LOCK_SHARED);
1345
+    }
1346
+
1347
+    /**
1348
+     * Reading from a files_encryption folder that's being renamed
1349
+     *
1350
+     * @dataProvider dataLockPaths
1351
+     *
1352
+     * @param string $rootPath
1353
+     * @param string $pathPrefix
1354
+     */
1355
+    public function testReadFromWriteUnlockablePath($rootPath, $pathPrefix): void {
1356
+        $rootPath = str_replace('{folder}', 'files_encryption', $rootPath);
1357
+        $pathPrefix = str_replace('{folder}', 'files_encryption', $pathPrefix);
1358
+
1359
+        $view = new View($rootPath);
1360
+        $storage = new Temporary([]);
1361
+        Filesystem::mount($storage, [], '/');
1362
+        $this->assertFalse($view->lockFile($pathPrefix . '/foo/bar', ILockingProvider::LOCK_EXCLUSIVE));
1363
+        $this->assertFalse($view->lockFile($pathPrefix . '/foo/bar/asd', ILockingProvider::LOCK_SHARED));
1364
+    }
1365
+
1366
+    /**
1367
+     * e.g. writing a file that's being downloaded
1368
+     *
1369
+     *
1370
+     * @dataProvider dataLockPaths
1371
+     *
1372
+     * @param string $rootPath
1373
+     * @param string $pathPrefix
1374
+     */
1375
+    public function testWriteToReadLockedFile($rootPath, $pathPrefix): void {
1376
+        $this->expectException(\OCP\Lock\LockedException::class);
1377
+
1378
+        $rootPath = str_replace('{folder}', 'files', $rootPath);
1379
+        $pathPrefix = str_replace('{folder}', 'files', $pathPrefix);
1380
+
1381
+        $view = new View($rootPath);
1382
+        $storage = new Temporary([]);
1383
+        Filesystem::mount($storage, [], '/');
1384
+        $this->assertTrue($view->lockFile($pathPrefix . '/foo/bar', ILockingProvider::LOCK_SHARED));
1385
+        $view->lockFile($pathPrefix . '/foo/bar', ILockingProvider::LOCK_EXCLUSIVE);
1386
+    }
1387
+
1388
+    /**
1389
+     * Writing a file that's being downloaded
1390
+     *
1391
+     * @dataProvider dataLockPaths
1392
+     *
1393
+     * @param string $rootPath
1394
+     * @param string $pathPrefix
1395
+     */
1396
+    public function testWriteToReadUnlockableFile($rootPath, $pathPrefix): void {
1397
+        $rootPath = str_replace('{folder}', 'files_encryption', $rootPath);
1398
+        $pathPrefix = str_replace('{folder}', 'files_encryption', $pathPrefix);
1399
+
1400
+        $view = new View($rootPath);
1401
+        $storage = new Temporary([]);
1402
+        Filesystem::mount($storage, [], '/');
1403
+        $this->assertFalse($view->lockFile($pathPrefix . '/foo/bar', ILockingProvider::LOCK_SHARED));
1404
+        $this->assertFalse($view->lockFile($pathPrefix . '/foo/bar', ILockingProvider::LOCK_EXCLUSIVE));
1405
+    }
1406
+
1407
+    /**
1408
+     * Test that locks are on mount point paths instead of mount root
1409
+     */
1410
+    public function testLockLocalMountPointPathInsteadOfStorageRoot(): void {
1411
+        $lockingProvider = \OC::$server->get(ILockingProvider::class);
1412
+        $view = new View('/testuser/files/');
1413
+        $storage = new Temporary([]);
1414
+        Filesystem::mount($storage, [], '/');
1415
+        $mountedStorage = new Temporary([]);
1416
+        Filesystem::mount($mountedStorage, [], '/testuser/files/mountpoint');
1417
+
1418
+        $this->assertTrue(
1419
+            $view->lockFile('/mountpoint', ILockingProvider::LOCK_EXCLUSIVE, true),
1420
+            'Can lock mount point'
1421
+        );
1422
+
1423
+        // no exception here because storage root was not locked
1424
+        $mountedStorage->acquireLock('', ILockingProvider::LOCK_EXCLUSIVE, $lockingProvider);
1425
+
1426
+        $thrown = false;
1427
+        try {
1428
+            $storage->acquireLock('/testuser/files/mountpoint', ILockingProvider::LOCK_EXCLUSIVE, $lockingProvider);
1429
+        } catch (LockedException $e) {
1430
+            $thrown = true;
1431
+        }
1432
+        $this->assertTrue($thrown, 'Mount point path was locked on root storage');
1433
+
1434
+        $lockingProvider->releaseAll();
1435
+    }
1436
+
1437
+    /**
1438
+     * Test that locks are on mount point paths and also mount root when requested
1439
+     */
1440
+    public function testLockStorageRootButNotLocalMountPoint(): void {
1441
+        $lockingProvider = \OC::$server->get(ILockingProvider::class);
1442
+        $view = new View('/testuser/files/');
1443
+        $storage = new Temporary([]);
1444
+        Filesystem::mount($storage, [], '/');
1445
+        $mountedStorage = new Temporary([]);
1446
+        Filesystem::mount($mountedStorage, [], '/testuser/files/mountpoint');
1447
+
1448
+        $this->assertTrue(
1449
+            $view->lockFile('/mountpoint', ILockingProvider::LOCK_EXCLUSIVE, false),
1450
+            'Can lock mount point'
1451
+        );
1452
+
1453
+        $thrown = false;
1454
+        try {
1455
+            $mountedStorage->acquireLock('', ILockingProvider::LOCK_EXCLUSIVE, $lockingProvider);
1456
+        } catch (LockedException $e) {
1457
+            $thrown = true;
1458
+        }
1459
+        $this->assertTrue($thrown, 'Mount point storage root was locked on original storage');
1460
+
1461
+        // local mount point was not locked
1462
+        $storage->acquireLock('/testuser/files/mountpoint', ILockingProvider::LOCK_EXCLUSIVE, $lockingProvider);
1463
+
1464
+        $lockingProvider->releaseAll();
1465
+    }
1466
+
1467
+    /**
1468
+     * Test that locks are on mount point paths and also mount root when requested
1469
+     */
1470
+    public function testLockMountPointPathFailReleasesBoth(): void {
1471
+        $lockingProvider = \OC::$server->get(ILockingProvider::class);
1472
+        $view = new View('/testuser/files/');
1473
+        $storage = new Temporary([]);
1474
+        Filesystem::mount($storage, [], '/');
1475
+        $mountedStorage = new Temporary([]);
1476
+        Filesystem::mount($mountedStorage, [], '/testuser/files/mountpoint.txt');
1477
+
1478
+        // this would happen if someone is writing on the mount point
1479
+        $mountedStorage->acquireLock('', ILockingProvider::LOCK_EXCLUSIVE, $lockingProvider);
1480
+
1481
+        $thrown = false;
1482
+        try {
1483
+            // this actually acquires two locks, one on the mount point and one on the storage root,
1484
+            // but the one on the storage root will fail
1485
+            $view->lockFile('/mountpoint.txt', ILockingProvider::LOCK_SHARED);
1486
+        } catch (LockedException $e) {
1487
+            $thrown = true;
1488
+        }
1489
+        $this->assertTrue($thrown, 'Cannot acquire shared lock because storage root is already locked');
1490
+
1491
+        // from here we expect that the lock on the local mount point was released properly
1492
+        // so acquiring an exclusive lock will succeed
1493
+        $storage->acquireLock('/testuser/files/mountpoint.txt', ILockingProvider::LOCK_EXCLUSIVE, $lockingProvider);
1494
+
1495
+        $lockingProvider->releaseAll();
1496
+    }
1497
+
1498
+    public static function dataLockPaths(): array {
1499
+        return [
1500
+            ['/testuser/{folder}', ''],
1501
+            ['/testuser', '/{folder}'],
1502
+            ['', '/testuser/{folder}'],
1503
+        ];
1504
+    }
1505
+
1506
+    public static function pathRelativeToFilesProvider(): array {
1507
+        return [
1508
+            ['admin/files', ''],
1509
+            ['admin/files/x', 'x'],
1510
+            ['/admin/files', ''],
1511
+            ['/admin/files/sub', 'sub'],
1512
+            ['/admin/files/sub/', 'sub'],
1513
+            ['/admin/files/sub/sub2', 'sub/sub2'],
1514
+            ['//admin//files/sub//sub2', 'sub/sub2'],
1515
+        ];
1516
+    }
1517
+
1518
+    /**
1519
+     * @dataProvider pathRelativeToFilesProvider
1520
+     */
1521
+    public function testGetPathRelativeToFiles($path, $expectedPath): void {
1522
+        $view = new View();
1523
+        $this->assertEquals($expectedPath, $view->getPathRelativeToFiles($path));
1524
+    }
1525
+
1526
+    public static function pathRelativeToFilesProviderExceptionCases(): array {
1527
+        return [
1528
+            [''],
1529
+            ['x'],
1530
+            ['files'],
1531
+            ['/files'],
1532
+            ['/admin/files_versions/abc'],
1533
+        ];
1534
+    }
1535
+
1536
+    /**
1537
+     * @dataProvider pathRelativeToFilesProviderExceptionCases
1538
+     * @param string $path
1539
+     */
1540
+    public function testGetPathRelativeToFilesWithInvalidArgument($path): void {
1541
+        $this->expectException(\InvalidArgumentException::class);
1542
+        $this->expectExceptionMessage('$absolutePath must be relative to "files"');
1543
+
1544
+        $view = new View();
1545
+        $view->getPathRelativeToFiles($path);
1546
+    }
1547
+
1548
+    public function testChangeLock(): void {
1549
+        $view = new View('/testuser/files/');
1550
+        $storage = new Temporary([]);
1551
+        Filesystem::mount($storage, [], '/');
1552
+
1553
+        $view->lockFile('/test/sub', ILockingProvider::LOCK_SHARED);
1554
+        $this->assertTrue($this->isFileLocked($view, '/test//sub', ILockingProvider::LOCK_SHARED));
1555
+        $this->assertFalse($this->isFileLocked($view, '/test//sub', ILockingProvider::LOCK_EXCLUSIVE));
1556
+
1557
+        $view->changeLock('//test/sub', ILockingProvider::LOCK_EXCLUSIVE);
1558
+        $this->assertTrue($this->isFileLocked($view, '/test//sub', ILockingProvider::LOCK_EXCLUSIVE));
1559
+
1560
+        $view->changeLock('test/sub', ILockingProvider::LOCK_SHARED);
1561
+        $this->assertTrue($this->isFileLocked($view, '/test//sub', ILockingProvider::LOCK_SHARED));
1562
+
1563
+        $view->unlockFile('/test/sub/', ILockingProvider::LOCK_SHARED);
1564
+
1565
+        $this->assertFalse($this->isFileLocked($view, '/test//sub', ILockingProvider::LOCK_SHARED));
1566
+        $this->assertFalse($this->isFileLocked($view, '/test//sub', ILockingProvider::LOCK_EXCLUSIVE));
1567
+    }
1568
+
1569
+    public static function hookPathProvider(): array {
1570
+        return [
1571
+            ['/foo/files', '/foo', true],
1572
+            ['/foo/files/bar', '/foo', true],
1573
+            ['/foo', '/foo', false],
1574
+            ['/foo', '/files/foo', true],
1575
+            ['/foo', 'filesfoo', false],
1576
+            ['', '/foo/files', true],
1577
+            ['', '/foo/files/bar.txt', true],
1578
+        ];
1579
+    }
1580
+
1581
+    /**
1582
+     * @dataProvider hookPathProvider
1583
+     * @param $root
1584
+     * @param $path
1585
+     * @param $shouldEmit
1586
+     */
1587
+    public function testHookPaths($root, $path, $shouldEmit): void {
1588
+        $filesystemReflection = new \ReflectionClass(Filesystem::class);
1589
+        $defaultRootValue = $filesystemReflection->getProperty('defaultInstance');
1590
+        $defaultRootValue->setAccessible(true);
1591
+        $oldRoot = $defaultRootValue->getValue();
1592
+        $defaultView = new View('/foo/files');
1593
+        $defaultRootValue->setValue(null, $defaultView);
1594
+        $view = new View($root);
1595
+        $result = self::invokePrivate($view, 'shouldEmitHooks', [$path]);
1596
+        $defaultRootValue->setValue(null, $oldRoot);
1597
+        $this->assertEquals($shouldEmit, $result);
1598
+    }
1599
+
1600
+    /**
1601
+     * Create test movable mount points
1602
+     *
1603
+     * @param array $mountPoints array of mount point locations
1604
+     * @return array array of MountPoint objects
1605
+     */
1606
+    private function createTestMovableMountPoints($mountPoints) {
1607
+        $mounts = [];
1608
+        foreach ($mountPoints as $mountPoint) {
1609
+            $storage = $this->getMockBuilder(Storage::class)
1610
+                ->onlyMethods([])
1611
+                ->getMock();
1612
+            $storage->method('getId')->willReturn('non-null-id');
1613
+            $storage->method('getStorageCache')->willReturnCallback(function () use ($storage) {
1614
+                return new \OC\Files\Cache\Storage($storage, true, \OC::$server->get(IDBConnection::class));
1615
+            });
1616
+
1617
+            $mounts[] = $this->getMockBuilder(TestMoveableMountPoint::class)
1618
+                ->onlyMethods(['moveMount'])
1619
+                ->setConstructorArgs([$storage, $mountPoint])
1620
+                ->getMock();
1621
+        }
1622
+
1623
+        /** @var IMountProvider|\PHPUnit\Framework\MockObject\MockObject $mountProvider */
1624
+        $mountProvider = $this->createMock(IMountProvider::class);
1625
+        $mountProvider->expects($this->any())
1626
+            ->method('getMountsForUser')
1627
+            ->willReturn($mounts);
1628
+
1629
+        $mountProviderCollection = \OC::$server->getMountProviderCollection();
1630
+        $mountProviderCollection->registerProvider($mountProvider);
1631
+
1632
+        return $mounts;
1633
+    }
1634
+
1635
+    /**
1636
+     * Test mount point move
1637
+     */
1638
+    public function testMountPointMove(): void {
1639
+        self::loginAsUser($this->user);
1640
+
1641
+        [$mount1, $mount2] = $this->createTestMovableMountPoints([
1642
+            $this->user . '/files/mount1',
1643
+            $this->user . '/files/mount2',
1644
+        ]);
1645
+        $mount1->expects($this->once())
1646
+            ->method('moveMount')
1647
+            ->willReturn(true);
1648
+
1649
+        $mount2->expects($this->once())
1650
+            ->method('moveMount')
1651
+            ->willReturn(true);
1652
+
1653
+        $view = new View('/' . $this->user . '/files/');
1654
+        $view->mkdir('sub');
1655
+
1656
+        $this->assertTrue($view->rename('mount1', 'renamed_mount'), 'Can rename mount point');
1657
+        $this->assertTrue($view->rename('mount2', 'sub/moved_mount'), 'Can move a mount point into a subdirectory');
1658
+    }
1659
+
1660
+    public function testMoveMountPointOverwrite(): void {
1661
+        self::loginAsUser($this->user);
1662
+
1663
+        [$mount1, $mount2] = $this->createTestMovableMountPoints([
1664
+            $this->user . '/files/mount1',
1665
+            $this->user . '/files/mount2',
1666
+        ]);
1667
+
1668
+        $mount1->expects($this->never())
1669
+            ->method('moveMount');
1670
+
1671
+        $mount2->expects($this->never())
1672
+            ->method('moveMount');
1673
+
1674
+        $view = new View('/' . $this->user . '/files/');
1675
+
1676
+        $this->expectException(ForbiddenException::class);
1677
+        $view->rename('mount1', 'mount2');
1678
+    }
1679
+
1680
+    public function testMoveMountPointIntoMount(): void {
1681
+        self::loginAsUser($this->user);
1682
+
1683
+        [$mount1, $mount2] = $this->createTestMovableMountPoints([
1684
+            $this->user . '/files/mount1',
1685
+            $this->user . '/files/mount2',
1686
+        ]);
1687
+
1688
+        $mount1->expects($this->never())
1689
+            ->method('moveMount');
1690
+
1691
+        $mount2->expects($this->never())
1692
+            ->method('moveMount');
1693
+
1694
+        $view = new View('/' . $this->user . '/files/');
1695
+
1696
+        $this->expectException(ForbiddenException::class);
1697
+        $view->rename('mount1', 'mount2/sub');
1698
+    }
1699
+
1700
+    /**
1701
+     * Test that moving a mount point into a shared folder is forbidden
1702
+     */
1703
+    public function testMoveMountPointIntoSharedFolder(): void {
1704
+        self::loginAsUser($this->user);
1705
+
1706
+        [$mount1, $mount2] = $this->createTestMovableMountPoints([
1707
+            $this->user . '/files/mount1',
1708
+            $this->user . '/files/mount2',
1709
+        ]);
1710
+
1711
+        $mount1->expects($this->never())
1712
+            ->method('moveMount');
1713
+
1714
+        $mount2->expects($this->once())
1715
+            ->method('moveMount')
1716
+            ->willReturn(true);
1717
+
1718
+        $view = new View('/' . $this->user . '/files/');
1719
+        $view->mkdir('shareddir');
1720
+        $view->mkdir('shareddir/sub');
1721
+        $view->mkdir('shareddir/sub2');
1722
+        // Create a similar named but non-shared folder
1723
+        $view->mkdir('shareddir notshared');
1724
+
1725
+        $fileId = $view->getFileInfo('shareddir')->getId();
1726
+        $userObject = \OC::$server->getUserManager()->createUser('test2', 'IHateNonMockableStaticClasses');
1727
+
1728
+        $userFolder = \OC::$server->getUserFolder($this->user);
1729
+        $shareDir = $userFolder->get('shareddir');
1730
+        $shareManager = \OC::$server->get(IShareManager::class);
1731
+        $share = $shareManager->newShare();
1732
+        $share->setSharedWith('test2')
1733
+            ->setSharedBy($this->user)
1734
+            ->setShareType(IShare::TYPE_USER)
1735
+            ->setPermissions(\OCP\Constants::PERMISSION_READ)
1736
+            ->setNode($shareDir);
1737
+        $shareManager->createShare($share);
1738
+
1739
+        try {
1740
+            $view->rename('mount1', 'shareddir');
1741
+            $this->fail('Cannot overwrite shared folder');
1742
+        } catch (ForbiddenException $e) {
1743
+
1744
+        }
1745
+        try {
1746
+            $view->rename('mount1', 'shareddir/sub');
1747
+            $this->fail('Cannot move mount point into shared folder');
1748
+        } catch (ForbiddenException $e) {
1749
+
1750
+        }
1751
+        try {
1752
+            $view->rename('mount1', 'shareddir/sub/sub2');
1753
+            $this->fail('Cannot move mount point into shared subfolder');
1754
+        } catch (ForbiddenException $e) {
1755
+
1756
+        }
1757
+        $this->assertTrue($view->rename('mount2', 'shareddir notshared/sub'), 'Can move mount point into a similarly named but non-shared folder');
1758
+
1759
+        $shareManager->deleteShare($share);
1760
+        $userObject->delete();
1761
+    }
1762
+
1763
+    public static function basicOperationProviderForLocks(): array {
1764
+        return [
1765
+            // --- write hook ----
1766
+            [
1767
+                'touch',
1768
+                ['touch-create.txt'],
1769
+                'touch-create.txt',
1770
+                'create',
1771
+                ILockingProvider::LOCK_SHARED,
1772
+                ILockingProvider::LOCK_EXCLUSIVE,
1773
+                ILockingProvider::LOCK_SHARED,
1774
+            ],
1775
+            [
1776
+                'fopen',
1777
+                ['test-write.txt', 'w'],
1778
+                'test-write.txt',
1779
+                'write',
1780
+                ILockingProvider::LOCK_SHARED,
1781
+                ILockingProvider::LOCK_EXCLUSIVE,
1782
+                null,
1783
+                // exclusive lock stays until fclose
1784
+                ILockingProvider::LOCK_EXCLUSIVE,
1785
+            ],
1786
+            [
1787
+                'mkdir',
1788
+                ['newdir'],
1789
+                'newdir',
1790
+                'write',
1791
+                ILockingProvider::LOCK_SHARED,
1792
+                ILockingProvider::LOCK_EXCLUSIVE,
1793
+                ILockingProvider::LOCK_SHARED,
1794
+            ],
1795
+            [
1796
+                'file_put_contents',
1797
+                ['file_put_contents.txt', 'blah'],
1798
+                'file_put_contents.txt',
1799
+                'write',
1800
+                ILockingProvider::LOCK_SHARED,
1801
+                ILockingProvider::LOCK_EXCLUSIVE,
1802
+                ILockingProvider::LOCK_SHARED,
1803
+                null,
1804
+                0,
1805
+            ],
1806
+
1807
+            // ---- delete hook ----
1808
+            [
1809
+                'rmdir',
1810
+                ['dir'],
1811
+                'dir',
1812
+                'delete',
1813
+                ILockingProvider::LOCK_SHARED,
1814
+                ILockingProvider::LOCK_EXCLUSIVE,
1815
+                ILockingProvider::LOCK_SHARED,
1816
+            ],
1817
+            [
1818
+                'unlink',
1819
+                ['test.txt'],
1820
+                'test.txt',
1821
+                'delete',
1822
+                ILockingProvider::LOCK_SHARED,
1823
+                ILockingProvider::LOCK_EXCLUSIVE,
1824
+                ILockingProvider::LOCK_SHARED,
1825
+            ],
1826
+
1827
+            // ---- read hook (no post hooks) ----
1828
+            [
1829
+                'file_get_contents',
1830
+                ['test.txt'],
1831
+                'test.txt',
1832
+                'read',
1833
+                ILockingProvider::LOCK_SHARED,
1834
+                ILockingProvider::LOCK_SHARED,
1835
+                null,
1836
+                null,
1837
+                false,
1838
+            ],
1839
+            [
1840
+                'fopen',
1841
+                ['test.txt', 'r'],
1842
+                'test.txt',
1843
+                'read',
1844
+                ILockingProvider::LOCK_SHARED,
1845
+                ILockingProvider::LOCK_SHARED,
1846
+                null,
1847
+            ],
1848
+            [
1849
+                'opendir',
1850
+                ['dir'],
1851
+                'dir',
1852
+                'read',
1853
+                ILockingProvider::LOCK_SHARED,
1854
+                ILockingProvider::LOCK_SHARED,
1855
+                null,
1856
+            ],
1857
+
1858
+            // ---- no lock, touch hook ---
1859
+            ['touch', ['test.txt'], 'test.txt', 'touch', null, null, null],
1860
+
1861
+            // ---- no hooks, no locks ---
1862
+            ['is_dir', ['dir'], 'dir', null],
1863
+            ['is_file', ['dir'], 'dir', null],
1864
+            [
1865
+                'stat',
1866
+                ['dir'],
1867
+                'dir',
1868
+                null,
1869
+                ILockingProvider::LOCK_SHARED,
1870
+                ILockingProvider::LOCK_SHARED,
1871
+                ILockingProvider::LOCK_SHARED,
1872
+                null,
1873
+                false,
1874
+            ],
1875
+            [
1876
+                'filetype',
1877
+                ['dir'],
1878
+                'dir',
1879
+                null,
1880
+                ILockingProvider::LOCK_SHARED,
1881
+                ILockingProvider::LOCK_SHARED,
1882
+                ILockingProvider::LOCK_SHARED,
1883
+                null,
1884
+                false,
1885
+            ],
1886
+            [
1887
+                'filesize',
1888
+                ['dir'],
1889
+                'dir',
1890
+                null,
1891
+                ILockingProvider::LOCK_SHARED,
1892
+                ILockingProvider::LOCK_SHARED,
1893
+                ILockingProvider::LOCK_SHARED,
1894
+                null,
1895
+                /* Return an int */
1896
+                100
1897
+            ],
1898
+            ['isCreatable', ['dir'], 'dir', null],
1899
+            ['isReadable', ['dir'], 'dir', null],
1900
+            ['isUpdatable', ['dir'], 'dir', null],
1901
+            ['isDeletable', ['dir'], 'dir', null],
1902
+            ['isSharable', ['dir'], 'dir', null],
1903
+            ['file_exists', ['dir'], 'dir', null],
1904
+            [
1905
+                'filemtime',
1906
+                ['dir'],
1907
+                'dir',
1908
+                null,
1909
+                ILockingProvider::LOCK_SHARED,
1910
+                ILockingProvider::LOCK_SHARED,
1911
+                ILockingProvider::LOCK_SHARED,
1912
+                null,
1913
+                false,
1914
+            ],
1915
+        ];
1916
+    }
1917
+
1918
+    /**
1919
+     * Test whether locks are set before and after the operation
1920
+     *
1921
+     * @dataProvider basicOperationProviderForLocks
1922
+     *
1923
+     * @param string $operation operation name on the view
1924
+     * @param array $operationArgs arguments for the operation
1925
+     * @param string $lockedPath path of the locked item to check
1926
+     * @param string $hookType hook type
1927
+     * @param int $expectedLockBefore expected lock during pre hooks
1928
+     * @param int $expectedLockDuring expected lock during operation
1929
+     * @param int $expectedLockAfter expected lock during post hooks
1930
+     * @param int $expectedStrayLock expected lock after returning, should
1931
+     *                               be null (unlock) for most operations
1932
+     */
1933
+    public function testLockBasicOperation(
1934
+        $operation,
1935
+        $operationArgs,
1936
+        $lockedPath,
1937
+        $hookType,
1938
+        $expectedLockBefore = ILockingProvider::LOCK_SHARED,
1939
+        $expectedLockDuring = ILockingProvider::LOCK_SHARED,
1940
+        $expectedLockAfter = ILockingProvider::LOCK_SHARED,
1941
+        $expectedStrayLock = null,
1942
+        $returnValue = true,
1943
+    ): void {
1944
+        $view = new View('/' . $this->user . '/files/');
1945
+
1946
+        /** @var Temporary&MockObject $storage */
1947
+        $storage = $this->getMockBuilder(Temporary::class)
1948
+            ->onlyMethods([$operation])
1949
+            ->getMock();
1950
+
1951
+        /* Pause trash to avoid the trashbin intercepting rmdir and unlink calls */
1952
+        Server::get(ITrashManager::class)->pauseTrash();
1953
+        /* Same thing with encryption wrapper */
1954
+        Server::get(IStorageFactory::class)->removeStorageWrapper('oc_encryption');
1955
+
1956
+        Filesystem::mount($storage, [], $this->user . '/');
1957
+
1958
+        // work directly on disk because mkdir might be mocked
1959
+        $realPath = $storage->getSourcePath('');
1960
+        mkdir($realPath . '/files');
1961
+        mkdir($realPath . '/files/dir');
1962
+        file_put_contents($realPath . '/files/test.txt', 'blah');
1963
+        $storage->getScanner()->scan('files');
1964
+
1965
+        $storage->expects($this->once())
1966
+            ->method($operation)
1967
+            ->willReturnCallback(
1968
+                function () use ($view, $lockedPath, &$lockTypeDuring, $returnValue) {
1969
+                    $lockTypeDuring = $this->getFileLockType($view, $lockedPath);
1970
+
1971
+                    return $returnValue;
1972
+                }
1973
+            );
1974
+
1975
+        $this->assertNull($this->getFileLockType($view, $lockedPath), 'File not locked before operation');
1976
+
1977
+        $this->connectMockHooks($hookType, $view, $lockedPath, $lockTypePre, $lockTypePost);
1978
+
1979
+        // do operation
1980
+        call_user_func_array([$view, $operation], $operationArgs);
1981
+
1982
+        if ($hookType !== null) {
1983
+            $this->assertEquals($expectedLockBefore, $lockTypePre, 'File locked properly during pre-hook');
1984
+            $this->assertEquals($expectedLockAfter, $lockTypePost, 'File locked properly during post-hook');
1985
+            $this->assertEquals($expectedLockDuring, $lockTypeDuring, 'File locked properly during operation');
1986
+        } else {
1987
+            $this->assertNull($lockTypeDuring, 'File not locked during operation');
1988
+        }
1989
+
1990
+        $this->assertEquals($expectedStrayLock, $this->getFileLockType($view, $lockedPath));
1991
+
1992
+        /* Resume trash to avoid side effects */
1993
+        Server::get(ITrashManager::class)->resumeTrash();
1994
+    }
1995
+
1996
+    /**
1997
+     * Test locks for file_put_content with stream.
1998
+     * This code path uses $storage->fopen instead
1999
+     */
2000
+    public function testLockFilePutContentWithStream(): void {
2001
+        $view = new View('/' . $this->user . '/files/');
2002
+
2003
+        $path = 'test_file_put_contents.txt';
2004
+        /** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */
2005
+        $storage = $this->getMockBuilder(Temporary::class)
2006
+            ->onlyMethods(['fopen'])
2007
+            ->getMock();
2008
+
2009
+        Filesystem::mount($storage, [], $this->user . '/');
2010
+        $storage->mkdir('files');
2011
+
2012
+        $storage->expects($this->once())
2013
+            ->method('fopen')
2014
+            ->willReturnCallback(
2015
+                function () use ($view, $path, &$lockTypeDuring) {
2016
+                    $lockTypeDuring = $this->getFileLockType($view, $path);
2017
+
2018
+                    return fopen('php://temp', 'r+');
2019
+                }
2020
+            );
2021
+
2022
+        $this->connectMockHooks('write', $view, $path, $lockTypePre, $lockTypePost);
2023
+
2024
+        $this->assertNull($this->getFileLockType($view, $path), 'File not locked before operation');
2025
+
2026
+        // do operation
2027
+        $view->file_put_contents($path, fopen('php://temp', 'r+'));
2028
+
2029
+        $this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypePre, 'File locked properly during pre-hook');
2030
+        $this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypePost, 'File locked properly during post-hook');
2031
+        $this->assertEquals(ILockingProvider::LOCK_EXCLUSIVE, $lockTypeDuring, 'File locked properly during operation');
2032
+
2033
+        $this->assertNull($this->getFileLockType($view, $path));
2034
+    }
2035
+
2036
+    /**
2037
+     * Test locks for fopen with fclose at the end
2038
+     */
2039
+    public function testLockFopen(): void {
2040
+        $view = new View('/' . $this->user . '/files/');
2041
+
2042
+        $path = 'test_file_put_contents.txt';
2043
+        /** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */
2044
+        $storage = $this->getMockBuilder(Temporary::class)
2045
+            ->onlyMethods(['fopen'])
2046
+            ->getMock();
2047
+
2048
+        Filesystem::mount($storage, [], $this->user . '/');
2049
+        $storage->mkdir('files');
2050
+
2051
+        $storage->expects($this->once())
2052
+            ->method('fopen')
2053
+            ->willReturnCallback(
2054
+                function () use ($view, $path, &$lockTypeDuring) {
2055
+                    $lockTypeDuring = $this->getFileLockType($view, $path);
2056
+
2057
+                    return fopen('php://temp', 'r+');
2058
+                }
2059
+            );
2060
+
2061
+        $this->connectMockHooks('write', $view, $path, $lockTypePre, $lockTypePost);
2062
+
2063
+        $this->assertNull($this->getFileLockType($view, $path), 'File not locked before operation');
2064
+
2065
+        // do operation
2066
+        $res = $view->fopen($path, 'w');
2067
+
2068
+        $this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypePre, 'File locked properly during pre-hook');
2069
+        $this->assertEquals(ILockingProvider::LOCK_EXCLUSIVE, $lockTypeDuring, 'File locked properly during operation');
2070
+        $this->assertNull($lockTypePost, 'No post hook, no lock check possible');
2071
+
2072
+        $this->assertEquals(ILockingProvider::LOCK_EXCLUSIVE, $lockTypeDuring, 'File still locked after fopen');
2073
+
2074
+        fclose($res);
2075
+
2076
+        $this->assertNull($this->getFileLockType($view, $path), 'File unlocked after fclose');
2077
+    }
2078
+
2079
+    /**
2080
+     * Test locks for fopen with fclose at the end
2081
+     *
2082
+     * @dataProvider basicOperationProviderForLocks
2083
+     *
2084
+     * @param string $operation operation name on the view
2085
+     * @param array $operationArgs arguments for the operation
2086
+     * @param string $path path of the locked item to check
2087
+     */
2088
+    public function testLockBasicOperationUnlocksAfterException(
2089
+        $operation,
2090
+        $operationArgs,
2091
+        $path,
2092
+    ): void {
2093
+        if ($operation === 'touch') {
2094
+            $this->markTestSkipped('touch handles storage exceptions internally');
2095
+        }
2096
+        $view = new View('/' . $this->user . '/files/');
2097
+
2098
+        /** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */
2099
+        $storage = $this->getMockBuilder(Temporary::class)
2100
+            ->onlyMethods([$operation])
2101
+            ->getMock();
2102
+
2103
+        /* Pause trash to avoid the trashbin intercepting rmdir and unlink calls */
2104
+        Server::get(ITrashManager::class)->pauseTrash();
2105
+        /* Same thing with encryption wrapper */
2106
+        Server::get(IStorageFactory::class)->removeStorageWrapper('oc_encryption');
2107
+
2108
+        Filesystem::mount($storage, [], $this->user . '/');
2109
+
2110
+        // work directly on disk because mkdir might be mocked
2111
+        $realPath = $storage->getSourcePath('');
2112
+        mkdir($realPath . '/files');
2113
+        mkdir($realPath . '/files/dir');
2114
+        file_put_contents($realPath . '/files/test.txt', 'blah');
2115
+        $storage->getScanner()->scan('files');
2116
+
2117
+        $storage->expects($this->once())
2118
+            ->method($operation)
2119
+            ->willReturnCallback(
2120
+                function () {
2121
+                    throw new \Exception('Simulated exception');
2122
+                }
2123
+            );
2124
+
2125
+        $thrown = false;
2126
+        try {
2127
+            call_user_func_array([$view, $operation], $operationArgs);
2128
+        } catch (\Exception $e) {
2129
+            $thrown = true;
2130
+            $this->assertEquals('Simulated exception', $e->getMessage());
2131
+        }
2132
+        $this->assertTrue($thrown, 'Exception was rethrown');
2133
+        $this->assertNull($this->getFileLockType($view, $path), 'File got unlocked after exception');
2134
+
2135
+        /* Resume trash to avoid side effects */
2136
+        Server::get(ITrashManager::class)->resumeTrash();
2137
+    }
2138
+
2139
+    public function testLockBasicOperationUnlocksAfterLockException(): void {
2140
+        $view = new View('/' . $this->user . '/files/');
2141
+
2142
+        $storage = new Temporary([]);
2143
+
2144
+        Filesystem::mount($storage, [], $this->user . '/');
2145
+
2146
+        $storage->mkdir('files');
2147
+        $storage->mkdir('files/dir');
2148
+        $storage->file_put_contents('files/test.txt', 'blah');
2149
+        $storage->getScanner()->scan('files');
2150
+
2151
+        // get a shared lock
2152
+        $handle = $view->fopen('test.txt', 'r');
2153
+
2154
+        $thrown = false;
2155
+        try {
2156
+            // try (and fail) to get a write lock
2157
+            $view->unlink('test.txt');
2158
+        } catch (\Exception $e) {
2159
+            $thrown = true;
2160
+            $this->assertInstanceOf(LockedException::class, $e);
2161
+        }
2162
+        $this->assertTrue($thrown, 'Exception was rethrown');
2163
+
2164
+        // clean shared lock
2165
+        fclose($handle);
2166
+
2167
+        $this->assertNull($this->getFileLockType($view, 'test.txt'), 'File got unlocked');
2168
+    }
2169
+
2170
+    /**
2171
+     * Test locks for fopen with fclose at the end
2172
+     *
2173
+     * @dataProvider basicOperationProviderForLocks
2174
+     *
2175
+     * @param string $operation operation name on the view
2176
+     * @param array $operationArgs arguments for the operation
2177
+     * @param string $path path of the locked item to check
2178
+     * @param string $hookType hook type
2179
+     */
2180
+    public function testLockBasicOperationUnlocksAfterCancelledHook(
2181
+        $operation,
2182
+        $operationArgs,
2183
+        $path,
2184
+        $hookType,
2185
+    ): void {
2186
+        $view = new View('/' . $this->user . '/files/');
2187
+
2188
+        /** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */
2189
+        $storage = $this->getMockBuilder(Temporary::class)
2190
+            ->onlyMethods([$operation])
2191
+            ->getMock();
2192
+
2193
+        Filesystem::mount($storage, [], $this->user . '/');
2194
+        $storage->mkdir('files');
2195
+
2196
+        Util::connectHook(
2197
+            Filesystem::CLASSNAME,
2198
+            $hookType,
2199
+            HookHelper::class,
2200
+            'cancellingCallback'
2201
+        );
2202
+
2203
+        call_user_func_array([$view, $operation], $operationArgs);
2204
+
2205
+        $this->assertNull($this->getFileLockType($view, $path), 'File got unlocked after exception');
2206
+    }
2207
+
2208
+    public static function lockFileRenameOrCopyDataProvider(): array {
2209
+        return [
2210
+            ['rename', ILockingProvider::LOCK_EXCLUSIVE],
2211
+            ['copy', ILockingProvider::LOCK_SHARED],
2212
+        ];
2213
+    }
2214
+
2215
+    /**
2216
+     * Test locks for rename or copy operation
2217
+     *
2218
+     * @dataProvider lockFileRenameOrCopyDataProvider
2219
+     *
2220
+     * @param string $operation operation to be done on the view
2221
+     * @param int $expectedLockTypeSourceDuring expected lock type on source file during
2222
+     *                                          the operation
2223
+     */
2224
+    public function testLockFileRename($operation, $expectedLockTypeSourceDuring): void {
2225
+        $view = new View('/' . $this->user . '/files/');
2226
+
2227
+        /** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */
2228
+        $storage = $this->getMockBuilder(Temporary::class)
2229
+            ->onlyMethods([$operation, 'getMetaData', 'filemtime'])
2230
+            ->getMock();
2231
+
2232
+        $storage->expects($this->any())
2233
+            ->method('getMetaData')
2234
+            ->will($this->returnValue([
2235
+                'mtime' => 1885434487,
2236
+                'etag' => '',
2237
+                'mimetype' => 'text/plain',
2238
+                'permissions' => Constants::PERMISSION_ALL,
2239
+                'size' => 3
2240
+            ]));
2241
+        $storage->expects($this->any())
2242
+            ->method('filemtime')
2243
+            ->willReturn(123456789);
2244
+
2245
+        $sourcePath = 'original.txt';
2246
+        $targetPath = 'target.txt';
2247
+
2248
+        /* Disable encryption wrapper to avoid it intercepting mocked call */
2249
+        Server::get(IStorageFactory::class)->removeStorageWrapper('oc_encryption');
2250
+
2251
+        Filesystem::mount($storage, [], $this->user . '/');
2252
+        $storage->mkdir('files');
2253
+        $view->file_put_contents($sourcePath, 'meh');
2254
+
2255
+        $storage->expects($this->once())
2256
+            ->method($operation)
2257
+            ->willReturnCallback(
2258
+                function () use ($view, $sourcePath, $targetPath, &$lockTypeSourceDuring, &$lockTypeTargetDuring) {
2259
+                    $lockTypeSourceDuring = $this->getFileLockType($view, $sourcePath);
2260
+                    $lockTypeTargetDuring = $this->getFileLockType($view, $targetPath);
2261
+
2262
+                    return true;
2263
+                }
2264
+            );
2265
+
2266
+        $this->connectMockHooks($operation, $view, $sourcePath, $lockTypeSourcePre, $lockTypeSourcePost);
2267
+        $this->connectMockHooks($operation, $view, $targetPath, $lockTypeTargetPre, $lockTypeTargetPost);
2268
+
2269
+        $this->assertNull($this->getFileLockType($view, $sourcePath), 'Source file not locked before operation');
2270
+        $this->assertNull($this->getFileLockType($view, $targetPath), 'Target file not locked before operation');
2271
+
2272
+        $view->$operation($sourcePath, $targetPath);
2273
+
2274
+        $this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeSourcePre, 'Source file locked properly during pre-hook');
2275
+        $this->assertEquals($expectedLockTypeSourceDuring, $lockTypeSourceDuring, 'Source file locked properly during operation');
2276
+        $this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeSourcePost, 'Source file locked properly during post-hook');
2277
+
2278
+        $this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeTargetPre, 'Target file locked properly during pre-hook');
2279
+        $this->assertEquals(ILockingProvider::LOCK_EXCLUSIVE, $lockTypeTargetDuring, 'Target file locked properly during operation');
2280
+        $this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeTargetPost, 'Target file locked properly during post-hook');
2281
+
2282
+        $this->assertNull($this->getFileLockType($view, $sourcePath), 'Source file not locked after operation');
2283
+        $this->assertNull($this->getFileLockType($view, $targetPath), 'Target file not locked after operation');
2284
+    }
2285
+
2286
+    /**
2287
+     * simulate a failed copy operation.
2288
+     * We expect that we catch the exception, free the lock and re-throw it.
2289
+     *
2290
+     */
2291
+    public function testLockFileCopyException(): void {
2292
+        $this->expectException(\Exception::class);
2293
+
2294
+        $view = new View('/' . $this->user . '/files/');
2295
+
2296
+        /** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */
2297
+        $storage = $this->getMockBuilder(Temporary::class)
2298
+            ->onlyMethods(['copy'])
2299
+            ->getMock();
2300
+
2301
+        $sourcePath = 'original.txt';
2302
+        $targetPath = 'target.txt';
2303
+
2304
+        /* Disable encryption wrapper to avoid it intercepting mocked call */
2305
+        Server::get(IStorageFactory::class)->removeStorageWrapper('oc_encryption');
2306
+
2307
+        Filesystem::mount($storage, [], $this->user . '/');
2308
+        $storage->mkdir('files');
2309
+        $view->file_put_contents($sourcePath, 'meh');
2310
+
2311
+        $storage->expects($this->once())
2312
+            ->method('copy')
2313
+            ->willReturnCallback(
2314
+                function () {
2315
+                    throw new \Exception();
2316
+                }
2317
+            );
2318
+
2319
+        $this->connectMockHooks('copy', $view, $sourcePath, $lockTypeSourcePre, $lockTypeSourcePost);
2320
+        $this->connectMockHooks('copy', $view, $targetPath, $lockTypeTargetPre, $lockTypeTargetPost);
2321
+
2322
+        $this->assertNull($this->getFileLockType($view, $sourcePath), 'Source file not locked before operation');
2323
+        $this->assertNull($this->getFileLockType($view, $targetPath), 'Target file not locked before operation');
2324
+
2325
+        try {
2326
+            $view->copy($sourcePath, $targetPath);
2327
+        } catch (\Exception $e) {
2328
+            $this->assertNull($this->getFileLockType($view, $sourcePath), 'Source file not locked after operation');
2329
+            $this->assertNull($this->getFileLockType($view, $targetPath), 'Target file not locked after operation');
2330
+            throw $e;
2331
+        }
2332
+    }
2333
+
2334
+    /**
2335
+     * Test rename operation: unlock first path when second path was locked
2336
+     */
2337
+    public function testLockFileRenameUnlockOnException(): void {
2338
+        self::loginAsUser('test');
2339
+
2340
+        $view = new View('/' . $this->user . '/files/');
2341
+
2342
+        $sourcePath = 'original.txt';
2343
+        $targetPath = 'target.txt';
2344
+        $view->file_put_contents($sourcePath, 'meh');
2345
+
2346
+        // simulate that the target path is already locked
2347
+        $view->lockFile($targetPath, ILockingProvider::LOCK_EXCLUSIVE);
2348
+
2349
+        $this->assertNull($this->getFileLockType($view, $sourcePath), 'Source file not locked before operation');
2350
+        $this->assertEquals(ILockingProvider::LOCK_EXCLUSIVE, $this->getFileLockType($view, $targetPath), 'Target file is locked before operation');
2351
+
2352
+        $thrown = false;
2353
+        try {
2354
+            $view->rename($sourcePath, $targetPath);
2355
+        } catch (LockedException $e) {
2356
+            $thrown = true;
2357
+        }
2358
+
2359
+        $this->assertTrue($thrown, 'LockedException thrown');
2360
+
2361
+        $this->assertNull($this->getFileLockType($view, $sourcePath), 'Source file not locked after operation');
2362
+        $this->assertEquals(ILockingProvider::LOCK_EXCLUSIVE, $this->getFileLockType($view, $targetPath), 'Target file still locked after operation');
2363
+
2364
+        $view->unlockFile($targetPath, ILockingProvider::LOCK_EXCLUSIVE);
2365
+    }
2366
+
2367
+    /**
2368
+     * Test rename operation: unlock first path when second path was locked
2369
+     */
2370
+    public function testGetOwner(): void {
2371
+        self::loginAsUser('test');
2372
+
2373
+        $view = new View('/test/files/');
2374
+
2375
+        $path = 'foo.txt';
2376
+        $view->file_put_contents($path, 'meh');
2377
+
2378
+        $this->assertEquals('test', $view->getFileInfo($path)->getOwner()->getUID());
2379
+
2380
+        $folderInfo = $view->getDirectoryContent('');
2381
+        $folderInfo = array_values(array_filter($folderInfo, function (FileInfo $info) {
2382
+            return $info->getName() === 'foo.txt';
2383
+        }));
2384
+
2385
+        $this->assertEquals('test', $folderInfo[0]->getOwner()->getUID());
2386
+
2387
+        $subStorage = new Temporary();
2388
+        Filesystem::mount($subStorage, [], '/test/files/asd');
2389
+
2390
+        $folderInfo = $view->getDirectoryContent('');
2391
+        $folderInfo = array_values(array_filter($folderInfo, function (FileInfo $info) {
2392
+            return $info->getName() === 'asd';
2393
+        }));
2394
+
2395
+        $this->assertEquals('test', $folderInfo[0]->getOwner()->getUID());
2396
+    }
2397
+
2398
+    public static function lockFileRenameOrCopyCrossStorageDataProvider(): array {
2399
+        return [
2400
+            ['rename', 'moveFromStorage', ILockingProvider::LOCK_EXCLUSIVE],
2401
+            ['copy', 'copyFromStorage', ILockingProvider::LOCK_SHARED],
2402
+        ];
2403
+    }
2404
+
2405
+    /**
2406
+     * Test locks for rename or copy operation cross-storage
2407
+     *
2408
+     * @dataProvider lockFileRenameOrCopyCrossStorageDataProvider
2409
+     *
2410
+     * @param string $viewOperation operation to be done on the view
2411
+     * @param string $storageOperation operation to be mocked on the storage
2412
+     * @param int $expectedLockTypeSourceDuring expected lock type on source file during
2413
+     *                                          the operation
2414
+     */
2415
+    public function testLockFileRenameCrossStorage($viewOperation, $storageOperation, $expectedLockTypeSourceDuring): void {
2416
+        $view = new View('/' . $this->user . '/files/');
2417
+
2418
+        /** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */
2419
+        $storage = $this->getMockBuilder(Temporary::class)
2420
+            ->onlyMethods([$storageOperation])
2421
+            ->getMock();
2422
+        /** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage2 */
2423
+        $storage2 = $this->getMockBuilder(Temporary::class)
2424
+            ->onlyMethods([$storageOperation, 'getMetaData', 'filemtime'])
2425
+            ->getMock();
2426
+
2427
+        $storage2->expects($this->any())
2428
+            ->method('getMetaData')
2429
+            ->will($this->returnValue([
2430
+                'mtime' => 1885434487,
2431
+                'etag' => '',
2432
+                'mimetype' => 'text/plain',
2433
+                'permissions' => Constants::PERMISSION_ALL,
2434
+                'size' => 3
2435
+            ]));
2436
+        $storage2->expects($this->any())
2437
+            ->method('filemtime')
2438
+            ->willReturn(123456789);
2439
+
2440
+        $sourcePath = 'original.txt';
2441
+        $targetPath = 'substorage/target.txt';
2442
+
2443
+        /* Disable encryption wrapper to avoid it intercepting mocked call */
2444
+        Server::get(IStorageFactory::class)->removeStorageWrapper('oc_encryption');
2445
+
2446
+        Filesystem::mount($storage, [], $this->user . '/');
2447
+        Filesystem::mount($storage2, [], $this->user . '/files/substorage');
2448
+        $storage->mkdir('files');
2449
+        $view->file_put_contents($sourcePath, 'meh');
2450
+        $storage2->getUpdater()->update('');
2451
+
2452
+        $storage->expects($this->never())
2453
+            ->method($storageOperation);
2454
+        $storage2->expects($this->once())
2455
+            ->method($storageOperation)
2456
+            ->willReturnCallback(
2457
+                function () use ($view, $sourcePath, $targetPath, &$lockTypeSourceDuring, &$lockTypeTargetDuring) {
2458
+                    $lockTypeSourceDuring = $this->getFileLockType($view, $sourcePath);
2459
+                    $lockTypeTargetDuring = $this->getFileLockType($view, $targetPath);
2460
+
2461
+                    return true;
2462
+                }
2463
+            );
2464
+
2465
+        $this->connectMockHooks($viewOperation, $view, $sourcePath, $lockTypeSourcePre, $lockTypeSourcePost);
2466
+        $this->connectMockHooks($viewOperation, $view, $targetPath, $lockTypeTargetPre, $lockTypeTargetPost);
2467
+
2468
+        $this->assertNull($this->getFileLockType($view, $sourcePath), 'Source file not locked before operation');
2469
+        $this->assertNull($this->getFileLockType($view, $targetPath), 'Target file not locked before operation');
2470
+
2471
+        $view->$viewOperation($sourcePath, $targetPath);
2472
+
2473
+        $this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeSourcePre, 'Source file locked properly during pre-hook');
2474
+        $this->assertEquals($expectedLockTypeSourceDuring, $lockTypeSourceDuring, 'Source file locked properly during operation');
2475
+        $this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeSourcePost, 'Source file locked properly during post-hook');
2476
+
2477
+        $this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeTargetPre, 'Target file locked properly during pre-hook');
2478
+        $this->assertEquals(ILockingProvider::LOCK_EXCLUSIVE, $lockTypeTargetDuring, 'Target file locked properly during operation');
2479
+        $this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeTargetPost, 'Target file locked properly during post-hook');
2480
+
2481
+        $this->assertNull($this->getFileLockType($view, $sourcePath), 'Source file not locked after operation');
2482
+        $this->assertNull($this->getFileLockType($view, $targetPath), 'Target file not locked after operation');
2483
+    }
2484
+
2485
+    /**
2486
+     * Test locks when moving a mount point
2487
+     */
2488
+    public function testLockMoveMountPoint(): void {
2489
+        self::loginAsUser('test');
2490
+
2491
+        [$mount] = $this->createTestMovableMountPoints([
2492
+            $this->user . '/files/substorage',
2493
+        ]);
2494
+
2495
+        $view = new View('/' . $this->user . '/files/');
2496
+        $view->mkdir('subdir');
2497
+
2498
+        $sourcePath = 'substorage';
2499
+        $targetPath = 'subdir/substorage_moved';
2500
+
2501
+        $mount->expects($this->once())
2502
+            ->method('moveMount')
2503
+            ->willReturnCallback(
2504
+                function ($target) use ($mount, $view, $sourcePath, $targetPath, &$lockTypeSourceDuring, &$lockTypeTargetDuring, &$lockTypeSharedRootDuring) {
2505
+                    $lockTypeSourceDuring = $this->getFileLockType($view, $sourcePath, true);
2506
+                    $lockTypeTargetDuring = $this->getFileLockType($view, $targetPath, true);
2507
+
2508
+                    $lockTypeSharedRootDuring = $this->getFileLockType($view, $sourcePath, false);
2509
+
2510
+                    $mount->setMountPoint($target);
2511
+
2512
+                    return true;
2513
+                }
2514
+            );
2515
+
2516
+        $this->connectMockHooks('rename', $view, $sourcePath, $lockTypeSourcePre, $lockTypeSourcePost, true);
2517
+        $this->connectMockHooks('rename', $view, $targetPath, $lockTypeTargetPre, $lockTypeTargetPost, true);
2518
+        // in pre-hook, mount point is still on $sourcePath
2519
+        $this->connectMockHooks('rename', $view, $sourcePath, $lockTypeSharedRootPre, $dummy, false);
2520
+        // in post-hook, mount point is now on $targetPath
2521
+        $this->connectMockHooks('rename', $view, $targetPath, $dummy, $lockTypeSharedRootPost, false);
2522
+
2523
+        $this->assertNull($this->getFileLockType($view, $sourcePath, false), 'Shared storage root not locked before operation');
2524
+        $this->assertNull($this->getFileLockType($view, $sourcePath, true), 'Source path not locked before operation');
2525
+        $this->assertNull($this->getFileLockType($view, $targetPath, true), 'Target path not locked before operation');
2526
+
2527
+        $view->rename($sourcePath, $targetPath);
2528
+
2529
+        $this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeSourcePre, 'Source path locked properly during pre-hook');
2530
+        $this->assertEquals(ILockingProvider::LOCK_EXCLUSIVE, $lockTypeSourceDuring, 'Source path locked properly during operation');
2531
+        $this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeSourcePost, 'Source path locked properly during post-hook');
2532
+
2533
+        $this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeTargetPre, 'Target path locked properly during pre-hook');
2534
+        $this->assertEquals(ILockingProvider::LOCK_EXCLUSIVE, $lockTypeTargetDuring, 'Target path locked properly during operation');
2535
+        $this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeTargetPost, 'Target path locked properly during post-hook');
2536
+
2537
+        $this->assertNull($lockTypeSharedRootPre, 'Shared storage root not locked during pre-hook');
2538
+        $this->assertNull($lockTypeSharedRootDuring, 'Shared storage root not locked during move');
2539
+        $this->assertNull($lockTypeSharedRootPost, 'Shared storage root not locked during post-hook');
2540
+
2541
+        $this->assertNull($this->getFileLockType($view, $sourcePath, false), 'Shared storage root not locked after operation');
2542
+        $this->assertNull($this->getFileLockType($view, $sourcePath, true), 'Source path not locked after operation');
2543
+        $this->assertNull($this->getFileLockType($view, $targetPath, true), 'Target path not locked after operation');
2544
+    }
2545
+
2546
+    /**
2547
+     * Connect hook callbacks for hook type
2548
+     *
2549
+     * @param string $hookType hook type or null for none
2550
+     * @param View $view view to check the lock on
2551
+     * @param string $path path for which to check the lock
2552
+     * @param int $lockTypePre variable to receive lock type that was active in the pre-hook
2553
+     * @param int $lockTypePost variable to receive lock type that was active in the post-hook
2554
+     * @param bool $onMountPoint true to check the mount point instead of the
2555
+     *                           mounted storage
2556
+     */
2557
+    private function connectMockHooks($hookType, $view, $path, &$lockTypePre, &$lockTypePost, $onMountPoint = false) {
2558
+        if ($hookType === null) {
2559
+            return;
2560
+        }
2561
+
2562
+        $eventHandler = $this->getMockBuilder(TestEventHandler::class)
2563
+            ->onlyMethods(['preCallback', 'postCallback'])
2564
+            ->getMock();
2565
+
2566
+        $eventHandler->expects($this->any())
2567
+            ->method('preCallback')
2568
+            ->willReturnCallback(
2569
+                function () use ($view, $path, $onMountPoint, &$lockTypePre) {
2570
+                    $lockTypePre = $this->getFileLockType($view, $path, $onMountPoint);
2571
+                }
2572
+            );
2573
+        $eventHandler->expects($this->any())
2574
+            ->method('postCallback')
2575
+            ->willReturnCallback(
2576
+                function () use ($view, $path, $onMountPoint, &$lockTypePost) {
2577
+                    $lockTypePost = $this->getFileLockType($view, $path, $onMountPoint);
2578
+                }
2579
+            );
2580
+
2581
+        if ($hookType !== null) {
2582
+            Util::connectHook(
2583
+                Filesystem::CLASSNAME,
2584
+                $hookType,
2585
+                $eventHandler,
2586
+                'preCallback'
2587
+            );
2588
+            Util::connectHook(
2589
+                Filesystem::CLASSNAME,
2590
+                'post_' . $hookType,
2591
+                $eventHandler,
2592
+                'postCallback'
2593
+            );
2594
+        }
2595
+    }
2596
+
2597
+    /**
2598
+     * Returns the file lock type
2599
+     *
2600
+     * @param View $view view
2601
+     * @param string $path path
2602
+     * @param bool $onMountPoint true to check the mount point instead of the
2603
+     *                           mounted storage
2604
+     *
2605
+     * @return int lock type or null if file was not locked
2606
+     */
2607
+    private function getFileLockType(View $view, $path, $onMountPoint = false) {
2608
+        if ($this->isFileLocked($view, $path, ILockingProvider::LOCK_EXCLUSIVE, $onMountPoint)) {
2609
+            return ILockingProvider::LOCK_EXCLUSIVE;
2610
+        } elseif ($this->isFileLocked($view, $path, ILockingProvider::LOCK_SHARED, $onMountPoint)) {
2611
+            return ILockingProvider::LOCK_SHARED;
2612
+        }
2613
+        return null;
2614
+    }
2615
+
2616
+
2617
+    public function testRemoveMoveableMountPoint(): void {
2618
+        $mountPoint = '/' . $this->user . '/files/mount/';
2619
+
2620
+        // Mock the mount point
2621
+        /** @var TestMoveableMountPoint|\PHPUnit\Framework\MockObject\MockObject $mount */
2622
+        $mount = $this->createMock(TestMoveableMountPoint::class);
2623
+        $mount->expects($this->once())
2624
+            ->method('getMountPoint')
2625
+            ->willReturn($mountPoint);
2626
+        $mount->expects($this->once())
2627
+            ->method('removeMount')
2628
+            ->willReturn('foo');
2629
+        $mount->expects($this->any())
2630
+            ->method('getInternalPath')
2631
+            ->willReturn('');
2632
+
2633
+        // Register mount
2634
+        Filesystem::getMountManager()->addMount($mount);
2635
+
2636
+        // Listen for events
2637
+        $eventHandler = $this->getMockBuilder(TestEventHandler::class)
2638
+            ->onlyMethods(['umount', 'post_umount'])
2639
+            ->getMock();
2640
+        $eventHandler->expects($this->once())
2641
+            ->method('umount')
2642
+            ->with([Filesystem::signal_param_path => '/mount']);
2643
+        $eventHandler->expects($this->once())
2644
+            ->method('post_umount')
2645
+            ->with([Filesystem::signal_param_path => '/mount']);
2646
+        Util::connectHook(
2647
+            Filesystem::CLASSNAME,
2648
+            'umount',
2649
+            $eventHandler,
2650
+            'umount'
2651
+        );
2652
+        Util::connectHook(
2653
+            Filesystem::CLASSNAME,
2654
+            'post_umount',
2655
+            $eventHandler,
2656
+            'post_umount'
2657
+        );
2658
+
2659
+        //Delete the mountpoint
2660
+        $view = new View('/' . $this->user . '/files');
2661
+        $this->assertEquals('foo', $view->rmdir('mount'));
2662
+    }
2663
+
2664
+    public static function mimeFilterProvider(): array {
2665
+        return [
2666
+            [null, ['test1.txt', 'test2.txt', 'test3.md', 'test4.png']],
2667
+            ['text/plain', ['test1.txt', 'test2.txt']],
2668
+            ['text/markdown', ['test3.md']],
2669
+            ['text', ['test1.txt', 'test2.txt', 'test3.md']],
2670
+        ];
2671
+    }
2672
+
2673
+    /**
2674
+     * @param string $filter
2675
+     * @param string[] $expected
2676
+     * @dataProvider mimeFilterProvider
2677
+     */
2678
+    public function testGetDirectoryContentMimeFilter($filter, $expected): void {
2679
+        $storage1 = new Temporary();
2680
+        $root = self::getUniqueID('/');
2681
+        Filesystem::mount($storage1, [], $root . '/');
2682
+        $view = new View($root);
2683
+
2684
+        $view->file_put_contents('test1.txt', 'asd');
2685
+        $view->file_put_contents('test2.txt', 'asd');
2686
+        $view->file_put_contents('test3.md', 'asd');
2687
+        $view->file_put_contents('test4.png', '');
2688
+
2689
+        $content = $view->getDirectoryContent('', $filter);
2690
+
2691
+        $files = array_map(function (FileInfo $info) {
2692
+            return $info->getName();
2693
+        }, $content);
2694
+        sort($files);
2695
+
2696
+        $this->assertEquals($expected, $files);
2697
+    }
2698
+
2699
+    public function testFilePutContentsClearsChecksum(): void {
2700
+        $storage = new Temporary([]);
2701
+        $scanner = $storage->getScanner();
2702
+        $storage->file_put_contents('foo.txt', 'bar');
2703
+        Filesystem::mount($storage, [], '/test/');
2704
+        $scanner->scan('');
2705
+
2706
+        $view = new View('/test/foo.txt');
2707
+        $view->putFileInfo('.', ['checksum' => '42']);
2708
+
2709
+        $this->assertEquals('bar', $view->file_get_contents(''));
2710
+        $fh = tmpfile();
2711
+        fwrite($fh, 'fooo');
2712
+        rewind($fh);
2713
+        clearstatcache();
2714
+        $view->file_put_contents('', $fh);
2715
+        $this->assertEquals('fooo', $view->file_get_contents(''));
2716
+        $data = $view->getFileInfo('.');
2717
+        $this->assertEquals('', $data->getChecksum());
2718
+    }
2719
+
2720
+    public function testDeleteGhostFile(): void {
2721
+        $storage = new Temporary([]);
2722
+        $scanner = $storage->getScanner();
2723
+        $cache = $storage->getCache();
2724
+        $storage->file_put_contents('foo.txt', 'bar');
2725
+        Filesystem::mount($storage, [], '/test/');
2726
+        $scanner->scan('');
2727
+
2728
+        $storage->unlink('foo.txt');
2729
+
2730
+        $this->assertTrue($cache->inCache('foo.txt'));
2731
+
2732
+        $view = new View('/test');
2733
+        $rootInfo = $view->getFileInfo('');
2734
+        $this->assertEquals(3, $rootInfo->getSize());
2735
+        $view->unlink('foo.txt');
2736
+        $newInfo = $view->getFileInfo('');
2737
+
2738
+        $this->assertFalse($cache->inCache('foo.txt'));
2739
+        $this->assertNotEquals($rootInfo->getEtag(), $newInfo->getEtag());
2740
+        $this->assertEquals(0, $newInfo->getSize());
2741
+    }
2742
+
2743
+    public function testDeleteGhostFolder(): void {
2744
+        $storage = new Temporary([]);
2745
+        $scanner = $storage->getScanner();
2746
+        $cache = $storage->getCache();
2747
+        $storage->mkdir('foo');
2748
+        $storage->file_put_contents('foo/foo.txt', 'bar');
2749
+        Filesystem::mount($storage, [], '/test/');
2750
+        $scanner->scan('');
2751
+
2752
+        $storage->rmdir('foo');
2753
+
2754
+        $this->assertTrue($cache->inCache('foo'));
2755
+        $this->assertTrue($cache->inCache('foo/foo.txt'));
2756
+
2757
+        $view = new View('/test');
2758
+        $rootInfo = $view->getFileInfo('');
2759
+        $this->assertEquals(3, $rootInfo->getSize());
2760
+        $view->rmdir('foo');
2761
+        $newInfo = $view->getFileInfo('');
2762
+
2763
+        $this->assertFalse($cache->inCache('foo'));
2764
+        $this->assertFalse($cache->inCache('foo/foo.txt'));
2765
+        $this->assertNotEquals($rootInfo->getEtag(), $newInfo->getEtag());
2766
+        $this->assertEquals(0, $newInfo->getSize());
2767
+    }
2768
+
2769
+    public function testCreateParentDirectories(): void {
2770
+        $view = $this->getMockBuilder(View::class)
2771
+            ->disableOriginalConstructor()
2772
+            ->onlyMethods([
2773
+                'is_file',
2774
+                'file_exists',
2775
+                'mkdir',
2776
+            ])
2777
+            ->getMock();
2778
+
2779
+        $view->expects($this->exactly(3))
2780
+            ->method('is_file')
2781
+            ->willReturnMap([
2782
+                ['/new', false],
2783
+                ['/new/folder', false],
2784
+                ['/new/folder/structure', false],
2785
+            ]);
2786
+        $view->expects($this->exactly(3))
2787
+            ->method('file_exists')
2788
+            ->willReturnMap([
2789
+                ['/new', true],
2790
+                ['/new/folder', false],
2791
+                ['/new/folder/structure', false],
2792
+            ]);
2793
+
2794
+        $calls = ['/new/folder', '/new/folder/structure'];
2795
+        $view->expects($this->exactly(2))
2796
+            ->method('mkdir')
2797
+            ->willReturnCallback(function ($dir) use (&$calls) {
2798
+                $expected = array_shift($calls);
2799
+                $this->assertEquals($expected, $dir);
2800
+            });
2801
+
2802
+        $this->assertTrue(self::invokePrivate($view, 'createParentDirectories', ['/new/folder/structure']));
2803
+    }
2804
+
2805
+    public function testCreateParentDirectoriesWithExistingFile(): void {
2806
+        $view = $this->getMockBuilder(View::class)
2807
+            ->disableOriginalConstructor()
2808
+            ->onlyMethods([
2809
+                'is_file',
2810
+                'file_exists',
2811
+                'mkdir',
2812
+            ])
2813
+            ->getMock();
2814
+
2815
+        $view
2816
+            ->expects($this->once())
2817
+            ->method('is_file')
2818
+            ->with('/file.txt')
2819
+            ->willReturn(true);
2820
+        $this->assertFalse(self::invokePrivate($view, 'createParentDirectories', ['/file.txt/folder/structure']));
2821
+    }
2822
+
2823
+    public function testCacheExtension(): void {
2824
+        $storage = new Temporary([]);
2825
+        $scanner = $storage->getScanner();
2826
+        $storage->file_put_contents('foo.txt', 'bar');
2827
+        $scanner->scan('');
2828
+
2829
+        Filesystem::mount($storage, [], '/test/');
2830
+        $view = new View('/test');
2831
+
2832
+        $info = $view->getFileInfo('/foo.txt');
2833
+        $this->assertEquals(0, $info->getUploadTime());
2834
+        $this->assertEquals(0, $info->getCreationTime());
2835
+
2836
+        $view->putFileInfo('/foo.txt', ['upload_time' => 25]);
2837
+
2838
+        $info = $view->getFileInfo('/foo.txt');
2839
+        $this->assertEquals(25, $info->getUploadTime());
2840
+        $this->assertEquals(0, $info->getCreationTime());
2841
+    }
2842
+
2843
+    public function testFopenGone(): void {
2844
+        $storage = new Temporary([]);
2845
+        $scanner = $storage->getScanner();
2846
+        $storage->file_put_contents('foo.txt', 'bar');
2847
+        $scanner->scan('');
2848
+        $cache = $storage->getCache();
2849
+
2850
+        Filesystem::mount($storage, [], '/test/');
2851
+        $view = new View('/test');
2852
+
2853
+        $storage->unlink('foo.txt');
2854
+
2855
+        $this->assertTrue($cache->inCache('foo.txt'));
2856
+
2857
+        $this->assertFalse($view->fopen('foo.txt', 'r'));
2858
+
2859
+        $this->assertFalse($cache->inCache('foo.txt'));
2860
+    }
2861
+
2862
+    public function testMountpointParentsCreated(): void {
2863
+        $storage1 = $this->getTestStorage();
2864
+        Filesystem::mount($storage1, [], '/');
2865
+
2866
+        $storage2 = $this->getTestStorage();
2867
+        Filesystem::mount($storage2, [], '/A/B/C');
2868
+
2869
+        $rootView = new View('');
2870
+
2871
+        $folderData = $rootView->getDirectoryContent('/');
2872
+        $this->assertCount(4, $folderData);
2873
+        $this->assertEquals('folder', $folderData[0]['name']);
2874
+        $this->assertEquals('foo.png', $folderData[1]['name']);
2875
+        $this->assertEquals('foo.txt', $folderData[2]['name']);
2876
+        $this->assertEquals('A', $folderData[3]['name']);
2877
+
2878
+        $folderData = $rootView->getDirectoryContent('/A');
2879
+        $this->assertCount(1, $folderData);
2880
+        $this->assertEquals('B', $folderData[0]['name']);
2881
+
2882
+        $folderData = $rootView->getDirectoryContent('/A/B');
2883
+        $this->assertCount(1, $folderData);
2884
+        $this->assertEquals('C', $folderData[0]['name']);
2885
+
2886
+        $folderData = $rootView->getDirectoryContent('/A/B/C');
2887
+        $this->assertCount(3, $folderData);
2888
+        $this->assertEquals('folder', $folderData[0]['name']);
2889
+        $this->assertEquals('foo.png', $folderData[1]['name']);
2890
+        $this->assertEquals('foo.txt', $folderData[2]['name']);
2891
+    }
2892
+
2893
+    public function testCopyPreservesContent() {
2894
+        $viewUser1 = new View('/' . 'userId' . '/files');
2895
+        $viewUser1->mkdir('');
2896
+        $viewUser1->file_put_contents('foo.txt', 'foo');
2897
+        $viewUser1->copy('foo.txt', 'bar.txt');
2898
+        $this->assertEquals('foo', $viewUser1->file_get_contents('bar.txt'));
2899
+    }
2900 2900
 }
Please login to merge, or discard this patch.
Spacing   +90 added lines, -90 removed lines patch added patch discarded remove patch
@@ -143,7 +143,7 @@  discard block
 block discarded – undo
143 143
 		}
144 144
 
145 145
 		if ($this->tempStorage) {
146
-			system('rm -rf ' . escapeshellarg($this->tempStorage->getDataDir()));
146
+			system('rm -rf '.escapeshellarg($this->tempStorage->getDataDir()));
147 147
 		}
148 148
 
149 149
 		self::logout();
@@ -169,11 +169,11 @@  discard block
 block discarded – undo
169 169
 		$storage2 = $this->getTestStorage();
170 170
 		$storage3 = $this->getTestStorage();
171 171
 		$root = self::getUniqueID('/');
172
-		Filesystem::mount($storage1, [], $root . '/');
173
-		Filesystem::mount($storage2, [], $root . '/substorage');
174
-		Filesystem::mount($storage3, [], $root . '/folder/anotherstorage');
172
+		Filesystem::mount($storage1, [], $root.'/');
173
+		Filesystem::mount($storage2, [], $root.'/substorage');
174
+		Filesystem::mount($storage3, [], $root.'/folder/anotherstorage');
175 175
 		$textSize = strlen("dummy file data\n");
176
-		$imageSize = filesize(\OC::$SERVERROOT . '/core/img/logo/logo.png');
176
+		$imageSize = filesize(\OC::$SERVERROOT.'/core/img/logo/logo.png');
177 177
 		$storageSize = $textSize * 2 + $imageSize;
178 178
 
179 179
 		$storageInfo = $storage3->getCache()->get('');
@@ -230,7 +230,7 @@  discard block
 block discarded – undo
230 230
 		$this->assertEquals('foo.png', $folderData[1]['name']);
231 231
 		$this->assertEquals('foo.txt', $folderData[2]['name']);
232 232
 
233
-		$folderView = new View($root . '/folder');
233
+		$folderView = new View($root.'/folder');
234 234
 		$this->assertEquals($rootView->getFileInfo('/folder'), $folderView->getFileInfo('/'));
235 235
 
236 236
 		$cachedData = $rootView->getFileInfo('/foo.txt');
@@ -519,10 +519,10 @@  discard block
 block discarded – undo
519 519
 	}
520 520
 
521 521
 	public function moveBetweenStorages($storage1, $storage2) {
522
-		Filesystem::mount($storage1, [], '/' . $this->user . '/');
523
-		Filesystem::mount($storage2, [], '/' . $this->user . '/substorage');
522
+		Filesystem::mount($storage1, [], '/'.$this->user.'/');
523
+		Filesystem::mount($storage2, [], '/'.$this->user.'/substorage');
524 524
 
525
-		$rootView = new View('/' . $this->user);
525
+		$rootView = new View('/'.$this->user);
526 526
 		$rootView->rename('foo.txt', 'substorage/folder/foo.txt');
527 527
 		$this->assertFalse($rootView->file_exists('foo.txt'));
528 528
 		$this->assertTrue($rootView->file_exists('substorage/folder/foo.txt'));
@@ -646,11 +646,11 @@  discard block
 block discarded – undo
646 646
 		$storage2 = $this->getTestStorage();
647 647
 		$defaultRoot = Filesystem::getRoot();
648 648
 		Filesystem::mount($storage1, [], '/');
649
-		Filesystem::mount($storage2, [], $defaultRoot . '/substorage');
649
+		Filesystem::mount($storage2, [], $defaultRoot.'/substorage');
650 650
 		\OC_Hook::connect('OC_Filesystem', 'post_write', $this, 'dummyHook');
651 651
 
652 652
 		$rootView = new View('');
653
-		$subView = new View($defaultRoot . '/substorage');
653
+		$subView = new View($defaultRoot.'/substorage');
654 654
 		$this->hookPath = null;
655 655
 
656 656
 		$rootView->file_put_contents('/foo.txt', 'asd');
@@ -690,7 +690,7 @@  discard block
 block discarded – undo
690 690
 		 */
691 691
 		$storage = new $class([]);
692 692
 		$textData = "dummy file data\n";
693
-		$imgData = file_get_contents(\OC::$SERVERROOT . '/core/img/logo/logo.png');
693
+		$imgData = file_get_contents(\OC::$SERVERROOT.'/core/img/logo/logo.png');
694 694
 		$storage->mkdir('folder');
695 695
 		$storage->file_put_contents('foo.txt', $textData);
696 696
 		$storage->file_put_contents('foo.png', $imgData);
@@ -712,10 +712,10 @@  discard block
 block discarded – undo
712 712
 		$storage2 = $this->getTestStorage();
713 713
 		$defaultRoot = Filesystem::getRoot();
714 714
 		Filesystem::mount($storage1, [], '/');
715
-		Filesystem::mount($storage2, [], $defaultRoot . '_substorage');
715
+		Filesystem::mount($storage2, [], $defaultRoot.'_substorage');
716 716
 		\OC_Hook::connect('OC_Filesystem', 'post_write', $this, 'dummyHook');
717 717
 
718
-		$subView = new View($defaultRoot . '_substorage');
718
+		$subView = new View($defaultRoot.'_substorage');
719 719
 		$this->hookPath = null;
720 720
 
721 721
 		$subView->file_put_contents('/foo.txt', 'asd');
@@ -810,7 +810,7 @@  discard block
 block discarded – undo
810 810
 
811 811
 		$rootView = new View('');
812 812
 		foreach ($names as $name) {
813
-			$rootView->file_put_contents('/' . $name, 'dummy content');
813
+			$rootView->file_put_contents('/'.$name, 'dummy content');
814 814
 		}
815 815
 
816 816
 		$list = $rootView->getDirectoryContent('/');
@@ -852,15 +852,15 @@  discard block
 block discarded – undo
852 852
 			$depth = ((4000 - $tmpdirLength) / 57);
853 853
 		}
854 854
 		foreach (range(0, $depth - 1) as $i) {
855
-			$longPath .= $ds . $folderName;
855
+			$longPath .= $ds.$folderName;
856 856
 			$result = $rootView->mkdir($longPath);
857
-			$this->assertTrue($result, "mkdir failed on $i - path length: " . strlen($longPath));
857
+			$this->assertTrue($result, "mkdir failed on $i - path length: ".strlen($longPath));
858 858
 
859
-			$result = $rootView->file_put_contents($longPath . "{$ds}test.txt", 'lorem');
859
+			$result = $rootView->file_put_contents($longPath."{$ds}test.txt", 'lorem');
860 860
 			$this->assertEquals(5, $result, "file_put_contents failed on $i");
861 861
 
862 862
 			$this->assertTrue($rootView->file_exists($longPath));
863
-			$this->assertTrue($rootView->file_exists($longPath . "{$ds}test.txt"));
863
+			$this->assertTrue($rootView->file_exists($longPath."{$ds}test.txt"));
864 864
 		}
865 865
 
866 866
 		$cache = $storage->getCache();
@@ -873,11 +873,11 @@  discard block
 block discarded – undo
873 873
 			$this->assertTrue(is_array($cachedFolder), "No cache entry for folder at $i");
874 874
 			$this->assertEquals($folderName, $cachedFolder['name'], "Wrong cache entry for folder at $i");
875 875
 
876
-			$cachedFile = $cache->get($longPath . '/test.txt');
876
+			$cachedFile = $cache->get($longPath.'/test.txt');
877 877
 			$this->assertTrue(is_array($cachedFile), "No cache entry for file at $i");
878 878
 			$this->assertEquals('test.txt', $cachedFile['name'], "Wrong cache entry for file at $i");
879 879
 
880
-			$longPath .= $ds . $folderName;
880
+			$longPath .= $ds.$folderName;
881 881
 		}
882 882
 	}
883 883
 
@@ -1090,7 +1090,7 @@  discard block
 block discarded – undo
1090 1090
 		$folderName = 'abcdefghijklmnopqrstuvwxyz012345678901234567890123456789';
1091 1091
 		$depth = (4000 / 57);
1092 1092
 		foreach (range(0, $depth + 1) as $i) {
1093
-			$longPath .= '/' . $folderName;
1093
+			$longPath .= '/'.$folderName;
1094 1094
 		}
1095 1095
 
1096 1096
 		$storage = new Temporary([]);
@@ -1340,8 +1340,8 @@  discard block
 block discarded – undo
1340 1340
 		$view = new View($rootPath);
1341 1341
 		$storage = new Temporary([]);
1342 1342
 		Filesystem::mount($storage, [], '/');
1343
-		$this->assertTrue($view->lockFile($pathPrefix . '/foo/bar', ILockingProvider::LOCK_EXCLUSIVE));
1344
-		$view->lockFile($pathPrefix . '/foo/bar/asd', ILockingProvider::LOCK_SHARED);
1343
+		$this->assertTrue($view->lockFile($pathPrefix.'/foo/bar', ILockingProvider::LOCK_EXCLUSIVE));
1344
+		$view->lockFile($pathPrefix.'/foo/bar/asd', ILockingProvider::LOCK_SHARED);
1345 1345
 	}
1346 1346
 
1347 1347
 	/**
@@ -1359,8 +1359,8 @@  discard block
 block discarded – undo
1359 1359
 		$view = new View($rootPath);
1360 1360
 		$storage = new Temporary([]);
1361 1361
 		Filesystem::mount($storage, [], '/');
1362
-		$this->assertFalse($view->lockFile($pathPrefix . '/foo/bar', ILockingProvider::LOCK_EXCLUSIVE));
1363
-		$this->assertFalse($view->lockFile($pathPrefix . '/foo/bar/asd', ILockingProvider::LOCK_SHARED));
1362
+		$this->assertFalse($view->lockFile($pathPrefix.'/foo/bar', ILockingProvider::LOCK_EXCLUSIVE));
1363
+		$this->assertFalse($view->lockFile($pathPrefix.'/foo/bar/asd', ILockingProvider::LOCK_SHARED));
1364 1364
 	}
1365 1365
 
1366 1366
 	/**
@@ -1381,8 +1381,8 @@  discard block
 block discarded – undo
1381 1381
 		$view = new View($rootPath);
1382 1382
 		$storage = new Temporary([]);
1383 1383
 		Filesystem::mount($storage, [], '/');
1384
-		$this->assertTrue($view->lockFile($pathPrefix . '/foo/bar', ILockingProvider::LOCK_SHARED));
1385
-		$view->lockFile($pathPrefix . '/foo/bar', ILockingProvider::LOCK_EXCLUSIVE);
1384
+		$this->assertTrue($view->lockFile($pathPrefix.'/foo/bar', ILockingProvider::LOCK_SHARED));
1385
+		$view->lockFile($pathPrefix.'/foo/bar', ILockingProvider::LOCK_EXCLUSIVE);
1386 1386
 	}
1387 1387
 
1388 1388
 	/**
@@ -1400,8 +1400,8 @@  discard block
 block discarded – undo
1400 1400
 		$view = new View($rootPath);
1401 1401
 		$storage = new Temporary([]);
1402 1402
 		Filesystem::mount($storage, [], '/');
1403
-		$this->assertFalse($view->lockFile($pathPrefix . '/foo/bar', ILockingProvider::LOCK_SHARED));
1404
-		$this->assertFalse($view->lockFile($pathPrefix . '/foo/bar', ILockingProvider::LOCK_EXCLUSIVE));
1403
+		$this->assertFalse($view->lockFile($pathPrefix.'/foo/bar', ILockingProvider::LOCK_SHARED));
1404
+		$this->assertFalse($view->lockFile($pathPrefix.'/foo/bar', ILockingProvider::LOCK_EXCLUSIVE));
1405 1405
 	}
1406 1406
 
1407 1407
 	/**
@@ -1610,7 +1610,7 @@  discard block
 block discarded – undo
1610 1610
 				->onlyMethods([])
1611 1611
 				->getMock();
1612 1612
 			$storage->method('getId')->willReturn('non-null-id');
1613
-			$storage->method('getStorageCache')->willReturnCallback(function () use ($storage) {
1613
+			$storage->method('getStorageCache')->willReturnCallback(function() use ($storage) {
1614 1614
 				return new \OC\Files\Cache\Storage($storage, true, \OC::$server->get(IDBConnection::class));
1615 1615
 			});
1616 1616
 
@@ -1639,8 +1639,8 @@  discard block
 block discarded – undo
1639 1639
 		self::loginAsUser($this->user);
1640 1640
 
1641 1641
 		[$mount1, $mount2] = $this->createTestMovableMountPoints([
1642
-			$this->user . '/files/mount1',
1643
-			$this->user . '/files/mount2',
1642
+			$this->user.'/files/mount1',
1643
+			$this->user.'/files/mount2',
1644 1644
 		]);
1645 1645
 		$mount1->expects($this->once())
1646 1646
 			->method('moveMount')
@@ -1650,7 +1650,7 @@  discard block
 block discarded – undo
1650 1650
 			->method('moveMount')
1651 1651
 			->willReturn(true);
1652 1652
 
1653
-		$view = new View('/' . $this->user . '/files/');
1653
+		$view = new View('/'.$this->user.'/files/');
1654 1654
 		$view->mkdir('sub');
1655 1655
 
1656 1656
 		$this->assertTrue($view->rename('mount1', 'renamed_mount'), 'Can rename mount point');
@@ -1661,8 +1661,8 @@  discard block
 block discarded – undo
1661 1661
 		self::loginAsUser($this->user);
1662 1662
 
1663 1663
 		[$mount1, $mount2] = $this->createTestMovableMountPoints([
1664
-			$this->user . '/files/mount1',
1665
-			$this->user . '/files/mount2',
1664
+			$this->user.'/files/mount1',
1665
+			$this->user.'/files/mount2',
1666 1666
 		]);
1667 1667
 
1668 1668
 		$mount1->expects($this->never())
@@ -1671,7 +1671,7 @@  discard block
 block discarded – undo
1671 1671
 		$mount2->expects($this->never())
1672 1672
 			->method('moveMount');
1673 1673
 
1674
-		$view = new View('/' . $this->user . '/files/');
1674
+		$view = new View('/'.$this->user.'/files/');
1675 1675
 
1676 1676
 		$this->expectException(ForbiddenException::class);
1677 1677
 		$view->rename('mount1', 'mount2');
@@ -1681,8 +1681,8 @@  discard block
 block discarded – undo
1681 1681
 		self::loginAsUser($this->user);
1682 1682
 
1683 1683
 		[$mount1, $mount2] = $this->createTestMovableMountPoints([
1684
-			$this->user . '/files/mount1',
1685
-			$this->user . '/files/mount2',
1684
+			$this->user.'/files/mount1',
1685
+			$this->user.'/files/mount2',
1686 1686
 		]);
1687 1687
 
1688 1688
 		$mount1->expects($this->never())
@@ -1691,7 +1691,7 @@  discard block
 block discarded – undo
1691 1691
 		$mount2->expects($this->never())
1692 1692
 			->method('moveMount');
1693 1693
 
1694
-		$view = new View('/' . $this->user . '/files/');
1694
+		$view = new View('/'.$this->user.'/files/');
1695 1695
 
1696 1696
 		$this->expectException(ForbiddenException::class);
1697 1697
 		$view->rename('mount1', 'mount2/sub');
@@ -1704,8 +1704,8 @@  discard block
 block discarded – undo
1704 1704
 		self::loginAsUser($this->user);
1705 1705
 
1706 1706
 		[$mount1, $mount2] = $this->createTestMovableMountPoints([
1707
-			$this->user . '/files/mount1',
1708
-			$this->user . '/files/mount2',
1707
+			$this->user.'/files/mount1',
1708
+			$this->user.'/files/mount2',
1709 1709
 		]);
1710 1710
 
1711 1711
 		$mount1->expects($this->never())
@@ -1715,7 +1715,7 @@  discard block
 block discarded – undo
1715 1715
 			->method('moveMount')
1716 1716
 			->willReturn(true);
1717 1717
 
1718
-		$view = new View('/' . $this->user . '/files/');
1718
+		$view = new View('/'.$this->user.'/files/');
1719 1719
 		$view->mkdir('shareddir');
1720 1720
 		$view->mkdir('shareddir/sub');
1721 1721
 		$view->mkdir('shareddir/sub2');
@@ -1941,7 +1941,7 @@  discard block
 block discarded – undo
1941 1941
 		$expectedStrayLock = null,
1942 1942
 		$returnValue = true,
1943 1943
 	): void {
1944
-		$view = new View('/' . $this->user . '/files/');
1944
+		$view = new View('/'.$this->user.'/files/');
1945 1945
 
1946 1946
 		/** @var Temporary&MockObject $storage */
1947 1947
 		$storage = $this->getMockBuilder(Temporary::class)
@@ -1953,19 +1953,19 @@  discard block
 block discarded – undo
1953 1953
 		/* Same thing with encryption wrapper */
1954 1954
 		Server::get(IStorageFactory::class)->removeStorageWrapper('oc_encryption');
1955 1955
 
1956
-		Filesystem::mount($storage, [], $this->user . '/');
1956
+		Filesystem::mount($storage, [], $this->user.'/');
1957 1957
 
1958 1958
 		// work directly on disk because mkdir might be mocked
1959 1959
 		$realPath = $storage->getSourcePath('');
1960
-		mkdir($realPath . '/files');
1961
-		mkdir($realPath . '/files/dir');
1962
-		file_put_contents($realPath . '/files/test.txt', 'blah');
1960
+		mkdir($realPath.'/files');
1961
+		mkdir($realPath.'/files/dir');
1962
+		file_put_contents($realPath.'/files/test.txt', 'blah');
1963 1963
 		$storage->getScanner()->scan('files');
1964 1964
 
1965 1965
 		$storage->expects($this->once())
1966 1966
 			->method($operation)
1967 1967
 			->willReturnCallback(
1968
-				function () use ($view, $lockedPath, &$lockTypeDuring, $returnValue) {
1968
+				function() use ($view, $lockedPath, &$lockTypeDuring, $returnValue) {
1969 1969
 					$lockTypeDuring = $this->getFileLockType($view, $lockedPath);
1970 1970
 
1971 1971
 					return $returnValue;
@@ -1998,7 +1998,7 @@  discard block
 block discarded – undo
1998 1998
 	 * This code path uses $storage->fopen instead
1999 1999
 	 */
2000 2000
 	public function testLockFilePutContentWithStream(): void {
2001
-		$view = new View('/' . $this->user . '/files/');
2001
+		$view = new View('/'.$this->user.'/files/');
2002 2002
 
2003 2003
 		$path = 'test_file_put_contents.txt';
2004 2004
 		/** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */
@@ -2006,13 +2006,13 @@  discard block
 block discarded – undo
2006 2006
 			->onlyMethods(['fopen'])
2007 2007
 			->getMock();
2008 2008
 
2009
-		Filesystem::mount($storage, [], $this->user . '/');
2009
+		Filesystem::mount($storage, [], $this->user.'/');
2010 2010
 		$storage->mkdir('files');
2011 2011
 
2012 2012
 		$storage->expects($this->once())
2013 2013
 			->method('fopen')
2014 2014
 			->willReturnCallback(
2015
-				function () use ($view, $path, &$lockTypeDuring) {
2015
+				function() use ($view, $path, &$lockTypeDuring) {
2016 2016
 					$lockTypeDuring = $this->getFileLockType($view, $path);
2017 2017
 
2018 2018
 					return fopen('php://temp', 'r+');
@@ -2037,7 +2037,7 @@  discard block
 block discarded – undo
2037 2037
 	 * Test locks for fopen with fclose at the end
2038 2038
 	 */
2039 2039
 	public function testLockFopen(): void {
2040
-		$view = new View('/' . $this->user . '/files/');
2040
+		$view = new View('/'.$this->user.'/files/');
2041 2041
 
2042 2042
 		$path = 'test_file_put_contents.txt';
2043 2043
 		/** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */
@@ -2045,13 +2045,13 @@  discard block
 block discarded – undo
2045 2045
 			->onlyMethods(['fopen'])
2046 2046
 			->getMock();
2047 2047
 
2048
-		Filesystem::mount($storage, [], $this->user . '/');
2048
+		Filesystem::mount($storage, [], $this->user.'/');
2049 2049
 		$storage->mkdir('files');
2050 2050
 
2051 2051
 		$storage->expects($this->once())
2052 2052
 			->method('fopen')
2053 2053
 			->willReturnCallback(
2054
-				function () use ($view, $path, &$lockTypeDuring) {
2054
+				function() use ($view, $path, &$lockTypeDuring) {
2055 2055
 					$lockTypeDuring = $this->getFileLockType($view, $path);
2056 2056
 
2057 2057
 					return fopen('php://temp', 'r+');
@@ -2093,7 +2093,7 @@  discard block
 block discarded – undo
2093 2093
 		if ($operation === 'touch') {
2094 2094
 			$this->markTestSkipped('touch handles storage exceptions internally');
2095 2095
 		}
2096
-		$view = new View('/' . $this->user . '/files/');
2096
+		$view = new View('/'.$this->user.'/files/');
2097 2097
 
2098 2098
 		/** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */
2099 2099
 		$storage = $this->getMockBuilder(Temporary::class)
@@ -2105,19 +2105,19 @@  discard block
 block discarded – undo
2105 2105
 		/* Same thing with encryption wrapper */
2106 2106
 		Server::get(IStorageFactory::class)->removeStorageWrapper('oc_encryption');
2107 2107
 
2108
-		Filesystem::mount($storage, [], $this->user . '/');
2108
+		Filesystem::mount($storage, [], $this->user.'/');
2109 2109
 
2110 2110
 		// work directly on disk because mkdir might be mocked
2111 2111
 		$realPath = $storage->getSourcePath('');
2112
-		mkdir($realPath . '/files');
2113
-		mkdir($realPath . '/files/dir');
2114
-		file_put_contents($realPath . '/files/test.txt', 'blah');
2112
+		mkdir($realPath.'/files');
2113
+		mkdir($realPath.'/files/dir');
2114
+		file_put_contents($realPath.'/files/test.txt', 'blah');
2115 2115
 		$storage->getScanner()->scan('files');
2116 2116
 
2117 2117
 		$storage->expects($this->once())
2118 2118
 			->method($operation)
2119 2119
 			->willReturnCallback(
2120
-				function () {
2120
+				function() {
2121 2121
 					throw new \Exception('Simulated exception');
2122 2122
 				}
2123 2123
 			);
@@ -2137,11 +2137,11 @@  discard block
 block discarded – undo
2137 2137
 	}
2138 2138
 
2139 2139
 	public function testLockBasicOperationUnlocksAfterLockException(): void {
2140
-		$view = new View('/' . $this->user . '/files/');
2140
+		$view = new View('/'.$this->user.'/files/');
2141 2141
 
2142 2142
 		$storage = new Temporary([]);
2143 2143
 
2144
-		Filesystem::mount($storage, [], $this->user . '/');
2144
+		Filesystem::mount($storage, [], $this->user.'/');
2145 2145
 
2146 2146
 		$storage->mkdir('files');
2147 2147
 		$storage->mkdir('files/dir');
@@ -2183,14 +2183,14 @@  discard block
 block discarded – undo
2183 2183
 		$path,
2184 2184
 		$hookType,
2185 2185
 	): void {
2186
-		$view = new View('/' . $this->user . '/files/');
2186
+		$view = new View('/'.$this->user.'/files/');
2187 2187
 
2188 2188
 		/** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */
2189 2189
 		$storage = $this->getMockBuilder(Temporary::class)
2190 2190
 			->onlyMethods([$operation])
2191 2191
 			->getMock();
2192 2192
 
2193
-		Filesystem::mount($storage, [], $this->user . '/');
2193
+		Filesystem::mount($storage, [], $this->user.'/');
2194 2194
 		$storage->mkdir('files');
2195 2195
 
2196 2196
 		Util::connectHook(
@@ -2222,7 +2222,7 @@  discard block
 block discarded – undo
2222 2222
 	 *                                          the operation
2223 2223
 	 */
2224 2224
 	public function testLockFileRename($operation, $expectedLockTypeSourceDuring): void {
2225
-		$view = new View('/' . $this->user . '/files/');
2225
+		$view = new View('/'.$this->user.'/files/');
2226 2226
 
2227 2227
 		/** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */
2228 2228
 		$storage = $this->getMockBuilder(Temporary::class)
@@ -2248,14 +2248,14 @@  discard block
 block discarded – undo
2248 2248
 		/* Disable encryption wrapper to avoid it intercepting mocked call */
2249 2249
 		Server::get(IStorageFactory::class)->removeStorageWrapper('oc_encryption');
2250 2250
 
2251
-		Filesystem::mount($storage, [], $this->user . '/');
2251
+		Filesystem::mount($storage, [], $this->user.'/');
2252 2252
 		$storage->mkdir('files');
2253 2253
 		$view->file_put_contents($sourcePath, 'meh');
2254 2254
 
2255 2255
 		$storage->expects($this->once())
2256 2256
 			->method($operation)
2257 2257
 			->willReturnCallback(
2258
-				function () use ($view, $sourcePath, $targetPath, &$lockTypeSourceDuring, &$lockTypeTargetDuring) {
2258
+				function() use ($view, $sourcePath, $targetPath, &$lockTypeSourceDuring, &$lockTypeTargetDuring) {
2259 2259
 					$lockTypeSourceDuring = $this->getFileLockType($view, $sourcePath);
2260 2260
 					$lockTypeTargetDuring = $this->getFileLockType($view, $targetPath);
2261 2261
 
@@ -2291,7 +2291,7 @@  discard block
 block discarded – undo
2291 2291
 	public function testLockFileCopyException(): void {
2292 2292
 		$this->expectException(\Exception::class);
2293 2293
 
2294
-		$view = new View('/' . $this->user . '/files/');
2294
+		$view = new View('/'.$this->user.'/files/');
2295 2295
 
2296 2296
 		/** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */
2297 2297
 		$storage = $this->getMockBuilder(Temporary::class)
@@ -2304,14 +2304,14 @@  discard block
 block discarded – undo
2304 2304
 		/* Disable encryption wrapper to avoid it intercepting mocked call */
2305 2305
 		Server::get(IStorageFactory::class)->removeStorageWrapper('oc_encryption');
2306 2306
 
2307
-		Filesystem::mount($storage, [], $this->user . '/');
2307
+		Filesystem::mount($storage, [], $this->user.'/');
2308 2308
 		$storage->mkdir('files');
2309 2309
 		$view->file_put_contents($sourcePath, 'meh');
2310 2310
 
2311 2311
 		$storage->expects($this->once())
2312 2312
 			->method('copy')
2313 2313
 			->willReturnCallback(
2314
-				function () {
2314
+				function() {
2315 2315
 					throw new \Exception();
2316 2316
 				}
2317 2317
 			);
@@ -2337,7 +2337,7 @@  discard block
 block discarded – undo
2337 2337
 	public function testLockFileRenameUnlockOnException(): void {
2338 2338
 		self::loginAsUser('test');
2339 2339
 
2340
-		$view = new View('/' . $this->user . '/files/');
2340
+		$view = new View('/'.$this->user.'/files/');
2341 2341
 
2342 2342
 		$sourcePath = 'original.txt';
2343 2343
 		$targetPath = 'target.txt';
@@ -2378,7 +2378,7 @@  discard block
 block discarded – undo
2378 2378
 		$this->assertEquals('test', $view->getFileInfo($path)->getOwner()->getUID());
2379 2379
 
2380 2380
 		$folderInfo = $view->getDirectoryContent('');
2381
-		$folderInfo = array_values(array_filter($folderInfo, function (FileInfo $info) {
2381
+		$folderInfo = array_values(array_filter($folderInfo, function(FileInfo $info) {
2382 2382
 			return $info->getName() === 'foo.txt';
2383 2383
 		}));
2384 2384
 
@@ -2388,7 +2388,7 @@  discard block
 block discarded – undo
2388 2388
 		Filesystem::mount($subStorage, [], '/test/files/asd');
2389 2389
 
2390 2390
 		$folderInfo = $view->getDirectoryContent('');
2391
-		$folderInfo = array_values(array_filter($folderInfo, function (FileInfo $info) {
2391
+		$folderInfo = array_values(array_filter($folderInfo, function(FileInfo $info) {
2392 2392
 			return $info->getName() === 'asd';
2393 2393
 		}));
2394 2394
 
@@ -2413,7 +2413,7 @@  discard block
 block discarded – undo
2413 2413
 	 *                                          the operation
2414 2414
 	 */
2415 2415
 	public function testLockFileRenameCrossStorage($viewOperation, $storageOperation, $expectedLockTypeSourceDuring): void {
2416
-		$view = new View('/' . $this->user . '/files/');
2416
+		$view = new View('/'.$this->user.'/files/');
2417 2417
 
2418 2418
 		/** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */
2419 2419
 		$storage = $this->getMockBuilder(Temporary::class)
@@ -2443,8 +2443,8 @@  discard block
 block discarded – undo
2443 2443
 		/* Disable encryption wrapper to avoid it intercepting mocked call */
2444 2444
 		Server::get(IStorageFactory::class)->removeStorageWrapper('oc_encryption');
2445 2445
 
2446
-		Filesystem::mount($storage, [], $this->user . '/');
2447
-		Filesystem::mount($storage2, [], $this->user . '/files/substorage');
2446
+		Filesystem::mount($storage, [], $this->user.'/');
2447
+		Filesystem::mount($storage2, [], $this->user.'/files/substorage');
2448 2448
 		$storage->mkdir('files');
2449 2449
 		$view->file_put_contents($sourcePath, 'meh');
2450 2450
 		$storage2->getUpdater()->update('');
@@ -2454,7 +2454,7 @@  discard block
 block discarded – undo
2454 2454
 		$storage2->expects($this->once())
2455 2455
 			->method($storageOperation)
2456 2456
 			->willReturnCallback(
2457
-				function () use ($view, $sourcePath, $targetPath, &$lockTypeSourceDuring, &$lockTypeTargetDuring) {
2457
+				function() use ($view, $sourcePath, $targetPath, &$lockTypeSourceDuring, &$lockTypeTargetDuring) {
2458 2458
 					$lockTypeSourceDuring = $this->getFileLockType($view, $sourcePath);
2459 2459
 					$lockTypeTargetDuring = $this->getFileLockType($view, $targetPath);
2460 2460
 
@@ -2489,10 +2489,10 @@  discard block
 block discarded – undo
2489 2489
 		self::loginAsUser('test');
2490 2490
 
2491 2491
 		[$mount] = $this->createTestMovableMountPoints([
2492
-			$this->user . '/files/substorage',
2492
+			$this->user.'/files/substorage',
2493 2493
 		]);
2494 2494
 
2495
-		$view = new View('/' . $this->user . '/files/');
2495
+		$view = new View('/'.$this->user.'/files/');
2496 2496
 		$view->mkdir('subdir');
2497 2497
 
2498 2498
 		$sourcePath = 'substorage';
@@ -2501,7 +2501,7 @@  discard block
 block discarded – undo
2501 2501
 		$mount->expects($this->once())
2502 2502
 			->method('moveMount')
2503 2503
 			->willReturnCallback(
2504
-				function ($target) use ($mount, $view, $sourcePath, $targetPath, &$lockTypeSourceDuring, &$lockTypeTargetDuring, &$lockTypeSharedRootDuring) {
2504
+				function($target) use ($mount, $view, $sourcePath, $targetPath, &$lockTypeSourceDuring, &$lockTypeTargetDuring, &$lockTypeSharedRootDuring) {
2505 2505
 					$lockTypeSourceDuring = $this->getFileLockType($view, $sourcePath, true);
2506 2506
 					$lockTypeTargetDuring = $this->getFileLockType($view, $targetPath, true);
2507 2507
 
@@ -2566,14 +2566,14 @@  discard block
 block discarded – undo
2566 2566
 		$eventHandler->expects($this->any())
2567 2567
 			->method('preCallback')
2568 2568
 			->willReturnCallback(
2569
-				function () use ($view, $path, $onMountPoint, &$lockTypePre) {
2569
+				function() use ($view, $path, $onMountPoint, &$lockTypePre) {
2570 2570
 					$lockTypePre = $this->getFileLockType($view, $path, $onMountPoint);
2571 2571
 				}
2572 2572
 			);
2573 2573
 		$eventHandler->expects($this->any())
2574 2574
 			->method('postCallback')
2575 2575
 			->willReturnCallback(
2576
-				function () use ($view, $path, $onMountPoint, &$lockTypePost) {
2576
+				function() use ($view, $path, $onMountPoint, &$lockTypePost) {
2577 2577
 					$lockTypePost = $this->getFileLockType($view, $path, $onMountPoint);
2578 2578
 				}
2579 2579
 			);
@@ -2587,7 +2587,7 @@  discard block
 block discarded – undo
2587 2587
 			);
2588 2588
 			Util::connectHook(
2589 2589
 				Filesystem::CLASSNAME,
2590
-				'post_' . $hookType,
2590
+				'post_'.$hookType,
2591 2591
 				$eventHandler,
2592 2592
 				'postCallback'
2593 2593
 			);
@@ -2615,7 +2615,7 @@  discard block
 block discarded – undo
2615 2615
 
2616 2616
 
2617 2617
 	public function testRemoveMoveableMountPoint(): void {
2618
-		$mountPoint = '/' . $this->user . '/files/mount/';
2618
+		$mountPoint = '/'.$this->user.'/files/mount/';
2619 2619
 
2620 2620
 		// Mock the mount point
2621 2621
 		/** @var TestMoveableMountPoint|\PHPUnit\Framework\MockObject\MockObject $mount */
@@ -2657,7 +2657,7 @@  discard block
 block discarded – undo
2657 2657
 		);
2658 2658
 
2659 2659
 		//Delete the mountpoint
2660
-		$view = new View('/' . $this->user . '/files');
2660
+		$view = new View('/'.$this->user.'/files');
2661 2661
 		$this->assertEquals('foo', $view->rmdir('mount'));
2662 2662
 	}
2663 2663
 
@@ -2678,7 +2678,7 @@  discard block
 block discarded – undo
2678 2678
 	public function testGetDirectoryContentMimeFilter($filter, $expected): void {
2679 2679
 		$storage1 = new Temporary();
2680 2680
 		$root = self::getUniqueID('/');
2681
-		Filesystem::mount($storage1, [], $root . '/');
2681
+		Filesystem::mount($storage1, [], $root.'/');
2682 2682
 		$view = new View($root);
2683 2683
 
2684 2684
 		$view->file_put_contents('test1.txt', 'asd');
@@ -2688,7 +2688,7 @@  discard block
 block discarded – undo
2688 2688
 
2689 2689
 		$content = $view->getDirectoryContent('', $filter);
2690 2690
 
2691
-		$files = array_map(function (FileInfo $info) {
2691
+		$files = array_map(function(FileInfo $info) {
2692 2692
 			return $info->getName();
2693 2693
 		}, $content);
2694 2694
 		sort($files);
@@ -2794,7 +2794,7 @@  discard block
 block discarded – undo
2794 2794
 		$calls = ['/new/folder', '/new/folder/structure'];
2795 2795
 		$view->expects($this->exactly(2))
2796 2796
 			->method('mkdir')
2797
-			->willReturnCallback(function ($dir) use (&$calls) {
2797
+			->willReturnCallback(function($dir) use (&$calls) {
2798 2798
 				$expected = array_shift($calls);
2799 2799
 				$this->assertEquals($expected, $dir);
2800 2800
 			});
@@ -2891,7 +2891,7 @@  discard block
 block discarded – undo
2891 2891
 	}
2892 2892
 
2893 2893
 	public function testCopyPreservesContent() {
2894
-		$viewUser1 = new View('/' . 'userId' . '/files');
2894
+		$viewUser1 = new View('/'.'userId'.'/files');
2895 2895
 		$viewUser1->mkdir('');
2896 2896
 		$viewUser1->file_put_contents('foo.txt', 'foo');
2897 2897
 		$viewUser1->copy('foo.txt', 'bar.txt');
Please login to merge, or discard this patch.
apps/files_trashbin/tests/TrashbinTest.php 2 patches
Indentation   +654 added lines, -654 removed lines patch added patch discarded remove patch
@@ -34,674 +34,674 @@
 block discarded – undo
34 34
  * @group DB
35 35
  */
36 36
 class TrashbinTest extends \Test\TestCase {
37
-	public const TEST_TRASHBIN_USER1 = 'test-trashbin-user1';
38
-	public const TEST_TRASHBIN_USER2 = 'test-trashbin-user2';
39
-
40
-	private $trashRoot1;
41
-	private $trashRoot2;
42
-
43
-	private static $rememberRetentionObligation;
44
-
45
-	/**
46
-	 * @var bool
47
-	 */
48
-	private static $trashBinStatus;
49
-
50
-	/**
51
-	 * @var View
52
-	 */
53
-	private $rootView;
54
-
55
-	public static function setUpBeforeClass(): void {
56
-		parent::setUpBeforeClass();
57
-
58
-		$appManager = Server::get(IAppManager::class);
59
-		self::$trashBinStatus = $appManager->isEnabledForUser('files_trashbin');
60
-
61
-		// reset backend
62
-		Server::get(IUserManager::class)->clearBackends();
63
-		Server::get(IUserManager::class)->registerBackend(new Database());
64
-
65
-		// clear share hooks
66
-		\OC_Hook::clear('OCP\\Share');
67
-		\OC::registerShareHooks(Server::get(SystemConfig::class));
68
-
69
-		// init files sharing
70
-		new Application();
71
-
72
-		//disable encryption
73
-		Server::get(IAppManager::class)->disableApp('encryption');
74
-
75
-		$config = Server::get(IConfig::class);
76
-		//configure trashbin
77
-		self::$rememberRetentionObligation = $config->getSystemValue('trashbin_retention_obligation', Expiration::DEFAULT_RETENTION_OBLIGATION);
78
-		/** @var Expiration $expiration */
79
-		$expiration = Server::get(Expiration::class);
80
-		$expiration->setRetentionObligation('auto, 2');
81
-
82
-		// register trashbin hooks
83
-		$trashbinApp = new TrashbinApplication();
84
-		$trashbinApp->boot(new BootContext(new DIContainer('', [], \OC::$server)));
85
-
86
-		// create test user
87
-		self::loginHelper(self::TEST_TRASHBIN_USER2, true);
88
-		self::loginHelper(self::TEST_TRASHBIN_USER1, true);
89
-	}
90
-
91
-
92
-	public static function tearDownAfterClass(): void {
93
-		// cleanup test user
94
-		$user = Server::get(IUserManager::class)->get(self::TEST_TRASHBIN_USER1);
95
-		if ($user !== null) {
96
-			$user->delete();
97
-		}
98
-
99
-		/** @var Expiration $expiration */
100
-		$expiration = Server::get(Expiration::class);
101
-		$expiration->setRetentionObligation(self::$rememberRetentionObligation);
102
-
103
-		\OC_Hook::clear();
104
-
105
-		Filesystem::getLoader()->removeStorageWrapper('oc_trashbin');
106
-
107
-		if (self::$trashBinStatus) {
108
-			Server::get(IAppManager::class)->enableApp('files_trashbin');
109
-		}
110
-
111
-		parent::tearDownAfterClass();
112
-	}
113
-
114
-	protected function setUp(): void {
115
-		parent::setUp();
116
-
117
-		Server::get(IAppManager::class)->enableApp('files_trashbin');
118
-		$config = Server::get(IConfig::class);
119
-		$mockConfig = $this->getMockBuilder(AllConfig::class)
120
-			->onlyMethods(['getSystemValue'])
121
-			->setConstructorArgs([Server::get(\OC\SystemConfig::class)])
122
-			->getMock();
123
-		$mockConfig->expects($this->any())
124
-			->method('getSystemValue')
125
-			->willReturnCallback(static function ($key, $default) use ($config) {
126
-				if ($key === 'filesystem_check_changes') {
127
-					return Watcher::CHECK_ONCE;
128
-				} else {
129
-					return $config->getSystemValue($key, $default);
130
-				}
131
-			});
132
-		$this->overwriteService(AllConfig::class, $mockConfig);
133
-
134
-		$this->trashRoot1 = '/' . self::TEST_TRASHBIN_USER1 . '/files_trashbin';
135
-		$this->trashRoot2 = '/' . self::TEST_TRASHBIN_USER2 . '/files_trashbin';
136
-		$this->rootView = new View();
137
-		self::loginHelper(self::TEST_TRASHBIN_USER1);
138
-	}
139
-
140
-	protected function tearDown(): void {
141
-		$this->restoreService(AllConfig::class);
142
-		// disable trashbin to be able to properly clean up
143
-		Server::get(IAppManager::class)->disableApp('files_trashbin');
144
-
145
-		$this->rootView->deleteAll('/' . self::TEST_TRASHBIN_USER1 . '/files');
146
-		$this->rootView->deleteAll('/' . self::TEST_TRASHBIN_USER2 . '/files');
147
-		$this->rootView->deleteAll($this->trashRoot1);
148
-		$this->rootView->deleteAll($this->trashRoot2);
149
-
150
-		// clear trash table
151
-		$connection = Server::get(IDBConnection::class);
152
-		$connection->executeUpdate('DELETE FROM `*PREFIX*files_trash`');
153
-
154
-		parent::tearDown();
155
-	}
156
-
157
-	/**
158
-	 * test expiration of files older then the max storage time defined for the trash
159
-	 */
160
-	public function testExpireOldFiles(): void {
161
-
162
-		/** @var ITimeFactory $time */
163
-		$time = Server::get(ITimeFactory::class);
164
-		$currentTime = $time->getTime();
165
-		$expireAt = $currentTime - 2 * 24 * 60 * 60;
166
-		$expiredDate = $currentTime - 3 * 24 * 60 * 60;
167
-
168
-		// create some files
169
-		Filesystem::file_put_contents('file1.txt', 'file1');
170
-		Filesystem::file_put_contents('file2.txt', 'file2');
171
-		Filesystem::file_put_contents('file3.txt', 'file3');
172
-
173
-		// delete them so that they end up in the trash bin
174
-		Filesystem::unlink('file1.txt');
175
-		Filesystem::unlink('file2.txt');
176
-		Filesystem::unlink('file3.txt');
177
-
178
-		//make sure that files are in the trash bin
179
-		$filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'name');
180
-		$this->assertSame(3, count($filesInTrash));
181
-
182
-		// every second file will get a date in the past so that it will get expired
183
-		$manipulatedList = $this->manipulateDeleteTime($filesInTrash, $this->trashRoot1, $expiredDate);
184
-
185
-		$testClass = new TrashbinForTesting();
186
-		[$sizeOfDeletedFiles, $count] = $testClass->dummyDeleteExpiredFiles($manipulatedList, $expireAt);
187
-
188
-		$this->assertSame(10, $sizeOfDeletedFiles);
189
-		$this->assertSame(2, $count);
190
-
191
-		// only file2.txt should be left
192
-		$remainingFiles = array_slice($manipulatedList, $count);
193
-		$this->assertSame(1, count($remainingFiles));
194
-		$remainingFile = reset($remainingFiles);
195
-		// TODO: failing test
196
-		#$this->assertSame('file2.txt', $remainingFile['name']);
197
-
198
-		// check that file1.txt and file3.txt was really deleted
199
-		$newTrashContent = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1);
200
-		$this->assertSame(1, count($newTrashContent));
201
-		$element = reset($newTrashContent);
202
-		// TODO: failing test
203
-		#$this->assertSame('file2.txt', $element['name']);
204
-	}
205
-
206
-	/**
207
-	 * test expiration of files older then the max storage time defined for the trash
208
-	 * in this test we delete a shared file and check if both trash bins, the one from
209
-	 * the owner of the file and the one from the user who deleted the file get expired
210
-	 * correctly
211
-	 */
212
-	public function testExpireOldFilesShared(): void {
213
-		$currentTime = time();
214
-		$folder = 'trashTest-' . $currentTime . '/';
215
-		$expiredDate = $currentTime - 3 * 24 * 60 * 60;
216
-
217
-		// create some files
218
-		Filesystem::mkdir($folder);
219
-		Filesystem::file_put_contents($folder . 'user1-1.txt', 'file1');
220
-		Filesystem::file_put_contents($folder . 'user1-2.txt', 'file2');
221
-		Filesystem::file_put_contents($folder . 'user1-3.txt', 'file3');
222
-		Filesystem::file_put_contents($folder . 'user1-4.txt', 'file4');
223
-
224
-		//share user1-4.txt with user2
225
-		$node = \OC::$server->getUserFolder(self::TEST_TRASHBIN_USER1)->get($folder);
226
-		$share = Server::get(\OCP\Share\IManager::class)->newShare();
227
-		$share->setShareType(IShare::TYPE_USER)
228
-			->setNode($node)
229
-			->setSharedBy(self::TEST_TRASHBIN_USER1)
230
-			->setSharedWith(self::TEST_TRASHBIN_USER2)
231
-			->setPermissions(Constants::PERMISSION_ALL);
232
-		$share = Server::get(\OCP\Share\IManager::class)->createShare($share);
233
-		Server::get(\OCP\Share\IManager::class)->acceptShare($share, self::TEST_TRASHBIN_USER2);
234
-
235
-		// delete them so that they end up in the trash bin
236
-		Filesystem::unlink($folder . 'user1-1.txt');
237
-		Filesystem::unlink($folder . 'user1-2.txt');
238
-		Filesystem::unlink($folder . 'user1-3.txt');
239
-
240
-		$filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'name');
241
-		$this->assertSame(3, count($filesInTrash));
242
-
243
-		// every second file will get a date in the past so that it will get expired
244
-		$this->manipulateDeleteTime($filesInTrash, $this->trashRoot1, $expiredDate);
245
-
246
-		// login as user2
247
-		self::loginHelper(self::TEST_TRASHBIN_USER2);
248
-
249
-		$this->assertTrue(Filesystem::file_exists($folder . 'user1-4.txt'));
250
-
251
-		// create some files
252
-		Filesystem::file_put_contents('user2-1.txt', 'file1');
253
-		Filesystem::file_put_contents('user2-2.txt', 'file2');
254
-
255
-		// delete them so that they end up in the trash bin
256
-		Filesystem::unlink('user2-1.txt');
257
-		Filesystem::unlink('user2-2.txt');
258
-
259
-		$filesInTrashUser2 = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER2, 'name');
260
-		$this->assertSame(2, count($filesInTrashUser2));
261
-
262
-		// every second file will get a date in the past so that it will get expired
263
-		$this->manipulateDeleteTime($filesInTrashUser2, $this->trashRoot2, $expiredDate);
264
-
265
-		Filesystem::unlink($folder . 'user1-4.txt');
266
-
267
-		$this->runCommands();
268
-
269
-		$filesInTrashUser2AfterDelete = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER2);
270
-
271
-		// user2-1.txt should have been expired
272
-		$this->verifyArray($filesInTrashUser2AfterDelete, ['user2-2.txt', 'user1-4.txt']);
273
-
274
-		self::loginHelper(self::TEST_TRASHBIN_USER1);
275
-
276
-		// user1-1.txt and user1-3.txt should have been expired
277
-		$filesInTrashUser1AfterDelete = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1);
278
-
279
-		$this->verifyArray($filesInTrashUser1AfterDelete, ['user1-2.txt', 'user1-4.txt']);
280
-	}
281
-
282
-	/**
283
-	 * verify that the array contains the expected results
284
-	 *
285
-	 * @param FileInfo[] $result
286
-	 * @param string[] $expected
287
-	 */
288
-	private function verifyArray($result, $expected) {
289
-		$this->assertSame(count($expected), count($result));
290
-		foreach ($expected as $expectedFile) {
291
-			$found = false;
292
-			foreach ($result as $fileInTrash) {
293
-				if ($expectedFile === $fileInTrash['name']) {
294
-					$found = true;
295
-					break;
296
-				}
297
-			}
298
-			if (!$found) {
299
-				// if we didn't found the expected file, something went wrong
300
-				$this->assertTrue(false, "can't find expected file '" . $expectedFile . "' in trash bin");
301
-			}
302
-		}
303
-	}
304
-
305
-	/**
306
-	 * @param FileInfo[] $files
307
-	 * @param string $trashRoot
308
-	 * @param integer $expireDate
309
-	 */
310
-	private function manipulateDeleteTime($files, $trashRoot, $expireDate) {
311
-		$counter = 0;
312
-		foreach ($files as &$file) {
313
-			// modify every second file
314
-			$counter = ($counter + 1) % 2;
315
-			if ($counter === 1) {
316
-				$source = $trashRoot . '/files/' . $file['name'] . '.d' . $file['mtime'];
317
-				$target = Filesystem::normalizePath($trashRoot . '/files/' . $file['name'] . '.d' . $expireDate);
318
-				$this->rootView->rename($source, $target);
319
-				$file['mtime'] = $expireDate;
320
-			}
321
-		}
322
-		return \OCA\Files\Helper::sortFiles($files, 'mtime');
323
-	}
324
-
325
-
326
-	/**
327
-	 * test expiration of old files in the trash bin until the max size
328
-	 * of the trash bin is met again
329
-	 */
330
-	public function testExpireOldFilesUtilLimitsAreMet(): void {
331
-
332
-		// create some files
333
-		Filesystem::file_put_contents('file1.txt', 'file1');
334
-		Filesystem::file_put_contents('file2.txt', 'file2');
335
-		Filesystem::file_put_contents('file3.txt', 'file3');
336
-
337
-		// delete them so that they end up in the trash bin
338
-		Filesystem::unlink('file3.txt');
339
-		sleep(1); // make sure that every file has a unique mtime
340
-		Filesystem::unlink('file2.txt');
341
-		sleep(1); // make sure that every file has a unique mtime
342
-		Filesystem::unlink('file1.txt');
343
-
344
-		//make sure that files are in the trash bin
345
-		$filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime');
346
-		$this->assertSame(3, count($filesInTrash));
347
-
348
-		$testClass = new TrashbinForTesting();
349
-		$sizeOfDeletedFiles = $testClass->dummyDeleteFiles($filesInTrash, -8);
350
-
351
-		// the two oldest files (file3.txt and file2.txt) should be deleted
352
-		$this->assertSame(10, $sizeOfDeletedFiles);
353
-
354
-		$newTrashContent = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1);
355
-		$this->assertSame(1, count($newTrashContent));
356
-		$element = reset($newTrashContent);
357
-		$this->assertSame('file1.txt', $element['name']);
358
-	}
359
-
360
-	/**
361
-	 * Test restoring a file
362
-	 */
363
-	public function testRestoreFileInRoot(): void {
364
-		$userFolder = \OC::$server->getUserFolder();
365
-		$file = $userFolder->newFile('file1.txt');
366
-		$file->putContent('foo');
367
-
368
-		$this->assertTrue($userFolder->nodeExists('file1.txt'));
369
-
370
-		$file->delete();
371
-
372
-		$this->assertFalse($userFolder->nodeExists('file1.txt'));
373
-
374
-		$filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime');
375
-		$this->assertCount(1, $filesInTrash);
376
-
377
-		/** @var FileInfo */
378
-		$trashedFile = $filesInTrash[0];
379
-
380
-		$this->assertTrue(
381
-			Trashbin::restore(
382
-				'file1.txt.d' . $trashedFile->getMtime(),
383
-				$trashedFile->getName(),
384
-				$trashedFile->getMtime()
385
-			)
386
-		);
387
-
388
-		$file = $userFolder->get('file1.txt');
389
-		$this->assertEquals('foo', $file->getContent());
390
-	}
391
-
392
-	/**
393
-	 * Test restoring a file in subfolder
394
-	 */
395
-	public function testRestoreFileInSubfolder(): void {
396
-		$userFolder = \OC::$server->getUserFolder();
397
-		$folder = $userFolder->newFolder('folder');
398
-		$file = $folder->newFile('file1.txt');
399
-		$file->putContent('foo');
400
-
401
-		$this->assertTrue($userFolder->nodeExists('folder/file1.txt'));
402
-
403
-		$file->delete();
404
-
405
-		$this->assertFalse($userFolder->nodeExists('folder/file1.txt'));
406
-
407
-		$filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime');
408
-		$this->assertCount(1, $filesInTrash);
409
-
410
-		/** @var FileInfo */
411
-		$trashedFile = $filesInTrash[0];
412
-
413
-		$this->assertTrue(
414
-			Trashbin::restore(
415
-				'file1.txt.d' . $trashedFile->getMtime(),
416
-				$trashedFile->getName(),
417
-				$trashedFile->getMtime()
418
-			)
419
-		);
420
-
421
-		$file = $userFolder->get('folder/file1.txt');
422
-		$this->assertEquals('foo', $file->getContent());
423
-	}
424
-
425
-	/**
426
-	 * Test restoring a folder
427
-	 */
428
-	public function testRestoreFolder(): void {
429
-		$userFolder = \OC::$server->getUserFolder();
430
-		$folder = $userFolder->newFolder('folder');
431
-		$file = $folder->newFile('file1.txt');
432
-		$file->putContent('foo');
433
-
434
-		$this->assertTrue($userFolder->nodeExists('folder'));
435
-
436
-		$folder->delete();
437
-
438
-		$this->assertFalse($userFolder->nodeExists('folder'));
439
-
440
-		$filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime');
441
-		$this->assertCount(1, $filesInTrash);
37
+    public const TEST_TRASHBIN_USER1 = 'test-trashbin-user1';
38
+    public const TEST_TRASHBIN_USER2 = 'test-trashbin-user2';
39
+
40
+    private $trashRoot1;
41
+    private $trashRoot2;
42
+
43
+    private static $rememberRetentionObligation;
44
+
45
+    /**
46
+     * @var bool
47
+     */
48
+    private static $trashBinStatus;
49
+
50
+    /**
51
+     * @var View
52
+     */
53
+    private $rootView;
54
+
55
+    public static function setUpBeforeClass(): void {
56
+        parent::setUpBeforeClass();
57
+
58
+        $appManager = Server::get(IAppManager::class);
59
+        self::$trashBinStatus = $appManager->isEnabledForUser('files_trashbin');
60
+
61
+        // reset backend
62
+        Server::get(IUserManager::class)->clearBackends();
63
+        Server::get(IUserManager::class)->registerBackend(new Database());
64
+
65
+        // clear share hooks
66
+        \OC_Hook::clear('OCP\\Share');
67
+        \OC::registerShareHooks(Server::get(SystemConfig::class));
68
+
69
+        // init files sharing
70
+        new Application();
71
+
72
+        //disable encryption
73
+        Server::get(IAppManager::class)->disableApp('encryption');
74
+
75
+        $config = Server::get(IConfig::class);
76
+        //configure trashbin
77
+        self::$rememberRetentionObligation = $config->getSystemValue('trashbin_retention_obligation', Expiration::DEFAULT_RETENTION_OBLIGATION);
78
+        /** @var Expiration $expiration */
79
+        $expiration = Server::get(Expiration::class);
80
+        $expiration->setRetentionObligation('auto, 2');
81
+
82
+        // register trashbin hooks
83
+        $trashbinApp = new TrashbinApplication();
84
+        $trashbinApp->boot(new BootContext(new DIContainer('', [], \OC::$server)));
85
+
86
+        // create test user
87
+        self::loginHelper(self::TEST_TRASHBIN_USER2, true);
88
+        self::loginHelper(self::TEST_TRASHBIN_USER1, true);
89
+    }
90
+
91
+
92
+    public static function tearDownAfterClass(): void {
93
+        // cleanup test user
94
+        $user = Server::get(IUserManager::class)->get(self::TEST_TRASHBIN_USER1);
95
+        if ($user !== null) {
96
+            $user->delete();
97
+        }
98
+
99
+        /** @var Expiration $expiration */
100
+        $expiration = Server::get(Expiration::class);
101
+        $expiration->setRetentionObligation(self::$rememberRetentionObligation);
102
+
103
+        \OC_Hook::clear();
104
+
105
+        Filesystem::getLoader()->removeStorageWrapper('oc_trashbin');
106
+
107
+        if (self::$trashBinStatus) {
108
+            Server::get(IAppManager::class)->enableApp('files_trashbin');
109
+        }
110
+
111
+        parent::tearDownAfterClass();
112
+    }
113
+
114
+    protected function setUp(): void {
115
+        parent::setUp();
116
+
117
+        Server::get(IAppManager::class)->enableApp('files_trashbin');
118
+        $config = Server::get(IConfig::class);
119
+        $mockConfig = $this->getMockBuilder(AllConfig::class)
120
+            ->onlyMethods(['getSystemValue'])
121
+            ->setConstructorArgs([Server::get(\OC\SystemConfig::class)])
122
+            ->getMock();
123
+        $mockConfig->expects($this->any())
124
+            ->method('getSystemValue')
125
+            ->willReturnCallback(static function ($key, $default) use ($config) {
126
+                if ($key === 'filesystem_check_changes') {
127
+                    return Watcher::CHECK_ONCE;
128
+                } else {
129
+                    return $config->getSystemValue($key, $default);
130
+                }
131
+            });
132
+        $this->overwriteService(AllConfig::class, $mockConfig);
133
+
134
+        $this->trashRoot1 = '/' . self::TEST_TRASHBIN_USER1 . '/files_trashbin';
135
+        $this->trashRoot2 = '/' . self::TEST_TRASHBIN_USER2 . '/files_trashbin';
136
+        $this->rootView = new View();
137
+        self::loginHelper(self::TEST_TRASHBIN_USER1);
138
+    }
139
+
140
+    protected function tearDown(): void {
141
+        $this->restoreService(AllConfig::class);
142
+        // disable trashbin to be able to properly clean up
143
+        Server::get(IAppManager::class)->disableApp('files_trashbin');
144
+
145
+        $this->rootView->deleteAll('/' . self::TEST_TRASHBIN_USER1 . '/files');
146
+        $this->rootView->deleteAll('/' . self::TEST_TRASHBIN_USER2 . '/files');
147
+        $this->rootView->deleteAll($this->trashRoot1);
148
+        $this->rootView->deleteAll($this->trashRoot2);
149
+
150
+        // clear trash table
151
+        $connection = Server::get(IDBConnection::class);
152
+        $connection->executeUpdate('DELETE FROM `*PREFIX*files_trash`');
153
+
154
+        parent::tearDown();
155
+    }
156
+
157
+    /**
158
+     * test expiration of files older then the max storage time defined for the trash
159
+     */
160
+    public function testExpireOldFiles(): void {
161
+
162
+        /** @var ITimeFactory $time */
163
+        $time = Server::get(ITimeFactory::class);
164
+        $currentTime = $time->getTime();
165
+        $expireAt = $currentTime - 2 * 24 * 60 * 60;
166
+        $expiredDate = $currentTime - 3 * 24 * 60 * 60;
167
+
168
+        // create some files
169
+        Filesystem::file_put_contents('file1.txt', 'file1');
170
+        Filesystem::file_put_contents('file2.txt', 'file2');
171
+        Filesystem::file_put_contents('file3.txt', 'file3');
172
+
173
+        // delete them so that they end up in the trash bin
174
+        Filesystem::unlink('file1.txt');
175
+        Filesystem::unlink('file2.txt');
176
+        Filesystem::unlink('file3.txt');
177
+
178
+        //make sure that files are in the trash bin
179
+        $filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'name');
180
+        $this->assertSame(3, count($filesInTrash));
181
+
182
+        // every second file will get a date in the past so that it will get expired
183
+        $manipulatedList = $this->manipulateDeleteTime($filesInTrash, $this->trashRoot1, $expiredDate);
184
+
185
+        $testClass = new TrashbinForTesting();
186
+        [$sizeOfDeletedFiles, $count] = $testClass->dummyDeleteExpiredFiles($manipulatedList, $expireAt);
187
+
188
+        $this->assertSame(10, $sizeOfDeletedFiles);
189
+        $this->assertSame(2, $count);
190
+
191
+        // only file2.txt should be left
192
+        $remainingFiles = array_slice($manipulatedList, $count);
193
+        $this->assertSame(1, count($remainingFiles));
194
+        $remainingFile = reset($remainingFiles);
195
+        // TODO: failing test
196
+        #$this->assertSame('file2.txt', $remainingFile['name']);
197
+
198
+        // check that file1.txt and file3.txt was really deleted
199
+        $newTrashContent = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1);
200
+        $this->assertSame(1, count($newTrashContent));
201
+        $element = reset($newTrashContent);
202
+        // TODO: failing test
203
+        #$this->assertSame('file2.txt', $element['name']);
204
+    }
205
+
206
+    /**
207
+     * test expiration of files older then the max storage time defined for the trash
208
+     * in this test we delete a shared file and check if both trash bins, the one from
209
+     * the owner of the file and the one from the user who deleted the file get expired
210
+     * correctly
211
+     */
212
+    public function testExpireOldFilesShared(): void {
213
+        $currentTime = time();
214
+        $folder = 'trashTest-' . $currentTime . '/';
215
+        $expiredDate = $currentTime - 3 * 24 * 60 * 60;
216
+
217
+        // create some files
218
+        Filesystem::mkdir($folder);
219
+        Filesystem::file_put_contents($folder . 'user1-1.txt', 'file1');
220
+        Filesystem::file_put_contents($folder . 'user1-2.txt', 'file2');
221
+        Filesystem::file_put_contents($folder . 'user1-3.txt', 'file3');
222
+        Filesystem::file_put_contents($folder . 'user1-4.txt', 'file4');
223
+
224
+        //share user1-4.txt with user2
225
+        $node = \OC::$server->getUserFolder(self::TEST_TRASHBIN_USER1)->get($folder);
226
+        $share = Server::get(\OCP\Share\IManager::class)->newShare();
227
+        $share->setShareType(IShare::TYPE_USER)
228
+            ->setNode($node)
229
+            ->setSharedBy(self::TEST_TRASHBIN_USER1)
230
+            ->setSharedWith(self::TEST_TRASHBIN_USER2)
231
+            ->setPermissions(Constants::PERMISSION_ALL);
232
+        $share = Server::get(\OCP\Share\IManager::class)->createShare($share);
233
+        Server::get(\OCP\Share\IManager::class)->acceptShare($share, self::TEST_TRASHBIN_USER2);
234
+
235
+        // delete them so that they end up in the trash bin
236
+        Filesystem::unlink($folder . 'user1-1.txt');
237
+        Filesystem::unlink($folder . 'user1-2.txt');
238
+        Filesystem::unlink($folder . 'user1-3.txt');
239
+
240
+        $filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'name');
241
+        $this->assertSame(3, count($filesInTrash));
242
+
243
+        // every second file will get a date in the past so that it will get expired
244
+        $this->manipulateDeleteTime($filesInTrash, $this->trashRoot1, $expiredDate);
245
+
246
+        // login as user2
247
+        self::loginHelper(self::TEST_TRASHBIN_USER2);
248
+
249
+        $this->assertTrue(Filesystem::file_exists($folder . 'user1-4.txt'));
250
+
251
+        // create some files
252
+        Filesystem::file_put_contents('user2-1.txt', 'file1');
253
+        Filesystem::file_put_contents('user2-2.txt', 'file2');
254
+
255
+        // delete them so that they end up in the trash bin
256
+        Filesystem::unlink('user2-1.txt');
257
+        Filesystem::unlink('user2-2.txt');
258
+
259
+        $filesInTrashUser2 = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER2, 'name');
260
+        $this->assertSame(2, count($filesInTrashUser2));
261
+
262
+        // every second file will get a date in the past so that it will get expired
263
+        $this->manipulateDeleteTime($filesInTrashUser2, $this->trashRoot2, $expiredDate);
264
+
265
+        Filesystem::unlink($folder . 'user1-4.txt');
266
+
267
+        $this->runCommands();
268
+
269
+        $filesInTrashUser2AfterDelete = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER2);
270
+
271
+        // user2-1.txt should have been expired
272
+        $this->verifyArray($filesInTrashUser2AfterDelete, ['user2-2.txt', 'user1-4.txt']);
273
+
274
+        self::loginHelper(self::TEST_TRASHBIN_USER1);
275
+
276
+        // user1-1.txt and user1-3.txt should have been expired
277
+        $filesInTrashUser1AfterDelete = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1);
278
+
279
+        $this->verifyArray($filesInTrashUser1AfterDelete, ['user1-2.txt', 'user1-4.txt']);
280
+    }
281
+
282
+    /**
283
+     * verify that the array contains the expected results
284
+     *
285
+     * @param FileInfo[] $result
286
+     * @param string[] $expected
287
+     */
288
+    private function verifyArray($result, $expected) {
289
+        $this->assertSame(count($expected), count($result));
290
+        foreach ($expected as $expectedFile) {
291
+            $found = false;
292
+            foreach ($result as $fileInTrash) {
293
+                if ($expectedFile === $fileInTrash['name']) {
294
+                    $found = true;
295
+                    break;
296
+                }
297
+            }
298
+            if (!$found) {
299
+                // if we didn't found the expected file, something went wrong
300
+                $this->assertTrue(false, "can't find expected file '" . $expectedFile . "' in trash bin");
301
+            }
302
+        }
303
+    }
304
+
305
+    /**
306
+     * @param FileInfo[] $files
307
+     * @param string $trashRoot
308
+     * @param integer $expireDate
309
+     */
310
+    private function manipulateDeleteTime($files, $trashRoot, $expireDate) {
311
+        $counter = 0;
312
+        foreach ($files as &$file) {
313
+            // modify every second file
314
+            $counter = ($counter + 1) % 2;
315
+            if ($counter === 1) {
316
+                $source = $trashRoot . '/files/' . $file['name'] . '.d' . $file['mtime'];
317
+                $target = Filesystem::normalizePath($trashRoot . '/files/' . $file['name'] . '.d' . $expireDate);
318
+                $this->rootView->rename($source, $target);
319
+                $file['mtime'] = $expireDate;
320
+            }
321
+        }
322
+        return \OCA\Files\Helper::sortFiles($files, 'mtime');
323
+    }
324
+
325
+
326
+    /**
327
+     * test expiration of old files in the trash bin until the max size
328
+     * of the trash bin is met again
329
+     */
330
+    public function testExpireOldFilesUtilLimitsAreMet(): void {
331
+
332
+        // create some files
333
+        Filesystem::file_put_contents('file1.txt', 'file1');
334
+        Filesystem::file_put_contents('file2.txt', 'file2');
335
+        Filesystem::file_put_contents('file3.txt', 'file3');
336
+
337
+        // delete them so that they end up in the trash bin
338
+        Filesystem::unlink('file3.txt');
339
+        sleep(1); // make sure that every file has a unique mtime
340
+        Filesystem::unlink('file2.txt');
341
+        sleep(1); // make sure that every file has a unique mtime
342
+        Filesystem::unlink('file1.txt');
343
+
344
+        //make sure that files are in the trash bin
345
+        $filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime');
346
+        $this->assertSame(3, count($filesInTrash));
347
+
348
+        $testClass = new TrashbinForTesting();
349
+        $sizeOfDeletedFiles = $testClass->dummyDeleteFiles($filesInTrash, -8);
350
+
351
+        // the two oldest files (file3.txt and file2.txt) should be deleted
352
+        $this->assertSame(10, $sizeOfDeletedFiles);
353
+
354
+        $newTrashContent = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1);
355
+        $this->assertSame(1, count($newTrashContent));
356
+        $element = reset($newTrashContent);
357
+        $this->assertSame('file1.txt', $element['name']);
358
+    }
359
+
360
+    /**
361
+     * Test restoring a file
362
+     */
363
+    public function testRestoreFileInRoot(): void {
364
+        $userFolder = \OC::$server->getUserFolder();
365
+        $file = $userFolder->newFile('file1.txt');
366
+        $file->putContent('foo');
367
+
368
+        $this->assertTrue($userFolder->nodeExists('file1.txt'));
369
+
370
+        $file->delete();
371
+
372
+        $this->assertFalse($userFolder->nodeExists('file1.txt'));
373
+
374
+        $filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime');
375
+        $this->assertCount(1, $filesInTrash);
376
+
377
+        /** @var FileInfo */
378
+        $trashedFile = $filesInTrash[0];
379
+
380
+        $this->assertTrue(
381
+            Trashbin::restore(
382
+                'file1.txt.d' . $trashedFile->getMtime(),
383
+                $trashedFile->getName(),
384
+                $trashedFile->getMtime()
385
+            )
386
+        );
387
+
388
+        $file = $userFolder->get('file1.txt');
389
+        $this->assertEquals('foo', $file->getContent());
390
+    }
391
+
392
+    /**
393
+     * Test restoring a file in subfolder
394
+     */
395
+    public function testRestoreFileInSubfolder(): void {
396
+        $userFolder = \OC::$server->getUserFolder();
397
+        $folder = $userFolder->newFolder('folder');
398
+        $file = $folder->newFile('file1.txt');
399
+        $file->putContent('foo');
400
+
401
+        $this->assertTrue($userFolder->nodeExists('folder/file1.txt'));
402
+
403
+        $file->delete();
404
+
405
+        $this->assertFalse($userFolder->nodeExists('folder/file1.txt'));
406
+
407
+        $filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime');
408
+        $this->assertCount(1, $filesInTrash);
409
+
410
+        /** @var FileInfo */
411
+        $trashedFile = $filesInTrash[0];
412
+
413
+        $this->assertTrue(
414
+            Trashbin::restore(
415
+                'file1.txt.d' . $trashedFile->getMtime(),
416
+                $trashedFile->getName(),
417
+                $trashedFile->getMtime()
418
+            )
419
+        );
420
+
421
+        $file = $userFolder->get('folder/file1.txt');
422
+        $this->assertEquals('foo', $file->getContent());
423
+    }
424
+
425
+    /**
426
+     * Test restoring a folder
427
+     */
428
+    public function testRestoreFolder(): void {
429
+        $userFolder = \OC::$server->getUserFolder();
430
+        $folder = $userFolder->newFolder('folder');
431
+        $file = $folder->newFile('file1.txt');
432
+        $file->putContent('foo');
433
+
434
+        $this->assertTrue($userFolder->nodeExists('folder'));
435
+
436
+        $folder->delete();
437
+
438
+        $this->assertFalse($userFolder->nodeExists('folder'));
439
+
440
+        $filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime');
441
+        $this->assertCount(1, $filesInTrash);
442 442
 
443
-		/** @var FileInfo */
444
-		$trashedFolder = $filesInTrash[0];
443
+        /** @var FileInfo */
444
+        $trashedFolder = $filesInTrash[0];
445 445
 
446
-		$this->assertTrue(
447
-			Trashbin::restore(
448
-				'folder.d' . $trashedFolder->getMtime(),
449
-				$trashedFolder->getName(),
450
-				$trashedFolder->getMtime()
451
-			)
452
-		);
446
+        $this->assertTrue(
447
+            Trashbin::restore(
448
+                'folder.d' . $trashedFolder->getMtime(),
449
+                $trashedFolder->getName(),
450
+                $trashedFolder->getMtime()
451
+            )
452
+        );
453 453
 
454
-		$file = $userFolder->get('folder/file1.txt');
455
-		$this->assertEquals('foo', $file->getContent());
456
-	}
457
-
458
-	/**
459
-	 * Test restoring a file from inside a trashed folder
460
-	 */
461
-	public function testRestoreFileFromTrashedSubfolder(): void {
462
-		$userFolder = \OC::$server->getUserFolder();
463
-		$folder = $userFolder->newFolder('folder');
464
-		$file = $folder->newFile('file1.txt');
465
-		$file->putContent('foo');
454
+        $file = $userFolder->get('folder/file1.txt');
455
+        $this->assertEquals('foo', $file->getContent());
456
+    }
457
+
458
+    /**
459
+     * Test restoring a file from inside a trashed folder
460
+     */
461
+    public function testRestoreFileFromTrashedSubfolder(): void {
462
+        $userFolder = \OC::$server->getUserFolder();
463
+        $folder = $userFolder->newFolder('folder');
464
+        $file = $folder->newFile('file1.txt');
465
+        $file->putContent('foo');
466 466
 
467
-		$this->assertTrue($userFolder->nodeExists('folder'));
467
+        $this->assertTrue($userFolder->nodeExists('folder'));
468 468
 
469
-		$folder->delete();
470
-
471
-		$this->assertFalse($userFolder->nodeExists('folder'));
472
-
473
-		$filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime');
474
-		$this->assertCount(1, $filesInTrash);
475
-
476
-		/** @var FileInfo */
477
-		$trashedFile = $filesInTrash[0];
478
-
479
-		$this->assertTrue(
480
-			Trashbin::restore(
481
-				'folder.d' . $trashedFile->getMtime() . '/file1.txt',
482
-				'file1.txt',
483
-				$trashedFile->getMtime()
484
-			)
485
-		);
469
+        $folder->delete();
470
+
471
+        $this->assertFalse($userFolder->nodeExists('folder'));
472
+
473
+        $filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime');
474
+        $this->assertCount(1, $filesInTrash);
475
+
476
+        /** @var FileInfo */
477
+        $trashedFile = $filesInTrash[0];
478
+
479
+        $this->assertTrue(
480
+            Trashbin::restore(
481
+                'folder.d' . $trashedFile->getMtime() . '/file1.txt',
482
+                'file1.txt',
483
+                $trashedFile->getMtime()
484
+            )
485
+        );
486 486
 
487
-		$file = $userFolder->get('file1.txt');
488
-		$this->assertEquals('foo', $file->getContent());
489
-	}
487
+        $file = $userFolder->get('file1.txt');
488
+        $this->assertEquals('foo', $file->getContent());
489
+    }
490 490
 
491
-	/**
492
-	 * Test restoring a file whenever the source folder was removed.
493
-	 * The file should then land in the root.
494
-	 */
495
-	public function testRestoreFileWithMissingSourceFolder(): void {
496
-		$userFolder = \OC::$server->getUserFolder();
497
-		$folder = $userFolder->newFolder('folder');
498
-		$file = $folder->newFile('file1.txt');
499
-		$file->putContent('foo');
491
+    /**
492
+     * Test restoring a file whenever the source folder was removed.
493
+     * The file should then land in the root.
494
+     */
495
+    public function testRestoreFileWithMissingSourceFolder(): void {
496
+        $userFolder = \OC::$server->getUserFolder();
497
+        $folder = $userFolder->newFolder('folder');
498
+        $file = $folder->newFile('file1.txt');
499
+        $file->putContent('foo');
500 500
 
501
-		$this->assertTrue($userFolder->nodeExists('folder/file1.txt'));
501
+        $this->assertTrue($userFolder->nodeExists('folder/file1.txt'));
502 502
 
503
-		$file->delete();
503
+        $file->delete();
504 504
 
505
-		$this->assertFalse($userFolder->nodeExists('folder/file1.txt'));
506
-
507
-		$filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime');
508
-		$this->assertCount(1, $filesInTrash);
509
-
510
-		/** @var FileInfo */
511
-		$trashedFile = $filesInTrash[0];
512
-
513
-		// delete source folder
514
-		$folder->delete();
515
-
516
-		$this->assertTrue(
517
-			Trashbin::restore(
518
-				'file1.txt.d' . $trashedFile->getMtime(),
519
-				$trashedFile->getName(),
520
-				$trashedFile->getMtime()
521
-			)
522
-		);
523
-
524
-		$file = $userFolder->get('file1.txt');
525
-		$this->assertEquals('foo', $file->getContent());
526
-	}
527
-
528
-	/**
529
-	 * Test restoring a file in the root folder whenever there is another file
530
-	 * with the same name in the root folder
531
-	 */
532
-	public function testRestoreFileDoesNotOverwriteExistingInRoot(): void {
533
-		$userFolder = \OC::$server->getUserFolder();
534
-		$file = $userFolder->newFile('file1.txt');
535
-		$file->putContent('foo');
536
-
537
-		$this->assertTrue($userFolder->nodeExists('file1.txt'));
538
-
539
-		$file->delete();
540
-
541
-		$this->assertFalse($userFolder->nodeExists('file1.txt'));
542
-
543
-		$filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime');
544
-		$this->assertCount(1, $filesInTrash);
545
-
546
-		/** @var FileInfo */
547
-		$trashedFile = $filesInTrash[0];
548
-
549
-		// create another file
550
-		$file = $userFolder->newFile('file1.txt');
551
-		$file->putContent('bar');
552
-
553
-		$this->assertTrue(
554
-			Trashbin::restore(
555
-				'file1.txt.d' . $trashedFile->getMtime(),
556
-				$trashedFile->getName(),
557
-				$trashedFile->getMtime()
558
-			)
559
-		);
560
-
561
-		$anotherFile = $userFolder->get('file1.txt');
562
-		$this->assertEquals('bar', $anotherFile->getContent());
563
-
564
-		$restoredFile = $userFolder->get('file1 (restored).txt');
565
-		$this->assertEquals('foo', $restoredFile->getContent());
566
-	}
567
-
568
-	/**
569
-	 * Test restoring a file whenever there is another file
570
-	 * with the same name in the source folder
571
-	 */
572
-	public function testRestoreFileDoesNotOverwriteExistingInSubfolder(): void {
573
-		$userFolder = \OC::$server->getUserFolder();
574
-		$folder = $userFolder->newFolder('folder');
575
-		$file = $folder->newFile('file1.txt');
576
-		$file->putContent('foo');
577
-
578
-		$this->assertTrue($userFolder->nodeExists('folder/file1.txt'));
579
-
580
-		$file->delete();
581
-
582
-		$this->assertFalse($userFolder->nodeExists('folder/file1.txt'));
583
-
584
-		$filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime');
585
-		$this->assertCount(1, $filesInTrash);
586
-
587
-		/** @var FileInfo */
588
-		$trashedFile = $filesInTrash[0];
589
-
590
-		// create another file
591
-		$file = $folder->newFile('file1.txt');
592
-		$file->putContent('bar');
593
-
594
-		$this->assertTrue(
595
-			Trashbin::restore(
596
-				'file1.txt.d' . $trashedFile->getMtime(),
597
-				$trashedFile->getName(),
598
-				$trashedFile->getMtime()
599
-			)
600
-		);
601
-
602
-		$anotherFile = $userFolder->get('folder/file1.txt');
603
-		$this->assertEquals('bar', $anotherFile->getContent());
604
-
605
-		$restoredFile = $userFolder->get('folder/file1 (restored).txt');
606
-		$this->assertEquals('foo', $restoredFile->getContent());
607
-	}
608
-
609
-	/**
610
-	 * Test restoring a non-existing file from trashbin, returns false
611
-	 */
612
-	public function testRestoreUnexistingFile(): void {
613
-		$this->assertFalse(
614
-			Trashbin::restore(
615
-				'unexist.txt.d123456',
616
-				'unexist.txt',
617
-				'123456'
618
-			)
619
-		);
620
-	}
621
-
622
-	/**
623
-	 * Test restoring a file into a read-only folder, will restore
624
-	 * the file to root instead
625
-	 */
626
-	public function testRestoreFileIntoReadOnlySourceFolder(): void {
627
-		$userFolder = \OC::$server->getUserFolder();
628
-		$folder = $userFolder->newFolder('folder');
629
-		$file = $folder->newFile('file1.txt');
630
-		$file->putContent('foo');
631
-
632
-		$this->assertTrue($userFolder->nodeExists('folder/file1.txt'));
633
-
634
-		$file->delete();
635
-
636
-		$this->assertFalse($userFolder->nodeExists('folder/file1.txt'));
637
-
638
-		$filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime');
639
-		$this->assertCount(1, $filesInTrash);
640
-
641
-		/** @var FileInfo */
642
-		$trashedFile = $filesInTrash[0];
643
-
644
-		// delete source folder
645
-		[$storage, $internalPath] = $this->rootView->resolvePath('/' . self::TEST_TRASHBIN_USER1 . '/files/folder');
646
-		if ($storage instanceof Local) {
647
-			$folderAbsPath = $storage->getSourcePath($internalPath);
648
-			// make folder read-only
649
-			chmod($folderAbsPath, 0555);
650
-
651
-			$this->assertTrue(
652
-				Trashbin::restore(
653
-					'file1.txt.d' . $trashedFile->getMtime(),
654
-					$trashedFile->getName(),
655
-					$trashedFile->getMtime()
656
-				)
657
-			);
658
-
659
-			$file = $userFolder->get('file1.txt');
660
-			$this->assertEquals('foo', $file->getContent());
661
-
662
-			chmod($folderAbsPath, 0755);
663
-		}
664
-	}
665
-
666
-	/**
667
-	 * @param string $user
668
-	 * @param bool $create
669
-	 */
670
-	public static function loginHelper($user, $create = false) {
671
-		if ($create) {
672
-			try {
673
-				Server::get(IUserManager::class)->createUser($user, $user);
674
-			} catch (\Exception $e) { // catch username is already being used from previous aborted runs
675
-			}
676
-		}
677
-
678
-		\OC_Util::tearDownFS();
679
-		\OC_User::setUserId('');
680
-		Filesystem::tearDown();
681
-		\OC_User::setUserId($user);
682
-		\OC_Util::setupFS($user);
683
-		\OC::$server->getUserFolder($user);
684
-	}
505
+        $this->assertFalse($userFolder->nodeExists('folder/file1.txt'));
506
+
507
+        $filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime');
508
+        $this->assertCount(1, $filesInTrash);
509
+
510
+        /** @var FileInfo */
511
+        $trashedFile = $filesInTrash[0];
512
+
513
+        // delete source folder
514
+        $folder->delete();
515
+
516
+        $this->assertTrue(
517
+            Trashbin::restore(
518
+                'file1.txt.d' . $trashedFile->getMtime(),
519
+                $trashedFile->getName(),
520
+                $trashedFile->getMtime()
521
+            )
522
+        );
523
+
524
+        $file = $userFolder->get('file1.txt');
525
+        $this->assertEquals('foo', $file->getContent());
526
+    }
527
+
528
+    /**
529
+     * Test restoring a file in the root folder whenever there is another file
530
+     * with the same name in the root folder
531
+     */
532
+    public function testRestoreFileDoesNotOverwriteExistingInRoot(): void {
533
+        $userFolder = \OC::$server->getUserFolder();
534
+        $file = $userFolder->newFile('file1.txt');
535
+        $file->putContent('foo');
536
+
537
+        $this->assertTrue($userFolder->nodeExists('file1.txt'));
538
+
539
+        $file->delete();
540
+
541
+        $this->assertFalse($userFolder->nodeExists('file1.txt'));
542
+
543
+        $filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime');
544
+        $this->assertCount(1, $filesInTrash);
545
+
546
+        /** @var FileInfo */
547
+        $trashedFile = $filesInTrash[0];
548
+
549
+        // create another file
550
+        $file = $userFolder->newFile('file1.txt');
551
+        $file->putContent('bar');
552
+
553
+        $this->assertTrue(
554
+            Trashbin::restore(
555
+                'file1.txt.d' . $trashedFile->getMtime(),
556
+                $trashedFile->getName(),
557
+                $trashedFile->getMtime()
558
+            )
559
+        );
560
+
561
+        $anotherFile = $userFolder->get('file1.txt');
562
+        $this->assertEquals('bar', $anotherFile->getContent());
563
+
564
+        $restoredFile = $userFolder->get('file1 (restored).txt');
565
+        $this->assertEquals('foo', $restoredFile->getContent());
566
+    }
567
+
568
+    /**
569
+     * Test restoring a file whenever there is another file
570
+     * with the same name in the source folder
571
+     */
572
+    public function testRestoreFileDoesNotOverwriteExistingInSubfolder(): void {
573
+        $userFolder = \OC::$server->getUserFolder();
574
+        $folder = $userFolder->newFolder('folder');
575
+        $file = $folder->newFile('file1.txt');
576
+        $file->putContent('foo');
577
+
578
+        $this->assertTrue($userFolder->nodeExists('folder/file1.txt'));
579
+
580
+        $file->delete();
581
+
582
+        $this->assertFalse($userFolder->nodeExists('folder/file1.txt'));
583
+
584
+        $filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime');
585
+        $this->assertCount(1, $filesInTrash);
586
+
587
+        /** @var FileInfo */
588
+        $trashedFile = $filesInTrash[0];
589
+
590
+        // create another file
591
+        $file = $folder->newFile('file1.txt');
592
+        $file->putContent('bar');
593
+
594
+        $this->assertTrue(
595
+            Trashbin::restore(
596
+                'file1.txt.d' . $trashedFile->getMtime(),
597
+                $trashedFile->getName(),
598
+                $trashedFile->getMtime()
599
+            )
600
+        );
601
+
602
+        $anotherFile = $userFolder->get('folder/file1.txt');
603
+        $this->assertEquals('bar', $anotherFile->getContent());
604
+
605
+        $restoredFile = $userFolder->get('folder/file1 (restored).txt');
606
+        $this->assertEquals('foo', $restoredFile->getContent());
607
+    }
608
+
609
+    /**
610
+     * Test restoring a non-existing file from trashbin, returns false
611
+     */
612
+    public function testRestoreUnexistingFile(): void {
613
+        $this->assertFalse(
614
+            Trashbin::restore(
615
+                'unexist.txt.d123456',
616
+                'unexist.txt',
617
+                '123456'
618
+            )
619
+        );
620
+    }
621
+
622
+    /**
623
+     * Test restoring a file into a read-only folder, will restore
624
+     * the file to root instead
625
+     */
626
+    public function testRestoreFileIntoReadOnlySourceFolder(): void {
627
+        $userFolder = \OC::$server->getUserFolder();
628
+        $folder = $userFolder->newFolder('folder');
629
+        $file = $folder->newFile('file1.txt');
630
+        $file->putContent('foo');
631
+
632
+        $this->assertTrue($userFolder->nodeExists('folder/file1.txt'));
633
+
634
+        $file->delete();
635
+
636
+        $this->assertFalse($userFolder->nodeExists('folder/file1.txt'));
637
+
638
+        $filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime');
639
+        $this->assertCount(1, $filesInTrash);
640
+
641
+        /** @var FileInfo */
642
+        $trashedFile = $filesInTrash[0];
643
+
644
+        // delete source folder
645
+        [$storage, $internalPath] = $this->rootView->resolvePath('/' . self::TEST_TRASHBIN_USER1 . '/files/folder');
646
+        if ($storage instanceof Local) {
647
+            $folderAbsPath = $storage->getSourcePath($internalPath);
648
+            // make folder read-only
649
+            chmod($folderAbsPath, 0555);
650
+
651
+            $this->assertTrue(
652
+                Trashbin::restore(
653
+                    'file1.txt.d' . $trashedFile->getMtime(),
654
+                    $trashedFile->getName(),
655
+                    $trashedFile->getMtime()
656
+                )
657
+            );
658
+
659
+            $file = $userFolder->get('file1.txt');
660
+            $this->assertEquals('foo', $file->getContent());
661
+
662
+            chmod($folderAbsPath, 0755);
663
+        }
664
+    }
665
+
666
+    /**
667
+     * @param string $user
668
+     * @param bool $create
669
+     */
670
+    public static function loginHelper($user, $create = false) {
671
+        if ($create) {
672
+            try {
673
+                Server::get(IUserManager::class)->createUser($user, $user);
674
+            } catch (\Exception $e) { // catch username is already being used from previous aborted runs
675
+            }
676
+        }
677
+
678
+        \OC_Util::tearDownFS();
679
+        \OC_User::setUserId('');
680
+        Filesystem::tearDown();
681
+        \OC_User::setUserId($user);
682
+        \OC_Util::setupFS($user);
683
+        \OC::$server->getUserFolder($user);
684
+    }
685 685
 }
686 686
 
687 687
 
688 688
 // just a dummy class to make protected methods available for testing
689 689
 class TrashbinForTesting extends Trashbin {
690 690
 
691
-	/**
692
-	 * @param FileInfo[] $files
693
-	 * @param integer $limit
694
-	 */
695
-	public function dummyDeleteExpiredFiles($files) {
696
-		// dummy value for $retention_obligation because it is not needed here
697
-		return parent::deleteExpiredFiles($files, TrashbinTest::TEST_TRASHBIN_USER1);
698
-	}
699
-
700
-	/**
701
-	 * @param FileInfo[] $files
702
-	 * @param integer $availableSpace
703
-	 */
704
-	public function dummyDeleteFiles($files, $availableSpace) {
705
-		return parent::deleteFiles($files, TrashbinTest::TEST_TRASHBIN_USER1, $availableSpace);
706
-	}
691
+    /**
692
+     * @param FileInfo[] $files
693
+     * @param integer $limit
694
+     */
695
+    public function dummyDeleteExpiredFiles($files) {
696
+        // dummy value for $retention_obligation because it is not needed here
697
+        return parent::deleteExpiredFiles($files, TrashbinTest::TEST_TRASHBIN_USER1);
698
+    }
699
+
700
+    /**
701
+     * @param FileInfo[] $files
702
+     * @param integer $availableSpace
703
+     */
704
+    public function dummyDeleteFiles($files, $availableSpace) {
705
+        return parent::deleteFiles($files, TrashbinTest::TEST_TRASHBIN_USER1, $availableSpace);
706
+    }
707 707
 }
Please login to merge, or discard this patch.
Spacing   +27 added lines, -27 removed lines patch added patch discarded remove patch
@@ -122,7 +122,7 @@  discard block
 block discarded – undo
122 122
 			->getMock();
123 123
 		$mockConfig->expects($this->any())
124 124
 			->method('getSystemValue')
125
-			->willReturnCallback(static function ($key, $default) use ($config) {
125
+			->willReturnCallback(static function($key, $default) use ($config) {
126 126
 				if ($key === 'filesystem_check_changes') {
127 127
 					return Watcher::CHECK_ONCE;
128 128
 				} else {
@@ -131,8 +131,8 @@  discard block
 block discarded – undo
131 131
 			});
132 132
 		$this->overwriteService(AllConfig::class, $mockConfig);
133 133
 
134
-		$this->trashRoot1 = '/' . self::TEST_TRASHBIN_USER1 . '/files_trashbin';
135
-		$this->trashRoot2 = '/' . self::TEST_TRASHBIN_USER2 . '/files_trashbin';
134
+		$this->trashRoot1 = '/'.self::TEST_TRASHBIN_USER1.'/files_trashbin';
135
+		$this->trashRoot2 = '/'.self::TEST_TRASHBIN_USER2.'/files_trashbin';
136 136
 		$this->rootView = new View();
137 137
 		self::loginHelper(self::TEST_TRASHBIN_USER1);
138 138
 	}
@@ -142,8 +142,8 @@  discard block
 block discarded – undo
142 142
 		// disable trashbin to be able to properly clean up
143 143
 		Server::get(IAppManager::class)->disableApp('files_trashbin');
144 144
 
145
-		$this->rootView->deleteAll('/' . self::TEST_TRASHBIN_USER1 . '/files');
146
-		$this->rootView->deleteAll('/' . self::TEST_TRASHBIN_USER2 . '/files');
145
+		$this->rootView->deleteAll('/'.self::TEST_TRASHBIN_USER1.'/files');
146
+		$this->rootView->deleteAll('/'.self::TEST_TRASHBIN_USER2.'/files');
147 147
 		$this->rootView->deleteAll($this->trashRoot1);
148 148
 		$this->rootView->deleteAll($this->trashRoot2);
149 149
 
@@ -211,15 +211,15 @@  discard block
 block discarded – undo
211 211
 	 */
212 212
 	public function testExpireOldFilesShared(): void {
213 213
 		$currentTime = time();
214
-		$folder = 'trashTest-' . $currentTime . '/';
214
+		$folder = 'trashTest-'.$currentTime.'/';
215 215
 		$expiredDate = $currentTime - 3 * 24 * 60 * 60;
216 216
 
217 217
 		// create some files
218 218
 		Filesystem::mkdir($folder);
219
-		Filesystem::file_put_contents($folder . 'user1-1.txt', 'file1');
220
-		Filesystem::file_put_contents($folder . 'user1-2.txt', 'file2');
221
-		Filesystem::file_put_contents($folder . 'user1-3.txt', 'file3');
222
-		Filesystem::file_put_contents($folder . 'user1-4.txt', 'file4');
219
+		Filesystem::file_put_contents($folder.'user1-1.txt', 'file1');
220
+		Filesystem::file_put_contents($folder.'user1-2.txt', 'file2');
221
+		Filesystem::file_put_contents($folder.'user1-3.txt', 'file3');
222
+		Filesystem::file_put_contents($folder.'user1-4.txt', 'file4');
223 223
 
224 224
 		//share user1-4.txt with user2
225 225
 		$node = \OC::$server->getUserFolder(self::TEST_TRASHBIN_USER1)->get($folder);
@@ -233,9 +233,9 @@  discard block
 block discarded – undo
233 233
 		Server::get(\OCP\Share\IManager::class)->acceptShare($share, self::TEST_TRASHBIN_USER2);
234 234
 
235 235
 		// delete them so that they end up in the trash bin
236
-		Filesystem::unlink($folder . 'user1-1.txt');
237
-		Filesystem::unlink($folder . 'user1-2.txt');
238
-		Filesystem::unlink($folder . 'user1-3.txt');
236
+		Filesystem::unlink($folder.'user1-1.txt');
237
+		Filesystem::unlink($folder.'user1-2.txt');
238
+		Filesystem::unlink($folder.'user1-3.txt');
239 239
 
240 240
 		$filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'name');
241 241
 		$this->assertSame(3, count($filesInTrash));
@@ -246,7 +246,7 @@  discard block
 block discarded – undo
246 246
 		// login as user2
247 247
 		self::loginHelper(self::TEST_TRASHBIN_USER2);
248 248
 
249
-		$this->assertTrue(Filesystem::file_exists($folder . 'user1-4.txt'));
249
+		$this->assertTrue(Filesystem::file_exists($folder.'user1-4.txt'));
250 250
 
251 251
 		// create some files
252 252
 		Filesystem::file_put_contents('user2-1.txt', 'file1');
@@ -262,7 +262,7 @@  discard block
 block discarded – undo
262 262
 		// every second file will get a date in the past so that it will get expired
263 263
 		$this->manipulateDeleteTime($filesInTrashUser2, $this->trashRoot2, $expiredDate);
264 264
 
265
-		Filesystem::unlink($folder . 'user1-4.txt');
265
+		Filesystem::unlink($folder.'user1-4.txt');
266 266
 
267 267
 		$this->runCommands();
268 268
 
@@ -297,7 +297,7 @@  discard block
 block discarded – undo
297 297
 			}
298 298
 			if (!$found) {
299 299
 				// if we didn't found the expected file, something went wrong
300
-				$this->assertTrue(false, "can't find expected file '" . $expectedFile . "' in trash bin");
300
+				$this->assertTrue(false, "can't find expected file '".$expectedFile."' in trash bin");
301 301
 			}
302 302
 		}
303 303
 	}
@@ -313,8 +313,8 @@  discard block
 block discarded – undo
313 313
 			// modify every second file
314 314
 			$counter = ($counter + 1) % 2;
315 315
 			if ($counter === 1) {
316
-				$source = $trashRoot . '/files/' . $file['name'] . '.d' . $file['mtime'];
317
-				$target = Filesystem::normalizePath($trashRoot . '/files/' . $file['name'] . '.d' . $expireDate);
316
+				$source = $trashRoot.'/files/'.$file['name'].'.d'.$file['mtime'];
317
+				$target = Filesystem::normalizePath($trashRoot.'/files/'.$file['name'].'.d'.$expireDate);
318 318
 				$this->rootView->rename($source, $target);
319 319
 				$file['mtime'] = $expireDate;
320 320
 			}
@@ -379,7 +379,7 @@  discard block
 block discarded – undo
379 379
 
380 380
 		$this->assertTrue(
381 381
 			Trashbin::restore(
382
-				'file1.txt.d' . $trashedFile->getMtime(),
382
+				'file1.txt.d'.$trashedFile->getMtime(),
383 383
 				$trashedFile->getName(),
384 384
 				$trashedFile->getMtime()
385 385
 			)
@@ -412,7 +412,7 @@  discard block
 block discarded – undo
412 412
 
413 413
 		$this->assertTrue(
414 414
 			Trashbin::restore(
415
-				'file1.txt.d' . $trashedFile->getMtime(),
415
+				'file1.txt.d'.$trashedFile->getMtime(),
416 416
 				$trashedFile->getName(),
417 417
 				$trashedFile->getMtime()
418 418
 			)
@@ -445,7 +445,7 @@  discard block
 block discarded – undo
445 445
 
446 446
 		$this->assertTrue(
447 447
 			Trashbin::restore(
448
-				'folder.d' . $trashedFolder->getMtime(),
448
+				'folder.d'.$trashedFolder->getMtime(),
449 449
 				$trashedFolder->getName(),
450 450
 				$trashedFolder->getMtime()
451 451
 			)
@@ -478,7 +478,7 @@  discard block
 block discarded – undo
478 478
 
479 479
 		$this->assertTrue(
480 480
 			Trashbin::restore(
481
-				'folder.d' . $trashedFile->getMtime() . '/file1.txt',
481
+				'folder.d'.$trashedFile->getMtime().'/file1.txt',
482 482
 				'file1.txt',
483 483
 				$trashedFile->getMtime()
484 484
 			)
@@ -515,7 +515,7 @@  discard block
 block discarded – undo
515 515
 
516 516
 		$this->assertTrue(
517 517
 			Trashbin::restore(
518
-				'file1.txt.d' . $trashedFile->getMtime(),
518
+				'file1.txt.d'.$trashedFile->getMtime(),
519 519
 				$trashedFile->getName(),
520 520
 				$trashedFile->getMtime()
521 521
 			)
@@ -552,7 +552,7 @@  discard block
 block discarded – undo
552 552
 
553 553
 		$this->assertTrue(
554 554
 			Trashbin::restore(
555
-				'file1.txt.d' . $trashedFile->getMtime(),
555
+				'file1.txt.d'.$trashedFile->getMtime(),
556 556
 				$trashedFile->getName(),
557 557
 				$trashedFile->getMtime()
558 558
 			)
@@ -593,7 +593,7 @@  discard block
 block discarded – undo
593 593
 
594 594
 		$this->assertTrue(
595 595
 			Trashbin::restore(
596
-				'file1.txt.d' . $trashedFile->getMtime(),
596
+				'file1.txt.d'.$trashedFile->getMtime(),
597 597
 				$trashedFile->getName(),
598 598
 				$trashedFile->getMtime()
599 599
 			)
@@ -642,7 +642,7 @@  discard block
 block discarded – undo
642 642
 		$trashedFile = $filesInTrash[0];
643 643
 
644 644
 		// delete source folder
645
-		[$storage, $internalPath] = $this->rootView->resolvePath('/' . self::TEST_TRASHBIN_USER1 . '/files/folder');
645
+		[$storage, $internalPath] = $this->rootView->resolvePath('/'.self::TEST_TRASHBIN_USER1.'/files/folder');
646 646
 		if ($storage instanceof Local) {
647 647
 			$folderAbsPath = $storage->getSourcePath($internalPath);
648 648
 			// make folder read-only
@@ -650,7 +650,7 @@  discard block
 block discarded – undo
650 650
 
651 651
 			$this->assertTrue(
652 652
 				Trashbin::restore(
653
-					'file1.txt.d' . $trashedFile->getMtime(),
653
+					'file1.txt.d'.$trashedFile->getMtime(),
654 654
 					$trashedFile->getName(),
655 655
 					$trashedFile->getMtime()
656 656
 				)
Please login to merge, or discard this patch.
apps/admin_audit/lib/Actions/Files.php 2 patches
Indentation   +139 added lines, -139 removed lines patch added patch discarded remove patch
@@ -25,151 +25,151 @@
 block discarded – undo
25 25
  * @package OCA\AdminAudit\Actions
26 26
  */
27 27
 class Files extends Action {
28
-	/**
29
-	 * Logs file read actions
30
-	 */
31
-	public function read(BeforeNodeReadEvent $event): void {
32
-		try {
33
-			$node = $event->getNode();
34
-			$params = [
35
-				'id' => $node instanceof NonExistingFile ? null : $node->getId(),
36
-				'path' => $node->getPath(),
37
-			];
38
-		} catch (InvalidPathException|NotFoundException $e) {
39
-			Server::get(LoggerInterface::class)->error(
40
-				'Exception thrown in file read: ' . $e->getMessage(), ['app' => 'admin_audit', 'exception' => $e]
41
-			);
42
-			return;
43
-		}
44
-		$this->log(
45
-			'File with id "%s" accessed: "%s"',
46
-			$params,
47
-			array_keys($params)
48
-		);
49
-	}
28
+    /**
29
+     * Logs file read actions
30
+     */
31
+    public function read(BeforeNodeReadEvent $event): void {
32
+        try {
33
+            $node = $event->getNode();
34
+            $params = [
35
+                'id' => $node instanceof NonExistingFile ? null : $node->getId(),
36
+                'path' => $node->getPath(),
37
+            ];
38
+        } catch (InvalidPathException|NotFoundException $e) {
39
+            Server::get(LoggerInterface::class)->error(
40
+                'Exception thrown in file read: ' . $e->getMessage(), ['app' => 'admin_audit', 'exception' => $e]
41
+            );
42
+            return;
43
+        }
44
+        $this->log(
45
+            'File with id "%s" accessed: "%s"',
46
+            $params,
47
+            array_keys($params)
48
+        );
49
+    }
50 50
 
51
-	/**
52
-	 * Logs rename actions of files
53
-	 */
54
-	public function afterRename(NodeRenamedEvent $event): void {
55
-		try {
56
-			$target = $event->getTarget();
57
-			$source = $event->getSource();
58
-			$params = [
59
-				'newid' => $target->getId(),
60
-				'oldpath' => $source->getPath(),
61
-				'newpath' => $target->getPath(),
62
-			];
63
-		} catch (InvalidPathException|NotFoundException $e) {
64
-			Server::get(LoggerInterface::class)->error(
65
-				'Exception thrown in file rename: ' . $e->getMessage(), ['app' => 'admin_audit', 'exception' => $e]
66
-			);
67
-			return;
68
-		}
51
+    /**
52
+     * Logs rename actions of files
53
+     */
54
+    public function afterRename(NodeRenamedEvent $event): void {
55
+        try {
56
+            $target = $event->getTarget();
57
+            $source = $event->getSource();
58
+            $params = [
59
+                'newid' => $target->getId(),
60
+                'oldpath' => $source->getPath(),
61
+                'newpath' => $target->getPath(),
62
+            ];
63
+        } catch (InvalidPathException|NotFoundException $e) {
64
+            Server::get(LoggerInterface::class)->error(
65
+                'Exception thrown in file rename: ' . $e->getMessage(), ['app' => 'admin_audit', 'exception' => $e]
66
+            );
67
+            return;
68
+        }
69 69
 
70
-		$this->log(
71
-			'File renamed with id "%s" from "%s" to "%s"',
72
-			$params,
73
-			array_keys($params)
74
-		);
75
-	}
70
+        $this->log(
71
+            'File renamed with id "%s" from "%s" to "%s"',
72
+            $params,
73
+            array_keys($params)
74
+        );
75
+    }
76 76
 
77 77
 
78
-	/**
79
-	 * Logs creation of files
80
-	 */
81
-	public function create(NodeCreatedEvent $event): void {
82
-		try {
83
-			$params = [
84
-				'id' => $event->getNode()->getId(),
85
-				'path' => $event->getNode()->getPath(),
86
-			];
87
-		} catch (InvalidPathException|NotFoundException $e) {
88
-			Server::get(LoggerInterface::class)->error(
89
-				'Exception thrown in file create: ' . $e->getMessage(), ['app' => 'admin_audit', 'exception' => $e]
90
-			);
91
-			return;
92
-		}
93
-		if ($params['path'] === '/' || $params['path'] === '') {
94
-			return;
95
-		}
96
-		$this->log(
97
-			'File with id "%s" created: "%s"',
98
-			$params,
99
-			array_keys($params)
100
-		);
101
-	}
78
+    /**
79
+     * Logs creation of files
80
+     */
81
+    public function create(NodeCreatedEvent $event): void {
82
+        try {
83
+            $params = [
84
+                'id' => $event->getNode()->getId(),
85
+                'path' => $event->getNode()->getPath(),
86
+            ];
87
+        } catch (InvalidPathException|NotFoundException $e) {
88
+            Server::get(LoggerInterface::class)->error(
89
+                'Exception thrown in file create: ' . $e->getMessage(), ['app' => 'admin_audit', 'exception' => $e]
90
+            );
91
+            return;
92
+        }
93
+        if ($params['path'] === '/' || $params['path'] === '') {
94
+            return;
95
+        }
96
+        $this->log(
97
+            'File with id "%s" created: "%s"',
98
+            $params,
99
+            array_keys($params)
100
+        );
101
+    }
102 102
 
103
-	/**
104
-	 * Logs copying of files
105
-	 */
106
-	public function copy(NodeCopiedEvent $event): void {
107
-		try {
108
-			$params = [
109
-				'oldid' => $event->getSource()->getId(),
110
-				'newid' => $event->getTarget()->getId(),
111
-				'oldpath' => $event->getSource()->getPath(),
112
-				'newpath' => $event->getTarget()->getPath(),
113
-			];
114
-		} catch (InvalidPathException|NotFoundException $e) {
115
-			Server::get(LoggerInterface::class)->error(
116
-				'Exception thrown in file copy: ' . $e->getMessage(), ['app' => 'admin_audit', 'exception' => $e]
117
-			);
118
-			return;
119
-		}
120
-		$this->log(
121
-			'File id copied from: "%s" to "%s", path from "%s" to "%s"',
122
-			$params,
123
-			array_keys($params)
124
-		);
125
-	}
103
+    /**
104
+     * Logs copying of files
105
+     */
106
+    public function copy(NodeCopiedEvent $event): void {
107
+        try {
108
+            $params = [
109
+                'oldid' => $event->getSource()->getId(),
110
+                'newid' => $event->getTarget()->getId(),
111
+                'oldpath' => $event->getSource()->getPath(),
112
+                'newpath' => $event->getTarget()->getPath(),
113
+            ];
114
+        } catch (InvalidPathException|NotFoundException $e) {
115
+            Server::get(LoggerInterface::class)->error(
116
+                'Exception thrown in file copy: ' . $e->getMessage(), ['app' => 'admin_audit', 'exception' => $e]
117
+            );
118
+            return;
119
+        }
120
+        $this->log(
121
+            'File id copied from: "%s" to "%s", path from "%s" to "%s"',
122
+            $params,
123
+            array_keys($params)
124
+        );
125
+    }
126 126
 
127
-	/**
128
-	 * Logs writing of files
129
-	 */
130
-	public function write(NodeWrittenEvent $event): void {
131
-		$node = $event->getNode();
132
-		try {
133
-			$params = [
134
-				'id' => $node->getId(),
135
-				'path' => $node->getPath(),
136
-			];
137
-		} catch (InvalidPathException|NotFoundException $e) {
138
-			Server::get(LoggerInterface::class)->error(
139
-				'Exception thrown in file write: ' . $e->getMessage(), ['app' => 'admin_audit', 'exception' => $e]
140
-			);
141
-			return;
142
-		}
143
-		if ($params['path'] === '/' || $params['path'] === '') {
144
-			return;
145
-		}
127
+    /**
128
+     * Logs writing of files
129
+     */
130
+    public function write(NodeWrittenEvent $event): void {
131
+        $node = $event->getNode();
132
+        try {
133
+            $params = [
134
+                'id' => $node->getId(),
135
+                'path' => $node->getPath(),
136
+            ];
137
+        } catch (InvalidPathException|NotFoundException $e) {
138
+            Server::get(LoggerInterface::class)->error(
139
+                'Exception thrown in file write: ' . $e->getMessage(), ['app' => 'admin_audit', 'exception' => $e]
140
+            );
141
+            return;
142
+        }
143
+        if ($params['path'] === '/' || $params['path'] === '') {
144
+            return;
145
+        }
146 146
 
147
-		$this->log(
148
-			'File with id "%s" written to: "%s"',
149
-			$params,
150
-			array_keys($params)
151
-		);
152
-	}
147
+        $this->log(
148
+            'File with id "%s" written to: "%s"',
149
+            $params,
150
+            array_keys($params)
151
+        );
152
+    }
153 153
 
154
-	/**
155
-	 * Logs deletions of files
156
-	 */
157
-	public function delete(BeforeNodeDeletedEvent $event): void {
158
-		try {
159
-			$params = [
160
-				'id' => $event->getNode()->getId(),
161
-				'path' => $event->getNode()->getPath(),
162
-			];
163
-		} catch (InvalidPathException|NotFoundException $e) {
164
-			Server::get(LoggerInterface::class)->error(
165
-				'Exception thrown in file delete: ' . $e->getMessage(), ['app' => 'admin_audit', 'exception' => $e]
166
-			);
167
-			return;
168
-		}
169
-		$this->log(
170
-			'File with id "%s" deleted: "%s"',
171
-			$params,
172
-			array_keys($params)
173
-		);
174
-	}
154
+    /**
155
+     * Logs deletions of files
156
+     */
157
+    public function delete(BeforeNodeDeletedEvent $event): void {
158
+        try {
159
+            $params = [
160
+                'id' => $event->getNode()->getId(),
161
+                'path' => $event->getNode()->getPath(),
162
+            ];
163
+        } catch (InvalidPathException|NotFoundException $e) {
164
+            Server::get(LoggerInterface::class)->error(
165
+                'Exception thrown in file delete: ' . $e->getMessage(), ['app' => 'admin_audit', 'exception' => $e]
166
+            );
167
+            return;
168
+        }
169
+        $this->log(
170
+            'File with id "%s" deleted: "%s"',
171
+            $params,
172
+            array_keys($params)
173
+        );
174
+    }
175 175
 }
Please login to merge, or discard this patch.
Spacing   +12 added lines, -12 removed lines patch added patch discarded remove patch
@@ -35,9 +35,9 @@  discard block
 block discarded – undo
35 35
 				'id' => $node instanceof NonExistingFile ? null : $node->getId(),
36 36
 				'path' => $node->getPath(),
37 37
 			];
38
-		} catch (InvalidPathException|NotFoundException $e) {
38
+		} catch (InvalidPathException | NotFoundException $e) {
39 39
 			Server::get(LoggerInterface::class)->error(
40
-				'Exception thrown in file read: ' . $e->getMessage(), ['app' => 'admin_audit', 'exception' => $e]
40
+				'Exception thrown in file read: '.$e->getMessage(), ['app' => 'admin_audit', 'exception' => $e]
41 41
 			);
42 42
 			return;
43 43
 		}
@@ -60,9 +60,9 @@  discard block
 block discarded – undo
60 60
 				'oldpath' => $source->getPath(),
61 61
 				'newpath' => $target->getPath(),
62 62
 			];
63
-		} catch (InvalidPathException|NotFoundException $e) {
63
+		} catch (InvalidPathException | NotFoundException $e) {
64 64
 			Server::get(LoggerInterface::class)->error(
65
-				'Exception thrown in file rename: ' . $e->getMessage(), ['app' => 'admin_audit', 'exception' => $e]
65
+				'Exception thrown in file rename: '.$e->getMessage(), ['app' => 'admin_audit', 'exception' => $e]
66 66
 			);
67 67
 			return;
68 68
 		}
@@ -84,9 +84,9 @@  discard block
 block discarded – undo
84 84
 				'id' => $event->getNode()->getId(),
85 85
 				'path' => $event->getNode()->getPath(),
86 86
 			];
87
-		} catch (InvalidPathException|NotFoundException $e) {
87
+		} catch (InvalidPathException | NotFoundException $e) {
88 88
 			Server::get(LoggerInterface::class)->error(
89
-				'Exception thrown in file create: ' . $e->getMessage(), ['app' => 'admin_audit', 'exception' => $e]
89
+				'Exception thrown in file create: '.$e->getMessage(), ['app' => 'admin_audit', 'exception' => $e]
90 90
 			);
91 91
 			return;
92 92
 		}
@@ -111,9 +111,9 @@  discard block
 block discarded – undo
111 111
 				'oldpath' => $event->getSource()->getPath(),
112 112
 				'newpath' => $event->getTarget()->getPath(),
113 113
 			];
114
-		} catch (InvalidPathException|NotFoundException $e) {
114
+		} catch (InvalidPathException | NotFoundException $e) {
115 115
 			Server::get(LoggerInterface::class)->error(
116
-				'Exception thrown in file copy: ' . $e->getMessage(), ['app' => 'admin_audit', 'exception' => $e]
116
+				'Exception thrown in file copy: '.$e->getMessage(), ['app' => 'admin_audit', 'exception' => $e]
117 117
 			);
118 118
 			return;
119 119
 		}
@@ -134,9 +134,9 @@  discard block
 block discarded – undo
134 134
 				'id' => $node->getId(),
135 135
 				'path' => $node->getPath(),
136 136
 			];
137
-		} catch (InvalidPathException|NotFoundException $e) {
137
+		} catch (InvalidPathException | NotFoundException $e) {
138 138
 			Server::get(LoggerInterface::class)->error(
139
-				'Exception thrown in file write: ' . $e->getMessage(), ['app' => 'admin_audit', 'exception' => $e]
139
+				'Exception thrown in file write: '.$e->getMessage(), ['app' => 'admin_audit', 'exception' => $e]
140 140
 			);
141 141
 			return;
142 142
 		}
@@ -160,9 +160,9 @@  discard block
 block discarded – undo
160 160
 				'id' => $event->getNode()->getId(),
161 161
 				'path' => $event->getNode()->getPath(),
162 162
 			];
163
-		} catch (InvalidPathException|NotFoundException $e) {
163
+		} catch (InvalidPathException | NotFoundException $e) {
164 164
 			Server::get(LoggerInterface::class)->error(
165
-				'Exception thrown in file delete: ' . $e->getMessage(), ['app' => 'admin_audit', 'exception' => $e]
165
+				'Exception thrown in file delete: '.$e->getMessage(), ['app' => 'admin_audit', 'exception' => $e]
166 166
 			);
167 167
 			return;
168 168
 		}
Please login to merge, or discard this patch.
apps/admin_audit/lib/AppInfo/Application.php 2 patches
Indentation   +147 added lines, -147 removed lines patch added patch discarded remove patch
@@ -72,154 +72,154 @@
 block discarded – undo
72 72
 use Psr\Container\ContainerInterface;
73 73
 
74 74
 class Application extends App implements IBootstrap {
75
-	public function __construct() {
76
-		parent::__construct('admin_audit');
77
-	}
78
-
79
-	public function register(IRegistrationContext $context): void {
80
-		$context->registerService(IAuditLogger::class, function (ContainerInterface $c) {
81
-			return new AuditLogger($c->get(ILogFactory::class), $c->get(IConfig::class));
82
-		});
83
-
84
-		$context->registerEventListener(CriticalActionPerformedEvent::class, CriticalActionPerformedEventListener::class);
85
-
86
-		// User management events
87
-		$context->registerEventListener(UserCreatedEvent::class, UserManagementEventListener::class);
88
-		$context->registerEventListener(UserDeletedEvent::class, UserManagementEventListener::class);
89
-		$context->registerEventListener(UserChangedEvent::class, UserManagementEventListener::class);
90
-		$context->registerEventListener(PasswordUpdatedEvent::class, UserManagementEventListener::class);
91
-		$context->registerEventListener(UserIdAssignedEvent::class, UserManagementEventListener::class);
92
-		$context->registerEventListener(UserIdUnassignedEvent::class, UserManagementEventListener::class);
93
-
94
-		// Group management events
95
-		$context->registerEventListener(UserAddedEvent::class, GroupManagementEventListener::class);
96
-		$context->registerEventListener(UserRemovedEvent::class, GroupManagementEventListener::class);
97
-		$context->registerEventListener(GroupCreatedEvent::class, GroupManagementEventListener::class);
98
-		$context->registerEventListener(GroupDeletedEvent::class, GroupManagementEventListener::class);
99
-
100
-		// Sharing events
101
-		$context->registerEventListener(ShareCreatedEvent::class, SharingEventListener::class);
102
-		$context->registerEventListener(ShareDeletedEvent::class, SharingEventListener::class);
103
-
104
-		// Auth events
105
-		$context->registerEventListener(BeforeUserLoggedInEvent::class, AuthEventListener::class);
106
-		$context->registerEventListener(UserLoggedInWithCookieEvent::class, AuthEventListener::class);
107
-		$context->registerEventListener(UserLoggedInEvent::class, AuthEventListener::class);
108
-		$context->registerEventListener(BeforeUserLoggedOutEvent::class, AuthEventListener::class);
109
-		$context->registerEventListener(AnyLoginFailedEvent::class, AuthEventListener::class);
110
-
111
-		// File events
112
-		$context->registerEventListener(BeforePreviewFetchedEvent::class, FileEventListener::class);
113
-		$context->registerEventListener(VersionRestoredEvent::class, FileEventListener::class);
114
-
115
-		// Security events
116
-		$context->registerEventListener(TwoFactorProviderChallengePassed::class, SecurityEventListener::class);
117
-		$context->registerEventListener(TwoFactorProviderChallengeFailed::class, SecurityEventListener::class);
118
-
119
-		// App management events
120
-		$context->registerEventListener(AppEnableEvent::class, AppManagementEventListener::class);
121
-		$context->registerEventListener(AppDisableEvent::class, AppManagementEventListener::class);
122
-		$context->registerEventListener(AppUpdateEvent::class, AppManagementEventListener::class);
123
-
124
-		// Console events
125
-		$context->registerEventListener(ConsoleEvent::class, ConsoleEventListener::class);
126
-	}
127
-
128
-	public function boot(IBootContext $context): void {
129
-		/** @var IAuditLogger $logger */
130
-		$logger = $context->getAppContainer()->get(IAuditLogger::class);
131
-
132
-		/*
75
+    public function __construct() {
76
+        parent::__construct('admin_audit');
77
+    }
78
+
79
+    public function register(IRegistrationContext $context): void {
80
+        $context->registerService(IAuditLogger::class, function (ContainerInterface $c) {
81
+            return new AuditLogger($c->get(ILogFactory::class), $c->get(IConfig::class));
82
+        });
83
+
84
+        $context->registerEventListener(CriticalActionPerformedEvent::class, CriticalActionPerformedEventListener::class);
85
+
86
+        // User management events
87
+        $context->registerEventListener(UserCreatedEvent::class, UserManagementEventListener::class);
88
+        $context->registerEventListener(UserDeletedEvent::class, UserManagementEventListener::class);
89
+        $context->registerEventListener(UserChangedEvent::class, UserManagementEventListener::class);
90
+        $context->registerEventListener(PasswordUpdatedEvent::class, UserManagementEventListener::class);
91
+        $context->registerEventListener(UserIdAssignedEvent::class, UserManagementEventListener::class);
92
+        $context->registerEventListener(UserIdUnassignedEvent::class, UserManagementEventListener::class);
93
+
94
+        // Group management events
95
+        $context->registerEventListener(UserAddedEvent::class, GroupManagementEventListener::class);
96
+        $context->registerEventListener(UserRemovedEvent::class, GroupManagementEventListener::class);
97
+        $context->registerEventListener(GroupCreatedEvent::class, GroupManagementEventListener::class);
98
+        $context->registerEventListener(GroupDeletedEvent::class, GroupManagementEventListener::class);
99
+
100
+        // Sharing events
101
+        $context->registerEventListener(ShareCreatedEvent::class, SharingEventListener::class);
102
+        $context->registerEventListener(ShareDeletedEvent::class, SharingEventListener::class);
103
+
104
+        // Auth events
105
+        $context->registerEventListener(BeforeUserLoggedInEvent::class, AuthEventListener::class);
106
+        $context->registerEventListener(UserLoggedInWithCookieEvent::class, AuthEventListener::class);
107
+        $context->registerEventListener(UserLoggedInEvent::class, AuthEventListener::class);
108
+        $context->registerEventListener(BeforeUserLoggedOutEvent::class, AuthEventListener::class);
109
+        $context->registerEventListener(AnyLoginFailedEvent::class, AuthEventListener::class);
110
+
111
+        // File events
112
+        $context->registerEventListener(BeforePreviewFetchedEvent::class, FileEventListener::class);
113
+        $context->registerEventListener(VersionRestoredEvent::class, FileEventListener::class);
114
+
115
+        // Security events
116
+        $context->registerEventListener(TwoFactorProviderChallengePassed::class, SecurityEventListener::class);
117
+        $context->registerEventListener(TwoFactorProviderChallengeFailed::class, SecurityEventListener::class);
118
+
119
+        // App management events
120
+        $context->registerEventListener(AppEnableEvent::class, AppManagementEventListener::class);
121
+        $context->registerEventListener(AppDisableEvent::class, AppManagementEventListener::class);
122
+        $context->registerEventListener(AppUpdateEvent::class, AppManagementEventListener::class);
123
+
124
+        // Console events
125
+        $context->registerEventListener(ConsoleEvent::class, ConsoleEventListener::class);
126
+    }
127
+
128
+    public function boot(IBootContext $context): void {
129
+        /** @var IAuditLogger $logger */
130
+        $logger = $context->getAppContainer()->get(IAuditLogger::class);
131
+
132
+        /*
133 133
 		 * TODO: once the hooks are migrated to lazy events, this should be done
134 134
 		 *       in \OCA\AdminAudit\AppInfo\Application::register
135 135
 		 */
136
-		$this->registerLegacyHooks($logger, $context->getServerContainer());
137
-	}
138
-
139
-	/**
140
-	 * Register hooks in order to log them
141
-	 */
142
-	private function registerLegacyHooks(IAuditLogger $logger, ContainerInterface $serverContainer): void {
143
-		/** @var IEventDispatcher $eventDispatcher */
144
-		$eventDispatcher = $serverContainer->get(IEventDispatcher::class);
145
-		$this->sharingLegacyHooks($logger);
146
-		$this->fileHooks($logger, $eventDispatcher);
147
-		$this->trashbinHooks($logger);
148
-		$this->versionsHooks($logger);
149
-		$this->tagHooks($logger, $eventDispatcher);
150
-	}
151
-
152
-	private function sharingLegacyHooks(IAuditLogger $logger): void {
153
-		$shareActions = new Sharing($logger);
154
-
155
-		Util::connectHook(Share::class, 'post_update_permissions', $shareActions, 'updatePermissions');
156
-		Util::connectHook(Share::class, 'post_update_password', $shareActions, 'updatePassword');
157
-		Util::connectHook(Share::class, 'post_set_expiration_date', $shareActions, 'updateExpirationDate');
158
-		Util::connectHook(Share::class, 'share_link_access', $shareActions, 'shareAccessed');
159
-	}
160
-
161
-	private function tagHooks(IAuditLogger $logger,
162
-		IEventDispatcher $eventDispatcher): void {
163
-		$eventDispatcher->addListener(ManagerEvent::EVENT_CREATE, function (ManagerEvent $event) use ($logger): void {
164
-			$tagActions = new TagManagement($logger);
165
-			$tagActions->createTag($event->getTag());
166
-		});
167
-	}
168
-
169
-	private function fileHooks(IAuditLogger $logger, IEventDispatcher $eventDispatcher): void {
170
-		$fileActions = new Files($logger);
171
-
172
-		$eventDispatcher->addListener(
173
-			NodeRenamedEvent::class,
174
-			function (NodeRenamedEvent $event) use ($fileActions): void {
175
-				$fileActions->afterRename($event);
176
-			}
177
-		);
178
-
179
-		$eventDispatcher->addListener(
180
-			NodeCreatedEvent::class,
181
-			function (NodeCreatedEvent $event) use ($fileActions): void {
182
-				$fileActions->create($event);
183
-			}
184
-		);
185
-
186
-		$eventDispatcher->addListener(
187
-			NodeCopiedEvent::class,
188
-			function (NodeCopiedEvent $event) use ($fileActions): void {
189
-				$fileActions->copy($event);
190
-			}
191
-		);
192
-
193
-		$eventDispatcher->addListener(
194
-			NodeWrittenEvent::class,
195
-			function (NodeWrittenEvent $event) use ($fileActions): void {
196
-				$fileActions->write($event);
197
-			}
198
-		);
199
-
200
-		$eventDispatcher->addListener(
201
-			BeforeNodeReadEvent::class,
202
-			function (BeforeNodeReadEvent $event) use ($fileActions): void {
203
-				$fileActions->read($event);
204
-			}
205
-		);
206
-
207
-		$eventDispatcher->addListener(
208
-			BeforeNodeDeletedEvent::class,
209
-			function (BeforeNodeDeletedEvent $event) use ($fileActions): void {
210
-				$fileActions->delete($event);
211
-			}
212
-		);
213
-	}
214
-
215
-	private function versionsHooks(IAuditLogger $logger): void {
216
-		$versionsActions = new Versions($logger);
217
-		Util::connectHook('\OCP\Versions', 'delete', $versionsActions, 'delete');
218
-	}
219
-
220
-	private function trashbinHooks(IAuditLogger $logger): void {
221
-		$trashActions = new Trashbin($logger);
222
-		Util::connectHook('\OCP\Trashbin', 'preDelete', $trashActions, 'delete');
223
-		Util::connectHook('\OCA\Files_Trashbin\Trashbin', 'post_restore', $trashActions, 'restore');
224
-	}
136
+        $this->registerLegacyHooks($logger, $context->getServerContainer());
137
+    }
138
+
139
+    /**
140
+     * Register hooks in order to log them
141
+     */
142
+    private function registerLegacyHooks(IAuditLogger $logger, ContainerInterface $serverContainer): void {
143
+        /** @var IEventDispatcher $eventDispatcher */
144
+        $eventDispatcher = $serverContainer->get(IEventDispatcher::class);
145
+        $this->sharingLegacyHooks($logger);
146
+        $this->fileHooks($logger, $eventDispatcher);
147
+        $this->trashbinHooks($logger);
148
+        $this->versionsHooks($logger);
149
+        $this->tagHooks($logger, $eventDispatcher);
150
+    }
151
+
152
+    private function sharingLegacyHooks(IAuditLogger $logger): void {
153
+        $shareActions = new Sharing($logger);
154
+
155
+        Util::connectHook(Share::class, 'post_update_permissions', $shareActions, 'updatePermissions');
156
+        Util::connectHook(Share::class, 'post_update_password', $shareActions, 'updatePassword');
157
+        Util::connectHook(Share::class, 'post_set_expiration_date', $shareActions, 'updateExpirationDate');
158
+        Util::connectHook(Share::class, 'share_link_access', $shareActions, 'shareAccessed');
159
+    }
160
+
161
+    private function tagHooks(IAuditLogger $logger,
162
+        IEventDispatcher $eventDispatcher): void {
163
+        $eventDispatcher->addListener(ManagerEvent::EVENT_CREATE, function (ManagerEvent $event) use ($logger): void {
164
+            $tagActions = new TagManagement($logger);
165
+            $tagActions->createTag($event->getTag());
166
+        });
167
+    }
168
+
169
+    private function fileHooks(IAuditLogger $logger, IEventDispatcher $eventDispatcher): void {
170
+        $fileActions = new Files($logger);
171
+
172
+        $eventDispatcher->addListener(
173
+            NodeRenamedEvent::class,
174
+            function (NodeRenamedEvent $event) use ($fileActions): void {
175
+                $fileActions->afterRename($event);
176
+            }
177
+        );
178
+
179
+        $eventDispatcher->addListener(
180
+            NodeCreatedEvent::class,
181
+            function (NodeCreatedEvent $event) use ($fileActions): void {
182
+                $fileActions->create($event);
183
+            }
184
+        );
185
+
186
+        $eventDispatcher->addListener(
187
+            NodeCopiedEvent::class,
188
+            function (NodeCopiedEvent $event) use ($fileActions): void {
189
+                $fileActions->copy($event);
190
+            }
191
+        );
192
+
193
+        $eventDispatcher->addListener(
194
+            NodeWrittenEvent::class,
195
+            function (NodeWrittenEvent $event) use ($fileActions): void {
196
+                $fileActions->write($event);
197
+            }
198
+        );
199
+
200
+        $eventDispatcher->addListener(
201
+            BeforeNodeReadEvent::class,
202
+            function (BeforeNodeReadEvent $event) use ($fileActions): void {
203
+                $fileActions->read($event);
204
+            }
205
+        );
206
+
207
+        $eventDispatcher->addListener(
208
+            BeforeNodeDeletedEvent::class,
209
+            function (BeforeNodeDeletedEvent $event) use ($fileActions): void {
210
+                $fileActions->delete($event);
211
+            }
212
+        );
213
+    }
214
+
215
+    private function versionsHooks(IAuditLogger $logger): void {
216
+        $versionsActions = new Versions($logger);
217
+        Util::connectHook('\OCP\Versions', 'delete', $versionsActions, 'delete');
218
+    }
219
+
220
+    private function trashbinHooks(IAuditLogger $logger): void {
221
+        $trashActions = new Trashbin($logger);
222
+        Util::connectHook('\OCP\Trashbin', 'preDelete', $trashActions, 'delete');
223
+        Util::connectHook('\OCA\Files_Trashbin\Trashbin', 'post_restore', $trashActions, 'restore');
224
+    }
225 225
 }
Please login to merge, or discard this patch.
Spacing   +8 added lines, -8 removed lines patch added patch discarded remove patch
@@ -77,7 +77,7 @@  discard block
 block discarded – undo
77 77
 	}
78 78
 
79 79
 	public function register(IRegistrationContext $context): void {
80
-		$context->registerService(IAuditLogger::class, function (ContainerInterface $c) {
80
+		$context->registerService(IAuditLogger::class, function(ContainerInterface $c) {
81 81
 			return new AuditLogger($c->get(ILogFactory::class), $c->get(IConfig::class));
82 82
 		});
83 83
 
@@ -160,7 +160,7 @@  discard block
 block discarded – undo
160 160
 
161 161
 	private function tagHooks(IAuditLogger $logger,
162 162
 		IEventDispatcher $eventDispatcher): void {
163
-		$eventDispatcher->addListener(ManagerEvent::EVENT_CREATE, function (ManagerEvent $event) use ($logger): void {
163
+		$eventDispatcher->addListener(ManagerEvent::EVENT_CREATE, function(ManagerEvent $event) use ($logger): void {
164 164
 			$tagActions = new TagManagement($logger);
165 165
 			$tagActions->createTag($event->getTag());
166 166
 		});
@@ -171,42 +171,42 @@  discard block
 block discarded – undo
171 171
 
172 172
 		$eventDispatcher->addListener(
173 173
 			NodeRenamedEvent::class,
174
-			function (NodeRenamedEvent $event) use ($fileActions): void {
174
+			function(NodeRenamedEvent $event) use ($fileActions): void {
175 175
 				$fileActions->afterRename($event);
176 176
 			}
177 177
 		);
178 178
 
179 179
 		$eventDispatcher->addListener(
180 180
 			NodeCreatedEvent::class,
181
-			function (NodeCreatedEvent $event) use ($fileActions): void {
181
+			function(NodeCreatedEvent $event) use ($fileActions): void {
182 182
 				$fileActions->create($event);
183 183
 			}
184 184
 		);
185 185
 
186 186
 		$eventDispatcher->addListener(
187 187
 			NodeCopiedEvent::class,
188
-			function (NodeCopiedEvent $event) use ($fileActions): void {
188
+			function(NodeCopiedEvent $event) use ($fileActions): void {
189 189
 				$fileActions->copy($event);
190 190
 			}
191 191
 		);
192 192
 
193 193
 		$eventDispatcher->addListener(
194 194
 			NodeWrittenEvent::class,
195
-			function (NodeWrittenEvent $event) use ($fileActions): void {
195
+			function(NodeWrittenEvent $event) use ($fileActions): void {
196 196
 				$fileActions->write($event);
197 197
 			}
198 198
 		);
199 199
 
200 200
 		$eventDispatcher->addListener(
201 201
 			BeforeNodeReadEvent::class,
202
-			function (BeforeNodeReadEvent $event) use ($fileActions): void {
202
+			function(BeforeNodeReadEvent $event) use ($fileActions): void {
203 203
 				$fileActions->read($event);
204 204
 			}
205 205
 		);
206 206
 
207 207
 		$eventDispatcher->addListener(
208 208
 			BeforeNodeDeletedEvent::class,
209
-			function (BeforeNodeDeletedEvent $event) use ($fileActions): void {
209
+			function(BeforeNodeDeletedEvent $event) use ($fileActions): void {
210 210
 				$fileActions->delete($event);
211 211
 			}
212 212
 		);
Please login to merge, or discard this patch.
apps/files_versions/tests/VersioningTest.php 2 patches
Indentation   +875 added lines, -875 removed lines patch added patch discarded remove patch
@@ -36,964 +36,964 @@
 block discarded – undo
36 36
  * @group DB
37 37
  */
38 38
 class VersioningTest extends \Test\TestCase {
39
-	public const TEST_VERSIONS_USER = 'test-versions-user';
40
-	public const TEST_VERSIONS_USER2 = 'test-versions-user2';
41
-	public const USERS_VERSIONS_ROOT = '/test-versions-user/files_versions';
42
-
43
-	/**
44
-	 * @var View
45
-	 */
46
-	private $rootView;
47
-	/**
48
-	 * @var VersionsMapper
49
-	 */
50
-	private $versionsMapper;
51
-	/**
52
-	 * @var IMimeTypeLoader
53
-	 */
54
-	private $mimeTypeLoader;
55
-	private $user1;
56
-	private $user2;
57
-
58
-	public static function setUpBeforeClass(): void {
59
-		parent::setUpBeforeClass();
60
-
61
-		$application = new Application();
62
-
63
-		// create test user
64
-		self::loginHelper(self::TEST_VERSIONS_USER2, true);
65
-		self::loginHelper(self::TEST_VERSIONS_USER, true);
66
-	}
67
-
68
-	public static function tearDownAfterClass(): void {
69
-		// cleanup test user
70
-		$user = Server::get(IUserManager::class)->get(self::TEST_VERSIONS_USER);
71
-		if ($user !== null) {
72
-			$user->delete();
73
-		}
74
-		$user = Server::get(IUserManager::class)->get(self::TEST_VERSIONS_USER2);
75
-		if ($user !== null) {
76
-			$user->delete();
77
-		}
78
-
79
-		parent::tearDownAfterClass();
80
-	}
81
-
82
-	protected function setUp(): void {
83
-		parent::setUp();
84
-
85
-		$config = Server::get(IConfig::class);
86
-		$mockConfig = $this->getMockBuilder(AllConfig::class)
87
-			->onlyMethods(['getSystemValue'])
88
-			->setConstructorArgs([Server::get(\OC\SystemConfig::class)])
89
-			->getMock();
90
-		$mockConfig->expects($this->any())
91
-			->method('getSystemValue')
92
-			->willReturnCallback(function ($key, $default) use ($config) {
93
-				if ($key === 'filesystem_check_changes') {
94
-					return Watcher::CHECK_ONCE;
95
-				} else {
96
-					return $config->getSystemValue($key, $default);
97
-				}
98
-			});
99
-		$this->overwriteService(AllConfig::class, $mockConfig);
100
-
101
-		// clear hooks
102
-		\OC_Hook::clear();
103
-		\OC::registerShareHooks(Server::get(SystemConfig::class));
104
-		\OC::$server->boot();
105
-
106
-		self::loginHelper(self::TEST_VERSIONS_USER);
107
-		$this->rootView = new View();
108
-		if (!$this->rootView->file_exists(self::USERS_VERSIONS_ROOT)) {
109
-			$this->rootView->mkdir(self::USERS_VERSIONS_ROOT);
110
-		}
111
-
112
-		$this->versionsMapper = Server::get(VersionsMapper::class);
113
-		$this->mimeTypeLoader = Server::get(IMimeTypeLoader::class);
114
-
115
-		$this->user1 = $this->createMock(IUser::class);
116
-		$this->user1->method('getUID')
117
-			->willReturn(self::TEST_VERSIONS_USER);
118
-		$this->user2 = $this->createMock(IUser::class);
119
-		$this->user2->method('getUID')
120
-			->willReturn(self::TEST_VERSIONS_USER2);
121
-	}
122
-
123
-	protected function tearDown(): void {
124
-		$this->restoreService(AllConfig::class);
125
-
126
-		if ($this->rootView) {
127
-			$this->rootView->deleteAll(self::TEST_VERSIONS_USER . '/files/');
128
-			$this->rootView->deleteAll(self::TEST_VERSIONS_USER2 . '/files/');
129
-			$this->rootView->deleteAll(self::TEST_VERSIONS_USER . '/files_versions/');
130
-			$this->rootView->deleteAll(self::TEST_VERSIONS_USER2 . '/files_versions/');
131
-		}
132
-
133
-		\OC_Hook::clear();
134
-
135
-		parent::tearDown();
136
-	}
137
-
138
-	/**
139
-	 * @medium
140
-	 * test expire logic
141
-	 * @dataProvider versionsProvider
142
-	 */
143
-	public function testGetExpireList($versions, $sizeOfAllDeletedFiles): void {
144
-
145
-		// last interval end at 2592000
146
-		$startTime = 5000000;
147
-
148
-		$testClass = new VersionStorageToTest();
149
-		[$deleted, $size] = $testClass->callProtectedGetExpireList($startTime, $versions);
150
-
151
-		// we should have deleted 16 files each of the size 1
152
-		$this->assertEquals($sizeOfAllDeletedFiles, $size);
153
-
154
-		// the deleted array should only contain versions which should be deleted
155
-		foreach ($deleted as $key => $path) {
156
-			unset($versions[$key]);
157
-			$this->assertEquals('delete', substr($path, 0, strlen('delete')));
158
-		}
159
-
160
-		// the versions array should only contain versions which should be kept
161
-		foreach ($versions as $version) {
162
-			$this->assertEquals('keep', $version['path']);
163
-		}
164
-	}
165
-
166
-	public function versionsProvider() {
167
-		return [
168
-			// first set of versions uniformly distributed versions
169
-			[
170
-				[
171
-					// first slice (10sec) keep one version every 2 seconds
172
-					['version' => 4999999, 'path' => 'keep', 'size' => 1],
173
-					['version' => 4999998, 'path' => 'delete', 'size' => 1],
174
-					['version' => 4999997, 'path' => 'keep', 'size' => 1],
175
-					['version' => 4999995, 'path' => 'keep', 'size' => 1],
176
-					['version' => 4999994, 'path' => 'delete', 'size' => 1],
177
-					//next slice (60sec) starts at 4999990 keep one version every 10 secons
178
-					['version' => 4999988, 'path' => 'keep', 'size' => 1],
179
-					['version' => 4999978, 'path' => 'keep', 'size' => 1],
180
-					['version' => 4999975, 'path' => 'delete', 'size' => 1],
181
-					['version' => 4999972, 'path' => 'delete', 'size' => 1],
182
-					['version' => 4999967, 'path' => 'keep', 'size' => 1],
183
-					['version' => 4999958, 'path' => 'delete', 'size' => 1],
184
-					['version' => 4999957, 'path' => 'keep', 'size' => 1],
185
-					//next slice (3600sec) start at 4999940 keep one version every 60 seconds
186
-					['version' => 4999900, 'path' => 'keep', 'size' => 1],
187
-					['version' => 4999841, 'path' => 'delete', 'size' => 1],
188
-					['version' => 4999840, 'path' => 'keep', 'size' => 1],
189
-					['version' => 4999780, 'path' => 'keep', 'size' => 1],
190
-					['version' => 4996401, 'path' => 'keep', 'size' => 1],
191
-					// next slice (86400sec) start at 4996400 keep one version every 3600 seconds
192
-					['version' => 4996350, 'path' => 'delete', 'size' => 1],
193
-					['version' => 4992800, 'path' => 'keep', 'size' => 1],
194
-					['version' => 4989800, 'path' => 'delete', 'size' => 1],
195
-					['version' => 4989700, 'path' => 'delete', 'size' => 1],
196
-					['version' => 4989200, 'path' => 'keep', 'size' => 1],
197
-					// next slice (2592000sec) start at 4913600 keep one version every 86400 seconds
198
-					['version' => 4913600, 'path' => 'keep', 'size' => 1],
199
-					['version' => 4852800, 'path' => 'delete', 'size' => 1],
200
-					['version' => 4827201, 'path' => 'delete', 'size' => 1],
201
-					['version' => 4827200, 'path' => 'keep', 'size' => 1],
202
-					['version' => 4777201, 'path' => 'delete', 'size' => 1],
203
-					['version' => 4777501, 'path' => 'delete', 'size' => 1],
204
-					['version' => 4740000, 'path' => 'keep', 'size' => 1],
205
-					// final slice starts at 2408000 keep one version every 604800 secons
206
-					['version' => 2408000, 'path' => 'keep', 'size' => 1],
207
-					['version' => 1803201, 'path' => 'delete', 'size' => 1],
208
-					['version' => 1803200, 'path' => 'keep', 'size' => 1],
209
-					['version' => 1800199, 'path' => 'delete', 'size' => 1],
210
-					['version' => 1800100, 'path' => 'delete', 'size' => 1],
211
-					['version' => 1198300, 'path' => 'keep', 'size' => 1],
212
-				],
213
-				16 // size of all deleted files (every file has the size 1)
214
-			],
215
-			// second set of versions, here we have only really old versions
216
-			[
217
-				[
218
-					// first slice (10sec) keep one version every 2 seconds
219
-					// next slice (60sec) starts at 4999990 keep one version every 10 secons
220
-					// next slice (3600sec) start at 4999940 keep one version every 60 seconds
221
-					// next slice (86400sec) start at 4996400 keep one version every 3600 seconds
222
-					['version' => 4996400, 'path' => 'keep', 'size' => 1],
223
-					['version' => 4996350, 'path' => 'delete', 'size' => 1],
224
-					['version' => 4996350, 'path' => 'delete', 'size' => 1],
225
-					['version' => 4992800, 'path' => 'keep', 'size' => 1],
226
-					['version' => 4989800, 'path' => 'delete', 'size' => 1],
227
-					['version' => 4989700, 'path' => 'delete', 'size' => 1],
228
-					['version' => 4989200, 'path' => 'keep', 'size' => 1],
229
-					// next slice (2592000sec) start at 4913600 keep one version every 86400 seconds
230
-					['version' => 4913600, 'path' => 'keep', 'size' => 1],
231
-					['version' => 4852800, 'path' => 'delete', 'size' => 1],
232
-					['version' => 4827201, 'path' => 'delete', 'size' => 1],
233
-					['version' => 4827200, 'path' => 'keep', 'size' => 1],
234
-					['version' => 4777201, 'path' => 'delete', 'size' => 1],
235
-					['version' => 4777501, 'path' => 'delete', 'size' => 1],
236
-					['version' => 4740000, 'path' => 'keep', 'size' => 1],
237
-					// final slice starts at 2408000 keep one version every 604800 secons
238
-					['version' => 2408000, 'path' => 'keep', 'size' => 1],
239
-					['version' => 1803201, 'path' => 'delete', 'size' => 1],
240
-					['version' => 1803200, 'path' => 'keep', 'size' => 1],
241
-					['version' => 1800199, 'path' => 'delete', 'size' => 1],
242
-					['version' => 1800100, 'path' => 'delete', 'size' => 1],
243
-					['version' => 1198300, 'path' => 'keep', 'size' => 1],
244
-				],
245
-				11 // size of all deleted files (every file has the size 1)
246
-			],
247
-			// third set of versions, with some gaps between
248
-			[
249
-				[
250
-					// first slice (10sec) keep one version every 2 seconds
251
-					['version' => 4999999, 'path' => 'keep', 'size' => 1],
252
-					['version' => 4999998, 'path' => 'delete', 'size' => 1],
253
-					['version' => 4999997, 'path' => 'keep', 'size' => 1],
254
-					['version' => 4999995, 'path' => 'keep', 'size' => 1],
255
-					['version' => 4999994, 'path' => 'delete', 'size' => 1],
256
-					//next slice (60sec) starts at 4999990 keep one version every 10 secons
257
-					['version' => 4999988, 'path' => 'keep', 'size' => 1],
258
-					['version' => 4999978, 'path' => 'keep', 'size' => 1],
259
-					//next slice (3600sec) start at 4999940 keep one version every 60 seconds
260
-					// next slice (86400sec) start at 4996400 keep one version every 3600 seconds
261
-					['version' => 4989200, 'path' => 'keep', 'size' => 1],
262
-					// next slice (2592000sec) start at 4913600 keep one version every 86400 seconds
263
-					['version' => 4913600, 'path' => 'keep', 'size' => 1],
264
-					['version' => 4852800, 'path' => 'delete', 'size' => 1],
265
-					['version' => 4827201, 'path' => 'delete', 'size' => 1],
266
-					['version' => 4827200, 'path' => 'keep', 'size' => 1],
267
-					['version' => 4777201, 'path' => 'delete', 'size' => 1],
268
-					['version' => 4777501, 'path' => 'delete', 'size' => 1],
269
-					['version' => 4740000, 'path' => 'keep', 'size' => 1],
270
-					// final slice starts at 2408000 keep one version every 604800 secons
271
-					['version' => 2408000, 'path' => 'keep', 'size' => 1],
272
-					['version' => 1803201, 'path' => 'delete', 'size' => 1],
273
-					['version' => 1803200, 'path' => 'keep', 'size' => 1],
274
-					['version' => 1800199, 'path' => 'delete', 'size' => 1],
275
-					['version' => 1800100, 'path' => 'delete', 'size' => 1],
276
-					['version' => 1198300, 'path' => 'keep', 'size' => 1],
277
-				],
278
-				9 // size of all deleted files (every file has the size 1)
279
-			],
280
-			// fourth set of versions: empty (see issue #19066)
281
-			[
282
-				[],
283
-				0
284
-			]
285
-
286
-		];
287
-	}
288
-
289
-	public function testRename(): void {
290
-		Filesystem::file_put_contents('test.txt', 'test file');
291
-
292
-		$t1 = time();
293
-		// second version is two weeks older, this way we make sure that no
294
-		// version will be expired
295
-		$t2 = $t1 - 60 * 60 * 24 * 14;
296
-
297
-		// create some versions
298
-		$v1 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t1;
299
-		$v2 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t2;
300
-		$v1Renamed = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t1;
301
-		$v2Renamed = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t2;
302
-
303
-		$this->rootView->file_put_contents($v1, 'version1');
304
-		$this->rootView->file_put_contents($v2, 'version2');
305
-
306
-		// execute rename hook of versions app
307
-		Filesystem::rename('test.txt', 'test2.txt');
308
-
309
-		$this->runCommands();
310
-
311
-		$this->assertFalse($this->rootView->file_exists($v1), 'version 1 of old file does not exist');
312
-		$this->assertFalse($this->rootView->file_exists($v2), 'version 2 of old file does not exist');
313
-
314
-		$this->assertTrue($this->rootView->file_exists($v1Renamed), 'version 1 of renamed file exists');
315
-		$this->assertTrue($this->rootView->file_exists($v2Renamed), 'version 2 of renamed file exists');
316
-	}
317
-
318
-	public function testRenameInSharedFolder(): void {
319
-		Filesystem::mkdir('folder1');
320
-		Filesystem::mkdir('folder1/folder2');
321
-		Filesystem::file_put_contents('folder1/test.txt', 'test file');
322
-
323
-		$t1 = time();
324
-		// second version is two weeks older, this way we make sure that no
325
-		// version will be expired
326
-		$t2 = $t1 - 60 * 60 * 24 * 14;
327
-
328
-		$this->rootView->mkdir(self::USERS_VERSIONS_ROOT . '/folder1');
329
-		// create some versions
330
-		$v1 = self::USERS_VERSIONS_ROOT . '/folder1/test.txt.v' . $t1;
331
-		$v2 = self::USERS_VERSIONS_ROOT . '/folder1/test.txt.v' . $t2;
332
-		$v1Renamed = self::USERS_VERSIONS_ROOT . '/folder1/folder2/test.txt.v' . $t1;
333
-		$v2Renamed = self::USERS_VERSIONS_ROOT . '/folder1/folder2/test.txt.v' . $t2;
334
-
335
-		$this->rootView->file_put_contents($v1, 'version1');
336
-		$this->rootView->file_put_contents($v2, 'version2');
337
-
338
-		$node = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER)->get('folder1');
339
-		$share = Server::get(\OCP\Share\IManager::class)->newShare();
340
-		$share->setNode($node)
341
-			->setShareType(IShare::TYPE_USER)
342
-			->setSharedBy(self::TEST_VERSIONS_USER)
343
-			->setSharedWith(self::TEST_VERSIONS_USER2)
344
-			->setPermissions(Constants::PERMISSION_ALL);
345
-		$share = Server::get(\OCP\Share\IManager::class)->createShare($share);
346
-		Server::get(\OCP\Share\IManager::class)->acceptShare($share, self::TEST_VERSIONS_USER2);
347
-
348
-		self::loginHelper(self::TEST_VERSIONS_USER2);
349
-
350
-		$this->assertTrue(Filesystem::file_exists('folder1/test.txt'));
351
-
352
-		// execute rename hook of versions app
353
-		Filesystem::rename('/folder1/test.txt', '/folder1/folder2/test.txt');
354
-
355
-		$this->runCommands();
356
-
357
-		self::loginHelper(self::TEST_VERSIONS_USER);
358
-
359
-		$this->assertFalse($this->rootView->file_exists($v1), 'version 1 of old file does not exist');
360
-		$this->assertFalse($this->rootView->file_exists($v2), 'version 2 of old file does not exist');
361
-
362
-		$this->assertTrue($this->rootView->file_exists($v1Renamed), 'version 1 of renamed file exists');
363
-		$this->assertTrue($this->rootView->file_exists($v2Renamed), 'version 2 of renamed file exists');
364
-
365
-		Server::get(\OCP\Share\IManager::class)->deleteShare($share);
366
-	}
367
-
368
-	public function testMoveFolder(): void {
369
-		Filesystem::mkdir('folder1');
370
-		Filesystem::mkdir('folder2');
371
-		Filesystem::file_put_contents('folder1/test.txt', 'test file');
372
-
373
-		$t1 = time();
374
-		// second version is two weeks older, this way we make sure that no
375
-		// version will be expired
376
-		$t2 = $t1 - 60 * 60 * 24 * 14;
377
-
378
-		// create some versions
379
-		$this->rootView->mkdir(self::USERS_VERSIONS_ROOT . '/folder1');
380
-		$v1 = self::USERS_VERSIONS_ROOT . '/folder1/test.txt.v' . $t1;
381
-		$v2 = self::USERS_VERSIONS_ROOT . '/folder1/test.txt.v' . $t2;
382
-		$v1Renamed = self::USERS_VERSIONS_ROOT . '/folder2/folder1/test.txt.v' . $t1;
383
-		$v2Renamed = self::USERS_VERSIONS_ROOT . '/folder2/folder1/test.txt.v' . $t2;
39
+    public const TEST_VERSIONS_USER = 'test-versions-user';
40
+    public const TEST_VERSIONS_USER2 = 'test-versions-user2';
41
+    public const USERS_VERSIONS_ROOT = '/test-versions-user/files_versions';
42
+
43
+    /**
44
+     * @var View
45
+     */
46
+    private $rootView;
47
+    /**
48
+     * @var VersionsMapper
49
+     */
50
+    private $versionsMapper;
51
+    /**
52
+     * @var IMimeTypeLoader
53
+     */
54
+    private $mimeTypeLoader;
55
+    private $user1;
56
+    private $user2;
57
+
58
+    public static function setUpBeforeClass(): void {
59
+        parent::setUpBeforeClass();
60
+
61
+        $application = new Application();
62
+
63
+        // create test user
64
+        self::loginHelper(self::TEST_VERSIONS_USER2, true);
65
+        self::loginHelper(self::TEST_VERSIONS_USER, true);
66
+    }
67
+
68
+    public static function tearDownAfterClass(): void {
69
+        // cleanup test user
70
+        $user = Server::get(IUserManager::class)->get(self::TEST_VERSIONS_USER);
71
+        if ($user !== null) {
72
+            $user->delete();
73
+        }
74
+        $user = Server::get(IUserManager::class)->get(self::TEST_VERSIONS_USER2);
75
+        if ($user !== null) {
76
+            $user->delete();
77
+        }
78
+
79
+        parent::tearDownAfterClass();
80
+    }
81
+
82
+    protected function setUp(): void {
83
+        parent::setUp();
84
+
85
+        $config = Server::get(IConfig::class);
86
+        $mockConfig = $this->getMockBuilder(AllConfig::class)
87
+            ->onlyMethods(['getSystemValue'])
88
+            ->setConstructorArgs([Server::get(\OC\SystemConfig::class)])
89
+            ->getMock();
90
+        $mockConfig->expects($this->any())
91
+            ->method('getSystemValue')
92
+            ->willReturnCallback(function ($key, $default) use ($config) {
93
+                if ($key === 'filesystem_check_changes') {
94
+                    return Watcher::CHECK_ONCE;
95
+                } else {
96
+                    return $config->getSystemValue($key, $default);
97
+                }
98
+            });
99
+        $this->overwriteService(AllConfig::class, $mockConfig);
100
+
101
+        // clear hooks
102
+        \OC_Hook::clear();
103
+        \OC::registerShareHooks(Server::get(SystemConfig::class));
104
+        \OC::$server->boot();
105
+
106
+        self::loginHelper(self::TEST_VERSIONS_USER);
107
+        $this->rootView = new View();
108
+        if (!$this->rootView->file_exists(self::USERS_VERSIONS_ROOT)) {
109
+            $this->rootView->mkdir(self::USERS_VERSIONS_ROOT);
110
+        }
111
+
112
+        $this->versionsMapper = Server::get(VersionsMapper::class);
113
+        $this->mimeTypeLoader = Server::get(IMimeTypeLoader::class);
114
+
115
+        $this->user1 = $this->createMock(IUser::class);
116
+        $this->user1->method('getUID')
117
+            ->willReturn(self::TEST_VERSIONS_USER);
118
+        $this->user2 = $this->createMock(IUser::class);
119
+        $this->user2->method('getUID')
120
+            ->willReturn(self::TEST_VERSIONS_USER2);
121
+    }
122
+
123
+    protected function tearDown(): void {
124
+        $this->restoreService(AllConfig::class);
125
+
126
+        if ($this->rootView) {
127
+            $this->rootView->deleteAll(self::TEST_VERSIONS_USER . '/files/');
128
+            $this->rootView->deleteAll(self::TEST_VERSIONS_USER2 . '/files/');
129
+            $this->rootView->deleteAll(self::TEST_VERSIONS_USER . '/files_versions/');
130
+            $this->rootView->deleteAll(self::TEST_VERSIONS_USER2 . '/files_versions/');
131
+        }
132
+
133
+        \OC_Hook::clear();
134
+
135
+        parent::tearDown();
136
+    }
137
+
138
+    /**
139
+     * @medium
140
+     * test expire logic
141
+     * @dataProvider versionsProvider
142
+     */
143
+    public function testGetExpireList($versions, $sizeOfAllDeletedFiles): void {
144
+
145
+        // last interval end at 2592000
146
+        $startTime = 5000000;
147
+
148
+        $testClass = new VersionStorageToTest();
149
+        [$deleted, $size] = $testClass->callProtectedGetExpireList($startTime, $versions);
150
+
151
+        // we should have deleted 16 files each of the size 1
152
+        $this->assertEquals($sizeOfAllDeletedFiles, $size);
153
+
154
+        // the deleted array should only contain versions which should be deleted
155
+        foreach ($deleted as $key => $path) {
156
+            unset($versions[$key]);
157
+            $this->assertEquals('delete', substr($path, 0, strlen('delete')));
158
+        }
159
+
160
+        // the versions array should only contain versions which should be kept
161
+        foreach ($versions as $version) {
162
+            $this->assertEquals('keep', $version['path']);
163
+        }
164
+    }
165
+
166
+    public function versionsProvider() {
167
+        return [
168
+            // first set of versions uniformly distributed versions
169
+            [
170
+                [
171
+                    // first slice (10sec) keep one version every 2 seconds
172
+                    ['version' => 4999999, 'path' => 'keep', 'size' => 1],
173
+                    ['version' => 4999998, 'path' => 'delete', 'size' => 1],
174
+                    ['version' => 4999997, 'path' => 'keep', 'size' => 1],
175
+                    ['version' => 4999995, 'path' => 'keep', 'size' => 1],
176
+                    ['version' => 4999994, 'path' => 'delete', 'size' => 1],
177
+                    //next slice (60sec) starts at 4999990 keep one version every 10 secons
178
+                    ['version' => 4999988, 'path' => 'keep', 'size' => 1],
179
+                    ['version' => 4999978, 'path' => 'keep', 'size' => 1],
180
+                    ['version' => 4999975, 'path' => 'delete', 'size' => 1],
181
+                    ['version' => 4999972, 'path' => 'delete', 'size' => 1],
182
+                    ['version' => 4999967, 'path' => 'keep', 'size' => 1],
183
+                    ['version' => 4999958, 'path' => 'delete', 'size' => 1],
184
+                    ['version' => 4999957, 'path' => 'keep', 'size' => 1],
185
+                    //next slice (3600sec) start at 4999940 keep one version every 60 seconds
186
+                    ['version' => 4999900, 'path' => 'keep', 'size' => 1],
187
+                    ['version' => 4999841, 'path' => 'delete', 'size' => 1],
188
+                    ['version' => 4999840, 'path' => 'keep', 'size' => 1],
189
+                    ['version' => 4999780, 'path' => 'keep', 'size' => 1],
190
+                    ['version' => 4996401, 'path' => 'keep', 'size' => 1],
191
+                    // next slice (86400sec) start at 4996400 keep one version every 3600 seconds
192
+                    ['version' => 4996350, 'path' => 'delete', 'size' => 1],
193
+                    ['version' => 4992800, 'path' => 'keep', 'size' => 1],
194
+                    ['version' => 4989800, 'path' => 'delete', 'size' => 1],
195
+                    ['version' => 4989700, 'path' => 'delete', 'size' => 1],
196
+                    ['version' => 4989200, 'path' => 'keep', 'size' => 1],
197
+                    // next slice (2592000sec) start at 4913600 keep one version every 86400 seconds
198
+                    ['version' => 4913600, 'path' => 'keep', 'size' => 1],
199
+                    ['version' => 4852800, 'path' => 'delete', 'size' => 1],
200
+                    ['version' => 4827201, 'path' => 'delete', 'size' => 1],
201
+                    ['version' => 4827200, 'path' => 'keep', 'size' => 1],
202
+                    ['version' => 4777201, 'path' => 'delete', 'size' => 1],
203
+                    ['version' => 4777501, 'path' => 'delete', 'size' => 1],
204
+                    ['version' => 4740000, 'path' => 'keep', 'size' => 1],
205
+                    // final slice starts at 2408000 keep one version every 604800 secons
206
+                    ['version' => 2408000, 'path' => 'keep', 'size' => 1],
207
+                    ['version' => 1803201, 'path' => 'delete', 'size' => 1],
208
+                    ['version' => 1803200, 'path' => 'keep', 'size' => 1],
209
+                    ['version' => 1800199, 'path' => 'delete', 'size' => 1],
210
+                    ['version' => 1800100, 'path' => 'delete', 'size' => 1],
211
+                    ['version' => 1198300, 'path' => 'keep', 'size' => 1],
212
+                ],
213
+                16 // size of all deleted files (every file has the size 1)
214
+            ],
215
+            // second set of versions, here we have only really old versions
216
+            [
217
+                [
218
+                    // first slice (10sec) keep one version every 2 seconds
219
+                    // next slice (60sec) starts at 4999990 keep one version every 10 secons
220
+                    // next slice (3600sec) start at 4999940 keep one version every 60 seconds
221
+                    // next slice (86400sec) start at 4996400 keep one version every 3600 seconds
222
+                    ['version' => 4996400, 'path' => 'keep', 'size' => 1],
223
+                    ['version' => 4996350, 'path' => 'delete', 'size' => 1],
224
+                    ['version' => 4996350, 'path' => 'delete', 'size' => 1],
225
+                    ['version' => 4992800, 'path' => 'keep', 'size' => 1],
226
+                    ['version' => 4989800, 'path' => 'delete', 'size' => 1],
227
+                    ['version' => 4989700, 'path' => 'delete', 'size' => 1],
228
+                    ['version' => 4989200, 'path' => 'keep', 'size' => 1],
229
+                    // next slice (2592000sec) start at 4913600 keep one version every 86400 seconds
230
+                    ['version' => 4913600, 'path' => 'keep', 'size' => 1],
231
+                    ['version' => 4852800, 'path' => 'delete', 'size' => 1],
232
+                    ['version' => 4827201, 'path' => 'delete', 'size' => 1],
233
+                    ['version' => 4827200, 'path' => 'keep', 'size' => 1],
234
+                    ['version' => 4777201, 'path' => 'delete', 'size' => 1],
235
+                    ['version' => 4777501, 'path' => 'delete', 'size' => 1],
236
+                    ['version' => 4740000, 'path' => 'keep', 'size' => 1],
237
+                    // final slice starts at 2408000 keep one version every 604800 secons
238
+                    ['version' => 2408000, 'path' => 'keep', 'size' => 1],
239
+                    ['version' => 1803201, 'path' => 'delete', 'size' => 1],
240
+                    ['version' => 1803200, 'path' => 'keep', 'size' => 1],
241
+                    ['version' => 1800199, 'path' => 'delete', 'size' => 1],
242
+                    ['version' => 1800100, 'path' => 'delete', 'size' => 1],
243
+                    ['version' => 1198300, 'path' => 'keep', 'size' => 1],
244
+                ],
245
+                11 // size of all deleted files (every file has the size 1)
246
+            ],
247
+            // third set of versions, with some gaps between
248
+            [
249
+                [
250
+                    // first slice (10sec) keep one version every 2 seconds
251
+                    ['version' => 4999999, 'path' => 'keep', 'size' => 1],
252
+                    ['version' => 4999998, 'path' => 'delete', 'size' => 1],
253
+                    ['version' => 4999997, 'path' => 'keep', 'size' => 1],
254
+                    ['version' => 4999995, 'path' => 'keep', 'size' => 1],
255
+                    ['version' => 4999994, 'path' => 'delete', 'size' => 1],
256
+                    //next slice (60sec) starts at 4999990 keep one version every 10 secons
257
+                    ['version' => 4999988, 'path' => 'keep', 'size' => 1],
258
+                    ['version' => 4999978, 'path' => 'keep', 'size' => 1],
259
+                    //next slice (3600sec) start at 4999940 keep one version every 60 seconds
260
+                    // next slice (86400sec) start at 4996400 keep one version every 3600 seconds
261
+                    ['version' => 4989200, 'path' => 'keep', 'size' => 1],
262
+                    // next slice (2592000sec) start at 4913600 keep one version every 86400 seconds
263
+                    ['version' => 4913600, 'path' => 'keep', 'size' => 1],
264
+                    ['version' => 4852800, 'path' => 'delete', 'size' => 1],
265
+                    ['version' => 4827201, 'path' => 'delete', 'size' => 1],
266
+                    ['version' => 4827200, 'path' => 'keep', 'size' => 1],
267
+                    ['version' => 4777201, 'path' => 'delete', 'size' => 1],
268
+                    ['version' => 4777501, 'path' => 'delete', 'size' => 1],
269
+                    ['version' => 4740000, 'path' => 'keep', 'size' => 1],
270
+                    // final slice starts at 2408000 keep one version every 604800 secons
271
+                    ['version' => 2408000, 'path' => 'keep', 'size' => 1],
272
+                    ['version' => 1803201, 'path' => 'delete', 'size' => 1],
273
+                    ['version' => 1803200, 'path' => 'keep', 'size' => 1],
274
+                    ['version' => 1800199, 'path' => 'delete', 'size' => 1],
275
+                    ['version' => 1800100, 'path' => 'delete', 'size' => 1],
276
+                    ['version' => 1198300, 'path' => 'keep', 'size' => 1],
277
+                ],
278
+                9 // size of all deleted files (every file has the size 1)
279
+            ],
280
+            // fourth set of versions: empty (see issue #19066)
281
+            [
282
+                [],
283
+                0
284
+            ]
285
+
286
+        ];
287
+    }
288
+
289
+    public function testRename(): void {
290
+        Filesystem::file_put_contents('test.txt', 'test file');
291
+
292
+        $t1 = time();
293
+        // second version is two weeks older, this way we make sure that no
294
+        // version will be expired
295
+        $t2 = $t1 - 60 * 60 * 24 * 14;
296
+
297
+        // create some versions
298
+        $v1 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t1;
299
+        $v2 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t2;
300
+        $v1Renamed = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t1;
301
+        $v2Renamed = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t2;
302
+
303
+        $this->rootView->file_put_contents($v1, 'version1');
304
+        $this->rootView->file_put_contents($v2, 'version2');
305
+
306
+        // execute rename hook of versions app
307
+        Filesystem::rename('test.txt', 'test2.txt');
308
+
309
+        $this->runCommands();
310
+
311
+        $this->assertFalse($this->rootView->file_exists($v1), 'version 1 of old file does not exist');
312
+        $this->assertFalse($this->rootView->file_exists($v2), 'version 2 of old file does not exist');
313
+
314
+        $this->assertTrue($this->rootView->file_exists($v1Renamed), 'version 1 of renamed file exists');
315
+        $this->assertTrue($this->rootView->file_exists($v2Renamed), 'version 2 of renamed file exists');
316
+    }
317
+
318
+    public function testRenameInSharedFolder(): void {
319
+        Filesystem::mkdir('folder1');
320
+        Filesystem::mkdir('folder1/folder2');
321
+        Filesystem::file_put_contents('folder1/test.txt', 'test file');
322
+
323
+        $t1 = time();
324
+        // second version is two weeks older, this way we make sure that no
325
+        // version will be expired
326
+        $t2 = $t1 - 60 * 60 * 24 * 14;
327
+
328
+        $this->rootView->mkdir(self::USERS_VERSIONS_ROOT . '/folder1');
329
+        // create some versions
330
+        $v1 = self::USERS_VERSIONS_ROOT . '/folder1/test.txt.v' . $t1;
331
+        $v2 = self::USERS_VERSIONS_ROOT . '/folder1/test.txt.v' . $t2;
332
+        $v1Renamed = self::USERS_VERSIONS_ROOT . '/folder1/folder2/test.txt.v' . $t1;
333
+        $v2Renamed = self::USERS_VERSIONS_ROOT . '/folder1/folder2/test.txt.v' . $t2;
334
+
335
+        $this->rootView->file_put_contents($v1, 'version1');
336
+        $this->rootView->file_put_contents($v2, 'version2');
337
+
338
+        $node = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER)->get('folder1');
339
+        $share = Server::get(\OCP\Share\IManager::class)->newShare();
340
+        $share->setNode($node)
341
+            ->setShareType(IShare::TYPE_USER)
342
+            ->setSharedBy(self::TEST_VERSIONS_USER)
343
+            ->setSharedWith(self::TEST_VERSIONS_USER2)
344
+            ->setPermissions(Constants::PERMISSION_ALL);
345
+        $share = Server::get(\OCP\Share\IManager::class)->createShare($share);
346
+        Server::get(\OCP\Share\IManager::class)->acceptShare($share, self::TEST_VERSIONS_USER2);
347
+
348
+        self::loginHelper(self::TEST_VERSIONS_USER2);
349
+
350
+        $this->assertTrue(Filesystem::file_exists('folder1/test.txt'));
351
+
352
+        // execute rename hook of versions app
353
+        Filesystem::rename('/folder1/test.txt', '/folder1/folder2/test.txt');
354
+
355
+        $this->runCommands();
356
+
357
+        self::loginHelper(self::TEST_VERSIONS_USER);
358
+
359
+        $this->assertFalse($this->rootView->file_exists($v1), 'version 1 of old file does not exist');
360
+        $this->assertFalse($this->rootView->file_exists($v2), 'version 2 of old file does not exist');
361
+
362
+        $this->assertTrue($this->rootView->file_exists($v1Renamed), 'version 1 of renamed file exists');
363
+        $this->assertTrue($this->rootView->file_exists($v2Renamed), 'version 2 of renamed file exists');
364
+
365
+        Server::get(\OCP\Share\IManager::class)->deleteShare($share);
366
+    }
367
+
368
+    public function testMoveFolder(): void {
369
+        Filesystem::mkdir('folder1');
370
+        Filesystem::mkdir('folder2');
371
+        Filesystem::file_put_contents('folder1/test.txt', 'test file');
372
+
373
+        $t1 = time();
374
+        // second version is two weeks older, this way we make sure that no
375
+        // version will be expired
376
+        $t2 = $t1 - 60 * 60 * 24 * 14;
377
+
378
+        // create some versions
379
+        $this->rootView->mkdir(self::USERS_VERSIONS_ROOT . '/folder1');
380
+        $v1 = self::USERS_VERSIONS_ROOT . '/folder1/test.txt.v' . $t1;
381
+        $v2 = self::USERS_VERSIONS_ROOT . '/folder1/test.txt.v' . $t2;
382
+        $v1Renamed = self::USERS_VERSIONS_ROOT . '/folder2/folder1/test.txt.v' . $t1;
383
+        $v2Renamed = self::USERS_VERSIONS_ROOT . '/folder2/folder1/test.txt.v' . $t2;
384 384
 
385
-		$this->rootView->file_put_contents($v1, 'version1');
386
-		$this->rootView->file_put_contents($v2, 'version2');
385
+        $this->rootView->file_put_contents($v1, 'version1');
386
+        $this->rootView->file_put_contents($v2, 'version2');
387 387
 
388
-		// execute rename hook of versions app
389
-		Filesystem::rename('folder1', 'folder2/folder1');
388
+        // execute rename hook of versions app
389
+        Filesystem::rename('folder1', 'folder2/folder1');
390 390
 
391
-		$this->runCommands();
391
+        $this->runCommands();
392 392
 
393
-		$this->assertFalse($this->rootView->file_exists($v1));
394
-		$this->assertFalse($this->rootView->file_exists($v2));
393
+        $this->assertFalse($this->rootView->file_exists($v1));
394
+        $this->assertFalse($this->rootView->file_exists($v2));
395 395
 
396
-		$this->assertTrue($this->rootView->file_exists($v1Renamed));
397
-		$this->assertTrue($this->rootView->file_exists($v2Renamed));
398
-	}
396
+        $this->assertTrue($this->rootView->file_exists($v1Renamed));
397
+        $this->assertTrue($this->rootView->file_exists($v2Renamed));
398
+    }
399 399
 
400 400
 
401
-	public function testMoveFileIntoSharedFolderAsRecipient(): void {
402
-		Filesystem::mkdir('folder1');
403
-		$fileInfo = Filesystem::getFileInfo('folder1');
401
+    public function testMoveFileIntoSharedFolderAsRecipient(): void {
402
+        Filesystem::mkdir('folder1');
403
+        $fileInfo = Filesystem::getFileInfo('folder1');
404 404
 
405
-		$node = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER)->get('folder1');
406
-		$share = Server::get(\OCP\Share\IManager::class)->newShare();
407
-		$share->setNode($node)
408
-			->setShareType(IShare::TYPE_USER)
409
-			->setSharedBy(self::TEST_VERSIONS_USER)
410
-			->setSharedWith(self::TEST_VERSIONS_USER2)
411
-			->setPermissions(Constants::PERMISSION_ALL);
412
-		$share = Server::get(\OCP\Share\IManager::class)->createShare($share);
413
-		Server::get(\OCP\Share\IManager::class)->acceptShare($share, self::TEST_VERSIONS_USER2);
405
+        $node = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER)->get('folder1');
406
+        $share = Server::get(\OCP\Share\IManager::class)->newShare();
407
+        $share->setNode($node)
408
+            ->setShareType(IShare::TYPE_USER)
409
+            ->setSharedBy(self::TEST_VERSIONS_USER)
410
+            ->setSharedWith(self::TEST_VERSIONS_USER2)
411
+            ->setPermissions(Constants::PERMISSION_ALL);
412
+        $share = Server::get(\OCP\Share\IManager::class)->createShare($share);
413
+        Server::get(\OCP\Share\IManager::class)->acceptShare($share, self::TEST_VERSIONS_USER2);
414 414
 
415
-		self::loginHelper(self::TEST_VERSIONS_USER2);
416
-		$versionsFolder2 = '/' . self::TEST_VERSIONS_USER2 . '/files_versions';
417
-		Filesystem::file_put_contents('test.txt', 'test file');
415
+        self::loginHelper(self::TEST_VERSIONS_USER2);
416
+        $versionsFolder2 = '/' . self::TEST_VERSIONS_USER2 . '/files_versions';
417
+        Filesystem::file_put_contents('test.txt', 'test file');
418 418
 
419
-		$t1 = time();
420
-		// second version is two weeks older, this way we make sure that no
421
-		// version will be expired
422
-		$t2 = $t1 - 60 * 60 * 24 * 14;
419
+        $t1 = time();
420
+        // second version is two weeks older, this way we make sure that no
421
+        // version will be expired
422
+        $t2 = $t1 - 60 * 60 * 24 * 14;
423 423
 
424
-		$this->rootView->mkdir($versionsFolder2);
425
-		// create some versions
426
-		$v1 = $versionsFolder2 . '/test.txt.v' . $t1;
427
-		$v2 = $versionsFolder2 . '/test.txt.v' . $t2;
424
+        $this->rootView->mkdir($versionsFolder2);
425
+        // create some versions
426
+        $v1 = $versionsFolder2 . '/test.txt.v' . $t1;
427
+        $v2 = $versionsFolder2 . '/test.txt.v' . $t2;
428 428
 
429
-		$this->rootView->file_put_contents($v1, 'version1');
430
-		$this->rootView->file_put_contents($v2, 'version2');
429
+        $this->rootView->file_put_contents($v1, 'version1');
430
+        $this->rootView->file_put_contents($v2, 'version2');
431 431
 
432
-		// move file into the shared folder as recipient
433
-		$success = Filesystem::rename('/test.txt', '/folder1/test.txt');
432
+        // move file into the shared folder as recipient
433
+        $success = Filesystem::rename('/test.txt', '/folder1/test.txt');
434 434
 
435
-		$this->assertTrue($success);
436
-		$this->assertFalse($this->rootView->file_exists($v1));
437
-		$this->assertFalse($this->rootView->file_exists($v2));
435
+        $this->assertTrue($success);
436
+        $this->assertFalse($this->rootView->file_exists($v1));
437
+        $this->assertFalse($this->rootView->file_exists($v2));
438 438
 
439
-		self::loginHelper(self::TEST_VERSIONS_USER);
439
+        self::loginHelper(self::TEST_VERSIONS_USER);
440 440
 
441
-		$versionsFolder1 = '/' . self::TEST_VERSIONS_USER . '/files_versions';
441
+        $versionsFolder1 = '/' . self::TEST_VERSIONS_USER . '/files_versions';
442 442
 
443
-		$v1Renamed = $versionsFolder1 . '/folder1/test.txt.v' . $t1;
444
-		$v2Renamed = $versionsFolder1 . '/folder1/test.txt.v' . $t2;
443
+        $v1Renamed = $versionsFolder1 . '/folder1/test.txt.v' . $t1;
444
+        $v2Renamed = $versionsFolder1 . '/folder1/test.txt.v' . $t2;
445 445
 
446
-		$this->assertTrue($this->rootView->file_exists($v1Renamed));
447
-		$this->assertTrue($this->rootView->file_exists($v2Renamed));
446
+        $this->assertTrue($this->rootView->file_exists($v1Renamed));
447
+        $this->assertTrue($this->rootView->file_exists($v2Renamed));
448 448
 
449
-		Server::get(\OCP\Share\IManager::class)->deleteShare($share);
450
-	}
449
+        Server::get(\OCP\Share\IManager::class)->deleteShare($share);
450
+    }
451 451
 
452
-	public function testMoveFolderIntoSharedFolderAsRecipient(): void {
453
-		Filesystem::mkdir('folder1');
452
+    public function testMoveFolderIntoSharedFolderAsRecipient(): void {
453
+        Filesystem::mkdir('folder1');
454 454
 
455
-		$node = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER)->get('folder1');
456
-		$share = Server::get(\OCP\Share\IManager::class)->newShare();
457
-		$share->setNode($node)
458
-			->setShareType(IShare::TYPE_USER)
459
-			->setSharedBy(self::TEST_VERSIONS_USER)
460
-			->setSharedWith(self::TEST_VERSIONS_USER2)
461
-			->setPermissions(Constants::PERMISSION_ALL);
462
-		$share = Server::get(\OCP\Share\IManager::class)->createShare($share);
463
-		Server::get(\OCP\Share\IManager::class)->acceptShare($share, self::TEST_VERSIONS_USER2);
455
+        $node = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER)->get('folder1');
456
+        $share = Server::get(\OCP\Share\IManager::class)->newShare();
457
+        $share->setNode($node)
458
+            ->setShareType(IShare::TYPE_USER)
459
+            ->setSharedBy(self::TEST_VERSIONS_USER)
460
+            ->setSharedWith(self::TEST_VERSIONS_USER2)
461
+            ->setPermissions(Constants::PERMISSION_ALL);
462
+        $share = Server::get(\OCP\Share\IManager::class)->createShare($share);
463
+        Server::get(\OCP\Share\IManager::class)->acceptShare($share, self::TEST_VERSIONS_USER2);
464 464
 
465
-		self::loginHelper(self::TEST_VERSIONS_USER2);
466
-		$versionsFolder2 = '/' . self::TEST_VERSIONS_USER2 . '/files_versions';
467
-		Filesystem::mkdir('folder2');
468
-		Filesystem::file_put_contents('folder2/test.txt', 'test file');
465
+        self::loginHelper(self::TEST_VERSIONS_USER2);
466
+        $versionsFolder2 = '/' . self::TEST_VERSIONS_USER2 . '/files_versions';
467
+        Filesystem::mkdir('folder2');
468
+        Filesystem::file_put_contents('folder2/test.txt', 'test file');
469 469
 
470
-		$t1 = time();
471
-		// second version is two weeks older, this way we make sure that no
472
-		// version will be expired
473
-		$t2 = $t1 - 60 * 60 * 24 * 14;
470
+        $t1 = time();
471
+        // second version is two weeks older, this way we make sure that no
472
+        // version will be expired
473
+        $t2 = $t1 - 60 * 60 * 24 * 14;
474 474
 
475
-		$this->rootView->mkdir($versionsFolder2);
476
-		$this->rootView->mkdir($versionsFolder2 . '/folder2');
477
-		// create some versions
478
-		$v1 = $versionsFolder2 . '/folder2/test.txt.v' . $t1;
479
-		$v2 = $versionsFolder2 . '/folder2/test.txt.v' . $t2;
475
+        $this->rootView->mkdir($versionsFolder2);
476
+        $this->rootView->mkdir($versionsFolder2 . '/folder2');
477
+        // create some versions
478
+        $v1 = $versionsFolder2 . '/folder2/test.txt.v' . $t1;
479
+        $v2 = $versionsFolder2 . '/folder2/test.txt.v' . $t2;
480 480
 
481
-		$this->rootView->file_put_contents($v1, 'version1');
482
-		$this->rootView->file_put_contents($v2, 'version2');
481
+        $this->rootView->file_put_contents($v1, 'version1');
482
+        $this->rootView->file_put_contents($v2, 'version2');
483 483
 
484
-		// move file into the shared folder as recipient
485
-		Filesystem::rename('/folder2', '/folder1/folder2');
484
+        // move file into the shared folder as recipient
485
+        Filesystem::rename('/folder2', '/folder1/folder2');
486 486
 
487
-		$this->assertFalse($this->rootView->file_exists($v1));
488
-		$this->assertFalse($this->rootView->file_exists($v2));
487
+        $this->assertFalse($this->rootView->file_exists($v1));
488
+        $this->assertFalse($this->rootView->file_exists($v2));
489 489
 
490
-		self::loginHelper(self::TEST_VERSIONS_USER);
490
+        self::loginHelper(self::TEST_VERSIONS_USER);
491 491
 
492
-		$versionsFolder1 = '/' . self::TEST_VERSIONS_USER . '/files_versions';
492
+        $versionsFolder1 = '/' . self::TEST_VERSIONS_USER . '/files_versions';
493 493
 
494
-		$v1Renamed = $versionsFolder1 . '/folder1/folder2/test.txt.v' . $t1;
495
-		$v2Renamed = $versionsFolder1 . '/folder1/folder2/test.txt.v' . $t2;
494
+        $v1Renamed = $versionsFolder1 . '/folder1/folder2/test.txt.v' . $t1;
495
+        $v2Renamed = $versionsFolder1 . '/folder1/folder2/test.txt.v' . $t2;
496 496
 
497
-		$this->assertTrue($this->rootView->file_exists($v1Renamed));
498
-		$this->assertTrue($this->rootView->file_exists($v2Renamed));
497
+        $this->assertTrue($this->rootView->file_exists($v1Renamed));
498
+        $this->assertTrue($this->rootView->file_exists($v2Renamed));
499 499
 
500
-		Server::get(\OCP\Share\IManager::class)->deleteShare($share);
501
-	}
500
+        Server::get(\OCP\Share\IManager::class)->deleteShare($share);
501
+    }
502 502
 
503
-	public function testRenameSharedFile(): void {
504
-		Filesystem::file_put_contents('test.txt', 'test file');
503
+    public function testRenameSharedFile(): void {
504
+        Filesystem::file_put_contents('test.txt', 'test file');
505 505
 
506
-		$t1 = time();
507
-		// second version is two weeks older, this way we make sure that no
508
-		// version will be expired
509
-		$t2 = $t1 - 60 * 60 * 24 * 14;
506
+        $t1 = time();
507
+        // second version is two weeks older, this way we make sure that no
508
+        // version will be expired
509
+        $t2 = $t1 - 60 * 60 * 24 * 14;
510 510
 
511
-		$this->rootView->mkdir(self::USERS_VERSIONS_ROOT);
512
-		// create some versions
513
-		$v1 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t1;
514
-		$v2 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t2;
515
-		// the renamed versions should not exist! Because we only moved the mount point!
516
-		$v1Renamed = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t1;
517
-		$v2Renamed = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t2;
511
+        $this->rootView->mkdir(self::USERS_VERSIONS_ROOT);
512
+        // create some versions
513
+        $v1 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t1;
514
+        $v2 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t2;
515
+        // the renamed versions should not exist! Because we only moved the mount point!
516
+        $v1Renamed = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t1;
517
+        $v2Renamed = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t2;
518 518
 
519
-		$this->rootView->file_put_contents($v1, 'version1');
520
-		$this->rootView->file_put_contents($v2, 'version2');
519
+        $this->rootView->file_put_contents($v1, 'version1');
520
+        $this->rootView->file_put_contents($v2, 'version2');
521 521
 
522
-		$node = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER)->get('test.txt');
523
-		$share = Server::get(\OCP\Share\IManager::class)->newShare();
524
-		$share->setNode($node)
525
-			->setShareType(IShare::TYPE_USER)
526
-			->setSharedBy(self::TEST_VERSIONS_USER)
527
-			->setSharedWith(self::TEST_VERSIONS_USER2)
528
-			->setPermissions(Constants::PERMISSION_READ | Constants::PERMISSION_UPDATE | Constants::PERMISSION_SHARE);
529
-		$share = Server::get(\OCP\Share\IManager::class)->createShare($share);
530
-		Server::get(\OCP\Share\IManager::class)->acceptShare($share, self::TEST_VERSIONS_USER2);
522
+        $node = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER)->get('test.txt');
523
+        $share = Server::get(\OCP\Share\IManager::class)->newShare();
524
+        $share->setNode($node)
525
+            ->setShareType(IShare::TYPE_USER)
526
+            ->setSharedBy(self::TEST_VERSIONS_USER)
527
+            ->setSharedWith(self::TEST_VERSIONS_USER2)
528
+            ->setPermissions(Constants::PERMISSION_READ | Constants::PERMISSION_UPDATE | Constants::PERMISSION_SHARE);
529
+        $share = Server::get(\OCP\Share\IManager::class)->createShare($share);
530
+        Server::get(\OCP\Share\IManager::class)->acceptShare($share, self::TEST_VERSIONS_USER2);
531 531
 
532
-		self::loginHelper(self::TEST_VERSIONS_USER2);
532
+        self::loginHelper(self::TEST_VERSIONS_USER2);
533 533
 
534
-		$this->assertTrue(Filesystem::file_exists('test.txt'));
534
+        $this->assertTrue(Filesystem::file_exists('test.txt'));
535 535
 
536
-		// execute rename hook of versions app
537
-		Filesystem::rename('test.txt', 'test2.txt');
536
+        // execute rename hook of versions app
537
+        Filesystem::rename('test.txt', 'test2.txt');
538 538
 
539
-		self::loginHelper(self::TEST_VERSIONS_USER);
539
+        self::loginHelper(self::TEST_VERSIONS_USER);
540 540
 
541
-		$this->runCommands();
541
+        $this->runCommands();
542 542
 
543
-		$this->assertTrue($this->rootView->file_exists($v1));
544
-		$this->assertTrue($this->rootView->file_exists($v2));
543
+        $this->assertTrue($this->rootView->file_exists($v1));
544
+        $this->assertTrue($this->rootView->file_exists($v2));
545 545
 
546
-		$this->assertFalse($this->rootView->file_exists($v1Renamed));
547
-		$this->assertFalse($this->rootView->file_exists($v2Renamed));
546
+        $this->assertFalse($this->rootView->file_exists($v1Renamed));
547
+        $this->assertFalse($this->rootView->file_exists($v2Renamed));
548 548
 
549
-		Server::get(\OCP\Share\IManager::class)->deleteShare($share);
550
-	}
549
+        Server::get(\OCP\Share\IManager::class)->deleteShare($share);
550
+    }
551 551
 
552
-	public function testCopy(): void {
553
-		Filesystem::file_put_contents('test.txt', 'test file');
552
+    public function testCopy(): void {
553
+        Filesystem::file_put_contents('test.txt', 'test file');
554 554
 
555
-		$t1 = time();
556
-		// second version is two weeks older, this way we make sure that no
557
-		// version will be expired
558
-		$t2 = $t1 - 60 * 60 * 24 * 14;
555
+        $t1 = time();
556
+        // second version is two weeks older, this way we make sure that no
557
+        // version will be expired
558
+        $t2 = $t1 - 60 * 60 * 24 * 14;
559 559
 
560
-		// create some versions
561
-		$v1 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t1;
562
-		$v2 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t2;
563
-		$v1Copied = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t1;
564
-		$v2Copied = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t2;
560
+        // create some versions
561
+        $v1 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t1;
562
+        $v2 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t2;
563
+        $v1Copied = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t1;
564
+        $v2Copied = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t2;
565 565
 
566
-		$this->rootView->file_put_contents($v1, 'version1');
567
-		$this->rootView->file_put_contents($v2, 'version2');
566
+        $this->rootView->file_put_contents($v1, 'version1');
567
+        $this->rootView->file_put_contents($v2, 'version2');
568 568
 
569
-		// execute copy hook of versions app
570
-		Filesystem::copy('test.txt', 'test2.txt');
569
+        // execute copy hook of versions app
570
+        Filesystem::copy('test.txt', 'test2.txt');
571 571
 
572
-		$this->runCommands();
572
+        $this->runCommands();
573 573
 
574
-		$this->assertTrue($this->rootView->file_exists($v1), 'version 1 of original file exists');
575
-		$this->assertTrue($this->rootView->file_exists($v2), 'version 2 of original file exists');
574
+        $this->assertTrue($this->rootView->file_exists($v1), 'version 1 of original file exists');
575
+        $this->assertTrue($this->rootView->file_exists($v2), 'version 2 of original file exists');
576 576
 
577
-		$this->assertTrue($this->rootView->file_exists($v1Copied), 'version 1 of copied file exists');
578
-		$this->assertTrue($this->rootView->file_exists($v2Copied), 'version 2 of copied file exists');
579
-	}
577
+        $this->assertTrue($this->rootView->file_exists($v1Copied), 'version 1 of copied file exists');
578
+        $this->assertTrue($this->rootView->file_exists($v2Copied), 'version 2 of copied file exists');
579
+    }
580 580
 
581
-	/**
582
-	 * test if we find all versions and if the versions array contain
583
-	 * the correct 'path' and 'name'
584
-	 */
585
-	public function testGetVersions(): void {
586
-		$t1 = time();
587
-		// second version is two weeks older, this way we make sure that no
588
-		// version will be expired
589
-		$t2 = $t1 - 60 * 60 * 24 * 14;
581
+    /**
582
+     * test if we find all versions and if the versions array contain
583
+     * the correct 'path' and 'name'
584
+     */
585
+    public function testGetVersions(): void {
586
+        $t1 = time();
587
+        // second version is two weeks older, this way we make sure that no
588
+        // version will be expired
589
+        $t2 = $t1 - 60 * 60 * 24 * 14;
590 590
 
591
-		// create some versions
592
-		$v1 = self::USERS_VERSIONS_ROOT . '/subfolder/test.txt.v' . $t1;
593
-		$v2 = self::USERS_VERSIONS_ROOT . '/subfolder/test.txt.v' . $t2;
591
+        // create some versions
592
+        $v1 = self::USERS_VERSIONS_ROOT . '/subfolder/test.txt.v' . $t1;
593
+        $v2 = self::USERS_VERSIONS_ROOT . '/subfolder/test.txt.v' . $t2;
594 594
 
595
-		$this->rootView->mkdir(self::USERS_VERSIONS_ROOT . '/subfolder/');
595
+        $this->rootView->mkdir(self::USERS_VERSIONS_ROOT . '/subfolder/');
596 596
 
597
-		$this->rootView->file_put_contents($v1, 'version1');
598
-		$this->rootView->file_put_contents($v2, 'version2');
597
+        $this->rootView->file_put_contents($v1, 'version1');
598
+        $this->rootView->file_put_contents($v2, 'version2');
599 599
 
600
-		// execute copy hook of versions app
601
-		$versions = Storage::getVersions(self::TEST_VERSIONS_USER, '/subfolder/test.txt');
600
+        // execute copy hook of versions app
601
+        $versions = Storage::getVersions(self::TEST_VERSIONS_USER, '/subfolder/test.txt');
602 602
 
603
-		$this->assertCount(2, $versions);
603
+        $this->assertCount(2, $versions);
604 604
 
605
-		foreach ($versions as $version) {
606
-			$this->assertSame('/subfolder/test.txt', $version['path']);
607
-			$this->assertSame('test.txt', $version['name']);
608
-		}
605
+        foreach ($versions as $version) {
606
+            $this->assertSame('/subfolder/test.txt', $version['path']);
607
+            $this->assertSame('test.txt', $version['name']);
608
+        }
609 609
 
610
-		//cleanup
611
-		$this->rootView->deleteAll(self::USERS_VERSIONS_ROOT . '/subfolder');
612
-	}
610
+        //cleanup
611
+        $this->rootView->deleteAll(self::USERS_VERSIONS_ROOT . '/subfolder');
612
+    }
613 613
 
614
-	/**
615
-	 * test if we find all versions and if the versions array contain
616
-	 * the correct 'path' and 'name'
617
-	 */
618
-	public function testGetVersionsEmptyFile(): void {
619
-		// execute copy hook of versions app
620
-		$versions = Storage::getVersions(self::TEST_VERSIONS_USER, '');
621
-		$this->assertCount(0, $versions);
614
+    /**
615
+     * test if we find all versions and if the versions array contain
616
+     * the correct 'path' and 'name'
617
+     */
618
+    public function testGetVersionsEmptyFile(): void {
619
+        // execute copy hook of versions app
620
+        $versions = Storage::getVersions(self::TEST_VERSIONS_USER, '');
621
+        $this->assertCount(0, $versions);
622 622
 
623
-		$versions = Storage::getVersions(self::TEST_VERSIONS_USER, null);
624
-		$this->assertCount(0, $versions);
625
-	}
623
+        $versions = Storage::getVersions(self::TEST_VERSIONS_USER, null);
624
+        $this->assertCount(0, $versions);
625
+    }
626 626
 
627
-	public function testExpireNonexistingFile(): void {
628
-		$this->logout();
629
-		// needed to have a FS setup (the background job does this)
630
-		\OC_Util::setupFS(self::TEST_VERSIONS_USER);
627
+    public function testExpireNonexistingFile(): void {
628
+        $this->logout();
629
+        // needed to have a FS setup (the background job does this)
630
+        \OC_Util::setupFS(self::TEST_VERSIONS_USER);
631 631
 
632
-		$this->assertFalse(Storage::expire('/void/unexist.txt', self::TEST_VERSIONS_USER));
633
-	}
632
+        $this->assertFalse(Storage::expire('/void/unexist.txt', self::TEST_VERSIONS_USER));
633
+    }
634 634
 
635 635
 
636
-	public function testExpireNonexistingUser(): void {
637
-		$this->expectException(NoUserException::class);
636
+    public function testExpireNonexistingUser(): void {
637
+        $this->expectException(NoUserException::class);
638 638
 
639
-		$this->logout();
640
-		// needed to have a FS setup (the background job does this)
641
-		\OC_Util::setupFS(self::TEST_VERSIONS_USER);
642
-		Filesystem::file_put_contents('test.txt', 'test file');
639
+        $this->logout();
640
+        // needed to have a FS setup (the background job does this)
641
+        \OC_Util::setupFS(self::TEST_VERSIONS_USER);
642
+        Filesystem::file_put_contents('test.txt', 'test file');
643 643
 
644
-		$this->assertFalse(Storage::expire('test.txt', 'unexist'));
645
-	}
644
+        $this->assertFalse(Storage::expire('test.txt', 'unexist'));
645
+    }
646 646
 
647
-	public function testRestoreSameStorage(): void {
648
-		Filesystem::mkdir('sub');
649
-		$this->doTestRestore();
650
-	}
647
+    public function testRestoreSameStorage(): void {
648
+        Filesystem::mkdir('sub');
649
+        $this->doTestRestore();
650
+    }
651 651
 
652
-	public function testRestoreCrossStorage(): void {
653
-		$storage2 = new Temporary([]);
654
-		Filesystem::mount($storage2, [], self::TEST_VERSIONS_USER . '/files/sub');
652
+    public function testRestoreCrossStorage(): void {
653
+        $storage2 = new Temporary([]);
654
+        Filesystem::mount($storage2, [], self::TEST_VERSIONS_USER . '/files/sub');
655 655
 
656
-		$this->doTestRestore();
657
-	}
656
+        $this->doTestRestore();
657
+    }
658 658
 
659
-	public function testRestoreNoPermission(): void {
660
-		$this->loginAsUser(self::TEST_VERSIONS_USER);
659
+    public function testRestoreNoPermission(): void {
660
+        $this->loginAsUser(self::TEST_VERSIONS_USER);
661 661
 
662
-		$userHome = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER);
663
-		$node = $userHome->newFolder('folder');
664
-		$file = $node->newFile('test.txt');
662
+        $userHome = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER);
663
+        $node = $userHome->newFolder('folder');
664
+        $file = $node->newFile('test.txt');
665 665
 
666
-		$share = Server::get(\OCP\Share\IManager::class)->newShare();
667
-		$share->setNode($node)
668
-			->setShareType(IShare::TYPE_USER)
669
-			->setSharedBy(self::TEST_VERSIONS_USER)
670
-			->setSharedWith(self::TEST_VERSIONS_USER2)
671
-			->setPermissions(Constants::PERMISSION_READ);
672
-		$share = Server::get(\OCP\Share\IManager::class)->createShare($share);
673
-		Server::get(\OCP\Share\IManager::class)->acceptShare($share, self::TEST_VERSIONS_USER2);
674
-
675
-		$versions = $this->createAndCheckVersions(
676
-			Filesystem::getView(),
677
-			'folder/test.txt'
678
-		);
679
-
680
-		$file->putContent('test file');
681
-
682
-		$this->loginAsUser(self::TEST_VERSIONS_USER2);
666
+        $share = Server::get(\OCP\Share\IManager::class)->newShare();
667
+        $share->setNode($node)
668
+            ->setShareType(IShare::TYPE_USER)
669
+            ->setSharedBy(self::TEST_VERSIONS_USER)
670
+            ->setSharedWith(self::TEST_VERSIONS_USER2)
671
+            ->setPermissions(Constants::PERMISSION_READ);
672
+        $share = Server::get(\OCP\Share\IManager::class)->createShare($share);
673
+        Server::get(\OCP\Share\IManager::class)->acceptShare($share, self::TEST_VERSIONS_USER2);
674
+
675
+        $versions = $this->createAndCheckVersions(
676
+            Filesystem::getView(),
677
+            'folder/test.txt'
678
+        );
679
+
680
+        $file->putContent('test file');
681
+
682
+        $this->loginAsUser(self::TEST_VERSIONS_USER2);
683 683
 
684
-		$firstVersion = current($versions);
684
+        $firstVersion = current($versions);
685 685
 
686
-		$this->assertFalse(Storage::rollback('folder/test.txt', $firstVersion['version'], $this->user2), 'Revert did not happen');
686
+        $this->assertFalse(Storage::rollback('folder/test.txt', $firstVersion['version'], $this->user2), 'Revert did not happen');
687 687
 
688
-		$this->loginAsUser(self::TEST_VERSIONS_USER);
688
+        $this->loginAsUser(self::TEST_VERSIONS_USER);
689 689
 
690
-		Server::get(\OCP\Share\IManager::class)->deleteShare($share);
691
-		$this->assertEquals('test file', $file->getContent(), 'File content has not changed');
692
-	}
690
+        Server::get(\OCP\Share\IManager::class)->deleteShare($share);
691
+        $this->assertEquals('test file', $file->getContent(), 'File content has not changed');
692
+    }
693 693
 
694
-	public function testRestoreMovedShare(): void {
695
-		$this->markTestSkipped('Unreliable test');
696
-		$this->loginAsUser(self::TEST_VERSIONS_USER);
697
-
698
-		$userHome = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER);
699
-		$node = $userHome->newFolder('folder');
700
-		$file = $node->newFile('test.txt');
701
-
702
-		$userHome2 = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER2);
703
-		$userHome2->newFolder('subfolder');
704
-
705
-		$share = Server::get(\OCP\Share\IManager::class)->newShare();
706
-		$share->setNode($node)
707
-			->setShareType(IShare::TYPE_USER)
708
-			->setSharedBy(self::TEST_VERSIONS_USER)
709
-			->setSharedWith(self::TEST_VERSIONS_USER2)
710
-			->setPermissions(Constants::PERMISSION_ALL);
711
-		$share = Server::get(\OCP\Share\IManager::class)->createShare($share);
712
-		$shareManager = Server::get(\OCP\Share\IManager::class);
713
-		$shareManager->acceptShare($share, self::TEST_VERSIONS_USER2);
714
-
715
-		$share->setTarget('subfolder/folder');
716
-		$shareManager->moveShare($share, self::TEST_VERSIONS_USER2);
717
-
718
-		$versions = $this->createAndCheckVersions(
719
-			Filesystem::getView(),
720
-			'folder/test.txt'
721
-		);
722
-
723
-		$file->putContent('test file');
724
-
725
-		$this->loginAsUser(self::TEST_VERSIONS_USER2);
726
-
727
-		$firstVersion = current($versions);
728
-
729
-		$this->assertTrue(Storage::rollback('folder/test.txt', $firstVersion['version'], $this->user1));
730
-
731
-		$this->loginAsUser(self::TEST_VERSIONS_USER);
732
-
733
-		Server::get(\OCP\Share\IManager::class)->deleteShare($share);
734
-		$this->assertEquals('version 2', $file->getContent(), 'File content has not changed');
735
-	}
736
-
737
-	/**
738
-	 * @param string $hookName name of hook called
739
-	 * @param string $params variable to receive parameters provided by hook
740
-	 */
741
-	private function connectMockHooks($hookName, &$params) {
742
-		if ($hookName === null) {
743
-			return;
744
-		}
745
-
746
-		$eventHandler = $this->getMockBuilder(\stdclass::class)
747
-			->setMethods(['callback'])
748
-			->getMock();
749
-
750
-		$eventHandler->expects($this->any())
751
-			->method('callback')
752
-			->willReturnCallback(
753
-				function ($p) use (&$params): void {
754
-					$params = $p;
755
-				}
756
-			);
757
-
758
-		Util::connectHook(
759
-			'\OCP\Versions',
760
-			$hookName,
761
-			$eventHandler,
762
-			'callback'
763
-		);
764
-	}
765
-
766
-	private function doTestRestore() {
767
-		$filePath = self::TEST_VERSIONS_USER . '/files/sub/test.txt';
768
-		$this->rootView->file_put_contents($filePath, 'test file');
769
-
770
-		$fileInfo = $this->rootView->getFileInfo($filePath);
771
-		$t0 = $this->rootView->filemtime($filePath);
772
-
773
-		// not exactly the same timestamp as the file
774
-		$t1 = time() - 60;
775
-		// second version is two weeks older
776
-		$t2 = $t1 - 60 * 60 * 24 * 14;
777
-
778
-		// create some versions
779
-		$v1 = self::USERS_VERSIONS_ROOT . '/sub/test.txt.v' . $t1;
780
-		$v2 = self::USERS_VERSIONS_ROOT . '/sub/test.txt.v' . $t2;
781
-
782
-		$this->rootView->mkdir(self::USERS_VERSIONS_ROOT . '/sub');
783
-
784
-		$this->rootView->file_put_contents($v1, 'version1');
785
-		$fileInfoV1 = $this->rootView->getFileInfo($v1);
786
-		$versionEntity = new VersionEntity();
787
-		$versionEntity->setFileId($fileInfo->getId());
788
-		$versionEntity->setTimestamp($t1);
789
-		$versionEntity->setSize($fileInfoV1->getSize());
790
-		$versionEntity->setMimetype($this->mimeTypeLoader->getId($fileInfoV1->getMimetype()));
791
-		$versionEntity->setMetadata([]);
792
-		$this->versionsMapper->insert($versionEntity);
793
-
794
-		$this->rootView->file_put_contents($v2, 'version2');
795
-		$fileInfoV2 = $this->rootView->getFileInfo($v2);
796
-		$versionEntity = new VersionEntity();
797
-		$versionEntity->setFileId($fileInfo->getId());
798
-		$versionEntity->setTimestamp($t2);
799
-		$versionEntity->setSize($fileInfoV2->getSize());
800
-		$versionEntity->setMimetype($this->mimeTypeLoader->getId($fileInfoV2->getMimetype()));
801
-		$versionEntity->setMetadata([]);
802
-		$this->versionsMapper->insert($versionEntity);
803
-
804
-		$oldVersions = Storage::getVersions(
805
-			self::TEST_VERSIONS_USER, '/sub/test.txt'
806
-		);
807
-
808
-		$this->assertCount(2, $oldVersions);
809
-
810
-		$this->assertEquals('test file', $this->rootView->file_get_contents($filePath));
811
-		$info1 = $this->rootView->getFileInfo($filePath);
812
-
813
-		$eventDispatcher = Server::get(IEventDispatcher::class);
814
-		$eventFired = false;
815
-		$eventDispatcher->addListener(VersionRestoredEvent::class, function ($event) use (&$eventFired, $t2): void {
816
-			$eventFired = true;
817
-			$this->assertEquals('/sub/test.txt', $event->getVersion()->getVersionPath());
818
-			$this->assertTrue($event->getVersion()->getRevisionId() > 0);
819
-		});
820
-
821
-		$versionManager = Server::get(IVersionManager::class);
822
-		$versions = $versionManager->getVersionsForFile($this->user1, $info1);
823
-		$version = array_filter($versions, function ($version) use ($t2) {
824
-			return $version->getRevisionId() === $t2;
825
-		});
826
-		$this->assertTrue($versionManager->rollback(current($version)));
827
-
828
-		$this->assertTrue($eventFired, 'VersionRestoredEvent was not fired');
829
-
830
-		$this->assertEquals('version2', $this->rootView->file_get_contents($filePath));
831
-		$info2 = $this->rootView->getFileInfo($filePath);
832
-
833
-		$this->assertNotEquals(
834
-			$info2['etag'],
835
-			$info1['etag'],
836
-			'Etag must change after rolling back version'
837
-		);
838
-		$this->assertEquals(
839
-			$info2['fileid'],
840
-			$info1['fileid'],
841
-			'File id must not change after rolling back version'
842
-		);
843
-		$this->assertEquals(
844
-			$info2['mtime'],
845
-			$t2,
846
-			'Restored file has mtime from version'
847
-		);
848
-
849
-		$newVersions = Storage::getVersions(
850
-			self::TEST_VERSIONS_USER, '/sub/test.txt'
851
-		);
852
-
853
-		$this->assertTrue(
854
-			$this->rootView->file_exists(self::USERS_VERSIONS_ROOT . '/sub/test.txt.v' . $t0),
855
-			'A version file was created for the file before restoration'
856
-		);
857
-		$this->assertTrue(
858
-			$this->rootView->file_exists($v1),
859
-			'Untouched version file is still there'
860
-		);
861
-		$this->assertFalse(
862
-			$this->rootView->file_exists($v2),
863
-			'Restored version file gone from files_version folder'
864
-		);
865
-
866
-		$this->assertCount(2, $newVersions, 'Additional version created');
867
-
868
-		$this->assertTrue(
869
-			isset($newVersions[$t0 . '#' . 'test.txt']),
870
-			'A version was created for the file before restoration'
871
-		);
872
-		$this->assertTrue(
873
-			isset($newVersions[$t1 . '#' . 'test.txt']),
874
-			'Untouched version is still there'
875
-		);
876
-		$this->assertFalse(
877
-			isset($newVersions[$t2 . '#' . 'test.txt']),
878
-			'Restored version is not in the list any more'
879
-		);
880
-	}
881
-
882
-	/**
883
-	 * Test whether versions are created when overwriting as owner
884
-	 */
885
-	public function testStoreVersionAsOwner(): void {
886
-		$this->loginAsUser(self::TEST_VERSIONS_USER);
887
-
888
-		$this->createAndCheckVersions(
889
-			Filesystem::getView(),
890
-			'test.txt'
891
-		);
892
-	}
893
-
894
-	/**
895
-	 * Test whether versions are created when overwriting as share recipient
896
-	 */
897
-	public function testStoreVersionAsRecipient(): void {
898
-		$this->loginAsUser(self::TEST_VERSIONS_USER);
899
-
900
-		Filesystem::mkdir('folder');
901
-		Filesystem::file_put_contents('folder/test.txt', 'test file');
902
-
903
-		$node = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER)->get('folder');
904
-		$share = Server::get(\OCP\Share\IManager::class)->newShare();
905
-		$share->setNode($node)
906
-			->setShareType(IShare::TYPE_USER)
907
-			->setSharedBy(self::TEST_VERSIONS_USER)
908
-			->setSharedWith(self::TEST_VERSIONS_USER2)
909
-			->setPermissions(Constants::PERMISSION_ALL);
910
-		$share = Server::get(\OCP\Share\IManager::class)->createShare($share);
911
-		Server::get(\OCP\Share\IManager::class)->acceptShare($share, self::TEST_VERSIONS_USER2);
912
-
913
-		$this->loginAsUser(self::TEST_VERSIONS_USER2);
914
-
915
-		$this->createAndCheckVersions(
916
-			Filesystem::getView(),
917
-			'folder/test.txt'
918
-		);
919
-
920
-		Server::get(\OCP\Share\IManager::class)->deleteShare($share);
921
-	}
922
-
923
-	/**
924
-	 * Test whether versions are created when overwriting anonymously.
925
-	 *
926
-	 * When uploading through a public link or publicwebdav, no user
927
-	 * is logged in. File modification must still be able to find
928
-	 * the owner and create versions.
929
-	 */
930
-	public function testStoreVersionAsAnonymous(): void {
931
-		$this->logout();
932
-
933
-		// note: public link upload does this,
934
-		// needed to make the hooks fire
935
-		\OC_Util::setupFS(self::TEST_VERSIONS_USER);
936
-
937
-		$userView = new View('/' . self::TEST_VERSIONS_USER . '/files');
938
-		$this->createAndCheckVersions(
939
-			$userView,
940
-			'test.txt'
941
-		);
942
-	}
943
-
944
-	/**
945
-	 * @param View $view
946
-	 * @param string $path
947
-	 */
948
-	private function createAndCheckVersions(View $view, $path) {
949
-		$view->file_put_contents($path, 'test file');
950
-		$view->file_put_contents($path, 'version 1');
951
-		$view->file_put_contents($path, 'version 2');
952
-
953
-		$this->loginAsUser(self::TEST_VERSIONS_USER);
954
-
955
-		// need to scan for the versions
956
-		[$rootStorage,] = $this->rootView->resolvePath(self::TEST_VERSIONS_USER . '/files_versions');
957
-		$rootStorage->getScanner()->scan('files_versions');
958
-
959
-		$versions = Storage::getVersions(
960
-			self::TEST_VERSIONS_USER, '/' . $path
961
-		);
962
-
963
-		// note: we cannot predict how many versions are created due to
964
-		// test run timing
965
-		$this->assertGreaterThan(0, count($versions));
966
-
967
-		return $versions;
968
-	}
969
-
970
-	/**
971
-	 * @param string $user
972
-	 * @param bool $create
973
-	 */
974
-	public static function loginHelper($user, $create = false) {
975
-		if ($create) {
976
-			$backend = new \Test\Util\User\Dummy();
977
-			$backend->createUser($user, $user);
978
-			Server::get(IUserManager::class)->registerBackend($backend);
979
-		}
980
-
981
-		\OC_Util::tearDownFS();
982
-		\OC_User::setUserId('');
983
-		Filesystem::tearDown();
984
-		\OC_User::setUserId($user);
985
-		\OC_Util::setupFS($user);
986
-		\OC::$server->getUserFolder($user);
987
-	}
694
+    public function testRestoreMovedShare(): void {
695
+        $this->markTestSkipped('Unreliable test');
696
+        $this->loginAsUser(self::TEST_VERSIONS_USER);
697
+
698
+        $userHome = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER);
699
+        $node = $userHome->newFolder('folder');
700
+        $file = $node->newFile('test.txt');
701
+
702
+        $userHome2 = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER2);
703
+        $userHome2->newFolder('subfolder');
704
+
705
+        $share = Server::get(\OCP\Share\IManager::class)->newShare();
706
+        $share->setNode($node)
707
+            ->setShareType(IShare::TYPE_USER)
708
+            ->setSharedBy(self::TEST_VERSIONS_USER)
709
+            ->setSharedWith(self::TEST_VERSIONS_USER2)
710
+            ->setPermissions(Constants::PERMISSION_ALL);
711
+        $share = Server::get(\OCP\Share\IManager::class)->createShare($share);
712
+        $shareManager = Server::get(\OCP\Share\IManager::class);
713
+        $shareManager->acceptShare($share, self::TEST_VERSIONS_USER2);
714
+
715
+        $share->setTarget('subfolder/folder');
716
+        $shareManager->moveShare($share, self::TEST_VERSIONS_USER2);
717
+
718
+        $versions = $this->createAndCheckVersions(
719
+            Filesystem::getView(),
720
+            'folder/test.txt'
721
+        );
722
+
723
+        $file->putContent('test file');
724
+
725
+        $this->loginAsUser(self::TEST_VERSIONS_USER2);
726
+
727
+        $firstVersion = current($versions);
728
+
729
+        $this->assertTrue(Storage::rollback('folder/test.txt', $firstVersion['version'], $this->user1));
730
+
731
+        $this->loginAsUser(self::TEST_VERSIONS_USER);
732
+
733
+        Server::get(\OCP\Share\IManager::class)->deleteShare($share);
734
+        $this->assertEquals('version 2', $file->getContent(), 'File content has not changed');
735
+    }
736
+
737
+    /**
738
+     * @param string $hookName name of hook called
739
+     * @param string $params variable to receive parameters provided by hook
740
+     */
741
+    private function connectMockHooks($hookName, &$params) {
742
+        if ($hookName === null) {
743
+            return;
744
+        }
745
+
746
+        $eventHandler = $this->getMockBuilder(\stdclass::class)
747
+            ->setMethods(['callback'])
748
+            ->getMock();
749
+
750
+        $eventHandler->expects($this->any())
751
+            ->method('callback')
752
+            ->willReturnCallback(
753
+                function ($p) use (&$params): void {
754
+                    $params = $p;
755
+                }
756
+            );
757
+
758
+        Util::connectHook(
759
+            '\OCP\Versions',
760
+            $hookName,
761
+            $eventHandler,
762
+            'callback'
763
+        );
764
+    }
765
+
766
+    private function doTestRestore() {
767
+        $filePath = self::TEST_VERSIONS_USER . '/files/sub/test.txt';
768
+        $this->rootView->file_put_contents($filePath, 'test file');
769
+
770
+        $fileInfo = $this->rootView->getFileInfo($filePath);
771
+        $t0 = $this->rootView->filemtime($filePath);
772
+
773
+        // not exactly the same timestamp as the file
774
+        $t1 = time() - 60;
775
+        // second version is two weeks older
776
+        $t2 = $t1 - 60 * 60 * 24 * 14;
777
+
778
+        // create some versions
779
+        $v1 = self::USERS_VERSIONS_ROOT . '/sub/test.txt.v' . $t1;
780
+        $v2 = self::USERS_VERSIONS_ROOT . '/sub/test.txt.v' . $t2;
781
+
782
+        $this->rootView->mkdir(self::USERS_VERSIONS_ROOT . '/sub');
783
+
784
+        $this->rootView->file_put_contents($v1, 'version1');
785
+        $fileInfoV1 = $this->rootView->getFileInfo($v1);
786
+        $versionEntity = new VersionEntity();
787
+        $versionEntity->setFileId($fileInfo->getId());
788
+        $versionEntity->setTimestamp($t1);
789
+        $versionEntity->setSize($fileInfoV1->getSize());
790
+        $versionEntity->setMimetype($this->mimeTypeLoader->getId($fileInfoV1->getMimetype()));
791
+        $versionEntity->setMetadata([]);
792
+        $this->versionsMapper->insert($versionEntity);
793
+
794
+        $this->rootView->file_put_contents($v2, 'version2');
795
+        $fileInfoV2 = $this->rootView->getFileInfo($v2);
796
+        $versionEntity = new VersionEntity();
797
+        $versionEntity->setFileId($fileInfo->getId());
798
+        $versionEntity->setTimestamp($t2);
799
+        $versionEntity->setSize($fileInfoV2->getSize());
800
+        $versionEntity->setMimetype($this->mimeTypeLoader->getId($fileInfoV2->getMimetype()));
801
+        $versionEntity->setMetadata([]);
802
+        $this->versionsMapper->insert($versionEntity);
803
+
804
+        $oldVersions = Storage::getVersions(
805
+            self::TEST_VERSIONS_USER, '/sub/test.txt'
806
+        );
807
+
808
+        $this->assertCount(2, $oldVersions);
809
+
810
+        $this->assertEquals('test file', $this->rootView->file_get_contents($filePath));
811
+        $info1 = $this->rootView->getFileInfo($filePath);
812
+
813
+        $eventDispatcher = Server::get(IEventDispatcher::class);
814
+        $eventFired = false;
815
+        $eventDispatcher->addListener(VersionRestoredEvent::class, function ($event) use (&$eventFired, $t2): void {
816
+            $eventFired = true;
817
+            $this->assertEquals('/sub/test.txt', $event->getVersion()->getVersionPath());
818
+            $this->assertTrue($event->getVersion()->getRevisionId() > 0);
819
+        });
820
+
821
+        $versionManager = Server::get(IVersionManager::class);
822
+        $versions = $versionManager->getVersionsForFile($this->user1, $info1);
823
+        $version = array_filter($versions, function ($version) use ($t2) {
824
+            return $version->getRevisionId() === $t2;
825
+        });
826
+        $this->assertTrue($versionManager->rollback(current($version)));
827
+
828
+        $this->assertTrue($eventFired, 'VersionRestoredEvent was not fired');
829
+
830
+        $this->assertEquals('version2', $this->rootView->file_get_contents($filePath));
831
+        $info2 = $this->rootView->getFileInfo($filePath);
832
+
833
+        $this->assertNotEquals(
834
+            $info2['etag'],
835
+            $info1['etag'],
836
+            'Etag must change after rolling back version'
837
+        );
838
+        $this->assertEquals(
839
+            $info2['fileid'],
840
+            $info1['fileid'],
841
+            'File id must not change after rolling back version'
842
+        );
843
+        $this->assertEquals(
844
+            $info2['mtime'],
845
+            $t2,
846
+            'Restored file has mtime from version'
847
+        );
848
+
849
+        $newVersions = Storage::getVersions(
850
+            self::TEST_VERSIONS_USER, '/sub/test.txt'
851
+        );
852
+
853
+        $this->assertTrue(
854
+            $this->rootView->file_exists(self::USERS_VERSIONS_ROOT . '/sub/test.txt.v' . $t0),
855
+            'A version file was created for the file before restoration'
856
+        );
857
+        $this->assertTrue(
858
+            $this->rootView->file_exists($v1),
859
+            'Untouched version file is still there'
860
+        );
861
+        $this->assertFalse(
862
+            $this->rootView->file_exists($v2),
863
+            'Restored version file gone from files_version folder'
864
+        );
865
+
866
+        $this->assertCount(2, $newVersions, 'Additional version created');
867
+
868
+        $this->assertTrue(
869
+            isset($newVersions[$t0 . '#' . 'test.txt']),
870
+            'A version was created for the file before restoration'
871
+        );
872
+        $this->assertTrue(
873
+            isset($newVersions[$t1 . '#' . 'test.txt']),
874
+            'Untouched version is still there'
875
+        );
876
+        $this->assertFalse(
877
+            isset($newVersions[$t2 . '#' . 'test.txt']),
878
+            'Restored version is not in the list any more'
879
+        );
880
+    }
881
+
882
+    /**
883
+     * Test whether versions are created when overwriting as owner
884
+     */
885
+    public function testStoreVersionAsOwner(): void {
886
+        $this->loginAsUser(self::TEST_VERSIONS_USER);
887
+
888
+        $this->createAndCheckVersions(
889
+            Filesystem::getView(),
890
+            'test.txt'
891
+        );
892
+    }
893
+
894
+    /**
895
+     * Test whether versions are created when overwriting as share recipient
896
+     */
897
+    public function testStoreVersionAsRecipient(): void {
898
+        $this->loginAsUser(self::TEST_VERSIONS_USER);
899
+
900
+        Filesystem::mkdir('folder');
901
+        Filesystem::file_put_contents('folder/test.txt', 'test file');
902
+
903
+        $node = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER)->get('folder');
904
+        $share = Server::get(\OCP\Share\IManager::class)->newShare();
905
+        $share->setNode($node)
906
+            ->setShareType(IShare::TYPE_USER)
907
+            ->setSharedBy(self::TEST_VERSIONS_USER)
908
+            ->setSharedWith(self::TEST_VERSIONS_USER2)
909
+            ->setPermissions(Constants::PERMISSION_ALL);
910
+        $share = Server::get(\OCP\Share\IManager::class)->createShare($share);
911
+        Server::get(\OCP\Share\IManager::class)->acceptShare($share, self::TEST_VERSIONS_USER2);
912
+
913
+        $this->loginAsUser(self::TEST_VERSIONS_USER2);
914
+
915
+        $this->createAndCheckVersions(
916
+            Filesystem::getView(),
917
+            'folder/test.txt'
918
+        );
919
+
920
+        Server::get(\OCP\Share\IManager::class)->deleteShare($share);
921
+    }
922
+
923
+    /**
924
+     * Test whether versions are created when overwriting anonymously.
925
+     *
926
+     * When uploading through a public link or publicwebdav, no user
927
+     * is logged in. File modification must still be able to find
928
+     * the owner and create versions.
929
+     */
930
+    public function testStoreVersionAsAnonymous(): void {
931
+        $this->logout();
932
+
933
+        // note: public link upload does this,
934
+        // needed to make the hooks fire
935
+        \OC_Util::setupFS(self::TEST_VERSIONS_USER);
936
+
937
+        $userView = new View('/' . self::TEST_VERSIONS_USER . '/files');
938
+        $this->createAndCheckVersions(
939
+            $userView,
940
+            'test.txt'
941
+        );
942
+    }
943
+
944
+    /**
945
+     * @param View $view
946
+     * @param string $path
947
+     */
948
+    private function createAndCheckVersions(View $view, $path) {
949
+        $view->file_put_contents($path, 'test file');
950
+        $view->file_put_contents($path, 'version 1');
951
+        $view->file_put_contents($path, 'version 2');
952
+
953
+        $this->loginAsUser(self::TEST_VERSIONS_USER);
954
+
955
+        // need to scan for the versions
956
+        [$rootStorage,] = $this->rootView->resolvePath(self::TEST_VERSIONS_USER . '/files_versions');
957
+        $rootStorage->getScanner()->scan('files_versions');
958
+
959
+        $versions = Storage::getVersions(
960
+            self::TEST_VERSIONS_USER, '/' . $path
961
+        );
962
+
963
+        // note: we cannot predict how many versions are created due to
964
+        // test run timing
965
+        $this->assertGreaterThan(0, count($versions));
966
+
967
+        return $versions;
968
+    }
969
+
970
+    /**
971
+     * @param string $user
972
+     * @param bool $create
973
+     */
974
+    public static function loginHelper($user, $create = false) {
975
+        if ($create) {
976
+            $backend = new \Test\Util\User\Dummy();
977
+            $backend->createUser($user, $user);
978
+            Server::get(IUserManager::class)->registerBackend($backend);
979
+        }
980
+
981
+        \OC_Util::tearDownFS();
982
+        \OC_User::setUserId('');
983
+        Filesystem::tearDown();
984
+        \OC_User::setUserId($user);
985
+        \OC_Util::setupFS($user);
986
+        \OC::$server->getUserFolder($user);
987
+    }
988 988
 }
989 989
 
990 990
 // extend the original class to make it possible to test protected methods
991 991
 class VersionStorageToTest extends Storage {
992 992
 
993
-	/**
994
-	 * @param integer $time
995
-	 */
996
-	public function callProtectedGetExpireList($time, $versions) {
997
-		return self::getExpireList($time, $versions);
998
-	}
993
+    /**
994
+     * @param integer $time
995
+     */
996
+    public function callProtectedGetExpireList($time, $versions) {
997
+        return self::getExpireList($time, $versions);
998
+    }
999 999
 }
Please login to merge, or discard this patch.
Spacing   +59 added lines, -59 removed lines patch added patch discarded remove patch
@@ -89,7 +89,7 @@  discard block
 block discarded – undo
89 89
 			->getMock();
90 90
 		$mockConfig->expects($this->any())
91 91
 			->method('getSystemValue')
92
-			->willReturnCallback(function ($key, $default) use ($config) {
92
+			->willReturnCallback(function($key, $default) use ($config) {
93 93
 				if ($key === 'filesystem_check_changes') {
94 94
 					return Watcher::CHECK_ONCE;
95 95
 				} else {
@@ -124,10 +124,10 @@  discard block
 block discarded – undo
124 124
 		$this->restoreService(AllConfig::class);
125 125
 
126 126
 		if ($this->rootView) {
127
-			$this->rootView->deleteAll(self::TEST_VERSIONS_USER . '/files/');
128
-			$this->rootView->deleteAll(self::TEST_VERSIONS_USER2 . '/files/');
129
-			$this->rootView->deleteAll(self::TEST_VERSIONS_USER . '/files_versions/');
130
-			$this->rootView->deleteAll(self::TEST_VERSIONS_USER2 . '/files_versions/');
127
+			$this->rootView->deleteAll(self::TEST_VERSIONS_USER.'/files/');
128
+			$this->rootView->deleteAll(self::TEST_VERSIONS_USER2.'/files/');
129
+			$this->rootView->deleteAll(self::TEST_VERSIONS_USER.'/files_versions/');
130
+			$this->rootView->deleteAll(self::TEST_VERSIONS_USER2.'/files_versions/');
131 131
 		}
132 132
 
133 133
 		\OC_Hook::clear();
@@ -295,10 +295,10 @@  discard block
 block discarded – undo
295 295
 		$t2 = $t1 - 60 * 60 * 24 * 14;
296 296
 
297 297
 		// create some versions
298
-		$v1 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t1;
299
-		$v2 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t2;
300
-		$v1Renamed = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t1;
301
-		$v2Renamed = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t2;
298
+		$v1 = self::USERS_VERSIONS_ROOT.'/test.txt.v'.$t1;
299
+		$v2 = self::USERS_VERSIONS_ROOT.'/test.txt.v'.$t2;
300
+		$v1Renamed = self::USERS_VERSIONS_ROOT.'/test2.txt.v'.$t1;
301
+		$v2Renamed = self::USERS_VERSIONS_ROOT.'/test2.txt.v'.$t2;
302 302
 
303 303
 		$this->rootView->file_put_contents($v1, 'version1');
304 304
 		$this->rootView->file_put_contents($v2, 'version2');
@@ -325,12 +325,12 @@  discard block
 block discarded – undo
325 325
 		// version will be expired
326 326
 		$t2 = $t1 - 60 * 60 * 24 * 14;
327 327
 
328
-		$this->rootView->mkdir(self::USERS_VERSIONS_ROOT . '/folder1');
328
+		$this->rootView->mkdir(self::USERS_VERSIONS_ROOT.'/folder1');
329 329
 		// create some versions
330
-		$v1 = self::USERS_VERSIONS_ROOT . '/folder1/test.txt.v' . $t1;
331
-		$v2 = self::USERS_VERSIONS_ROOT . '/folder1/test.txt.v' . $t2;
332
-		$v1Renamed = self::USERS_VERSIONS_ROOT . '/folder1/folder2/test.txt.v' . $t1;
333
-		$v2Renamed = self::USERS_VERSIONS_ROOT . '/folder1/folder2/test.txt.v' . $t2;
330
+		$v1 = self::USERS_VERSIONS_ROOT.'/folder1/test.txt.v'.$t1;
331
+		$v2 = self::USERS_VERSIONS_ROOT.'/folder1/test.txt.v'.$t2;
332
+		$v1Renamed = self::USERS_VERSIONS_ROOT.'/folder1/folder2/test.txt.v'.$t1;
333
+		$v2Renamed = self::USERS_VERSIONS_ROOT.'/folder1/folder2/test.txt.v'.$t2;
334 334
 
335 335
 		$this->rootView->file_put_contents($v1, 'version1');
336 336
 		$this->rootView->file_put_contents($v2, 'version2');
@@ -376,11 +376,11 @@  discard block
 block discarded – undo
376 376
 		$t2 = $t1 - 60 * 60 * 24 * 14;
377 377
 
378 378
 		// create some versions
379
-		$this->rootView->mkdir(self::USERS_VERSIONS_ROOT . '/folder1');
380
-		$v1 = self::USERS_VERSIONS_ROOT . '/folder1/test.txt.v' . $t1;
381
-		$v2 = self::USERS_VERSIONS_ROOT . '/folder1/test.txt.v' . $t2;
382
-		$v1Renamed = self::USERS_VERSIONS_ROOT . '/folder2/folder1/test.txt.v' . $t1;
383
-		$v2Renamed = self::USERS_VERSIONS_ROOT . '/folder2/folder1/test.txt.v' . $t2;
379
+		$this->rootView->mkdir(self::USERS_VERSIONS_ROOT.'/folder1');
380
+		$v1 = self::USERS_VERSIONS_ROOT.'/folder1/test.txt.v'.$t1;
381
+		$v2 = self::USERS_VERSIONS_ROOT.'/folder1/test.txt.v'.$t2;
382
+		$v1Renamed = self::USERS_VERSIONS_ROOT.'/folder2/folder1/test.txt.v'.$t1;
383
+		$v2Renamed = self::USERS_VERSIONS_ROOT.'/folder2/folder1/test.txt.v'.$t2;
384 384
 
385 385
 		$this->rootView->file_put_contents($v1, 'version1');
386 386
 		$this->rootView->file_put_contents($v2, 'version2');
@@ -413,7 +413,7 @@  discard block
 block discarded – undo
413 413
 		Server::get(\OCP\Share\IManager::class)->acceptShare($share, self::TEST_VERSIONS_USER2);
414 414
 
415 415
 		self::loginHelper(self::TEST_VERSIONS_USER2);
416
-		$versionsFolder2 = '/' . self::TEST_VERSIONS_USER2 . '/files_versions';
416
+		$versionsFolder2 = '/'.self::TEST_VERSIONS_USER2.'/files_versions';
417 417
 		Filesystem::file_put_contents('test.txt', 'test file');
418 418
 
419 419
 		$t1 = time();
@@ -423,8 +423,8 @@  discard block
 block discarded – undo
423 423
 
424 424
 		$this->rootView->mkdir($versionsFolder2);
425 425
 		// create some versions
426
-		$v1 = $versionsFolder2 . '/test.txt.v' . $t1;
427
-		$v2 = $versionsFolder2 . '/test.txt.v' . $t2;
426
+		$v1 = $versionsFolder2.'/test.txt.v'.$t1;
427
+		$v2 = $versionsFolder2.'/test.txt.v'.$t2;
428 428
 
429 429
 		$this->rootView->file_put_contents($v1, 'version1');
430 430
 		$this->rootView->file_put_contents($v2, 'version2');
@@ -438,10 +438,10 @@  discard block
 block discarded – undo
438 438
 
439 439
 		self::loginHelper(self::TEST_VERSIONS_USER);
440 440
 
441
-		$versionsFolder1 = '/' . self::TEST_VERSIONS_USER . '/files_versions';
441
+		$versionsFolder1 = '/'.self::TEST_VERSIONS_USER.'/files_versions';
442 442
 
443
-		$v1Renamed = $versionsFolder1 . '/folder1/test.txt.v' . $t1;
444
-		$v2Renamed = $versionsFolder1 . '/folder1/test.txt.v' . $t2;
443
+		$v1Renamed = $versionsFolder1.'/folder1/test.txt.v'.$t1;
444
+		$v2Renamed = $versionsFolder1.'/folder1/test.txt.v'.$t2;
445 445
 
446 446
 		$this->assertTrue($this->rootView->file_exists($v1Renamed));
447 447
 		$this->assertTrue($this->rootView->file_exists($v2Renamed));
@@ -463,7 +463,7 @@  discard block
 block discarded – undo
463 463
 		Server::get(\OCP\Share\IManager::class)->acceptShare($share, self::TEST_VERSIONS_USER2);
464 464
 
465 465
 		self::loginHelper(self::TEST_VERSIONS_USER2);
466
-		$versionsFolder2 = '/' . self::TEST_VERSIONS_USER2 . '/files_versions';
466
+		$versionsFolder2 = '/'.self::TEST_VERSIONS_USER2.'/files_versions';
467 467
 		Filesystem::mkdir('folder2');
468 468
 		Filesystem::file_put_contents('folder2/test.txt', 'test file');
469 469
 
@@ -473,10 +473,10 @@  discard block
 block discarded – undo
473 473
 		$t2 = $t1 - 60 * 60 * 24 * 14;
474 474
 
475 475
 		$this->rootView->mkdir($versionsFolder2);
476
-		$this->rootView->mkdir($versionsFolder2 . '/folder2');
476
+		$this->rootView->mkdir($versionsFolder2.'/folder2');
477 477
 		// create some versions
478
-		$v1 = $versionsFolder2 . '/folder2/test.txt.v' . $t1;
479
-		$v2 = $versionsFolder2 . '/folder2/test.txt.v' . $t2;
478
+		$v1 = $versionsFolder2.'/folder2/test.txt.v'.$t1;
479
+		$v2 = $versionsFolder2.'/folder2/test.txt.v'.$t2;
480 480
 
481 481
 		$this->rootView->file_put_contents($v1, 'version1');
482 482
 		$this->rootView->file_put_contents($v2, 'version2');
@@ -489,10 +489,10 @@  discard block
 block discarded – undo
489 489
 
490 490
 		self::loginHelper(self::TEST_VERSIONS_USER);
491 491
 
492
-		$versionsFolder1 = '/' . self::TEST_VERSIONS_USER . '/files_versions';
492
+		$versionsFolder1 = '/'.self::TEST_VERSIONS_USER.'/files_versions';
493 493
 
494
-		$v1Renamed = $versionsFolder1 . '/folder1/folder2/test.txt.v' . $t1;
495
-		$v2Renamed = $versionsFolder1 . '/folder1/folder2/test.txt.v' . $t2;
494
+		$v1Renamed = $versionsFolder1.'/folder1/folder2/test.txt.v'.$t1;
495
+		$v2Renamed = $versionsFolder1.'/folder1/folder2/test.txt.v'.$t2;
496 496
 
497 497
 		$this->assertTrue($this->rootView->file_exists($v1Renamed));
498 498
 		$this->assertTrue($this->rootView->file_exists($v2Renamed));
@@ -510,11 +510,11 @@  discard block
 block discarded – undo
510 510
 
511 511
 		$this->rootView->mkdir(self::USERS_VERSIONS_ROOT);
512 512
 		// create some versions
513
-		$v1 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t1;
514
-		$v2 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t2;
513
+		$v1 = self::USERS_VERSIONS_ROOT.'/test.txt.v'.$t1;
514
+		$v2 = self::USERS_VERSIONS_ROOT.'/test.txt.v'.$t2;
515 515
 		// the renamed versions should not exist! Because we only moved the mount point!
516
-		$v1Renamed = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t1;
517
-		$v2Renamed = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t2;
516
+		$v1Renamed = self::USERS_VERSIONS_ROOT.'/test2.txt.v'.$t1;
517
+		$v2Renamed = self::USERS_VERSIONS_ROOT.'/test2.txt.v'.$t2;
518 518
 
519 519
 		$this->rootView->file_put_contents($v1, 'version1');
520 520
 		$this->rootView->file_put_contents($v2, 'version2');
@@ -558,10 +558,10 @@  discard block
 block discarded – undo
558 558
 		$t2 = $t1 - 60 * 60 * 24 * 14;
559 559
 
560 560
 		// create some versions
561
-		$v1 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t1;
562
-		$v2 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t2;
563
-		$v1Copied = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t1;
564
-		$v2Copied = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t2;
561
+		$v1 = self::USERS_VERSIONS_ROOT.'/test.txt.v'.$t1;
562
+		$v2 = self::USERS_VERSIONS_ROOT.'/test.txt.v'.$t2;
563
+		$v1Copied = self::USERS_VERSIONS_ROOT.'/test2.txt.v'.$t1;
564
+		$v2Copied = self::USERS_VERSIONS_ROOT.'/test2.txt.v'.$t2;
565 565
 
566 566
 		$this->rootView->file_put_contents($v1, 'version1');
567 567
 		$this->rootView->file_put_contents($v2, 'version2');
@@ -589,10 +589,10 @@  discard block
 block discarded – undo
589 589
 		$t2 = $t1 - 60 * 60 * 24 * 14;
590 590
 
591 591
 		// create some versions
592
-		$v1 = self::USERS_VERSIONS_ROOT . '/subfolder/test.txt.v' . $t1;
593
-		$v2 = self::USERS_VERSIONS_ROOT . '/subfolder/test.txt.v' . $t2;
592
+		$v1 = self::USERS_VERSIONS_ROOT.'/subfolder/test.txt.v'.$t1;
593
+		$v2 = self::USERS_VERSIONS_ROOT.'/subfolder/test.txt.v'.$t2;
594 594
 
595
-		$this->rootView->mkdir(self::USERS_VERSIONS_ROOT . '/subfolder/');
595
+		$this->rootView->mkdir(self::USERS_VERSIONS_ROOT.'/subfolder/');
596 596
 
597 597
 		$this->rootView->file_put_contents($v1, 'version1');
598 598
 		$this->rootView->file_put_contents($v2, 'version2');
@@ -608,7 +608,7 @@  discard block
 block discarded – undo
608 608
 		}
609 609
 
610 610
 		//cleanup
611
-		$this->rootView->deleteAll(self::USERS_VERSIONS_ROOT . '/subfolder');
611
+		$this->rootView->deleteAll(self::USERS_VERSIONS_ROOT.'/subfolder');
612 612
 	}
613 613
 
614 614
 	/**
@@ -651,7 +651,7 @@  discard block
 block discarded – undo
651 651
 
652 652
 	public function testRestoreCrossStorage(): void {
653 653
 		$storage2 = new Temporary([]);
654
-		Filesystem::mount($storage2, [], self::TEST_VERSIONS_USER . '/files/sub');
654
+		Filesystem::mount($storage2, [], self::TEST_VERSIONS_USER.'/files/sub');
655 655
 
656 656
 		$this->doTestRestore();
657 657
 	}
@@ -750,7 +750,7 @@  discard block
 block discarded – undo
750 750
 		$eventHandler->expects($this->any())
751 751
 			->method('callback')
752 752
 			->willReturnCallback(
753
-				function ($p) use (&$params): void {
753
+				function($p) use (&$params): void {
754 754
 					$params = $p;
755 755
 				}
756 756
 			);
@@ -764,7 +764,7 @@  discard block
 block discarded – undo
764 764
 	}
765 765
 
766 766
 	private function doTestRestore() {
767
-		$filePath = self::TEST_VERSIONS_USER . '/files/sub/test.txt';
767
+		$filePath = self::TEST_VERSIONS_USER.'/files/sub/test.txt';
768 768
 		$this->rootView->file_put_contents($filePath, 'test file');
769 769
 
770 770
 		$fileInfo = $this->rootView->getFileInfo($filePath);
@@ -776,10 +776,10 @@  discard block
 block discarded – undo
776 776
 		$t2 = $t1 - 60 * 60 * 24 * 14;
777 777
 
778 778
 		// create some versions
779
-		$v1 = self::USERS_VERSIONS_ROOT . '/sub/test.txt.v' . $t1;
780
-		$v2 = self::USERS_VERSIONS_ROOT . '/sub/test.txt.v' . $t2;
779
+		$v1 = self::USERS_VERSIONS_ROOT.'/sub/test.txt.v'.$t1;
780
+		$v2 = self::USERS_VERSIONS_ROOT.'/sub/test.txt.v'.$t2;
781 781
 
782
-		$this->rootView->mkdir(self::USERS_VERSIONS_ROOT . '/sub');
782
+		$this->rootView->mkdir(self::USERS_VERSIONS_ROOT.'/sub');
783 783
 
784 784
 		$this->rootView->file_put_contents($v1, 'version1');
785 785
 		$fileInfoV1 = $this->rootView->getFileInfo($v1);
@@ -812,7 +812,7 @@  discard block
 block discarded – undo
812 812
 
813 813
 		$eventDispatcher = Server::get(IEventDispatcher::class);
814 814
 		$eventFired = false;
815
-		$eventDispatcher->addListener(VersionRestoredEvent::class, function ($event) use (&$eventFired, $t2): void {
815
+		$eventDispatcher->addListener(VersionRestoredEvent::class, function($event) use (&$eventFired, $t2): void {
816 816
 			$eventFired = true;
817 817
 			$this->assertEquals('/sub/test.txt', $event->getVersion()->getVersionPath());
818 818
 			$this->assertTrue($event->getVersion()->getRevisionId() > 0);
@@ -820,7 +820,7 @@  discard block
 block discarded – undo
820 820
 
821 821
 		$versionManager = Server::get(IVersionManager::class);
822 822
 		$versions = $versionManager->getVersionsForFile($this->user1, $info1);
823
-		$version = array_filter($versions, function ($version) use ($t2) {
823
+		$version = array_filter($versions, function($version) use ($t2) {
824 824
 			return $version->getRevisionId() === $t2;
825 825
 		});
826 826
 		$this->assertTrue($versionManager->rollback(current($version)));
@@ -851,7 +851,7 @@  discard block
 block discarded – undo
851 851
 		);
852 852
 
853 853
 		$this->assertTrue(
854
-			$this->rootView->file_exists(self::USERS_VERSIONS_ROOT . '/sub/test.txt.v' . $t0),
854
+			$this->rootView->file_exists(self::USERS_VERSIONS_ROOT.'/sub/test.txt.v'.$t0),
855 855
 			'A version file was created for the file before restoration'
856 856
 		);
857 857
 		$this->assertTrue(
@@ -866,15 +866,15 @@  discard block
 block discarded – undo
866 866
 		$this->assertCount(2, $newVersions, 'Additional version created');
867 867
 
868 868
 		$this->assertTrue(
869
-			isset($newVersions[$t0 . '#' . 'test.txt']),
869
+			isset($newVersions[$t0.'#'.'test.txt']),
870 870
 			'A version was created for the file before restoration'
871 871
 		);
872 872
 		$this->assertTrue(
873
-			isset($newVersions[$t1 . '#' . 'test.txt']),
873
+			isset($newVersions[$t1.'#'.'test.txt']),
874 874
 			'Untouched version is still there'
875 875
 		);
876 876
 		$this->assertFalse(
877
-			isset($newVersions[$t2 . '#' . 'test.txt']),
877
+			isset($newVersions[$t2.'#'.'test.txt']),
878 878
 			'Restored version is not in the list any more'
879 879
 		);
880 880
 	}
@@ -934,7 +934,7 @@  discard block
 block discarded – undo
934 934
 		// needed to make the hooks fire
935 935
 		\OC_Util::setupFS(self::TEST_VERSIONS_USER);
936 936
 
937
-		$userView = new View('/' . self::TEST_VERSIONS_USER . '/files');
937
+		$userView = new View('/'.self::TEST_VERSIONS_USER.'/files');
938 938
 		$this->createAndCheckVersions(
939 939
 			$userView,
940 940
 			'test.txt'
@@ -953,11 +953,11 @@  discard block
 block discarded – undo
953 953
 		$this->loginAsUser(self::TEST_VERSIONS_USER);
954 954
 
955 955
 		// need to scan for the versions
956
-		[$rootStorage,] = $this->rootView->resolvePath(self::TEST_VERSIONS_USER . '/files_versions');
956
+		[$rootStorage, ] = $this->rootView->resolvePath(self::TEST_VERSIONS_USER.'/files_versions');
957 957
 		$rootStorage->getScanner()->scan('files_versions');
958 958
 
959 959
 		$versions = Storage::getVersions(
960
-			self::TEST_VERSIONS_USER, '/' . $path
960
+			self::TEST_VERSIONS_USER, '/'.$path
961 961
 		);
962 962
 
963 963
 		// note: we cannot predict how many versions are created due to
Please login to merge, or discard this patch.
apps/files_versions/lib/Listener/FileEventsListener.php 1 patch
Indentation   +362 added lines, -362 removed lines patch added patch discarded remove patch
@@ -42,366 +42,366 @@
 block discarded – undo
42 42
 
43 43
 /** @template-implements IEventListener<BeforeNodeCopiedEvent|BeforeNodeDeletedEvent|BeforeNodeRenamedEvent|BeforeNodeTouchedEvent|BeforeNodeWrittenEvent|NodeCopiedEvent|NodeCreatedEvent|NodeDeletedEvent|NodeRenamedEvent|NodeTouchedEvent|NodeWrittenEvent> */
44 44
 class FileEventsListener implements IEventListener {
45
-	/**
46
-	 * @var array<int, array>
47
-	 */
48
-	private array $writeHookInfo = [];
49
-	/**
50
-	 * @var array<int, Node>
51
-	 */
52
-	private array $nodesTouched = [];
53
-	/**
54
-	 * @var array<string, Node>
55
-	 */
56
-	private array $versionsDeleted = [];
57
-
58
-	public function __construct(
59
-		private IRootFolder $rootFolder,
60
-		private IVersionManager $versionManager,
61
-		private IMimeTypeLoader $mimeTypeLoader,
62
-		private IUserSession $userSession,
63
-		private LoggerInterface $logger,
64
-	) {
65
-	}
66
-
67
-	public function handle(Event $event): void {
68
-		if ($event instanceof NodeCreatedEvent) {
69
-			$this->created($event->getNode());
70
-		}
71
-
72
-		if ($event instanceof BeforeNodeTouchedEvent) {
73
-			$this->pre_touch_hook($event->getNode());
74
-		}
75
-
76
-		if ($event instanceof NodeTouchedEvent) {
77
-			$this->touch_hook($event->getNode());
78
-		}
79
-
80
-		if ($event instanceof BeforeNodeWrittenEvent) {
81
-			$this->write_hook($event->getNode());
82
-		}
83
-
84
-		if ($event instanceof NodeWrittenEvent) {
85
-			$this->post_write_hook($event->getNode());
86
-		}
87
-
88
-		if ($event instanceof BeforeNodeDeletedEvent) {
89
-			$this->pre_remove_hook($event->getNode());
90
-		}
91
-
92
-		if ($event instanceof NodeDeletedEvent) {
93
-			$this->remove_hook($event->getNode());
94
-		}
95
-
96
-		if ($event instanceof NodeRenamedEvent) {
97
-			$this->rename_hook($event->getSource(), $event->getTarget());
98
-		}
99
-
100
-		if ($event instanceof NodeCopiedEvent) {
101
-			$this->copy_hook($event->getSource(), $event->getTarget());
102
-		}
103
-
104
-		if ($event instanceof BeforeNodeRenamedEvent) {
105
-			$this->pre_renameOrCopy_hook($event->getSource(), $event->getTarget());
106
-		}
107
-
108
-		if ($event instanceof BeforeNodeCopiedEvent) {
109
-			$this->pre_renameOrCopy_hook($event->getSource(), $event->getTarget());
110
-		}
111
-	}
112
-
113
-	public function pre_touch_hook(Node $node): void {
114
-		// Do not handle folders.
115
-		if ($node instanceof Folder) {
116
-			return;
117
-		}
118
-
119
-		// $node is a non-existing on file creation.
120
-		if ($node instanceof NonExistingFile) {
121
-			return;
122
-		}
123
-
124
-		$this->nodesTouched[$node->getId()] = $node;
125
-	}
126
-
127
-	public function touch_hook(Node $node): void {
128
-		$previousNode = $this->nodesTouched[$node->getId()] ?? null;
129
-
130
-		if ($previousNode === null) {
131
-			return;
132
-		}
133
-
134
-		unset($this->nodesTouched[$node->getId()]);
135
-
136
-		try {
137
-			if ($node instanceof File && $this->versionManager instanceof INeedSyncVersionBackend) {
138
-				// We update the timestamp of the version entity associated with the previousNode.
139
-				$this->versionManager->updateVersionEntity($node, $previousNode->getMTime(), ['timestamp' => $node->getMTime()]);
140
-			}
141
-		} catch (DbalException $ex) {
142
-			// Ignore UniqueConstraintViolationException, as we are probably in the middle of a rollback
143
-			// Where the previous node would temporary have the mtime of the old version, so the rollback touches it to fix it.
144
-			if (!($ex->getPrevious() instanceof UniqueConstraintViolationException)) {
145
-				throw $ex;
146
-			}
147
-		} catch (DoesNotExistException $ex) {
148
-			// Ignore DoesNotExistException, as we are probably in the middle of a rollback
149
-			// Where the previous node would temporary have a wrong mtime, so the rollback touches it to fix it.
150
-		}
151
-	}
152
-
153
-	public function created(Node $node): void {
154
-		// Do not handle folders.
155
-		if ($node instanceof File && $this->versionManager instanceof INeedSyncVersionBackend) {
156
-			$this->versionManager->createVersionEntity($node);
157
-		}
158
-	}
159
-
160
-	/**
161
-	 * listen to write event.
162
-	 */
163
-	public function write_hook(Node $node): void {
164
-		// Do not handle folders.
165
-		if ($node instanceof Folder) {
166
-			return;
167
-		}
168
-
169
-		// $node is a non-existing on file creation.
170
-		if ($node instanceof NonExistingFile) {
171
-			return;
172
-		}
173
-
174
-		$path = $this->getPathForNode($node);
175
-		$result = Storage::store($path);
176
-
177
-		// Store the result of the version creation so it can be used in post_write_hook.
178
-		$this->writeHookInfo[$node->getId()] = [
179
-			'previousNode' => $node,
180
-			'versionCreated' => $result !== false
181
-		];
182
-	}
183
-
184
-	/**
185
-	 * listen to post_write event.
186
-	 */
187
-	public function post_write_hook(Node $node): void {
188
-		// Do not handle folders.
189
-		if ($node instanceof Folder) {
190
-			return;
191
-		}
192
-
193
-		$writeHookInfo = $this->writeHookInfo[$node->getId()] ?? null;
194
-
195
-		if ($writeHookInfo === null) {
196
-			return;
197
-		}
198
-
199
-		if (
200
-			$writeHookInfo['versionCreated']
201
-			&& $node->getMTime() !== $writeHookInfo['previousNode']->getMTime()
202
-		) {
203
-			// If a new version was created, insert a version in the DB for the current content.
204
-			// If both versions have the same mtime, it means the latest version file simply got overrode,
205
-			// so no need to create a new version.
206
-			$this->created($node);
207
-		} else {
208
-			try {
209
-				// If no new version was stored in the FS, no new version should be added in the DB.
210
-				// So we simply update the associated version.
211
-				if ($node instanceof File && $this->versionManager instanceof INeedSyncVersionBackend) {
212
-					$this->versionManager->updateVersionEntity(
213
-						$node,
214
-						$writeHookInfo['previousNode']->getMtime(),
215
-						[
216
-							'timestamp' => $node->getMTime(),
217
-							'size' => $node->getSize(),
218
-							'mimetype' => $this->mimeTypeLoader->getId($node->getMimetype()),
219
-						],
220
-					);
221
-				}
222
-			} catch (DoesNotExistException $e) {
223
-				// This happens if the versions app was not enabled while the file was created or updated the last time.
224
-				// meaning there is no such revision and we need to create this file.
225
-				if ($writeHookInfo['versionCreated']) {
226
-					$this->created($node);
227
-				} else {
228
-					// Normally this should not happen so we re-throw the exception to not hide any potential issues.
229
-					throw $e;
230
-				}
231
-			} catch (Exception $e) {
232
-				$this->logger->error('Failed to update existing version for ' . $node->getPath(), [
233
-					'exception' => $e,
234
-					'versionCreated' => $writeHookInfo['versionCreated'],
235
-					'previousNode' => [
236
-						'size' => $writeHookInfo['previousNode']->getSize(),
237
-						'mtime' => $writeHookInfo['previousNode']->getMTime(),
238
-					],
239
-					'node' => [
240
-						'size' => $node->getSize(),
241
-						'mtime' => $node->getMTime(),
242
-					]
243
-				]);
244
-				throw $e;
245
-			}
246
-		}
247
-
248
-		unset($this->writeHookInfo[$node->getId()]);
249
-	}
250
-
251
-	/**
252
-	 * Erase versions of deleted file
253
-	 *
254
-	 * This function is connected to the NodeDeletedEvent event
255
-	 * cleanup the versions directory if the actual file gets deleted
256
-	 */
257
-	public function remove_hook(Node $node): void {
258
-		// Need to normalize the path as there is an issue with path concatenation in View.php::getAbsolutePath.
259
-		$path = Filesystem::normalizePath($node->getPath());
260
-		if (!array_key_exists($path, $this->versionsDeleted)) {
261
-			return;
262
-		}
263
-		$node = $this->versionsDeleted[$path];
264
-		$relativePath = $this->getPathForNode($node);
265
-		unset($this->versionsDeleted[$path]);
266
-		Storage::delete($relativePath);
267
-		// If no new version was stored in the FS, no new version should be added in the DB.
268
-		// So we simply update the associated version.
269
-		if ($node instanceof File && $this->versionManager instanceof INeedSyncVersionBackend) {
270
-			$this->versionManager->deleteVersionsEntity($node);
271
-		}
272
-	}
273
-
274
-	/**
275
-	 * mark file as "deleted" so that we can clean up the versions if the file is gone
276
-	 */
277
-	public function pre_remove_hook(Node $node): void {
278
-		$path = $this->getPathForNode($node);
279
-		Storage::markDeletedFile($path);
280
-		$this->versionsDeleted[$node->getPath()] = $node;
281
-	}
282
-
283
-	/**
284
-	 * rename/move versions of renamed/moved files
285
-	 *
286
-	 * This function is connected to the NodeRenamedEvent event and adjust the name and location
287
-	 * of the stored versions along the actual file
288
-	 */
289
-	public function rename_hook(Node $source, Node $target): void {
290
-		$sourceBackend = $this->versionManager->getBackendForStorage($source->getParent()->getStorage());
291
-		$targetBackend = $this->versionManager->getBackendForStorage($target->getStorage());
292
-		// If different backends, do nothing.
293
-		if ($sourceBackend !== $targetBackend) {
294
-			return;
295
-		}
296
-
297
-		$oldPath = $this->getPathForNode($source);
298
-		$newPath = $this->getPathForNode($target);
299
-		Storage::renameOrCopy($oldPath, $newPath, 'rename');
300
-	}
301
-
302
-	/**
303
-	 * copy versions of copied files
304
-	 *
305
-	 * This function is connected to the NodeCopiedEvent event and copies the
306
-	 * the stored versions to the new location
307
-	 */
308
-	public function copy_hook(Node $source, Node $target): void {
309
-		$sourceBackend = $this->versionManager->getBackendForStorage($source->getParent()->getStorage());
310
-		$targetBackend = $this->versionManager->getBackendForStorage($target->getStorage());
311
-		// If different backends, do nothing.
312
-		if ($sourceBackend !== $targetBackend) {
313
-			return;
314
-		}
315
-
316
-		$oldPath = $this->getPathForNode($source);
317
-		$newPath = $this->getPathForNode($target);
318
-		Storage::renameOrCopy($oldPath, $newPath, 'copy');
319
-	}
320
-
321
-	/**
322
-	 * Remember owner and the owner path of the source file.
323
-	 * If the file already exists, then it was a upload of a existing file
324
-	 * over the web interface and we call Storage::store() directly
325
-	 *
326
-	 *
327
-	 */
328
-	public function pre_renameOrCopy_hook(Node $source, Node $target): void {
329
-		$sourceBackend = $this->versionManager->getBackendForStorage($source->getStorage());
330
-		$targetBackend = $this->versionManager->getBackendForStorage($target->getParent()->getStorage());
331
-		// If different backends, do nothing.
332
-		if ($sourceBackend !== $targetBackend) {
333
-			return;
334
-		}
335
-
336
-		// if we rename a movable mount point, then the versions don't have to be renamed
337
-		$oldPath = $this->getPathForNode($source);
338
-		$newPath = $this->getPathForNode($target);
339
-		if ($oldPath === null || $newPath === null) {
340
-			return;
341
-		}
342
-
343
-		$user = $this->userSession->getUser()?->getUID();
344
-		if ($user === null) {
345
-			return;
346
-		}
347
-
348
-		$absOldPath = Filesystem::normalizePath('/' . $user . '/files' . $oldPath);
349
-		$manager = Filesystem::getMountManager();
350
-		$mount = $manager->find($absOldPath);
351
-		$internalPath = $mount->getInternalPath($absOldPath);
352
-		if ($internalPath === '' and $mount instanceof MoveableMount) {
353
-			return;
354
-		}
355
-
356
-		$view = new View($user . '/files');
357
-		if ($view->file_exists($newPath)) {
358
-			Storage::store($newPath);
359
-		} else {
360
-			Storage::setSourcePathAndUser($oldPath);
361
-		}
362
-	}
363
-
364
-	/**
365
-	 * Retrieve the path relative to the current user root folder.
366
-	 * If no user is connected, try to use the node's owner.
367
-	 */
368
-	private function getPathForNode(Node $node): ?string {
369
-		$user = $this->userSession->getUser()?->getUID();
370
-		if ($user) {
371
-			$path = $this->rootFolder
372
-				->getUserFolder($user)
373
-				->getRelativePath($node->getPath());
374
-
375
-			if ($path !== null) {
376
-				return $path;
377
-			}
378
-		}
379
-
380
-		try {
381
-			$owner = $node->getOwner()?->getUid();
382
-		} catch (NotFoundException) {
383
-			$owner = null;
384
-		}
385
-
386
-		// If no owner, extract it from the path.
387
-		// e.g. /user/files/foobar.txt
388
-		if (!$owner) {
389
-			$parts = explode('/', $node->getPath(), 4);
390
-			if (count($parts) === 4) {
391
-				$owner = $parts[1];
392
-			}
393
-		}
394
-
395
-		if ($owner) {
396
-			$path = $this->rootFolder
397
-				->getUserFolder($owner)
398
-				->getRelativePath($node->getPath());
399
-
400
-			if ($path !== null) {
401
-				return $path;
402
-			}
403
-		}
404
-
405
-		return null;
406
-	}
45
+    /**
46
+     * @var array<int, array>
47
+     */
48
+    private array $writeHookInfo = [];
49
+    /**
50
+     * @var array<int, Node>
51
+     */
52
+    private array $nodesTouched = [];
53
+    /**
54
+     * @var array<string, Node>
55
+     */
56
+    private array $versionsDeleted = [];
57
+
58
+    public function __construct(
59
+        private IRootFolder $rootFolder,
60
+        private IVersionManager $versionManager,
61
+        private IMimeTypeLoader $mimeTypeLoader,
62
+        private IUserSession $userSession,
63
+        private LoggerInterface $logger,
64
+    ) {
65
+    }
66
+
67
+    public function handle(Event $event): void {
68
+        if ($event instanceof NodeCreatedEvent) {
69
+            $this->created($event->getNode());
70
+        }
71
+
72
+        if ($event instanceof BeforeNodeTouchedEvent) {
73
+            $this->pre_touch_hook($event->getNode());
74
+        }
75
+
76
+        if ($event instanceof NodeTouchedEvent) {
77
+            $this->touch_hook($event->getNode());
78
+        }
79
+
80
+        if ($event instanceof BeforeNodeWrittenEvent) {
81
+            $this->write_hook($event->getNode());
82
+        }
83
+
84
+        if ($event instanceof NodeWrittenEvent) {
85
+            $this->post_write_hook($event->getNode());
86
+        }
87
+
88
+        if ($event instanceof BeforeNodeDeletedEvent) {
89
+            $this->pre_remove_hook($event->getNode());
90
+        }
91
+
92
+        if ($event instanceof NodeDeletedEvent) {
93
+            $this->remove_hook($event->getNode());
94
+        }
95
+
96
+        if ($event instanceof NodeRenamedEvent) {
97
+            $this->rename_hook($event->getSource(), $event->getTarget());
98
+        }
99
+
100
+        if ($event instanceof NodeCopiedEvent) {
101
+            $this->copy_hook($event->getSource(), $event->getTarget());
102
+        }
103
+
104
+        if ($event instanceof BeforeNodeRenamedEvent) {
105
+            $this->pre_renameOrCopy_hook($event->getSource(), $event->getTarget());
106
+        }
107
+
108
+        if ($event instanceof BeforeNodeCopiedEvent) {
109
+            $this->pre_renameOrCopy_hook($event->getSource(), $event->getTarget());
110
+        }
111
+    }
112
+
113
+    public function pre_touch_hook(Node $node): void {
114
+        // Do not handle folders.
115
+        if ($node instanceof Folder) {
116
+            return;
117
+        }
118
+
119
+        // $node is a non-existing on file creation.
120
+        if ($node instanceof NonExistingFile) {
121
+            return;
122
+        }
123
+
124
+        $this->nodesTouched[$node->getId()] = $node;
125
+    }
126
+
127
+    public function touch_hook(Node $node): void {
128
+        $previousNode = $this->nodesTouched[$node->getId()] ?? null;
129
+
130
+        if ($previousNode === null) {
131
+            return;
132
+        }
133
+
134
+        unset($this->nodesTouched[$node->getId()]);
135
+
136
+        try {
137
+            if ($node instanceof File && $this->versionManager instanceof INeedSyncVersionBackend) {
138
+                // We update the timestamp of the version entity associated with the previousNode.
139
+                $this->versionManager->updateVersionEntity($node, $previousNode->getMTime(), ['timestamp' => $node->getMTime()]);
140
+            }
141
+        } catch (DbalException $ex) {
142
+            // Ignore UniqueConstraintViolationException, as we are probably in the middle of a rollback
143
+            // Where the previous node would temporary have the mtime of the old version, so the rollback touches it to fix it.
144
+            if (!($ex->getPrevious() instanceof UniqueConstraintViolationException)) {
145
+                throw $ex;
146
+            }
147
+        } catch (DoesNotExistException $ex) {
148
+            // Ignore DoesNotExistException, as we are probably in the middle of a rollback
149
+            // Where the previous node would temporary have a wrong mtime, so the rollback touches it to fix it.
150
+        }
151
+    }
152
+
153
+    public function created(Node $node): void {
154
+        // Do not handle folders.
155
+        if ($node instanceof File && $this->versionManager instanceof INeedSyncVersionBackend) {
156
+            $this->versionManager->createVersionEntity($node);
157
+        }
158
+    }
159
+
160
+    /**
161
+     * listen to write event.
162
+     */
163
+    public function write_hook(Node $node): void {
164
+        // Do not handle folders.
165
+        if ($node instanceof Folder) {
166
+            return;
167
+        }
168
+
169
+        // $node is a non-existing on file creation.
170
+        if ($node instanceof NonExistingFile) {
171
+            return;
172
+        }
173
+
174
+        $path = $this->getPathForNode($node);
175
+        $result = Storage::store($path);
176
+
177
+        // Store the result of the version creation so it can be used in post_write_hook.
178
+        $this->writeHookInfo[$node->getId()] = [
179
+            'previousNode' => $node,
180
+            'versionCreated' => $result !== false
181
+        ];
182
+    }
183
+
184
+    /**
185
+     * listen to post_write event.
186
+     */
187
+    public function post_write_hook(Node $node): void {
188
+        // Do not handle folders.
189
+        if ($node instanceof Folder) {
190
+            return;
191
+        }
192
+
193
+        $writeHookInfo = $this->writeHookInfo[$node->getId()] ?? null;
194
+
195
+        if ($writeHookInfo === null) {
196
+            return;
197
+        }
198
+
199
+        if (
200
+            $writeHookInfo['versionCreated']
201
+            && $node->getMTime() !== $writeHookInfo['previousNode']->getMTime()
202
+        ) {
203
+            // If a new version was created, insert a version in the DB for the current content.
204
+            // If both versions have the same mtime, it means the latest version file simply got overrode,
205
+            // so no need to create a new version.
206
+            $this->created($node);
207
+        } else {
208
+            try {
209
+                // If no new version was stored in the FS, no new version should be added in the DB.
210
+                // So we simply update the associated version.
211
+                if ($node instanceof File && $this->versionManager instanceof INeedSyncVersionBackend) {
212
+                    $this->versionManager->updateVersionEntity(
213
+                        $node,
214
+                        $writeHookInfo['previousNode']->getMtime(),
215
+                        [
216
+                            'timestamp' => $node->getMTime(),
217
+                            'size' => $node->getSize(),
218
+                            'mimetype' => $this->mimeTypeLoader->getId($node->getMimetype()),
219
+                        ],
220
+                    );
221
+                }
222
+            } catch (DoesNotExistException $e) {
223
+                // This happens if the versions app was not enabled while the file was created or updated the last time.
224
+                // meaning there is no such revision and we need to create this file.
225
+                if ($writeHookInfo['versionCreated']) {
226
+                    $this->created($node);
227
+                } else {
228
+                    // Normally this should not happen so we re-throw the exception to not hide any potential issues.
229
+                    throw $e;
230
+                }
231
+            } catch (Exception $e) {
232
+                $this->logger->error('Failed to update existing version for ' . $node->getPath(), [
233
+                    'exception' => $e,
234
+                    'versionCreated' => $writeHookInfo['versionCreated'],
235
+                    'previousNode' => [
236
+                        'size' => $writeHookInfo['previousNode']->getSize(),
237
+                        'mtime' => $writeHookInfo['previousNode']->getMTime(),
238
+                    ],
239
+                    'node' => [
240
+                        'size' => $node->getSize(),
241
+                        'mtime' => $node->getMTime(),
242
+                    ]
243
+                ]);
244
+                throw $e;
245
+            }
246
+        }
247
+
248
+        unset($this->writeHookInfo[$node->getId()]);
249
+    }
250
+
251
+    /**
252
+     * Erase versions of deleted file
253
+     *
254
+     * This function is connected to the NodeDeletedEvent event
255
+     * cleanup the versions directory if the actual file gets deleted
256
+     */
257
+    public function remove_hook(Node $node): void {
258
+        // Need to normalize the path as there is an issue with path concatenation in View.php::getAbsolutePath.
259
+        $path = Filesystem::normalizePath($node->getPath());
260
+        if (!array_key_exists($path, $this->versionsDeleted)) {
261
+            return;
262
+        }
263
+        $node = $this->versionsDeleted[$path];
264
+        $relativePath = $this->getPathForNode($node);
265
+        unset($this->versionsDeleted[$path]);
266
+        Storage::delete($relativePath);
267
+        // If no new version was stored in the FS, no new version should be added in the DB.
268
+        // So we simply update the associated version.
269
+        if ($node instanceof File && $this->versionManager instanceof INeedSyncVersionBackend) {
270
+            $this->versionManager->deleteVersionsEntity($node);
271
+        }
272
+    }
273
+
274
+    /**
275
+     * mark file as "deleted" so that we can clean up the versions if the file is gone
276
+     */
277
+    public function pre_remove_hook(Node $node): void {
278
+        $path = $this->getPathForNode($node);
279
+        Storage::markDeletedFile($path);
280
+        $this->versionsDeleted[$node->getPath()] = $node;
281
+    }
282
+
283
+    /**
284
+     * rename/move versions of renamed/moved files
285
+     *
286
+     * This function is connected to the NodeRenamedEvent event and adjust the name and location
287
+     * of the stored versions along the actual file
288
+     */
289
+    public function rename_hook(Node $source, Node $target): void {
290
+        $sourceBackend = $this->versionManager->getBackendForStorage($source->getParent()->getStorage());
291
+        $targetBackend = $this->versionManager->getBackendForStorage($target->getStorage());
292
+        // If different backends, do nothing.
293
+        if ($sourceBackend !== $targetBackend) {
294
+            return;
295
+        }
296
+
297
+        $oldPath = $this->getPathForNode($source);
298
+        $newPath = $this->getPathForNode($target);
299
+        Storage::renameOrCopy($oldPath, $newPath, 'rename');
300
+    }
301
+
302
+    /**
303
+     * copy versions of copied files
304
+     *
305
+     * This function is connected to the NodeCopiedEvent event and copies the
306
+     * the stored versions to the new location
307
+     */
308
+    public function copy_hook(Node $source, Node $target): void {
309
+        $sourceBackend = $this->versionManager->getBackendForStorage($source->getParent()->getStorage());
310
+        $targetBackend = $this->versionManager->getBackendForStorage($target->getStorage());
311
+        // If different backends, do nothing.
312
+        if ($sourceBackend !== $targetBackend) {
313
+            return;
314
+        }
315
+
316
+        $oldPath = $this->getPathForNode($source);
317
+        $newPath = $this->getPathForNode($target);
318
+        Storage::renameOrCopy($oldPath, $newPath, 'copy');
319
+    }
320
+
321
+    /**
322
+     * Remember owner and the owner path of the source file.
323
+     * If the file already exists, then it was a upload of a existing file
324
+     * over the web interface and we call Storage::store() directly
325
+     *
326
+     *
327
+     */
328
+    public function pre_renameOrCopy_hook(Node $source, Node $target): void {
329
+        $sourceBackend = $this->versionManager->getBackendForStorage($source->getStorage());
330
+        $targetBackend = $this->versionManager->getBackendForStorage($target->getParent()->getStorage());
331
+        // If different backends, do nothing.
332
+        if ($sourceBackend !== $targetBackend) {
333
+            return;
334
+        }
335
+
336
+        // if we rename a movable mount point, then the versions don't have to be renamed
337
+        $oldPath = $this->getPathForNode($source);
338
+        $newPath = $this->getPathForNode($target);
339
+        if ($oldPath === null || $newPath === null) {
340
+            return;
341
+        }
342
+
343
+        $user = $this->userSession->getUser()?->getUID();
344
+        if ($user === null) {
345
+            return;
346
+        }
347
+
348
+        $absOldPath = Filesystem::normalizePath('/' . $user . '/files' . $oldPath);
349
+        $manager = Filesystem::getMountManager();
350
+        $mount = $manager->find($absOldPath);
351
+        $internalPath = $mount->getInternalPath($absOldPath);
352
+        if ($internalPath === '' and $mount instanceof MoveableMount) {
353
+            return;
354
+        }
355
+
356
+        $view = new View($user . '/files');
357
+        if ($view->file_exists($newPath)) {
358
+            Storage::store($newPath);
359
+        } else {
360
+            Storage::setSourcePathAndUser($oldPath);
361
+        }
362
+    }
363
+
364
+    /**
365
+     * Retrieve the path relative to the current user root folder.
366
+     * If no user is connected, try to use the node's owner.
367
+     */
368
+    private function getPathForNode(Node $node): ?string {
369
+        $user = $this->userSession->getUser()?->getUID();
370
+        if ($user) {
371
+            $path = $this->rootFolder
372
+                ->getUserFolder($user)
373
+                ->getRelativePath($node->getPath());
374
+
375
+            if ($path !== null) {
376
+                return $path;
377
+            }
378
+        }
379
+
380
+        try {
381
+            $owner = $node->getOwner()?->getUid();
382
+        } catch (NotFoundException) {
383
+            $owner = null;
384
+        }
385
+
386
+        // If no owner, extract it from the path.
387
+        // e.g. /user/files/foobar.txt
388
+        if (!$owner) {
389
+            $parts = explode('/', $node->getPath(), 4);
390
+            if (count($parts) === 4) {
391
+                $owner = $parts[1];
392
+            }
393
+        }
394
+
395
+        if ($owner) {
396
+            $path = $this->rootFolder
397
+                ->getUserFolder($owner)
398
+                ->getRelativePath($node->getPath());
399
+
400
+            if ($path !== null) {
401
+                return $path;
402
+            }
403
+        }
404
+
405
+        return null;
406
+    }
407 407
 }
Please login to merge, or discard this patch.
apps/files_sharing/tests/TestCase.php 2 patches
Indentation   +210 added lines, -210 removed lines patch added patch discarded remove patch
@@ -33,214 +33,214 @@
 block discarded – undo
33 33
  * Base class for sharing tests.
34 34
  */
35 35
 abstract class TestCase extends \Test\TestCase {
36
-	use MountProviderTrait;
37
-
38
-	public const TEST_FILES_SHARING_API_USER1 = 'test-share-user1';
39
-	public const TEST_FILES_SHARING_API_USER2 = 'test-share-user2';
40
-	public const TEST_FILES_SHARING_API_USER3 = 'test-share-user3';
41
-	public const TEST_FILES_SHARING_API_USER4 = 'test-share-user4';
42
-
43
-	public const TEST_FILES_SHARING_API_GROUP1 = 'test-share-group1';
44
-
45
-	public $filename;
46
-	public $data;
47
-	/**
48
-	 * @var View
49
-	 */
50
-	public $view;
51
-	/**
52
-	 * @var View
53
-	 */
54
-	public $view2;
55
-	public $folder;
56
-	public $subfolder;
57
-
58
-	/** @var \OCP\Share\IManager */
59
-	protected $shareManager;
60
-	/** @var IRootFolder */
61
-	protected $rootFolder;
62
-
63
-	public static function setUpBeforeClass(): void {
64
-		parent::setUpBeforeClass();
65
-
66
-		$app = new Application();
67
-		$app->registerMountProviders(
68
-			Server::get(IMountProviderCollection::class),
69
-			Server::get(MountProvider::class),
70
-			Server::get(ExternalMountProvider::class),
71
-		);
72
-
73
-		// reset backend
74
-		Server::get(IUserManager::class)->clearBackends();
75
-		Server::get(IGroupManager::class)->clearBackends();
76
-
77
-		// clear share hooks
78
-		\OC_Hook::clear('OCP\\Share');
79
-		\OC::registerShareHooks(Server::get(SystemConfig::class));
80
-
81
-		// create users
82
-		$backend = new \Test\Util\User\Dummy();
83
-		Server::get(IUserManager::class)->registerBackend($backend);
84
-		$backend->createUser(self::TEST_FILES_SHARING_API_USER1, self::TEST_FILES_SHARING_API_USER1);
85
-		$backend->createUser(self::TEST_FILES_SHARING_API_USER2, self::TEST_FILES_SHARING_API_USER2);
86
-		$backend->createUser(self::TEST_FILES_SHARING_API_USER3, self::TEST_FILES_SHARING_API_USER3);
87
-		$backend->createUser(self::TEST_FILES_SHARING_API_USER4, self::TEST_FILES_SHARING_API_USER4);
88
-
89
-		// create group
90
-		$groupBackend = new \Test\Util\Group\Dummy();
91
-		$groupBackend->createGroup(self::TEST_FILES_SHARING_API_GROUP1);
92
-		$groupBackend->createGroup('group');
93
-		$groupBackend->createGroup('group1');
94
-		$groupBackend->createGroup('group2');
95
-		$groupBackend->createGroup('group3');
96
-		$groupBackend->addToGroup(self::TEST_FILES_SHARING_API_USER1, 'group');
97
-		$groupBackend->addToGroup(self::TEST_FILES_SHARING_API_USER2, 'group');
98
-		$groupBackend->addToGroup(self::TEST_FILES_SHARING_API_USER3, 'group');
99
-		$groupBackend->addToGroup(self::TEST_FILES_SHARING_API_USER2, 'group1');
100
-		$groupBackend->addToGroup(self::TEST_FILES_SHARING_API_USER3, 'group2');
101
-		$groupBackend->addToGroup(self::TEST_FILES_SHARING_API_USER4, 'group3');
102
-		$groupBackend->addToGroup(self::TEST_FILES_SHARING_API_USER2, self::TEST_FILES_SHARING_API_GROUP1);
103
-		Server::get(IGroupManager::class)->addBackend($groupBackend);
104
-	}
105
-
106
-	protected function setUp(): void {
107
-		parent::setUp();
108
-		Server::get(DisplayNameCache::class)->clear();
109
-
110
-		//login as user1
111
-		$this->loginHelper(self::TEST_FILES_SHARING_API_USER1);
112
-
113
-		$this->data = 'foobar';
114
-		$this->view = new View('/' . self::TEST_FILES_SHARING_API_USER1 . '/files');
115
-		$this->view2 = new View('/' . self::TEST_FILES_SHARING_API_USER2 . '/files');
116
-
117
-		$this->shareManager = Server::get(\OCP\Share\IManager::class);
118
-		$this->rootFolder = Server::get(IRootFolder::class);
119
-	}
120
-
121
-	protected function tearDown(): void {
122
-		$qb = Server::get(IDBConnection::class)->getQueryBuilder();
123
-		$qb->delete('share');
124
-		$qb->execute();
125
-
126
-		$qb = Server::get(IDBConnection::class)->getQueryBuilder();
127
-		$qb->delete('mounts');
128
-		$qb->execute();
129
-
130
-		$qb = Server::get(IDBConnection::class)->getQueryBuilder();
131
-		$qb->delete('filecache')->runAcrossAllShards();
132
-		$qb->execute();
133
-
134
-		parent::tearDown();
135
-	}
136
-
137
-	public static function tearDownAfterClass(): void {
138
-		// cleanup users
139
-		$user = Server::get(IUserManager::class)->get(self::TEST_FILES_SHARING_API_USER1);
140
-		if ($user !== null) {
141
-			$user->delete();
142
-		}
143
-		$user = Server::get(IUserManager::class)->get(self::TEST_FILES_SHARING_API_USER2);
144
-		if ($user !== null) {
145
-			$user->delete();
146
-		}
147
-		$user = Server::get(IUserManager::class)->get(self::TEST_FILES_SHARING_API_USER3);
148
-		if ($user !== null) {
149
-			$user->delete();
150
-		}
151
-
152
-		// delete group
153
-		$group = Server::get(IGroupManager::class)->get(self::TEST_FILES_SHARING_API_GROUP1);
154
-		if ($group) {
155
-			$group->delete();
156
-		}
157
-
158
-		\OC_Util::tearDownFS();
159
-		\OC_User::setUserId('');
160
-		Filesystem::tearDown();
161
-
162
-		// reset backend
163
-		Server::get(IUserManager::class)->clearBackends();
164
-		Server::get(IUserManager::class)->registerBackend(new \OC\User\Database());
165
-		Server::get(IGroupManager::class)->clearBackends();
166
-		Server::get(IGroupManager::class)->addBackend(new Database());
167
-
168
-		parent::tearDownAfterClass();
169
-	}
170
-
171
-	/**
172
-	 * @param string $user
173
-	 * @param bool $create
174
-	 * @param bool $password
175
-	 */
176
-	protected function loginHelper($user, $create = false, $password = false) {
177
-		if ($password === false) {
178
-			$password = $user;
179
-		}
180
-
181
-		if ($create) {
182
-			$userManager = Server::get(IUserManager::class);
183
-			$groupManager = Server::get(IGroupManager::class);
184
-
185
-			$userObject = $userManager->createUser($user, $password);
186
-			$group = $groupManager->createGroup('group');
187
-
188
-			if ($group && $userObject) {
189
-				$group->addUser($userObject);
190
-			}
191
-		}
192
-
193
-		\OC_Util::tearDownFS();
194
-		Storage::getGlobalCache()->clearCache();
195
-		Server::get(IUserSession::class)->setUser(null);
196
-		Filesystem::tearDown();
197
-		Server::get(IUserSession::class)->login($user, $password);
198
-		\OC::$server->getUserFolder($user);
199
-
200
-		\OC_Util::setupFS($user);
201
-	}
202
-
203
-	/**
204
-	 * get some information from a given share
205
-	 * @param int $shareID
206
-	 * @return array with: item_source, share_type, share_with, item_type, permissions
207
-	 */
208
-	protected function getShareFromId($shareID) {
209
-		$qb = Server::get(IDBConnection::class)->getQueryBuilder();
210
-		$qb->select('item_source', '`share_type', 'share_with', 'item_type', 'permissions')
211
-			->from('share')
212
-			->where(
213
-				$qb->expr()->eq('id', $qb->createNamedParameter($shareID))
214
-			);
215
-		$result = $qb->execute();
216
-		$share = $result->fetch();
217
-		$result->closeCursor();
218
-
219
-		return $share;
220
-	}
221
-
222
-	/**
223
-	 * @param int $type The share type
224
-	 * @param string $path The path to share relative to $initiators root
225
-	 * @param string $initiator
226
-	 * @param string $recipient
227
-	 * @param int $permissions
228
-	 * @return IShare
229
-	 */
230
-	protected function share($type, $path, $initiator, $recipient, $permissions) {
231
-		$userFolder = $this->rootFolder->getUserFolder($initiator);
232
-		$node = $userFolder->get($path);
233
-
234
-		$share = $this->shareManager->newShare();
235
-		$share->setShareType($type)
236
-			->setSharedWith($recipient)
237
-			->setSharedBy($initiator)
238
-			->setNode($node)
239
-			->setPermissions($permissions);
240
-		$share = $this->shareManager->createShare($share);
241
-		$share->setStatus(IShare::STATUS_ACCEPTED);
242
-		$share = $this->shareManager->updateShare($share);
243
-
244
-		return $share;
245
-	}
36
+    use MountProviderTrait;
37
+
38
+    public const TEST_FILES_SHARING_API_USER1 = 'test-share-user1';
39
+    public const TEST_FILES_SHARING_API_USER2 = 'test-share-user2';
40
+    public const TEST_FILES_SHARING_API_USER3 = 'test-share-user3';
41
+    public const TEST_FILES_SHARING_API_USER4 = 'test-share-user4';
42
+
43
+    public const TEST_FILES_SHARING_API_GROUP1 = 'test-share-group1';
44
+
45
+    public $filename;
46
+    public $data;
47
+    /**
48
+     * @var View
49
+     */
50
+    public $view;
51
+    /**
52
+     * @var View
53
+     */
54
+    public $view2;
55
+    public $folder;
56
+    public $subfolder;
57
+
58
+    /** @var \OCP\Share\IManager */
59
+    protected $shareManager;
60
+    /** @var IRootFolder */
61
+    protected $rootFolder;
62
+
63
+    public static function setUpBeforeClass(): void {
64
+        parent::setUpBeforeClass();
65
+
66
+        $app = new Application();
67
+        $app->registerMountProviders(
68
+            Server::get(IMountProviderCollection::class),
69
+            Server::get(MountProvider::class),
70
+            Server::get(ExternalMountProvider::class),
71
+        );
72
+
73
+        // reset backend
74
+        Server::get(IUserManager::class)->clearBackends();
75
+        Server::get(IGroupManager::class)->clearBackends();
76
+
77
+        // clear share hooks
78
+        \OC_Hook::clear('OCP\\Share');
79
+        \OC::registerShareHooks(Server::get(SystemConfig::class));
80
+
81
+        // create users
82
+        $backend = new \Test\Util\User\Dummy();
83
+        Server::get(IUserManager::class)->registerBackend($backend);
84
+        $backend->createUser(self::TEST_FILES_SHARING_API_USER1, self::TEST_FILES_SHARING_API_USER1);
85
+        $backend->createUser(self::TEST_FILES_SHARING_API_USER2, self::TEST_FILES_SHARING_API_USER2);
86
+        $backend->createUser(self::TEST_FILES_SHARING_API_USER3, self::TEST_FILES_SHARING_API_USER3);
87
+        $backend->createUser(self::TEST_FILES_SHARING_API_USER4, self::TEST_FILES_SHARING_API_USER4);
88
+
89
+        // create group
90
+        $groupBackend = new \Test\Util\Group\Dummy();
91
+        $groupBackend->createGroup(self::TEST_FILES_SHARING_API_GROUP1);
92
+        $groupBackend->createGroup('group');
93
+        $groupBackend->createGroup('group1');
94
+        $groupBackend->createGroup('group2');
95
+        $groupBackend->createGroup('group3');
96
+        $groupBackend->addToGroup(self::TEST_FILES_SHARING_API_USER1, 'group');
97
+        $groupBackend->addToGroup(self::TEST_FILES_SHARING_API_USER2, 'group');
98
+        $groupBackend->addToGroup(self::TEST_FILES_SHARING_API_USER3, 'group');
99
+        $groupBackend->addToGroup(self::TEST_FILES_SHARING_API_USER2, 'group1');
100
+        $groupBackend->addToGroup(self::TEST_FILES_SHARING_API_USER3, 'group2');
101
+        $groupBackend->addToGroup(self::TEST_FILES_SHARING_API_USER4, 'group3');
102
+        $groupBackend->addToGroup(self::TEST_FILES_SHARING_API_USER2, self::TEST_FILES_SHARING_API_GROUP1);
103
+        Server::get(IGroupManager::class)->addBackend($groupBackend);
104
+    }
105
+
106
+    protected function setUp(): void {
107
+        parent::setUp();
108
+        Server::get(DisplayNameCache::class)->clear();
109
+
110
+        //login as user1
111
+        $this->loginHelper(self::TEST_FILES_SHARING_API_USER1);
112
+
113
+        $this->data = 'foobar';
114
+        $this->view = new View('/' . self::TEST_FILES_SHARING_API_USER1 . '/files');
115
+        $this->view2 = new View('/' . self::TEST_FILES_SHARING_API_USER2 . '/files');
116
+
117
+        $this->shareManager = Server::get(\OCP\Share\IManager::class);
118
+        $this->rootFolder = Server::get(IRootFolder::class);
119
+    }
120
+
121
+    protected function tearDown(): void {
122
+        $qb = Server::get(IDBConnection::class)->getQueryBuilder();
123
+        $qb->delete('share');
124
+        $qb->execute();
125
+
126
+        $qb = Server::get(IDBConnection::class)->getQueryBuilder();
127
+        $qb->delete('mounts');
128
+        $qb->execute();
129
+
130
+        $qb = Server::get(IDBConnection::class)->getQueryBuilder();
131
+        $qb->delete('filecache')->runAcrossAllShards();
132
+        $qb->execute();
133
+
134
+        parent::tearDown();
135
+    }
136
+
137
+    public static function tearDownAfterClass(): void {
138
+        // cleanup users
139
+        $user = Server::get(IUserManager::class)->get(self::TEST_FILES_SHARING_API_USER1);
140
+        if ($user !== null) {
141
+            $user->delete();
142
+        }
143
+        $user = Server::get(IUserManager::class)->get(self::TEST_FILES_SHARING_API_USER2);
144
+        if ($user !== null) {
145
+            $user->delete();
146
+        }
147
+        $user = Server::get(IUserManager::class)->get(self::TEST_FILES_SHARING_API_USER3);
148
+        if ($user !== null) {
149
+            $user->delete();
150
+        }
151
+
152
+        // delete group
153
+        $group = Server::get(IGroupManager::class)->get(self::TEST_FILES_SHARING_API_GROUP1);
154
+        if ($group) {
155
+            $group->delete();
156
+        }
157
+
158
+        \OC_Util::tearDownFS();
159
+        \OC_User::setUserId('');
160
+        Filesystem::tearDown();
161
+
162
+        // reset backend
163
+        Server::get(IUserManager::class)->clearBackends();
164
+        Server::get(IUserManager::class)->registerBackend(new \OC\User\Database());
165
+        Server::get(IGroupManager::class)->clearBackends();
166
+        Server::get(IGroupManager::class)->addBackend(new Database());
167
+
168
+        parent::tearDownAfterClass();
169
+    }
170
+
171
+    /**
172
+     * @param string $user
173
+     * @param bool $create
174
+     * @param bool $password
175
+     */
176
+    protected function loginHelper($user, $create = false, $password = false) {
177
+        if ($password === false) {
178
+            $password = $user;
179
+        }
180
+
181
+        if ($create) {
182
+            $userManager = Server::get(IUserManager::class);
183
+            $groupManager = Server::get(IGroupManager::class);
184
+
185
+            $userObject = $userManager->createUser($user, $password);
186
+            $group = $groupManager->createGroup('group');
187
+
188
+            if ($group && $userObject) {
189
+                $group->addUser($userObject);
190
+            }
191
+        }
192
+
193
+        \OC_Util::tearDownFS();
194
+        Storage::getGlobalCache()->clearCache();
195
+        Server::get(IUserSession::class)->setUser(null);
196
+        Filesystem::tearDown();
197
+        Server::get(IUserSession::class)->login($user, $password);
198
+        \OC::$server->getUserFolder($user);
199
+
200
+        \OC_Util::setupFS($user);
201
+    }
202
+
203
+    /**
204
+     * get some information from a given share
205
+     * @param int $shareID
206
+     * @return array with: item_source, share_type, share_with, item_type, permissions
207
+     */
208
+    protected function getShareFromId($shareID) {
209
+        $qb = Server::get(IDBConnection::class)->getQueryBuilder();
210
+        $qb->select('item_source', '`share_type', 'share_with', 'item_type', 'permissions')
211
+            ->from('share')
212
+            ->where(
213
+                $qb->expr()->eq('id', $qb->createNamedParameter($shareID))
214
+            );
215
+        $result = $qb->execute();
216
+        $share = $result->fetch();
217
+        $result->closeCursor();
218
+
219
+        return $share;
220
+    }
221
+
222
+    /**
223
+     * @param int $type The share type
224
+     * @param string $path The path to share relative to $initiators root
225
+     * @param string $initiator
226
+     * @param string $recipient
227
+     * @param int $permissions
228
+     * @return IShare
229
+     */
230
+    protected function share($type, $path, $initiator, $recipient, $permissions) {
231
+        $userFolder = $this->rootFolder->getUserFolder($initiator);
232
+        $node = $userFolder->get($path);
233
+
234
+        $share = $this->shareManager->newShare();
235
+        $share->setShareType($type)
236
+            ->setSharedWith($recipient)
237
+            ->setSharedBy($initiator)
238
+            ->setNode($node)
239
+            ->setPermissions($permissions);
240
+        $share = $this->shareManager->createShare($share);
241
+        $share->setStatus(IShare::STATUS_ACCEPTED);
242
+        $share = $this->shareManager->updateShare($share);
243
+
244
+        return $share;
245
+    }
246 246
 }
Please login to merge, or discard this patch.
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -111,8 +111,8 @@
 block discarded – undo
111 111
 		$this->loginHelper(self::TEST_FILES_SHARING_API_USER1);
112 112
 
113 113
 		$this->data = 'foobar';
114
-		$this->view = new View('/' . self::TEST_FILES_SHARING_API_USER1 . '/files');
115
-		$this->view2 = new View('/' . self::TEST_FILES_SHARING_API_USER2 . '/files');
114
+		$this->view = new View('/'.self::TEST_FILES_SHARING_API_USER1.'/files');
115
+		$this->view2 = new View('/'.self::TEST_FILES_SHARING_API_USER2.'/files');
116 116
 
117 117
 		$this->shareManager = Server::get(\OCP\Share\IManager::class);
118 118
 		$this->rootFolder = Server::get(IRootFolder::class);
Please login to merge, or discard this patch.