[BUGFIX] EM unit test is broken
[Packages/TYPO3.CMS.git] / typo3 / sysext / extensionmanager / Tests / Unit / Utility / FileHandlingUtilityTest.php
1 <?php
2 namespace TYPO3\CMS\Extensionmanager\Tests\Unit\Utility;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 2012
8 * All rights reserved
9 *
10 * This script is part of the TYPO3 project. The TYPO3 project is
11 * free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * The GNU General Public License can be found at
17 * http://www.gnu.org/copyleft/gpl.html.
18 *
19 * This script is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * This copyright notice MUST APPEAR in all copies of the script!
25 ***************************************************************/
26
27 /**
28 * Testcase
29 *
30 */
31 class FileHandlingUtilityTest extends \TYPO3\CMS\Extbase\Tests\Unit\BaseTestCase {
32
33 /**
34 * @var array List of created fake extensions to be deleted in tearDown() again
35 */
36 protected $fakedExtensions = array();
37
38 /**
39 * @var array List of resources (files or empty directories) that need to be removed in tearDown() again
40 */
41 protected $resourcesToRemove = array();
42
43 /**
44 * @return void
45 */
46 public function tearDown() {
47 foreach ($this->fakedExtensions as $extension => $dummy) {
48 \TYPO3\CMS\Core\Utility\GeneralUtility::rmdir(PATH_site . 'typo3conf/ext/' . $extension, TRUE);
49 }
50 foreach ($this->resourcesToRemove as $resource) {
51 if (file_exists($resource) && is_file($resource)) {
52 unlink($resource);
53 } elseif(file_exists($resource) && is_dir($resource)) {
54 rmdir($resource);
55 }
56 }
57 }
58
59 /**
60 * Creates a fake extension inside typo3temp/. No configuration is created,
61 * just the folder
62 *
63 * @param bool $extkeyOnly
64 * @return string The extension key
65 */
66 protected function createFakeExtension($extkeyOnly = FALSE) {
67 $extKey = strtolower(uniqid('testing'));
68 $absExtPath = PATH_site . 'typo3conf/ext/' . $extKey . '/';
69 $relPath = 'typo3conf/ext/' . $extKey . '/';
70 $this->fakedExtensions[$extKey] = array(
71 'siteRelPath' => $relPath,
72 'siteAbsPath' => $absExtPath
73 );
74 if ($extkeyOnly === TRUE) {
75 return $extKey;
76 }
77 \TYPO3\CMS\Core\Utility\GeneralUtility::mkdir($absExtPath);
78 return $extKey;
79 }
80
81 /**
82 * @test
83 * @return void
84 */
85 public function makeAndClearExtensionDirRemovesExtensionDirIfAlreadyExists() {
86 $extKey = $this->createFakeExtension();
87 $fileHandlerMock = $this->getAccessibleMock('TYPO3\\CMS\\Extensionmanager\\Utility\\FileHandlingUtility', array('removeDirectory', 'addDirectory'));
88 $fileHandlerMock->expects($this->once())->method('removeDirectory')->with(PATH_site . 'typo3conf/ext/' . $extKey . '/');
89 $fileHandlerMock->_call('makeAndClearExtensionDir', $extKey);
90 }
91
92 /**
93 * @return array
94 */
95 public function invalidRelativePathDataProvider() {
96 return array(
97 array('../../'),
98 array('/foo/bar'),
99 array('foo//bar'),
100 array('foo/bar' . chr(0)),
101 );
102 }
103
104 /**
105 * @param string $invalidRelativePath
106 * @test
107 * @dataProvider invalidRelativePathDataProvider
108 * @expectedException \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException
109 */
110 public function getAbsolutePathThrowsExceptionForInvalidRelativePaths($invalidRelativePath) {
111 $fileHandlerMock = $this->getAccessibleMock('TYPO3\\CMS\\Extensionmanager\\Utility\\FileHandlingUtility', array('dummy'));
112 $fileHandlerMock->_call('getAbsolutePath', $invalidRelativePath);
113 }
114
115 /**
116 * @return array
117 */
118 public function validRelativePathDataProvider() {
119 return array(
120 array('foo/../bar', PATH_site . 'bar'),
121 array('bas', PATH_site . 'bas'),
122 );
123 }
124
125 /**
126 * @param string $validRelativePath
127 * @param string $expectedAbsolutePath
128 * @test
129 * @dataProvider validRelativePathDataProvider
130 */
131 public function getAbsolutePathReturnsAbsolutePathForValidRelativePaths($validRelativePath, $expectedAbsolutePath) {
132 $fileHandlerMock = $this->getAccessibleMock('TYPO3\\CMS\\Extensionmanager\\Utility\\FileHandlingUtility', array('dummy'));
133 $this->assertSame($expectedAbsolutePath, $fileHandlerMock->_call('getAbsolutePath', $validRelativePath));
134 }
135
136 /**
137 * @test
138 * @return void
139 */
140 public function makeAndClearExtensionDirAddsDir() {
141 $extKey = $this->createFakeExtension();
142 $fileHandlerMock = $this->getAccessibleMock('TYPO3\\CMS\\Extensionmanager\\Utility\\FileHandlingUtility', array('removeDirectory', 'addDirectory'));
143 $fileHandlerMock->expects($this->once())->method('addDirectory')->with(PATH_site . 'typo3conf/ext/' . $extKey . '/');
144 $fileHandlerMock->_call('makeAndClearExtensionDir', $extKey);
145 }
146
147 /**
148 * @test
149 * @expectedException \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException
150 * @return void
151 */
152 public function makeAndClearExtensionDirThrowsExceptionOnInvalidPath() {
153 $fileHandlerMock = $this->getAccessibleMock('TYPO3\\CMS\\Extensionmanager\\Utility\\FileHandlingUtility', array('removeDirectory', 'addDirectory'));
154 $fileHandlerMock->_call('makeAndClearExtensionDir', 'testing123', 'fakepath');
155 }
156
157 /**
158 * @test
159 * @return void
160 */
161 public function addDirectoryAddsDirectory() {
162 $extDirPath = $this->fakedExtensions[$this->createFakeExtension(TRUE)]['siteAbsPath'];
163 $fileHandlerMock = $this->getAccessibleMock('TYPO3\\CMS\\Extensionmanager\\Utility\\FileHandlingUtility', array('dummy'));
164 $this->assertFalse(is_dir($extDirPath));
165 $fileHandlerMock->_call('addDirectory', $extDirPath);
166 $this->assertTrue(is_dir($extDirPath));
167 }
168
169 /**
170 * @test
171 * @return void
172 */
173 public function removeDirectoryRemovesDirectory() {
174 $extDirPath = $this->fakedExtensions[$this->createFakeExtension()]['siteAbsPath'];
175 $fileHandlerMock = $this->getAccessibleMock('TYPO3\\CMS\\Extensionmanager\\Utility\\FileHandlingUtility', array('dummy'));
176 $this->assertTrue(is_dir($extDirPath));
177 $fileHandlerMock->_call('removeDirectory', $extDirPath);
178 $this->assertFalse(is_dir($extDirPath));
179 }
180
181 /**
182 * @test
183 * @return void
184 */
185 public function removeDirectoryRemovesSymlink() {
186 $absoluteSymlinkPath = PATH_site . 'typo3temp/' . uniqid('test_symlink_');
187 $absoluteFilePath = PATH_site . 'typo3temp/' . uniqid('test_file_');
188 touch($absoluteFilePath);
189 $this->resourcesToRemove[] = $absoluteFilePath;
190 symlink($absoluteFilePath, $absoluteSymlinkPath);
191 $fileHandler = new \TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility();
192 $fileHandler->removeDirectory($absoluteSymlinkPath);
193 $this->assertFalse(is_link($absoluteSymlinkPath));
194 }
195
196 /**
197 * @test
198 * @return void
199 */
200 public function removeDirectoryDoesNotRemoveContentOfSymlinkedTargetDirectory() {
201 $absoluteSymlinkPath = PATH_site . 'typo3temp/' . uniqid('test_symlink_');
202 $absoluteDirectoryPath = PATH_site . 'typo3temp/' . uniqid('test_dir_') . '/';
203 $relativeFilePath = uniqid('test_file_');
204
205 mkdir($absoluteDirectoryPath);
206 touch($absoluteDirectoryPath . $relativeFilePath);
207
208 $this->resourcesToRemove[] = $absoluteDirectoryPath . $relativeFilePath;
209 $this->resourcesToRemove[] = $absoluteDirectoryPath;
210
211 symlink($absoluteDirectoryPath, $absoluteSymlinkPath);
212
213 $fileHandler = new \TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility();
214 $fileHandler->removeDirectory($absoluteSymlinkPath);
215 $this->assertTrue(is_file($absoluteDirectoryPath . $relativeFilePath));
216 }
217
218 /**
219 * @test
220 * @return void
221 */
222 public function unpackExtensionFromExtensionDataArrayCreatesTheExtensionDirectory() {
223 $extensionData = array(
224 'extKey' => 'test'
225 );
226 $fileHandlerMock = $this->getAccessibleMock('TYPO3\\CMS\\Extensionmanager\\Utility\\FileHandlingUtility', array(
227 'makeAndClearExtensionDir',
228 'writeEmConfToFile',
229 'extractFilesArrayFromExtensionData',
230 'extractDirectoriesFromExtensionData',
231 'createDirectoriesForExtensionFiles',
232 'writeExtensionFiles'
233 ));
234 $fileHandlerMock->expects($this->once())->method('extractFilesArrayFromExtensionData')->will($this->returnValue(array()));
235 $fileHandlerMock->expects($this->once())->method('extractDirectoriesFromExtensionData')->will($this->returnValue(array()));
236 $fileHandlerMock->expects($this->once())->method('makeAndClearExtensionDir')->with($extensionData['extKey']);
237 $fileHandlerMock->_call('unpackExtensionFromExtensionDataArray', $extensionData);
238 }
239
240 /**
241 * @test
242 * @return void
243 */
244 public function extractFilesArrayFromExtensionDataReturnsFileArray() {
245 $extensionData = array(
246 'key' => 'test',
247 'FILES' => array(
248 'filename1' => 'dummycontent',
249 'filename2' => 'dummycontent2'
250 )
251 );
252 $fileHandlerMock = $this->getAccessibleMock('TYPO3\\CMS\\Extensionmanager\\Utility\\FileHandlingUtility', array('makeAndClearExtensionDir'));
253 $extractedFiles = $fileHandlerMock->_call('extractFilesArrayFromExtensionData', $extensionData);
254 $this->assertArrayHasKey('filename1', $extractedFiles);
255 $this->assertArrayHasKey('filename2', $extractedFiles);
256 }
257
258 /**
259 * @test
260 * @return void
261 */
262 public function writeExtensionFilesWritesFiles() {
263 $files = array(
264 'ChangeLog' => array(
265 'name' => 'ChangeLog',
266 'size' => 4559,
267 'mtime' => 1219448527,
268 'is_executable' => FALSE,
269 'content' => 'some content to write'
270 ),
271 'README' => array(
272 'name' => 'README',
273 'size' => 4566,
274 'mtime' => 1219448533,
275 'is_executable' => FALSE,
276 'content' => 'FEEL FREE TO ADD SOME DOCUMENTATION HERE'
277 )
278 );
279 $rootPath = ($extDirPath = $this->fakedExtensions[$this->createFakeExtension()]['siteAbsPath']);
280 $fileHandlerMock = $this->getAccessibleMock('TYPO3\\CMS\\Extensionmanager\\Utility\\FileHandlingUtility', array('makeAndClearExtensionDir'));
281 $fileHandlerMock->_call('writeExtensionFiles', $files, $rootPath);
282 $this->assertTrue(file_exists($rootPath . 'ChangeLog'));
283 }
284
285 /**
286 * @test
287 * @return void
288 */
289 public function extractDirectoriesFromExtensionDataExtractsDirectories() {
290 $files = array(
291 'doc/ChangeLog' => array(
292 'name' => 'ChangeLog',
293 'size' => 4559,
294 'mtime' => 1219448527,
295 'is_executable' => FALSE,
296 'content' => 'some content to write'
297 ),
298 'mod/doc/README' => array(
299 'name' => 'README',
300 'size' => 4566,
301 'mtime' => 1219448533,
302 'is_executable' => FALSE,
303 'content' => 'FEEL FREE TO ADD SOME DOCUMENTATION HERE'
304 )
305 );
306 $fileHandlerMock = $this->getAccessibleMock('TYPO3\\CMS\\Extensionmanager\\Utility\\FileHandlingUtility', array('makeAndClearExtensionDir'));
307 $extractedDirectories = $fileHandlerMock->_call('extractDirectoriesFromExtensionData', $files);
308 $this->assertContains('doc/', $extractedDirectories);
309 $this->assertContains('mod/doc/', $extractedDirectories);
310 }
311
312 /**
313 * @test
314 * @return void
315 */
316 public function createDirectoriesForExtensionFilesCreatesDirectories() {
317 $rootPath = $this->fakedExtensions[$this->createFakeExtension()]['siteAbsPath'];
318 $directories = array(
319 'doc/',
320 'mod/doc/'
321 );
322 $fileHandlerMock = $this->getAccessibleMock('TYPO3\\CMS\\Extensionmanager\\Utility\\FileHandlingUtility', array('makeAndClearExtensionDir'));
323 $this->assertFalse(is_dir($rootPath . 'doc/'));
324 $this->assertFalse(is_dir($rootPath . 'mod/doc/'));
325 $fileHandlerMock->_call('createDirectoriesForExtensionFiles', $directories, $rootPath);
326 $this->assertTrue(is_dir($rootPath . 'doc/'));
327 $this->assertTrue(is_dir($rootPath . 'mod/doc/'));
328 }
329
330 /**
331 * @test
332 * @return void
333 */
334 public function writeEmConfWritesEmConfFile() {
335 $extKey = $this->createFakeExtension();
336 $extensionData = array(
337 'extKey' => $extKey,
338 'EM_CONF' => array(
339 'title' => 'Plugin cache engine',
340 'description' => 'Provides an interface to cache plugin content elements based on 4.3 caching framework',
341 'category' => 'Frontend',
342 'shy' => 0
343 )
344 );
345 $rootPath = $this->fakedExtensions[$extKey]['siteAbsPath'];
346 $emConfUtilityMock = $this->getAccessibleMock('TYPO3\\CMS\\Extensionmanager\\Utility\\EmConfUtility', array('constructEmConf'));
347 $emConfUtilityMock->expects($this->once())->method('constructEmConf')->with($extensionData)->will($this->returnValue(var_export($extensionData['EM_CONF'], TRUE)));
348 $fileHandlerMock = $this->getAccessibleMock('TYPO3\\CMS\\Extensionmanager\\Utility\\FileHandlingUtility', array('makeAndClearExtensionDir'));
349 $fileHandlerMock->_set('emConfUtility', $emConfUtilityMock);
350 $fileHandlerMock->_call('writeEmConfToFile', $extensionData, $rootPath);
351 $this->assertTrue(file_exists($rootPath . 'ext_emconf.php'));
352 }
353
354 /**
355 * @return \PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility
356 */
357 protected function getPreparedFileHandlingMockForDirectoryCreationTests() {
358 /** @var $fileHandlerMock \TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility|\PHPUnit_Framework_MockObject_MockObject */
359 $fileHandlerMock = $this->getMock('TYPO3\\CMS\\Extensionmanager\\Utility\\FileHandlingUtility', array('createNestedDirectory', 'getAbsolutePath', 'directoryExists'));
360 $fileHandlerMock->expects($this->any())
361 ->method('getAbsolutePath')
362 ->will($this->returnArgument(0));
363 return $fileHandlerMock;
364 }
365
366 /**
367 * @test
368 */
369 public function uploadFolderIsNotCreatedIfNotRequested() {
370 $fileHandlerMock = $this->getPreparedFileHandlingMockForDirectoryCreationTests();
371 $fileHandlerMock->expects($this->never())
372 ->method('createNestedDirectory');
373 $fileHandlerMock->ensureConfiguredDirectoriesExist(array(
374 'key' => 'foo_bar',
375 'uploadfolder' => 0,
376 )
377 );
378 }
379
380 /**
381 * @test
382 */
383 public function additionalFoldersAreNotCreatedIfNotRequested() {
384 $fileHandlerMock = $this->getPreparedFileHandlingMockForDirectoryCreationTests();
385 $fileHandlerMock->expects($this->never())
386 ->method('createNestedDirectory');
387 $fileHandlerMock->ensureConfiguredDirectoriesExist(array(
388 'key' => 'foo_bar',
389 'createDirs' => '',
390 )
391 );
392 }
393
394 /**
395 * @test
396 */
397 public function configuredUploadFolderIsCreatedIfRequested() {
398 $fileHandlerMock = $this->getPreparedFileHandlingMockForDirectoryCreationTests();
399 $fileHandlerMock->expects($this->once())
400 ->method('createNestedDirectory')
401 ->with('uploads/tx_foobar/');
402 $fileHandlerMock->ensureConfiguredDirectoriesExist(array(
403 'key' => 'foo_bar',
404 'uploadfolder' => 1,
405 )
406 );
407 }
408
409 /**
410 * @test
411 */
412 public function configuredAdditionalDirectoriesAreCreatedIfRequested() {
413 $fileHandlerMock = $this->getPreparedFileHandlingMockForDirectoryCreationTests();
414 $fileHandlerMock->expects($this->exactly(2))
415 ->method('createNestedDirectory')
416 ->will($this->returnCallback(function($path) {
417 if (!in_array($path, array('foo/bar', 'baz/foo'))) {
418 throw new \Exception('Path "' . $path . '" is not expected to be created');
419 }
420
421 })
422 );
423 $fileHandlerMock->ensureConfiguredDirectoriesExist(array(
424 'key' => 'foo_bar',
425 'createDirs' => 'foo/bar, baz/foo',
426 )
427 );
428 }
429
430 /**
431 * @test
432 */
433 public function configuredDirectoriesAreNotCreatedIfTheyAlreadyExist() {
434 $fileHandlerMock = $this->getPreparedFileHandlingMockForDirectoryCreationTests();
435 $fileHandlerMock->expects($this->exactly(3))
436 ->method('directoryExists')
437 ->will($this->returnValue(TRUE));
438 $fileHandlerMock->expects($this->never())
439 ->method('createNestedDirectory');
440 $fileHandlerMock->ensureConfiguredDirectoriesExist(array(
441 'key' => 'foo_bar',
442 'uploadfolder' => 1,
443 'createDirs' => 'foo/bar, baz/foo',
444 )
445 );
446 }
447
448 /**
449 * Warning: This test asserts multiple things at once to keep the setup short.
450 *
451 * @test
452 */
453 public function createZipFileFromExtensionGeneratesCorrectArchive() {
454 // Create extension for testing:
455 $extKey = $this->createFakeExtension();
456 $extensionRoot = $this->fakedExtensions[$extKey]['siteAbsPath'];
457
458 // Build mocked fileHandlingUtility:
459 $fileHandlerMock = $this->getAccessibleMock(
460 'TYPO3\\CMS\\Extensionmanager\\Utility\\FileHandlingUtility',
461 array('getAbsoluteExtensionPath', 'getExtensionVersion')
462 );
463 $fileHandlerMock->expects($this->any())
464 ->method('getAbsoluteExtensionPath')
465 ->will($this->returnValue($extensionRoot));
466 $fileHandlerMock->expects($this->any())
467 ->method('getExtensionVersion')
468 ->will($this->returnValue('0.0.0'));
469
470 // Add files and directories to extension:
471 touch($extensionRoot . 'emptyFile.txt');
472 file_put_contents($extensionRoot . 'notEmptyFile.txt', 'content');
473 touch($extensionRoot . '.hiddenFile');
474 mkdir($extensionRoot . 'emptyDir');
475 mkdir($extensionRoot . 'notEmptyDir');
476 touch($extensionRoot . 'notEmptyDir/file.txt');
477
478 // Create zip-file from extension
479 $filename = $fileHandlerMock->_call('createZipFileFromExtension', $extKey);
480
481 $expectedFilename = PATH_site . 'typo3temp/' . $extKey . '_0.0.0_' . date('YmdHi') . '.zip';
482 $this->assertEquals($expectedFilename, $filename, 'Archive file name differs from expectation');
483
484 // File was created
485 $this->assertTrue(file_exists($filename), 'Zip file not created');
486 $this->resourcesToRemove[] = $filename;
487
488 // Read archive and check its contents
489 $archive = new \ZipArchive();
490 $this->assertTrue($archive->open($filename), 'Unable to open archive');
491 $this->assertEquals($archive->statName('emptyFile.txt')->size, 0, 'Empty file not in archive');
492 $this->assertEquals($archive->getFromName('notEmptyFile.txt'), 'content', 'Expected content not found');
493 $this->assertFalse($archive->statName('.hiddenFile'), 'Hidden file not in archive');
494 $this->assertTrue(is_array($archive->statName('emptyDir/')), 'Empty directory not in archive');
495 $this->assertTrue(is_array($archive->statName('notEmptyDir/')), 'Not empty directory not in archive');
496 $this->assertTrue(is_array($archive->statName('notEmptyDir/file.txt')), 'File within directory not in archive');
497
498 // Check that the archive has no additional content
499 $this->assertEquals($archive->numFiles, 5, 'Too many or too less files in archive');
500 }
501 }
502
503 ?>