MDL-37429 zipping improvements
[moodle.git] / lib / filestorage / tests / zip_packer_test.php
CommitLineData
79c966cf
PS
1<?php
2// This file is part of Moodle - http://moodle.org/
3//
4// Moodle is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Moodle is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
16
17/**
18 * Unit tests for /lib/filestorage/zip_packer.php and zip_archive.php
19 *
20 * @package core_files
21 * @category phpunit
22 * @copyright 2012 Petr Skoda
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 */
25
26defined('MOODLE_INTERNAL') || die();
27
28
29class zip_packer_testcase extends advanced_testcase {
30 protected $testfile;
31 protected $files;
32
33 protected function setUp() {
34 parent::setUp();
35
36 $this->testfile = __DIR__.'/fixtures/test.txt';
37
38 $fs = get_file_storage();
39 $context = context_system::instance();
40 if (!$file = $fs->get_file($context->id, 'phpunit', 'data', 0, '/', 'test.txt')) {
41 $file = $fs->create_file_from_pathname(
42 array('contextid'=>$context->id, 'component'=>'phpunit', 'filearea'=>'data', 'itemid'=>0, 'filepath'=>'/', 'filename'=>'test.txt'),
43 $this->testfile);
44 }
45
46 $this->files = array(
47 'test.test' => $this->testfile,
48 'testíček.txt' => $this->testfile,
49 'Prüfung.txt' => $this->testfile,
50 '测试.txt' => $this->testfile,
51 '試験.txt' => $this->testfile,
52 'Žluťoučký/Koníček.txt' => $file,
53 );
54 }
55
56 public function test_get_packer() {
57 $this->resetAfterTest(false);
58 $packer = get_file_packer();
59 $this->assertInstanceOf('zip_packer', $packer);
60
61 $packer = get_file_packer('application/zip');
62 $this->assertInstanceOf('zip_packer', $packer);
63 }
64
65 /**
66 * @depends test_get_packer
67 */
68 public function test_list_files() {
69 $this->resetAfterTest(false);
6fb8ae95
PS
70
71 $files = array(
72 __DIR__.'/fixtures/test_moodle_22.zip',
73 __DIR__.'/fixtures/test_moodle.zip',
74 __DIR__.'/fixtures/test_tc_8.zip',
75 __DIR__.'/fixtures/test_7zip_927.zip',
76 __DIR__.'/fixtures/test_winzip_165.zip',
77 __DIR__.'/fixtures/test_winrar_421.zip',
78 );
79
80 if (function_exists('normalizer_normalize')) {
81 // Unfortunately there is no way to standardise UTF-8 strings without INTL extension
82 $files[] = __DIR__.'/fixtures/test_infozip_3.zip';
83 $files[] = __DIR__.'/fixtures/test_osx_1074.zip';
84 }
79c966cf
PS
85
86 $packer = get_file_packer('application/zip');
87
6fb8ae95
PS
88 foreach ($files as $archive) {
89 $archivefiles = $packer->list_files($archive);
90 $this->assertTrue(is_array($archivefiles), "Archive not extracted properly: ".basename($archive).' ');
91 $this->assertTrue(count($this->files) === count($archivefiles) or count($this->files) === count($archivefiles) - 1); // Some zippers create empty dirs.
92 foreach($archivefiles as $file) {
93 if ($file->pathname === 'Žluťoučký/') {
94 // Some zippers create empty dirs.
95 continue;
96 }
97 $this->assertArrayHasKey($file->pathname, $this->files, "File $file->pathname not extracted properly: ".basename($archive).' ');
98 }
79c966cf
PS
99 }
100
6fb8ae95
PS
101 // Windows packer supports only DOS encoding
102 $archive = __DIR__.'/fixtures/test_win8_de.zip';
103 $archivefiles = $packer->list_files($archive);
104 $this->assertTrue(is_array($archivefiles), "Archive not extracted properly: ".basename($archive).' ');
105 $this->assertEquals(2, count($archivefiles));
79c966cf 106 foreach($archivefiles as $file) {
6fb8ae95
PS
107 $this->assertTrue($file->pathname === 'Prüfung.txt' or $file->pathname === 'test.test');
108 }
109
110 $zip_archive = new zip_archive();
111 $zip_archive->open(__DIR__.'/fixtures/test_win8_cz.zip', file_archive::OPEN, 'cp852');
112 $archivefiles = $zip_archive->list_files();
113 $this->assertTrue(is_array($archivefiles), "Archive not extracted properly: ".basename($archive).' ');
114 $this->assertEquals(3, count($archivefiles));
115 foreach($archivefiles as $file) {
116 $this->assertTrue($file->pathname === 'Žluťoučký/Koníček.txt' or $file->pathname === 'testíček.txt' or $file->pathname === 'test.test');
79c966cf 117 }
6fb8ae95 118 $zip_archive->close();
01b4040a
PS
119
120 // Empty archive extraction.
121 $archive = __DIR__.'/fixtures/empty.zip';
122 $archivefiles = $packer->list_files($archive);
123 $this->assertSame(array(), $archivefiles);
79c966cf
PS
124 }
125
126 /**
127 * @depends test_list_files
128 */
129 public function test_archive_to_pathname() {
130 global $CFG;
131
132 $this->resetAfterTest(false);
133
134 $packer = get_file_packer('application/zip');
135 $archive = "$CFG->tempdir/archive.zip";
136
01b4040a 137 $this->assertFileNotExists($archive);
79c966cf
PS
138 $result = $packer->archive_to_pathname($this->files, $archive);
139 $this->assertTrue($result);
01b4040a 140 $this->assertFileExists($archive);
79c966cf
PS
141
142 $archivefiles = $packer->list_files($archive);
143 $this->assertTrue(is_array($archivefiles));
144 $this->assertEquals(count($this->files), count($archivefiles));
145 foreach($archivefiles as $file) {
146 $this->assertArrayHasKey($file->pathname, $this->files);
147 }
f6b49abf
PS
148
149 // Test invalid files parameter.
150 $archive = "$CFG->tempdir/archive2.zip";
01b4040a 151 $this->assertFileNotExists($archive);
f6b49abf 152
01b4040a 153 $this->assertFileNotExists(__DIR__.'/xx/yy/ee.txt');
f6b49abf 154 $files = array('xtest.txt'=>__DIR__.'/xx/yy/ee.txt');
1017426c 155
01b4040a 156 $result = $packer->archive_to_pathname($files, $archive, false);
f6b49abf 157 $this->assertFalse($result);
01b4040a
PS
158 $this->assertDebuggingCalled();
159 $this->assertFileNotExists($archive);
160
f6b49abf 161 $result = $packer->archive_to_pathname($files, $archive);
f6b49abf 162 $this->assertTrue($result);
01b4040a
PS
163 $this->assertFileExists($archive);
164 $this->assertDebuggingCalled();
165 $archivefiles = $packer->list_files($archive);
166 $this->assertSame(array(), $archivefiles);
167 unlink($archive);
f6b49abf 168
01b4040a
PS
169 $this->assertFileNotExists(__DIR__.'/xx/yy/ee.txt');
170 $this->assertFileExists(__DIR__.'/fixtures/test.txt');
171 $files = array('xtest.txt'=>__DIR__.'/xx/yy/ee.txt', 'test.txt'=>__DIR__.'/fixtures/test.txt', 'ytest.txt'=>__DIR__.'/xx/yy/yy.txt');
172 $result = $packer->archive_to_pathname($files, $archive);
173 $this->assertTrue($result);
174 $this->assertFileExists($archive);
175 $archivefiles = $packer->list_files($archive);
176 $this->assertCount(1, $archivefiles);
177 $this->assertEquals('test.txt', $archivefiles[0]->pathname);
178 $dms = $this->getDebuggingMessages();
179 $this->assertCount(2, $dms);
180 $this->resetDebugging();
181 unlink($archive);
79c966cf
PS
182 }
183
184 /**
185 * @depends test_archive_to_pathname
186 */
187 public function test_archive_to_storage() {
188 $this->resetAfterTest(false);
189
190 $packer = get_file_packer('application/zip');
191 $fs = get_file_storage();
192 $context = context_system::instance();
193
194 $this->assertFalse($fs->file_exists($context->id, 'phpunit', 'test', 0, '/', 'archive.zip'));
195 $result = $packer->archive_to_storage($this->files, $context->id, 'phpunit', 'test', 0, '/', 'archive.zip');
196 $this->assertInstanceOf('stored_file', $result);
197 $this->assertTrue($fs->file_exists($context->id, 'phpunit', 'test', 0, '/', 'archive.zip'));
198
199 $archivefiles = $result->list_files($packer);
200 $this->assertTrue(is_array($archivefiles));
201 $this->assertEquals(count($this->files), count($archivefiles));
202 foreach($archivefiles as $file) {
203 $this->assertArrayHasKey($file->pathname, $this->files);
204 }
205 }
206
207 /**
208 * @depends test_archive_to_storage
209 */
210 public function test_extract_to_pathname() {
211 global $CFG;
212
213 $this->resetAfterTest(false);
214
215 $packer = get_file_packer('application/zip');
216 $fs = get_file_storage();
217 $context = context_system::instance();
218
219 $target = "$CFG->tempdir/test/";
220 $testcontent = file_get_contents($this->testfile);
221
222 @mkdir($target, $CFG->directorypermissions);
223 $this->assertTrue(is_dir($target));
224
225 $archive = "$CFG->tempdir/archive.zip";
01b4040a 226 $this->assertFileExists($archive);
79c966cf
PS
227 $result = $packer->extract_to_pathname($archive, $target);
228 $this->assertTrue(is_array($result));
229 $this->assertEquals(count($this->files), count($result));
230 foreach($this->files as $file=>$unused) {
231 $this->assertTrue($result[$file]);
01b4040a 232 $this->assertFileExists($target.$file);
79c966cf
PS
233 $this->assertSame($testcontent, file_get_contents($target.$file));
234 }
235
236 $archive = $fs->get_file($context->id, 'phpunit', 'test', 0, '/', 'archive.zip');
237 $this->assertNotEmpty($archive);
238 $result = $packer->extract_to_pathname($archive, $target);
239 $this->assertTrue(is_array($result));
240 $this->assertEquals(count($this->files), count($result));
241 foreach($this->files as $file=>$unused) {
242 $this->assertTrue($result[$file]);
01b4040a 243 $this->assertFileExists($target.$file);
79c966cf
PS
244 $this->assertSame($testcontent, file_get_contents($target.$file));
245 }
246 }
247
e462f46b
FM
248 /**
249 * @depends test_archive_to_storage
250 */
251 public function test_extract_to_pathname_onlyfiles() {
252 global $CFG;
253
254 $this->resetAfterTest(false);
255
256 $packer = get_file_packer('application/zip');
257 $fs = get_file_storage();
258 $context = context_system::instance();
259
260 $target = "$CFG->tempdir/onlyfiles/";
261 $testcontent = file_get_contents($this->testfile);
262
263 @mkdir($target, $CFG->directorypermissions);
264 $this->assertTrue(is_dir($target));
265
266 $onlyfiles = array('test', 'test.test', 'Žluťoučký/Koníček.txt', 'Idontexist');
267 $willbeextracted = array_intersect(array_keys($this->files), $onlyfiles);
268 $donotextract = array_diff(array_keys($this->files), $onlyfiles);
269
270 $archive = "$CFG->tempdir/archive.zip";
01b4040a 271 $this->assertFileExists($archive);
e462f46b
FM
272 $result = $packer->extract_to_pathname($archive, $target, $onlyfiles);
273 $this->assertTrue(is_array($result));
274 $this->assertEquals(count($willbeextracted), count($result));
275
276 foreach($willbeextracted as $file) {
277 $this->assertTrue($result[$file]);
01b4040a 278 $this->assertFileExists($target.$file);
e462f46b
FM
279 $this->assertSame($testcontent, file_get_contents($target.$file));
280 }
281 foreach($donotextract as $file) {
282 $this->assertFalse(isset($result[$file]));
01b4040a 283 $this->assertFileNotExists($target.$file);
e462f46b
FM
284 }
285
286 }
287
79c966cf
PS
288 /**
289 * @depends test_archive_to_storage
290 */
291 public function test_extract_to_storage() {
292 global $CFG;
293
01b4040a 294 $this->resetAfterTest(false);
79c966cf
PS
295
296 $packer = get_file_packer('application/zip');
297 $fs = get_file_storage();
298 $context = context_system::instance();
299
300 $testcontent = file_get_contents($this->testfile);
301
302 $archive = $fs->get_file($context->id, 'phpunit', 'test', 0, '/', 'archive.zip');
303 $this->assertNotEmpty($archive);
304 $result = $packer->extract_to_storage($archive, $context->id, 'phpunit', 'target', 0, '/');
305 $this->assertTrue(is_array($result));
306 $this->assertEquals(count($this->files), count($result));
307 foreach($this->files as $file=>$unused) {
308 $this->assertTrue($result[$file]);
309 $stored_file = $fs->get_file_by_hash(sha1("/$context->id/phpunit/target/0/$file"));
310 $this->assertInstanceOf('stored_file', $stored_file);
311 $this->assertSame($testcontent, $stored_file->get_content());
312 }
313
314 $archive = "$CFG->tempdir/archive.zip";
01b4040a 315 $this->assertFileExists($archive);
79c966cf
PS
316 $result = $packer->extract_to_storage($archive, $context->id, 'phpunit', 'target', 0, '/');
317 $this->assertTrue(is_array($result));
318 $this->assertEquals(count($this->files), count($result));
319 foreach($this->files as $file=>$unused) {
320 $this->assertTrue($result[$file]);
321 $stored_file = $fs->get_file_by_hash(sha1("/$context->id/phpunit/target/0/$file"));
322 $this->assertInstanceOf('stored_file', $stored_file);
323 $this->assertSame($testcontent, $stored_file->get_content());
324 }
01b4040a
PS
325 unlink($archive);
326 }
327
328 /**
329 * @depends test_extract_to_storage
330 */
331 public function test_add_files() {
332 global $CFG;
333
334 $this->resetAfterTest(false);
335
336 $packer = get_file_packer('application/zip');
337 $archive = "$CFG->tempdir/archive.zip";
338
339 $this->assertFileNotExists($archive);
340 $packer->archive_to_pathname(array(), $archive);
341 $this->assertFileExists($archive);
342
343 $zip_archive = new zip_archive();
344 $zip_archive->open($archive, file_archive::OPEN);
345 $this->assertEquals(0, $zip_archive->count());
346
347 $zip_archive->add_file_from_string('test.txt', 'test');
348 $zip_archive->close();
349 $zip_archive->open($archive, file_archive::OPEN);
350 $this->assertEquals(1, $zip_archive->count());
351
352 $zip_archive->add_directory('test2');
353 $zip_archive->close();
354 $zip_archive->open($archive, file_archive::OPEN);
355 $files = $zip_archive->list_files();
356 $this->assertCount(2, $files);
357 $this->assertEquals('test.txt', $files[0]->pathname);
358 $this->assertEquals('test2/', $files[1]->pathname);
359
360 $result = $zip_archive->add_file_from_pathname('test.txt', __DIR__.'/nonexistent/file.txt');
361 $this->assertFalse($result);
362 $zip_archive->close();
363 $zip_archive->open($archive, file_archive::OPEN);
364 $this->assertEquals(2, $zip_archive->count());
365
366 unlink($archive);
367 }
368
369 /**
370 * @depends test_add_files
371 */
372 public function test_open_archive() {
373 global $CFG;
374
375 $this->resetAfterTest(true);
376
377 $archive = "$CFG->tempdir/archive.zip";
378
379 $this->assertFileNotExists($archive);
380
381 $zip_archive = new zip_archive();
382 $result = $zip_archive->open($archive, file_archive::OPEN);
383 $this->assertFalse($result);
384 $this->assertDebuggingCalled();
385
386 $zip_archive = new zip_archive();
387 $result = $zip_archive->open($archive, file_archive::CREATE);
388 $this->assertTrue($result);
389 $zip_archive->add_file_from_string('test.txt', 'test');
390 $zip_archive->close();
391 $zip_archive->open($archive, file_archive::OPEN);
392 $this->assertEquals(1, $zip_archive->count());
393
394 $zip_archive = new zip_archive();
395 $result = $zip_archive->open($archive, file_archive::OVERWRITE);
396 $this->assertTrue($result);
397 $zip_archive->add_file_from_string('test2.txt', 'test');
398 $zip_archive->close();
399 $zip_archive->open($archive, file_archive::OPEN);
400 $this->assertEquals(1, $zip_archive->count());
401
402 unlink($archive);
403 $zip_archive = new zip_archive();
404 $result = $zip_archive->open($archive, file_archive::OVERWRITE);
405 $this->assertTrue($result);
406 $zip_archive->add_file_from_string('test2.txt', 'test');
407 $zip_archive->close();
408 $zip_archive->open($archive, file_archive::OPEN);
409 $this->assertEquals(1, $zip_archive->count());
79c966cf
PS
410 }
411}