TYPO3  7.6
FileHandlingUtilityTest.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Extensionmanager\Tests\Unit\Utility;
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
7  * It is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License, either version 2
9  * of the License, or any later version.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
16 
21 class FileHandlingUtilityTest extends \TYPO3\CMS\Core\Tests\UnitTestCase
22 {
26  protected $fakedExtensions = array();
27 
35  protected function createFakeExtension($extkeyOnly = false)
36  {
37  $extKey = strtolower($this->getUniqueId('testing'));
38  $absExtPath = PATH_site . 'typo3temp/ext-' . $extKey . '/';
39  $relPath = 'typo3temp/ext-' . $extKey . '/';
40  $this->fakedExtensions[$extKey] = array(
41  'siteRelPath' => $relPath,
42  'siteAbsPath' => $absExtPath
43  );
44  if ($extkeyOnly === true) {
45  return $extKey;
46  }
47  \TYPO3\CMS\Core\Utility\GeneralUtility::mkdir($absExtPath);
48  $this->testFilesToDelete[] = PATH_site . 'typo3temp/ext-' . $extKey;
49  return $extKey;
50  }
51 
57  {
58  $extKey = $this->createFakeExtension();
59  $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, array('removeDirectory', 'addDirectory', 'getExtensionDir'), array(), '', false);
60  $fileHandlerMock->expects($this->once())
61  ->method('removeDirectory')
62  ->with(PATH_site . 'typo3temp/ext-' . $extKey . '/');
63  $fileHandlerMock->expects($this->any())
64  ->method('getExtensionDir')
65  ->willReturn(PATH_site . 'typo3temp/ext-' . $extKey . '/');
66  $fileHandlerMock->_call('makeAndClearExtensionDir', $extKey);
67  }
68 
73  {
74  return array(
75  array('../../'),
76  array('/foo/bar'),
77  array('foo//bar'),
78  array('foo/bar' . chr(0)),
79  );
80  }
81 
88  public function getAbsolutePathThrowsExceptionForInvalidRelativePaths($invalidRelativePath)
89  {
90  $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, array('dummy'), array());
91  $fileHandlerMock->_call('getAbsolutePath', $invalidRelativePath);
92  }
93 
98  {
99  return array(
100  array('foo/../bar', PATH_site . 'bar'),
101  array('bas', PATH_site . 'bas'),
102  );
103  }
104 
111  public function getAbsolutePathReturnsAbsolutePathForValidRelativePaths($validRelativePath, $expectedAbsolutePath)
112  {
113  $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, array('dummy'));
114  $this->assertSame($expectedAbsolutePath, $fileHandlerMock->_call('getAbsolutePath', $validRelativePath));
115  }
116 
122  {
123  $extKey = $this->createFakeExtension();
124  $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, array('removeDirectory', 'addDirectory', 'getExtensionDir'));
125  $fileHandlerMock->expects($this->once())
126  ->method('addDirectory')
127  ->with(PATH_site . 'typo3temp/ext-' . $extKey . '/');
128  $fileHandlerMock->expects($this->any())
129  ->method('getExtensionDir')
130  ->willReturn(PATH_site . 'typo3temp/ext-' . $extKey . '/');
131  $fileHandlerMock->_call('makeAndClearExtensionDir', $extKey);
132  }
133 
140  {
141  $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, array('removeDirectory', 'addDirectory'));
142  $languageServiceMock = $this->getMock(\TYPO3\CMS\Lang\LanguageService::class);
143  $fileHandlerMock->_set('languageService', $languageServiceMock);
144  $fileHandlerMock->_call('makeAndClearExtensionDir', 'testing123', 'fakepath');
145  }
146 
151  public function addDirectoryAddsDirectory()
152  {
153  $extDirPath = PATH_site . '/typo3temp/' . $this->getUniqueId('test-extensions-');
154  $this->testFilesToDelete[] = $extDirPath;
155  $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, array('dummy'));
156  $fileHandlerMock->_call('addDirectory', $extDirPath);
157  $this->assertTrue(is_dir($extDirPath));
158  }
159 
165  {
166  $extDirPath = PATH_site . '/typo3temp/' . $this->getUniqueId('test-extensions-');
167  @mkdir($extDirPath);
168  $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, array('dummy'));
169  $fileHandlerMock->_call('removeDirectory', $extDirPath);
170  $this->assertFalse(is_dir($extDirPath));
171  }
172 
178  {
179  $absoluteSymlinkPath = PATH_site . 'typo3temp/' . $this->getUniqueId('test_symlink_');
180  $absoluteFilePath = PATH_site . 'typo3temp/' . $this->getUniqueId('test_file_');
181  touch($absoluteFilePath);
182  $this->testFilesToDelete[] = $absoluteFilePath;
183  symlink($absoluteFilePath, $absoluteSymlinkPath);
184  $fileHandler = new \TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility();
185  $fileHandler->removeDirectory($absoluteSymlinkPath);
186  $this->assertFalse(is_link($absoluteSymlinkPath));
187  }
188 
194  {
195  $absoluteSymlinkPath = PATH_site . 'typo3temp/' . $this->getUniqueId('test_symlink_');
196  $absoluteDirectoryPath = PATH_site . 'typo3temp/' . $this->getUniqueId('test_dir_') . '/';
197  $relativeFilePath = $this->getUniqueId('test_file_');
198 
199  mkdir($absoluteDirectoryPath);
200  touch($absoluteDirectoryPath . $relativeFilePath);
201 
202  $this->testFilesToDelete[] = $absoluteDirectoryPath . $relativeFilePath;
203  $this->testFilesToDelete[] = $absoluteDirectoryPath;
204 
205  symlink($absoluteDirectoryPath, $absoluteSymlinkPath);
206 
207  $fileHandler = new \TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility();
208  $fileHandler->removeDirectory($absoluteSymlinkPath);
209  $this->assertTrue(is_file($absoluteDirectoryPath . $relativeFilePath));
210  }
211 
217  {
218  $extensionData = array(
219  'extKey' => 'test'
220  );
221  $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, array(
222  'makeAndClearExtensionDir',
223  'writeEmConfToFile',
224  'extractFilesArrayFromExtensionData',
225  'extractDirectoriesFromExtensionData',
226  'createDirectoriesForExtensionFiles',
227  'writeExtensionFiles',
228  'reloadPackageInformation',
229  ));
230  $fileHandlerMock->expects($this->once())->method('extractFilesArrayFromExtensionData')->will($this->returnValue(array()));
231  $fileHandlerMock->expects($this->once())->method('extractDirectoriesFromExtensionData')->will($this->returnValue(array()));
232  $fileHandlerMock->expects($this->once())->method('makeAndClearExtensionDir')->with($extensionData['extKey']);
233  $fileHandlerMock->_call('unpackExtensionFromExtensionDataArray', $extensionData);
234  }
235 
241  {
242  $extensionData = array(
243  'extKey' => 'test'
244  );
245  $files = array(
246  'ChangeLog' => array(
247  'name' => 'ChangeLog',
248  'size' => 4559,
249  'mtime' => 1219448527,
250  'is_executable' => false,
251  'content' => 'some content to write'
252  ),
253  'doc/' => array(
254  'name' => 'doc/',
255  'size' => 0,
256  'mtime' => 1219448527,
257  'is_executable' => false,
258  'content' => ''
259  ),
260  'doc/ChangeLog' => array(
261  'name' => 'ChangeLog',
262  'size' => 4559,
263  'mtime' => 1219448527,
264  'is_executable' => false,
265  'content' => 'some content to write'
266  ),
267  );
268  $cleanedFiles = array(
269  'ChangeLog' => array(
270  'name' => 'ChangeLog',
271  'size' => 4559,
272  'mtime' => 1219448527,
273  'is_executable' => false,
274  'content' => 'some content to write'
275  ),
276  'doc/ChangeLog' => array(
277  'name' => 'ChangeLog',
278  'size' => 4559,
279  'mtime' => 1219448527,
280  'is_executable' => false,
281  'content' => 'some content to write'
282  ),
283  );
284  $directories = array(
285  'doc/',
286  'mod/doc/'
287  );
288 
289  $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, array(
290  'makeAndClearExtensionDir',
291  'writeEmConfToFile',
292  'extractFilesArrayFromExtensionData',
293  'extractDirectoriesFromExtensionData',
294  'createDirectoriesForExtensionFiles',
295  'writeExtensionFiles',
296  'reloadPackageInformation',
297  ));
298  $fileHandlerMock->expects($this->once())->method('extractFilesArrayFromExtensionData')->will($this->returnValue($files));
299  $fileHandlerMock->expects($this->once())->method('extractDirectoriesFromExtensionData')->will($this->returnValue($directories));
300  $fileHandlerMock->expects($this->once())->method('createDirectoriesForExtensionFiles')->with($directories);
301  $fileHandlerMock->expects($this->once())->method('writeExtensionFiles')->with($cleanedFiles);
302  $fileHandlerMock->expects($this->once())->method('reloadPackageInformation')->with('test');
303  $fileHandlerMock->_call('unpackExtensionFromExtensionDataArray', $extensionData);
304  }
305 
311  {
312  $extensionData = array(
313  'key' => 'test',
314  'FILES' => array(
315  'filename1' => 'dummycontent',
316  'filename2' => 'dummycontent2'
317  )
318  );
319  $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, array('makeAndClearExtensionDir'));
320  $extractedFiles = $fileHandlerMock->_call('extractFilesArrayFromExtensionData', $extensionData);
321  $this->assertArrayHasKey('filename1', $extractedFiles);
322  $this->assertArrayHasKey('filename2', $extractedFiles);
323  }
324 
330  {
331  $files = array(
332  'ChangeLog' => array(
333  'name' => 'ChangeLog',
334  'size' => 4559,
335  'mtime' => 1219448527,
336  'is_executable' => false,
337  'content' => 'some content to write'
338  ),
339  'README' => array(
340  'name' => 'README',
341  'size' => 4566,
342  'mtime' => 1219448533,
343  'is_executable' => false,
344  'content' => 'FEEL FREE TO ADD SOME DOCUMENTATION HERE'
345  )
346  );
347  $rootPath = ($extDirPath = $this->fakedExtensions[$this->createFakeExtension()]['siteAbsPath']);
348  $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, array('makeAndClearExtensionDir'));
349  $fileHandlerMock->_call('writeExtensionFiles', $files, $rootPath);
350  $this->assertTrue(file_exists($rootPath . 'ChangeLog'));
351  }
352 
358  {
359  $files = array(
360  'ChangeLog' => array(
361  'name' => 'ChangeLog',
362  'size' => 4559,
363  'mtime' => 1219448527,
364  'is_executable' => false,
365  'content' => 'some content to write'
366  ),
367  'doc/' => array(
368  'name' => 'doc/',
369  'size' => 0,
370  'mtime' => 1219448527,
371  'is_executable' => false,
372  'content' => ''
373  ),
374  'doc/ChangeLog' => array(
375  'name' => 'ChangeLog',
376  'size' => 4559,
377  'mtime' => 1219448527,
378  'is_executable' => false,
379  'content' => 'some content to write'
380  ),
381  'doc/README' => array(
382  'name' => 'README',
383  'size' => 4566,
384  'mtime' => 1219448533,
385  'is_executable' => false,
386  'content' => 'FEEL FREE TO ADD SOME DOCUMENTATION HERE'
387  ),
388  'mod/doc/README' => array(
389  'name' => 'README',
390  'size' => 4566,
391  'mtime' => 1219448533,
392  'is_executable' => false,
393  'content' => 'FEEL FREE TO ADD SOME DOCUMENTATION HERE'
394  )
395  );
396  $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, array('makeAndClearExtensionDir'));
397  $extractedDirectories = $fileHandlerMock->_call('extractDirectoriesFromExtensionData', $files);
398  $expected = array(
399  'doc/',
400  'mod/doc/'
401  );
402  $this->assertSame($expected, array_values($extractedDirectories));
403  }
404 
410  {
411  $rootPath = $this->fakedExtensions[$this->createFakeExtension()]['siteAbsPath'];
412  $directories = array(
413  'doc/',
414  'mod/doc/'
415  );
416  $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, array('makeAndClearExtensionDir'));
417  $this->assertFalse(is_dir($rootPath . 'doc/'));
418  $this->assertFalse(is_dir($rootPath . 'mod/doc/'));
419  $fileHandlerMock->_call('createDirectoriesForExtensionFiles', $directories, $rootPath);
420  $this->assertTrue(is_dir($rootPath . 'doc/'));
421  $this->assertTrue(is_dir($rootPath . 'mod/doc/'));
422  }
423 
428  public function writeEmConfWritesEmConfFile()
429  {
430  $extKey = $this->createFakeExtension();
431  $extensionData = array(
432  'extKey' => $extKey,
433  'EM_CONF' => array(
434  'title' => 'Plugin cache engine',
435  'description' => 'Provides an interface to cache plugin content elements based on 4.3 caching framework',
436  'category' => 'Frontend',
437  )
438  );
439  $rootPath = $this->fakedExtensions[$extKey]['siteAbsPath'];
440  $emConfUtilityMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\EmConfUtility::class, array('constructEmConf'));
441  $emConfUtilityMock->expects($this->once())->method('constructEmConf')->with($extensionData)->will($this->returnValue(var_export($extensionData['EM_CONF'], true)));
442  $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, array('makeAndClearExtensionDir'));
443  $fileHandlerMock->_set('emConfUtility', $emConfUtilityMock);
444  $fileHandlerMock->_call('writeEmConfToFile', $extensionData, $rootPath);
445  $this->assertTrue(file_exists($rootPath . 'ext_emconf.php'));
446  }
447 
451  protected function getPreparedFileHandlingMockForDirectoryCreationTests()
452  {
454  $fileHandlerMock = $this->getMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, array('createNestedDirectory', 'getAbsolutePath', 'directoryExists'));
455  $fileHandlerMock->expects($this->any())
456  ->method('getAbsolutePath')
457  ->will($this->returnArgument(0));
458  return $fileHandlerMock;
459  }
460 
465  {
466  $fileHandlerMock = $this->getPreparedFileHandlingMockForDirectoryCreationTests();
467  $fileHandlerMock->expects($this->never())
468  ->method('createNestedDirectory');
469  $fileHandlerMock->ensureConfiguredDirectoriesExist(array(
470  'key' => 'foo_bar',
471  'uploadfolder' => 0,
472  )
473  );
474  }
475 
480  {
481  $fileHandlerMock = $this->getPreparedFileHandlingMockForDirectoryCreationTests();
482  $fileHandlerMock->expects($this->never())
483  ->method('createNestedDirectory');
484  $fileHandlerMock->ensureConfiguredDirectoriesExist(array(
485  'key' => 'foo_bar',
486  'createDirs' => '',
487  )
488  );
489  }
490 
495  {
496  $fileHandlerMock = $this->getPreparedFileHandlingMockForDirectoryCreationTests();
497  $fileHandlerMock->expects($this->once())
498  ->method('createNestedDirectory')
499  ->with('uploads/tx_foobar/');
500  $fileHandlerMock->ensureConfiguredDirectoriesExist(array(
501  'key' => 'foo_bar',
502  'uploadfolder' => 1,
503  )
504  );
505  }
506 
511  {
512  $fileHandlerMock = $this->getPreparedFileHandlingMockForDirectoryCreationTests();
513  $fileHandlerMock->expects($this->exactly(2))
514  ->method('createNestedDirectory')
515  ->will($this->returnCallback(function ($path) {
516  if (!in_array($path, array('foo/bar', 'baz/foo'))) {
517  throw new \Exception('Path "' . $path . '" is not expected to be created');
518  }
519 
520  })
521  );
522  $fileHandlerMock->ensureConfiguredDirectoriesExist(array(
523  'key' => 'foo_bar',
524  'createDirs' => 'foo/bar, baz/foo',
525  )
526  );
527  }
528 
533  {
534  $fileHandlerMock = $this->getPreparedFileHandlingMockForDirectoryCreationTests();
535  $fileHandlerMock->expects($this->exactly(3))
536  ->method('directoryExists')
537  ->will($this->returnValue(true));
538  $fileHandlerMock->expects($this->never())
539  ->method('createNestedDirectory');
540  $fileHandlerMock->ensureConfiguredDirectoriesExist(array(
541  'key' => 'foo_bar',
542  'uploadfolder' => 1,
543  'createDirs' => 'foo/bar, baz/foo',
544  )
545  );
546  }
547 
554  {
555  // 42 second of first day in 1970 - used to have achieve stable file names
556  $GLOBALS['EXEC_TIME'] = 42;
557 
558  // Create extension for testing:
559  $extKey = $this->createFakeExtension();
560  $extensionRoot = $this->fakedExtensions[$extKey]['siteAbsPath'];
561 
562  // Build mocked fileHandlingUtility:
563  $fileHandlerMock = $this->getAccessibleMock(
564  \TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class,
565  array('getAbsoluteExtensionPath', 'getExtensionVersion')
566  );
567  $fileHandlerMock->expects($this->any())
568  ->method('getAbsoluteExtensionPath')
569  ->will($this->returnValue($extensionRoot));
570  $fileHandlerMock->expects($this->any())
571  ->method('getExtensionVersion')
572  ->will($this->returnValue('0.0.0'));
573 
574  // Add files and directories to extension:
575  touch($extensionRoot . 'emptyFile.txt');
576  file_put_contents($extensionRoot . 'notEmptyFile.txt', 'content');
577  touch($extensionRoot . '.hiddenFile');
578  mkdir($extensionRoot . 'emptyDir');
579  mkdir($extensionRoot . 'notEmptyDir');
580  touch($extensionRoot . 'notEmptyDir/file.txt');
581 
582  // Create zip-file from extension
583  $filename = $fileHandlerMock->_call('createZipFileFromExtension', $extKey);
584 
585  $expectedFilename = PATH_site . 'typo3temp/ExtensionManager/' . $extKey . '_0.0.0_' . date('YmdHi', 42) . '.zip';
586  $this->testFilesToDelete[] = $filename;
587  $this->assertEquals($expectedFilename, $filename, 'Archive file name differs from expectation');
588 
589  // File was created
590  $this->assertTrue(file_exists($filename), 'Zip file not created');
591 
592  // Read archive and check its contents
593  $archive = new \ZipArchive();
594  $this->assertTrue($archive->open($filename), 'Unable to open archive');
595  $this->assertEquals($archive->statName('emptyFile.txt')->size, 0, 'Empty file not in archive');
596  $this->assertEquals($archive->getFromName('notEmptyFile.txt'), 'content', 'Expected content not found');
597  $this->assertFalse($archive->statName('.hiddenFile'), 'Hidden file not in archive');
598  $this->assertTrue(is_array($archive->statName('emptyDir/')), 'Empty directory not in archive');
599  $this->assertTrue(is_array($archive->statName('notEmptyDir/')), 'Not empty directory not in archive');
600  $this->assertTrue(is_array($archive->statName('notEmptyDir/file.txt')), 'File within directory not in archive');
601 
602  // Check that the archive has no additional content
603  $this->assertEquals($archive->numFiles, 5, 'Too many or too less files in archive');
604  }
605 }