ILIAS  trunk Revision v12.0_alpha-1227-g7ff6d300864
ZipTest.php
Go to the documentation of this file.
1<?php
2
19namespace ILIAS\Filesystem\Util;
20
21use PHPUnit\Framework\Attributes\BackupGlobals;
22use PHPUnit\Framework\Attributes\BackupStaticProperties;
23use PHPUnit\Framework\Attributes\PreserveGlobalState;
24use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
25use PHPUnit\Framework\Attributes\DataProvider;
30use PHPUnit\Framework\TestCase;
33
37#[BackupGlobals(false)]
38#[BackupStaticProperties(false)]
39#[PreserveGlobalState(false)]
40#[RunTestsInSeparateProcesses]
41class ZipTest extends TestCase
42{
43 public const ZIPPED_ZIP = 'zipped.zip';
44 protected string $zips_dir = __DIR__ . '/zips/';
45 protected string $unzips_dir = __DIR__ . '/unzips/';
46
47 protected function setUp(): void
48 {
49 if (file_exists($this->unzips_dir . self::ZIPPED_ZIP)) {
50 unlink($this->unzips_dir . self::ZIPPED_ZIP);
51 }
52 if (!file_exists($this->unzips_dir)) {
53 mkdir($this->unzips_dir);
54 }
55 }
56
57 protected function tearDown(): void
58 {
59 if (file_exists($this->unzips_dir)) {
60 $this->recurseRmdir($this->unzips_dir);
61 }
62 }
63
64 public function testZip(): void
65 {
66 $zip_options = new ZipOptions();
67 $streams = [
68 Streams::ofResource(fopen($this->zips_dir . '1_folder_1_file_mac.zip', 'r')),
69 Streams::ofResource(fopen($this->zips_dir . '1_folder_win.zip', 'r')),
70 ];
71 $zip = new Zip($zip_options, ...$streams);
72 $zip_stream = $zip->get();
73 $this->assertGreaterThan(0, $zip_stream->getSize());
74
75 $unzip_again = new Unzip(new UnzipOptions(), $zip_stream);
76 $this->assertSame(2, $unzip_again->getAmountOfFiles());
77 }
78
79 public function testZip2(): void
80 {
81 $zip_options = new ZipOptions();
82 $streams = [
83 Streams::ofResource(fopen($this->zips_dir . '1_folder_1_file_mac.zip', 'r')),
84 ];
85 $zip = new Zip($zip_options, ...$streams);
86 $zip_stream = $zip->get();
87 $this->assertGreaterThan(0, $zip_stream->getSize());
88
89 $unzip_again = new Unzip(new UnzipOptions(), $zip_stream);
90 $this->assertSame(1, $unzip_again->getAmountOfFiles());
91 }
92
93 public function testZip3(): void
94 {
95 $zip_options = new ZipOptions();
96 $streams = [
97 Streams::ofResource(fopen($this->zips_dir . '1_folder_1_file_mac.zip', 'r')),
98 Streams::ofResource(fopen($this->zips_dir . '1_folder_mac.zip', 'r')),
99 Streams::ofResource(fopen($this->zips_dir . '1_folder_win.zip', 'r')),
100 Streams::ofResource(fopen($this->zips_dir . '3_folders_mac.zip', 'r')),
101 Streams::ofResource(fopen($this->zips_dir . '3_folders_win.zip', 'r')),
102 ];
103 $zip = new Zip($zip_options, ...$streams);
104 $zip_stream = $zip->get();
105 $this->assertGreaterThan(0, $zip_stream->getSize());
106
107 $unzip_again = new Unzip(new UnzipOptions(), $zip_stream);
108 $this->assertSame(5, $unzip_again->getAmountOfFiles());
109 }
110
111 public function testLegacyZip(): void
112 {
113 $legacy = new LegacyArchives();
114
115 define('CLIENT_WEB_DIR', __DIR__);
116 define('ILIAS_WEB_DIR', 'public/data');
117 define('CLIENT_ID', 'test');
118 define('CLIENT_DATA_DIR', __DIR__);
119 define('ILIAS_ABSOLUTE_PATH', __DIR__);
120
121 $legacy->zip($this->zips_dir, $this->unzips_dir . self::ZIPPED_ZIP, false);
122 $this->assertFileExists($this->unzips_dir . self::ZIPPED_ZIP);
123
124 $unzip_again = new Unzip(
125 new UnzipOptions(),
126 Streams::ofResource(fopen($this->unzips_dir . self::ZIPPED_ZIP, 'r'))
127 );
128 $expected = [
129 0 => './',
130 1 => '3_folders_win.zip',
131 2 => '1_folder_mac.zip',
132 3 => '3_folders_mac.zip',
133 4 => '1_folder_win.zip',
134 5 => '1_folder_1_file_mac.zip'
135 ];
136 sort($expected);
137
138 $actual = iterator_to_array($unzip_again->getPaths());
139 sort($actual);
140
141 $this->assertSame($expected, $actual);
142 $this->assertSame(5, $unzip_again->getAmountOfFiles());
143
144 $depth = 0;
145 foreach ($unzip_again->getPaths() as $path) {
146 $parts = explode('/', $path);
147 $depth = max($depth, count($parts));
148 }
149 $this->assertEquals(2, $depth);
150 $this->recurseRmdir($this->unzips_dir);
151 }
152
153 public function LegacyZipWithTop(): void
154 {
155 $legacy = new LegacyArchives();
156
157 define('CLIENT_WEB_DIR', __DIR__);
158 define('ILIAS_WEB_DIR', 'public/data');
159 define('CLIENT_ID', 'test');
160 define('CLIENT_DATA_DIR', __DIR__);
161 define('ILIAS_ABSOLUTE_PATH', __DIR__);
162
163 mkdir($this->unzips_dir);
164 $legacy->zip($this->zips_dir, $this->unzips_dir . self::ZIPPED_ZIP, true);
165 $this->assertFileExists($this->unzips_dir . self::ZIPPED_ZIP);
166
167 $unzip_again = new Unzip(
168 new UnzipOptions(),
169 Streams::ofResource(fopen($this->unzips_dir . self::ZIPPED_ZIP, 'r'))
170 );
171 $this->assertSame(5, $unzip_again->getAmountOfFiles());
172
173 $depth = 0;
174 foreach ($unzip_again->getPaths() as $path) {
175 $parts = explode('/', $path);
176 $depth = max($depth, count($parts));
177 }
178 $this->assertEquals(2, $depth);
179 $this->recurseRmdir($this->unzips_dir);
180 }
181
186 #[DataProvider('getZips')]
187 public function testUnzip(
188 string $zip,
189 bool $has_multiple_root_entries,
190 int $expected_amount_directories,
191 array $expected_directories,
192 int $expected_amount_files,
193 array $expected_files
194 ): void {
195 $this->assertStringContainsString('.zip', $zip);
196 $zip_path = $this->zips_dir . $zip;
197 $this->assertFileExists($zip_path);
198
199 $stream = Streams::ofResource(fopen($zip_path, 'rb'));
200 $options = new UnzipOptions();
201 $unzip = new Unzip($options, $stream);
202
203 $this->assertFalse($unzip->hasZipReadingError());
204 $this->assertSame($has_multiple_root_entries, $unzip->hasMultipleRootEntriesInZip());
205 $this->assertSame($expected_amount_directories, $unzip->getAmountOfDirectories());
206 $this->assertEquals($expected_directories, iterator_to_array($unzip->getDirectories()));
207 $this->assertSame($expected_amount_files, $unzip->getAmountOfFiles());
208 $this->assertEquals($expected_files, iterator_to_array($unzip->getFiles()));
209 }
210
211 public function testWrongZip(): void
212 {
213 $stream = Streams::ofResource(fopen(__FILE__, 'rb'));
214 $options = new UnzipOptions();
215 $unzip = new Unzip($options, $stream);
216 $this->assertTrue($unzip->hasZipReadingError());
217 $this->assertFalse($unzip->hasMultipleRootEntriesInZip());
218 $this->assertCount(0, iterator_to_array($unzip->getFiles()));
219 $this->assertCount(0, iterator_to_array($unzip->getDirectories()));
220 $this->assertCount(0, iterator_to_array($unzip->getPaths()));
221 $this->assertSame([], iterator_to_array($unzip->getDirectories()));
222 $this->assertSame([], iterator_to_array($unzip->getFiles()));
223 }
224
225 public function testLargeZIPs(): void
226 {
227 // get ulimit
228 $ulimit = (int) shell_exec('ulimit -n');
229 $limit = 2500;
230 if ($ulimit >= $limit) {
231 $this->markTestSkipped('ulimit is too high and would take too much resources');
232 }
233 $this->assertLessThan($limit, $ulimit);
234
235 $zip = new Zip(new ZipOptions());
236
237 $file_names = [];
238
239 for ($i = 0; $i < $ulimit * 2; $i++) {
240 $path_inside_zip = $file_names[] = 'test' . $i;
241 $zip->addStream(Streams::ofString('-'), $path_inside_zip);
242 }
243 $this->assertTrue(true); // no warning or error
244
245 // check if the zip now contains all files
246 $unzip = new Unzip(new UnzipOptions(), $zip->get());
247 $file_names_in_zip = iterator_to_array($unzip->getFiles());
248 sort($file_names);
249 sort($file_names_in_zip);
250 $this->assertEquals($file_names, $file_names_in_zip);
251 }
252
257 #[DataProvider('getZips')]
258 public function testLegacyUnzip(
259 string $zip,
260 bool $has_multiple_root_entries,
261 int $expected_amount_directories,
262 array $expected_directories,
263 int $expected_amount_files,
264 array $expected_files
265 ): void {
266 $legacy = new LegacyArchives();
267
268 $this->assertStringContainsString('.zip', $zip);
269 $zip_path = $this->zips_dir . $zip;
270 $this->assertFileExists($zip_path);
271
272 $temp_unzip_path = $this->unzips_dir . uniqid('unzip', true);
273
274 $return = $legacy->unzip(
275 $zip_path,
276 $temp_unzip_path
277 );
278
279 $this->assertTrue($return);
280
281 $unzipped_files = $this->directoryToArray($temp_unzip_path);
282 $expected_paths = array_merge($expected_directories, $expected_files);
283 sort($expected_paths);
284 $this->assertEquals($expected_paths, $unzipped_files);
285 $this->assertTrue($this->recurseRmdir($temp_unzip_path));
286 }
287
288 private function recurseRmdir(string $path_to_directory): bool
289 {
290 $files = array_diff(scandir($path_to_directory), ['.', '..']);
291 foreach ($files as $file) {
292 (is_dir("$path_to_directory/$file") && !is_link("$path_to_directory/$file")) ? $this->recurseRmdir(
293 "$path_to_directory/$file"
294 ) : unlink("$path_to_directory/$file");
295 }
296 return rmdir($path_to_directory);
297 }
298
302 private function directoryToArray(string $path_to_directory): array
303 {
304 $iterator = new \RecursiveIteratorIterator(
305 new \RecursiveDirectoryIterator($path_to_directory, \RecursiveDirectoryIterator::SKIP_DOTS),
306 \RecursiveIteratorIterator::SELF_FIRST,
307 \RecursiveIteratorIterator::CATCH_GET_CHILD
308 );
309 $paths = [];
310 foreach ($iterator as $item) {
311 $relative_path = str_replace($path_to_directory . '/', '', $item->getPathname());
312 $paths[] = $item->isDir() ? $relative_path . '/' : $relative_path;
313 }
314
315 sort($paths);
316
317 return $paths;
318 }
319
320 // PROVIDERS
321
322 public static function getZips(): \Iterator
323 {
324 yield ['1_folder_mac.zip', false, 10, self::$directories_one, 15, self::$files_one];
325 yield ['1_folder_win.zip', false, 10, self::$directories_one, 15, self::$files_one];
326 yield ['3_folders_mac.zip', true, 9, self::$directories_three, 12, self::$files_three];
327 yield ['3_folders_win.zip', true, 9, self::$directories_three, 12, self::$files_three];
328 yield ['1_folder_1_file_mac.zip', true, 3, self::$directories_mixed, 5, self::$files_mixed];
329 }
330
331 protected static array $files_mixed = [
332 0 => '03_Test.pdf',
333 1 => 'Ordner A/01_Test.pdf',
334 2 => 'Ordner A/02_Test.pdf',
335 3 => 'Ordner A/Ordner A_2/07_Test.pdf',
336 4 => 'Ordner A/Ordner A_2/08_Test.pdf'
337 ];
338
339 protected static array $directories_mixed = [
340 0 => 'Ordner A/',
341 1 => 'Ordner A/Ordner A_1/',
342 2 => 'Ordner A/Ordner A_2/'
343 ];
344
345 protected static array $directories_one = [
346 0 => 'Ordner 0/',
347 1 => 'Ordner 0/Ordner A/',
348 2 => 'Ordner 0/Ordner A/Ordner A_1/',
349 3 => 'Ordner 0/Ordner A/Ordner A_2/',
350 4 => 'Ordner 0/Ordner B/',
351 5 => 'Ordner 0/Ordner B/Ordner B_1/',
352 6 => 'Ordner 0/Ordner B/Ordner B_2/',
353 7 => 'Ordner 0/Ordner C/',
354 8 => 'Ordner 0/Ordner C/Ordner C_1/',
355 9 => 'Ordner 0/Ordner C/Ordner C_2/'
356 ];
357 protected static array $directories_three = [
358 0 => 'Ordner A/',
359 1 => 'Ordner A/Ordner A_1/',
360 2 => 'Ordner A/Ordner A_2/',
361 3 => 'Ordner B/',
362 4 => 'Ordner B/Ordner B_1/',
363 5 => 'Ordner B/Ordner B_2/',
364 6 => 'Ordner C/',
365 7 => 'Ordner C/Ordner C_1/',
366 8 => 'Ordner C/Ordner C_2/'
367 ];
368
369 protected static array $files_one = [
370 0 => 'Ordner 0/13_Test.pdf',
371 1 => 'Ordner 0/14_Test.pdf',
372 2 => 'Ordner 0/15_Test.pdf',
373 3 => 'Ordner 0/Ordner A/01_Test.pdf',
374 4 => 'Ordner 0/Ordner A/02_Test.pdf',
375 5 => 'Ordner 0/Ordner A/Ordner A_2/07_Test.pdf',
376 6 => 'Ordner 0/Ordner A/Ordner A_2/08_Test.pdf',
377 7 => 'Ordner 0/Ordner B/03_Test.pdf',
378 8 => 'Ordner 0/Ordner B/04_Test.pdf',
379 9 => 'Ordner 0/Ordner B/Ordner B_2/09_Test.pdf',
380 10 => 'Ordner 0/Ordner B/Ordner B_2/10_Test.pdf',
381 11 => 'Ordner 0/Ordner C/05_Test.pdf',
382 12 => 'Ordner 0/Ordner C/06_Test.pdf',
383 13 => 'Ordner 0/Ordner C/Ordner C_2/11_Test.pdf',
384 14 => 'Ordner 0/Ordner C/Ordner C_2/12_Test.pdf'
385 ];
386
387 protected static array $files_three = [
388 0 => 'Ordner A/01_Test.pdf',
389 1 => 'Ordner A/02_Test.pdf',
390 2 => 'Ordner A/Ordner A_2/07_Test.pdf',
391 3 => 'Ordner A/Ordner A_2/08_Test.pdf',
392 4 => 'Ordner B/03_Test.pdf',
393 5 => 'Ordner B/04_Test.pdf',
394 6 => 'Ordner B/Ordner B_2/09_Test.pdf',
395 7 => 'Ordner B/Ordner B_2/10_Test.pdf',
396 8 => 'Ordner C/05_Test.pdf',
397 9 => 'Ordner C/06_Test.pdf',
398 10 => 'Ordner C/Ordner C_2/11_Test.pdf',
399 11 => 'Ordner C/Ordner C_2/12_Test.pdf',
400 ];
401}
Stream factory which enables the user to create streams without the knowledge of the concrete class.
Definition: Streams.php:32
static ofString(string $string)
Creates a new stream with an initial value.
Definition: Streams.php:41
static ofResource($resource)
Wraps an already created resource with the stream abstraction.
Definition: Streams.php:64
recurseRmdir(string $path_to_directory)
Definition: ZipTest.php:288
directoryToArray(string $path_to_directory)
Definition: ZipTest.php:302
testLegacyUnzip(string $zip, bool $has_multiple_root_entries, int $expected_amount_directories, array $expected_directories, int $expected_amount_files, array $expected_files)
Definition: ZipTest.php:258
testUnzip(string $zip, bool $has_multiple_root_entries, int $expected_amount_directories, array $expected_directories, int $expected_amount_files, array $expected_files)
Definition: ZipTest.php:187
$path
Definition: ltiservices.php:30
if($clientAssertionType !='urn:ietf:params:oauth:client-assertion-type:jwt-bearer'|| $grantType !='client_credentials') $parts
Definition: ltitoken.php:61
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...