MDL-37429 zipping improvements
[moodle.git] / lib / filestorage / tests / zip_packer_test.php
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/>.
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  */
26 defined('MOODLE_INTERNAL') || die();
29 class zip_packer_testcase extends advanced_testcase {
30     protected $testfile;
31     protected $files;
33     protected function setUp() {
34         parent::setUp();
36         $this->testfile = __DIR__.'/fixtures/test.txt';
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         }
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     }
56     public function test_get_packer() {
57         $this->resetAfterTest(false);
58         $packer = get_file_packer();
59         $this->assertInstanceOf('zip_packer', $packer);
61         $packer = get_file_packer('application/zip');
62         $this->assertInstanceOf('zip_packer', $packer);
63     }
65     /**
66      * @depends test_get_packer
67      */
68     public function test_list_files() {
69         $this->resetAfterTest(false);
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         );
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         }
86         $packer = get_file_packer('application/zip');
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             }
99         }
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));
106         foreach($archivefiles as $file) {
107             $this->assertTrue($file->pathname === 'Prüfung.txt' or $file->pathname === 'test.test');
108         }
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');
117         }
118         $zip_archive->close();
120         // Empty archive extraction.
121         $archive = __DIR__.'/fixtures/empty.zip';
122         $archivefiles = $packer->list_files($archive);
123         $this->assertSame(array(), $archivefiles);
124     }
126     /**
127      * @depends test_list_files
128      */
129     public function test_archive_to_pathname() {
130         global $CFG;
132         $this->resetAfterTest(false);
134         $packer = get_file_packer('application/zip');
135         $archive = "$CFG->tempdir/archive.zip";
137         $this->assertFileNotExists($archive);
138         $result = $packer->archive_to_pathname($this->files, $archive);
139         $this->assertTrue($result);
140         $this->assertFileExists($archive);
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         }
149         // Test invalid files parameter.
150         $archive = "$CFG->tempdir/archive2.zip";
151         $this->assertFileNotExists($archive);
153         $this->assertFileNotExists(__DIR__.'/xx/yy/ee.txt');
154         $files = array('xtest.txt'=>__DIR__.'/xx/yy/ee.txt');
156         $result = $packer->archive_to_pathname($files, $archive, false);
157         $this->assertFalse($result);
158         $this->assertDebuggingCalled();
159         $this->assertFileNotExists($archive);
161         $result = $packer->archive_to_pathname($files, $archive);
162         $this->assertTrue($result);
163         $this->assertFileExists($archive);
164         $this->assertDebuggingCalled();
165         $archivefiles = $packer->list_files($archive);
166         $this->assertSame(array(), $archivefiles);
167         unlink($archive);
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);
182     }
184     /**
185      * @depends test_archive_to_pathname
186      */
187     public function test_archive_to_storage() {
188         $this->resetAfterTest(false);
190         $packer = get_file_packer('application/zip');
191         $fs = get_file_storage();
192         $context = context_system::instance();
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'));
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     }
207     /**
208      * @depends test_archive_to_storage
209      */
210     public function test_extract_to_pathname() {
211         global $CFG;
213         $this->resetAfterTest(false);
215         $packer = get_file_packer('application/zip');
216         $fs = get_file_storage();
217         $context = context_system::instance();
219         $target = "$CFG->tempdir/test/";
220         $testcontent = file_get_contents($this->testfile);
222         @mkdir($target, $CFG->directorypermissions);
223         $this->assertTrue(is_dir($target));
225         $archive = "$CFG->tempdir/archive.zip";
226         $this->assertFileExists($archive);
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]);
232             $this->assertFileExists($target.$file);
233             $this->assertSame($testcontent, file_get_contents($target.$file));
234         }
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]);
243             $this->assertFileExists($target.$file);
244             $this->assertSame($testcontent, file_get_contents($target.$file));
245         }
246     }
248     /**
249      * @depends test_archive_to_storage
250      */
251     public function test_extract_to_pathname_onlyfiles() {
252         global $CFG;
254         $this->resetAfterTest(false);
256         $packer = get_file_packer('application/zip');
257         $fs = get_file_storage();
258         $context = context_system::instance();
260         $target = "$CFG->tempdir/onlyfiles/";
261         $testcontent = file_get_contents($this->testfile);
263         @mkdir($target, $CFG->directorypermissions);
264         $this->assertTrue(is_dir($target));
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);
270         $archive = "$CFG->tempdir/archive.zip";
271         $this->assertFileExists($archive);
272         $result = $packer->extract_to_pathname($archive, $target, $onlyfiles);
273         $this->assertTrue(is_array($result));
274         $this->assertEquals(count($willbeextracted), count($result));
276         foreach($willbeextracted as $file) {
277             $this->assertTrue($result[$file]);
278             $this->assertFileExists($target.$file);
279             $this->assertSame($testcontent, file_get_contents($target.$file));
280         }
281         foreach($donotextract as $file) {
282             $this->assertFalse(isset($result[$file]));
283             $this->assertFileNotExists($target.$file);
284         }
286     }
288     /**
289      * @depends test_archive_to_storage
290      */
291     public function test_extract_to_storage() {
292         global $CFG;
294         $this->resetAfterTest(false);
296         $packer = get_file_packer('application/zip');
297         $fs = get_file_storage();
298         $context = context_system::instance();
300         $testcontent = file_get_contents($this->testfile);
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         }
314         $archive = "$CFG->tempdir/archive.zip";
315         $this->assertFileExists($archive);
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         }
325         unlink($archive);
326     }
328     /**
329      * @depends test_extract_to_storage
330      */
331     public function test_add_files() {
332         global $CFG;
334         $this->resetAfterTest(false);
336         $packer = get_file_packer('application/zip');
337         $archive = "$CFG->tempdir/archive.zip";
339         $this->assertFileNotExists($archive);
340         $packer->archive_to_pathname(array(), $archive);
341         $this->assertFileExists($archive);
343         $zip_archive = new zip_archive();
344         $zip_archive->open($archive, file_archive::OPEN);
345         $this->assertEquals(0, $zip_archive->count());
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());
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);
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());
366         unlink($archive);
367     }
369     /**
370      * @depends test_add_files
371      */
372     public function test_open_archive() {
373         global $CFG;
375         $this->resetAfterTest(true);
377         $archive = "$CFG->tempdir/archive.zip";
379         $this->assertFileNotExists($archive);
381         $zip_archive = new zip_archive();
382         $result = $zip_archive->open($archive, file_archive::OPEN);
383         $this->assertFalse($result);
384         $this->assertDebuggingCalled();
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());
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());
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());
410     }