5a9ea50f154a42f521a65352ec8edfff37f5b03b
[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 * 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 use TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException;
17
18 /**
19 * Testcase
20 *
21 */
22 class FileHandlingUtilityTest extends \TYPO3\CMS\Core\Tests\UnitTestCase
23 {
24 /**
25 * @var array List of created fake extensions to be deleted in tearDown() again
26 */
27 protected $fakedExtensions = [];
28
29 /**
30 * Creates a fake extension inside typo3temp/. No configuration is created,
31 * just the folder
32 *
33 * @param bool $extkeyOnly
34 * @return string The extension key
35 */
36 protected function createFakeExtension($extkeyOnly = false)
37 {
38 $extKey = strtolower($this->getUniqueId('testing'));
39 $absExtPath = PATH_site . 'typo3temp/var/tests/ext-' . $extKey . '/';
40 $relPath = 'typo3temp/var/tests/ext-' . $extKey . '/';
41 $this->fakedExtensions[$extKey] = [
42 'siteRelPath' => $relPath,
43 'siteAbsPath' => $absExtPath
44 ];
45 if ($extkeyOnly === true) {
46 return $extKey;
47 }
48 \TYPO3\CMS\Core\Utility\GeneralUtility::mkdir($absExtPath);
49 $this->testFilesToDelete[] = PATH_site . 'typo3temp/var/tests/ext-' . $extKey;
50 return $extKey;
51 }
52
53 /**
54 * @test
55 * @return void
56 */
57 public function makeAndClearExtensionDirRemovesExtensionDirIfAlreadyExists()
58 {
59 $extKey = $this->createFakeExtension();
60 $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, ['removeDirectory', 'addDirectory', 'getExtensionDir'], [], '', false);
61 $fileHandlerMock->expects($this->once())
62 ->method('removeDirectory')
63 ->with(PATH_site . 'typo3temp/var/tests/ext-' . $extKey . '/');
64 $fileHandlerMock->expects($this->any())
65 ->method('getExtensionDir')
66 ->willReturn(PATH_site . 'typo3temp/var/tests/ext-' . $extKey . '/');
67 $fileHandlerMock->_call('makeAndClearExtensionDir', $extKey);
68 }
69
70 /**
71 * @return array
72 */
73 public function invalidRelativePathDataProvider()
74 {
75 return [
76 ['../../'],
77 ['/foo/bar'],
78 ['foo//bar'],
79 ['foo/bar' . chr(0)],
80 ];
81 }
82
83 /**
84 * @param string $invalidRelativePath
85 * @test
86 * @dataProvider invalidRelativePathDataProvider
87 */
88 public function getAbsolutePathThrowsExceptionForInvalidRelativePaths($invalidRelativePath)
89 {
90 $this->expectException(ExtensionManagerException::class);
91 $this->expectExceptionCode(1350742864);
92 $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, ['dummy'], []);
93 $fileHandlerMock->_call('getAbsolutePath', $invalidRelativePath);
94 }
95
96 /**
97 * @return array
98 */
99 public function validRelativePathDataProvider()
100 {
101 return [
102 ['foo/../bar', PATH_site . 'bar'],
103 ['bas', PATH_site . 'bas'],
104 ];
105 }
106
107 /**
108 * @param string $validRelativePath
109 * @param string $expectedAbsolutePath
110 * @test
111 * @dataProvider validRelativePathDataProvider
112 */
113 public function getAbsolutePathReturnsAbsolutePathForValidRelativePaths($validRelativePath, $expectedAbsolutePath)
114 {
115 $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, ['dummy']);
116 $this->assertSame($expectedAbsolutePath, $fileHandlerMock->_call('getAbsolutePath', $validRelativePath));
117 }
118
119 /**
120 * @test
121 * @return void
122 */
123 public function makeAndClearExtensionDirAddsDir()
124 {
125 $extKey = $this->createFakeExtension();
126 $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, ['removeDirectory', 'addDirectory', 'getExtensionDir']);
127 $fileHandlerMock->expects($this->once())
128 ->method('addDirectory')
129 ->with(PATH_site . 'typo3temp/var/tests/ext-' . $extKey . '/');
130 $fileHandlerMock->expects($this->any())
131 ->method('getExtensionDir')
132 ->willReturn(PATH_site . 'typo3temp/var/tests/ext-' . $extKey . '/');
133 $fileHandlerMock->_call('makeAndClearExtensionDir', $extKey);
134 }
135
136 /**
137 * @test
138 * @return void
139 */
140 public function makeAndClearExtensionDirThrowsExceptionOnInvalidPath()
141 {
142 $this->expectException(ExtensionManagerException::class);
143 $this->expectExceptionCode(1337280417);
144 $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, ['removeDirectory', 'addDirectory']);
145 $languageServiceMock = $this->getMockBuilder(\TYPO3\CMS\Lang\LanguageService::class)->getMock();
146 $fileHandlerMock->_set('languageService', $languageServiceMock);
147 $fileHandlerMock->_call('makeAndClearExtensionDir', 'testing123', 'fakepath');
148 }
149
150 /**
151 * @test
152 * @return void
153 */
154 public function addDirectoryAddsDirectory()
155 {
156 $extDirPath = PATH_site . '/typo3temp/var/tests/' . $this->getUniqueId('test-extensions-');
157 $this->testFilesToDelete[] = $extDirPath;
158 $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, ['dummy']);
159 $fileHandlerMock->_call('addDirectory', $extDirPath);
160 $this->assertTrue(is_dir($extDirPath));
161 }
162
163 /**
164 * @test
165 * @return void
166 */
167 public function removeDirectoryRemovesDirectory()
168 {
169 $extDirPath = PATH_site . '/typo3temp/var/tests/' . $this->getUniqueId('test-extensions-');
170 @mkdir($extDirPath);
171 $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, ['dummy']);
172 $fileHandlerMock->_call('removeDirectory', $extDirPath);
173 $this->assertFalse(is_dir($extDirPath));
174 }
175
176 /**
177 * @test
178 * @return void
179 */
180 public function removeDirectoryRemovesSymlink()
181 {
182 $absoluteSymlinkPath = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('test_symlink_');
183 $absoluteFilePath = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('test_file_');
184 touch($absoluteFilePath);
185 $this->testFilesToDelete[] = $absoluteFilePath;
186 symlink($absoluteFilePath, $absoluteSymlinkPath);
187 $fileHandler = new \TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility();
188 $fileHandler->removeDirectory($absoluteSymlinkPath);
189 $this->assertFalse(is_link($absoluteSymlinkPath));
190 }
191
192 /**
193 * @test
194 * @return void
195 */
196 public function removeDirectoryDoesNotRemoveContentOfSymlinkedTargetDirectory()
197 {
198 $absoluteSymlinkPath = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('test_symlink_');
199 $absoluteDirectoryPath = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('test_dir_') . '/';
200 $relativeFilePath = $this->getUniqueId('test_file_');
201
202 mkdir($absoluteDirectoryPath);
203 touch($absoluteDirectoryPath . $relativeFilePath);
204
205 $this->testFilesToDelete[] = $absoluteDirectoryPath . $relativeFilePath;
206 $this->testFilesToDelete[] = $absoluteDirectoryPath;
207
208 symlink($absoluteDirectoryPath, $absoluteSymlinkPath);
209
210 $fileHandler = new \TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility();
211 $fileHandler->removeDirectory($absoluteSymlinkPath);
212 $this->assertTrue(is_file($absoluteDirectoryPath . $relativeFilePath));
213 }
214
215 /**
216 * @test
217 * @return void
218 */
219 public function unpackExtensionFromExtensionDataArrayCreatesTheExtensionDirectory()
220 {
221 $extensionData = [
222 'extKey' => 'test'
223 ];
224 $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, [
225 'makeAndClearExtensionDir',
226 'writeEmConfToFile',
227 'extractFilesArrayFromExtensionData',
228 'extractDirectoriesFromExtensionData',
229 'createDirectoriesForExtensionFiles',
230 'writeExtensionFiles',
231 'reloadPackageInformation',
232 ]);
233 $fileHandlerMock->expects($this->once())->method('extractFilesArrayFromExtensionData')->will($this->returnValue([]));
234 $fileHandlerMock->expects($this->once())->method('extractDirectoriesFromExtensionData')->will($this->returnValue([]));
235 $fileHandlerMock->expects($this->once())->method('makeAndClearExtensionDir')->with($extensionData['extKey']);
236 $fileHandlerMock->_call('unpackExtensionFromExtensionDataArray', $extensionData);
237 }
238
239 /**
240 * @test
241 * @return void
242 */
243 public function unpackExtensionFromExtensionDataArrayStripsDirectoriesFromFilesArray()
244 {
245 $extensionData = [
246 'extKey' => 'test'
247 ];
248 $files = [
249 'ChangeLog' => [
250 'name' => 'ChangeLog',
251 'size' => 4559,
252 'mtime' => 1219448527,
253 'is_executable' => false,
254 'content' => 'some content to write'
255 ],
256 'doc/' => [
257 'name' => 'doc/',
258 'size' => 0,
259 'mtime' => 1219448527,
260 'is_executable' => false,
261 'content' => ''
262 ],
263 'doc/ChangeLog' => [
264 'name' => 'ChangeLog',
265 'size' => 4559,
266 'mtime' => 1219448527,
267 'is_executable' => false,
268 'content' => 'some content to write'
269 ],
270 ];
271 $cleanedFiles = [
272 'ChangeLog' => [
273 'name' => 'ChangeLog',
274 'size' => 4559,
275 'mtime' => 1219448527,
276 'is_executable' => false,
277 'content' => 'some content to write'
278 ],
279 'doc/ChangeLog' => [
280 'name' => 'ChangeLog',
281 'size' => 4559,
282 'mtime' => 1219448527,
283 'is_executable' => false,
284 'content' => 'some content to write'
285 ],
286 ];
287 $directories = [
288 'doc/',
289 'mod/doc/'
290 ];
291
292 $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, [
293 'makeAndClearExtensionDir',
294 'writeEmConfToFile',
295 'extractFilesArrayFromExtensionData',
296 'extractDirectoriesFromExtensionData',
297 'createDirectoriesForExtensionFiles',
298 'writeExtensionFiles',
299 'reloadPackageInformation',
300 ]);
301 $fileHandlerMock->expects($this->once())->method('extractFilesArrayFromExtensionData')->will($this->returnValue($files));
302 $fileHandlerMock->expects($this->once())->method('extractDirectoriesFromExtensionData')->will($this->returnValue($directories));
303 $fileHandlerMock->expects($this->once())->method('createDirectoriesForExtensionFiles')->with($directories);
304 $fileHandlerMock->expects($this->once())->method('writeExtensionFiles')->with($cleanedFiles);
305 $fileHandlerMock->expects($this->once())->method('reloadPackageInformation')->with('test');
306 $fileHandlerMock->_call('unpackExtensionFromExtensionDataArray', $extensionData);
307 }
308
309 /**
310 * @test
311 * @return void
312 */
313 public function extractFilesArrayFromExtensionDataReturnsFileArray()
314 {
315 $extensionData = [
316 'key' => 'test',
317 'FILES' => [
318 'filename1' => 'dummycontent',
319 'filename2' => 'dummycontent2'
320 ]
321 ];
322 $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, ['makeAndClearExtensionDir']);
323 $extractedFiles = $fileHandlerMock->_call('extractFilesArrayFromExtensionData', $extensionData);
324 $this->assertArrayHasKey('filename1', $extractedFiles);
325 $this->assertArrayHasKey('filename2', $extractedFiles);
326 }
327
328 /**
329 * @test
330 * @return void
331 */
332 public function writeExtensionFilesWritesFiles()
333 {
334 $files = [
335 'ChangeLog' => [
336 'name' => 'ChangeLog',
337 'size' => 4559,
338 'mtime' => 1219448527,
339 'is_executable' => false,
340 'content' => 'some content to write'
341 ],
342 'README' => [
343 'name' => 'README',
344 'size' => 4566,
345 'mtime' => 1219448533,
346 'is_executable' => false,
347 'content' => 'FEEL FREE TO ADD SOME DOCUMENTATION HERE'
348 ]
349 ];
350 $rootPath = ($extDirPath = $this->fakedExtensions[$this->createFakeExtension()]['siteAbsPath']);
351 $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, ['makeAndClearExtensionDir']);
352 $fileHandlerMock->_call('writeExtensionFiles', $files, $rootPath);
353 $this->assertTrue(file_exists($rootPath . 'ChangeLog'));
354 }
355
356 /**
357 * @test
358 * @return void
359 */
360 public function extractDirectoriesFromExtensionDataExtractsDirectories()
361 {
362 $files = [
363 'ChangeLog' => [
364 'name' => 'ChangeLog',
365 'size' => 4559,
366 'mtime' => 1219448527,
367 'is_executable' => false,
368 'content' => 'some content to write'
369 ],
370 'doc/' => [
371 'name' => 'doc/',
372 'size' => 0,
373 'mtime' => 1219448527,
374 'is_executable' => false,
375 'content' => ''
376 ],
377 'doc/ChangeLog' => [
378 'name' => 'ChangeLog',
379 'size' => 4559,
380 'mtime' => 1219448527,
381 'is_executable' => false,
382 'content' => 'some content to write'
383 ],
384 'doc/README' => [
385 'name' => 'README',
386 'size' => 4566,
387 'mtime' => 1219448533,
388 'is_executable' => false,
389 'content' => 'FEEL FREE TO ADD SOME DOCUMENTATION HERE'
390 ],
391 'mod/doc/README' => [
392 'name' => 'README',
393 'size' => 4566,
394 'mtime' => 1219448533,
395 'is_executable' => false,
396 'content' => 'FEEL FREE TO ADD SOME DOCUMENTATION HERE'
397 ]
398 ];
399 $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, ['makeAndClearExtensionDir']);
400 $extractedDirectories = $fileHandlerMock->_call('extractDirectoriesFromExtensionData', $files);
401 $expected = [
402 'doc/',
403 'mod/doc/'
404 ];
405 $this->assertSame($expected, array_values($extractedDirectories));
406 }
407
408 /**
409 * @test
410 * @return void
411 */
412 public function createDirectoriesForExtensionFilesCreatesDirectories()
413 {
414 $rootPath = $this->fakedExtensions[$this->createFakeExtension()]['siteAbsPath'];
415 $directories = [
416 'doc/',
417 'mod/doc/'
418 ];
419 $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, ['makeAndClearExtensionDir']);
420 $this->assertFalse(is_dir($rootPath . 'doc/'));
421 $this->assertFalse(is_dir($rootPath . 'mod/doc/'));
422 $fileHandlerMock->_call('createDirectoriesForExtensionFiles', $directories, $rootPath);
423 $this->assertTrue(is_dir($rootPath . 'doc/'));
424 $this->assertTrue(is_dir($rootPath . 'mod/doc/'));
425 }
426
427 /**
428 * @test
429 * @return void
430 */
431 public function writeEmConfWritesEmConfFile()
432 {
433 $extKey = $this->createFakeExtension();
434 $extensionData = [
435 'extKey' => $extKey,
436 'EM_CONF' => [
437 'title' => 'Plugin cache engine',
438 'description' => 'Provides an interface to cache plugin content elements based on 4.3 caching framework',
439 'category' => 'Frontend',
440 ]
441 ];
442 $rootPath = $this->fakedExtensions[$extKey]['siteAbsPath'];
443 $emConfUtilityMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\EmConfUtility::class, ['constructEmConf']);
444 $emConfUtilityMock->expects($this->once())->method('constructEmConf')->with($extensionData)->will($this->returnValue(var_export($extensionData['EM_CONF'], true)));
445 $fileHandlerMock = $this->getAccessibleMock(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class, ['makeAndClearExtensionDir']);
446 $fileHandlerMock->_set('emConfUtility', $emConfUtilityMock);
447 $fileHandlerMock->_call('writeEmConfToFile', $extensionData, $rootPath);
448 $this->assertTrue(file_exists($rootPath . 'ext_emconf.php'));
449 }
450
451 /**
452 * @return \PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility
453 */
454 protected function getPreparedFileHandlingMockForDirectoryCreationTests()
455 {
456 /** @var $fileHandlerMock \TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility|\PHPUnit_Framework_MockObject_MockObject */
457 $fileHandlerMock = $this->getMockBuilder(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class)
458 ->setMethods(['createNestedDirectory', 'getAbsolutePath', 'directoryExists'])
459 ->getMock();
460 $fileHandlerMock->expects($this->any())
461 ->method('getAbsolutePath')
462 ->will($this->returnArgument(0));
463 return $fileHandlerMock;
464 }
465
466 /**
467 * @test
468 */
469 public function uploadFolderIsNotCreatedIfNotRequested()
470 {
471 $fileHandlerMock = $this->getPreparedFileHandlingMockForDirectoryCreationTests();
472 $fileHandlerMock->expects($this->never())
473 ->method('createNestedDirectory');
474 $fileHandlerMock->ensureConfiguredDirectoriesExist([
475 'key' => 'foo_bar',
476 'uploadfolder' => 0,
477 ]
478 );
479 }
480
481 /**
482 * @test
483 */
484 public function additionalFoldersAreNotCreatedIfNotRequested()
485 {
486 $fileHandlerMock = $this->getPreparedFileHandlingMockForDirectoryCreationTests();
487 $fileHandlerMock->expects($this->never())
488 ->method('createNestedDirectory');
489 $fileHandlerMock->ensureConfiguredDirectoriesExist([
490 'key' => 'foo_bar',
491 'createDirs' => '',
492 ]
493 );
494 }
495
496 /**
497 * @test
498 */
499 public function configuredUploadFolderIsCreatedIfRequested()
500 {
501 $fileHandlerMock = $this->getPreparedFileHandlingMockForDirectoryCreationTests();
502 $fileHandlerMock->expects($this->once())
503 ->method('createNestedDirectory')
504 ->with('uploads/tx_foobar/');
505 $fileHandlerMock->ensureConfiguredDirectoriesExist([
506 'key' => 'foo_bar',
507 'uploadfolder' => 1,
508 ]
509 );
510 }
511
512 /**
513 * @test
514 */
515 public function configuredAdditionalDirectoriesAreCreatedIfRequested()
516 {
517 $fileHandlerMock = $this->getPreparedFileHandlingMockForDirectoryCreationTests();
518 $fileHandlerMock->expects($this->exactly(2))
519 ->method('createNestedDirectory')
520 ->will($this->returnCallback(function ($path) {
521 if (!in_array($path, ['foo/bar', 'baz/foo'])) {
522 throw new \Exception('Path "' . $path . '" is not expected to be created');
523 }
524
525 })
526 );
527 $fileHandlerMock->ensureConfiguredDirectoriesExist([
528 'key' => 'foo_bar',
529 'createDirs' => 'foo/bar, baz/foo',
530 ]
531 );
532 }
533
534 /**
535 * @test
536 */
537 public function configuredDirectoriesAreNotCreatedIfTheyAlreadyExist()
538 {
539 $fileHandlerMock = $this->getPreparedFileHandlingMockForDirectoryCreationTests();
540 $fileHandlerMock->expects($this->exactly(3))
541 ->method('directoryExists')
542 ->will($this->returnValue(true));
543 $fileHandlerMock->expects($this->never())
544 ->method('createNestedDirectory');
545 $fileHandlerMock->ensureConfiguredDirectoriesExist([
546 'key' => 'foo_bar',
547 'uploadfolder' => 1,
548 'createDirs' => 'foo/bar, baz/foo',
549 ]
550 );
551 }
552
553 /**
554 * Warning: This test asserts multiple things at once to keep the setup short.
555 *
556 * @test
557 */
558 public function createZipFileFromExtensionGeneratesCorrectArchive()
559 {
560 // 42 second of first day in 1970 - used to have achieve stable file names
561 $GLOBALS['EXEC_TIME'] = 42;
562
563 // Create extension for testing:
564 $extKey = $this->createFakeExtension();
565 $extensionRoot = $this->fakedExtensions[$extKey]['siteAbsPath'];
566
567 // Build mocked fileHandlingUtility:
568 $fileHandlerMock = $this->getAccessibleMock(
569 \TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility::class,
570 ['getAbsoluteExtensionPath', 'getExtensionVersion']
571 );
572 $fileHandlerMock->expects($this->any())
573 ->method('getAbsoluteExtensionPath')
574 ->will($this->returnValue($extensionRoot));
575 $fileHandlerMock->expects($this->any())
576 ->method('getExtensionVersion')
577 ->will($this->returnValue('0.0.0'));
578
579 // Add files and directories to extension:
580 touch($extensionRoot . 'emptyFile.txt');
581 file_put_contents($extensionRoot . 'notEmptyFile.txt', 'content');
582 touch($extensionRoot . '.hiddenFile');
583 mkdir($extensionRoot . 'emptyDir');
584 mkdir($extensionRoot . 'notEmptyDir');
585 touch($extensionRoot . 'notEmptyDir/file.txt');
586
587 // Create zip-file from extension
588 $filename = $fileHandlerMock->_call('createZipFileFromExtension', $extKey);
589
590 $expectedFilename = PATH_site . 'typo3temp/var/ExtensionManager/' . $extKey . '_0.0.0_' . date('YmdHi', 42) . '.zip';
591 $this->testFilesToDelete[] = $filename;
592 $this->assertEquals($expectedFilename, $filename, 'Archive file name differs from expectation');
593
594 // File was created
595 $this->assertTrue(file_exists($filename), 'Zip file not created');
596
597 // Read archive and check its contents
598 $archive = new \ZipArchive();
599 $this->assertTrue($archive->open($filename), 'Unable to open archive');
600 $this->assertEquals($archive->statName('emptyFile.txt')->size, 0, 'Empty file not in archive');
601 $this->assertEquals($archive->getFromName('notEmptyFile.txt'), 'content', 'Expected content not found');
602 $this->assertFalse($archive->statName('.hiddenFile'), 'Hidden file not in archive');
603 $this->assertTrue(is_array($archive->statName('emptyDir/')), 'Empty directory not in archive');
604 $this->assertTrue(is_array($archive->statName('notEmptyDir/')), 'Not empty directory not in archive');
605 $this->assertTrue(is_array($archive->statName('notEmptyDir/file.txt')), 'File within directory not in archive');
606
607 // Check that the archive has no additional content
608 $this->assertEquals($archive->numFiles, 5, 'Too many or too less files in archive');
609 }
610 }