d98183f643b746ef6dff097c3973ba477fa28b10
[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();
119     }
121     /**
122      * @depends test_list_files
123      */
124     public function test_archive_to_pathname() {
125         global $CFG;
127         $this->resetAfterTest(false);
129         $packer = get_file_packer('application/zip');
130         $archive = "$CFG->tempdir/archive.zip";
132         $this->assertFalse(file_exists($archive));
133         $result = $packer->archive_to_pathname($this->files, $archive);
134         $this->assertTrue($result);
135         $this->assertTrue(file_exists($archive));
137         $archivefiles = $packer->list_files($archive);
138         $this->assertTrue(is_array($archivefiles));
139         $this->assertEquals(count($this->files), count($archivefiles));
140         foreach($archivefiles as $file) {
141             $this->assertArrayHasKey($file->pathname, $this->files);
142         }
144         // Test invalid files parameter.
145         $archive = "$CFG->tempdir/archive2.zip";
146         $this->assertFalse(file_exists($archive));
148         $this->assertFalse(file_exists(__DIR__.'/xx/yy/ee.txt'));
149         $files = array('xtest.txt'=>__DIR__.'/xx/yy/ee.txt');
151         // TODO MDL-37429 Ugly hack to handle multiple debugging message. To be
152         // removed once this issue has been integrated.
153         ob_start();
154         $result = $packer->archive_to_pathname($files, $archive);
155         $d = ob_end_clean();
156         $this->assertTrue($d !== '');
157         $this->assertFalse($result);
158         // TODO MDL-37429 Uncomment the following line and remove the ugly hack above once this
159         // issue has been integrated. In a nutshell, we need support for multiple debug messages.
160         // $this->assertDebuggingCalled();
162         $this->assertTrue(file_exists(__DIR__.'/fixtures/test.txt'));
163         $files = array();
164         $files['""""'] = null; // Invalid directory name.
165         $files['test.txt'] = __DIR__.'/fixtures/test.txt';
166         $result = $packer->archive_to_pathname($files, $archive);
167         $this->assertTrue($result);
168         $this->resetDebugging();
170         @unlink($archive);
171     }
173     /**
174      * @depends test_archive_to_pathname
175      */
176     public function test_archive_to_storage() {
177         $this->resetAfterTest(false);
179         $packer = get_file_packer('application/zip');
180         $fs = get_file_storage();
181         $context = context_system::instance();
183         $this->assertFalse($fs->file_exists($context->id, 'phpunit', 'test', 0, '/', 'archive.zip'));
184         $result = $packer->archive_to_storage($this->files, $context->id, 'phpunit', 'test', 0, '/', 'archive.zip');
185         $this->assertInstanceOf('stored_file', $result);
186         $this->assertTrue($fs->file_exists($context->id, 'phpunit', 'test', 0, '/', 'archive.zip'));
188         $archivefiles = $result->list_files($packer);
189         $this->assertTrue(is_array($archivefiles));
190         $this->assertEquals(count($this->files), count($archivefiles));
191         foreach($archivefiles as $file) {
192             $this->assertArrayHasKey($file->pathname, $this->files);
193         }
194     }
196     /**
197      * @depends test_archive_to_storage
198      */
199     public function test_extract_to_pathname() {
200         global $CFG;
202         $this->resetAfterTest(false);
204         $packer = get_file_packer('application/zip');
205         $fs = get_file_storage();
206         $context = context_system::instance();
208         $target = "$CFG->tempdir/test/";
209         $testcontent = file_get_contents($this->testfile);
211         @mkdir($target, $CFG->directorypermissions);
212         $this->assertTrue(is_dir($target));
214         $archive = "$CFG->tempdir/archive.zip";
215         $this->assertTrue(file_exists($archive));
216         $result = $packer->extract_to_pathname($archive, $target);
217         $this->assertTrue(is_array($result));
218         $this->assertEquals(count($this->files), count($result));
219         foreach($this->files as $file=>$unused) {
220             $this->assertTrue($result[$file]);
221             $this->assertTrue(file_exists($target.$file));
222             $this->assertSame($testcontent, file_get_contents($target.$file));
223         }
225         $archive = $fs->get_file($context->id, 'phpunit', 'test', 0, '/', 'archive.zip');
226         $this->assertNotEmpty($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->assertTrue(file_exists($target.$file));
233             $this->assertSame($testcontent, file_get_contents($target.$file));
234         }
235     }
237     /**
238      * @depends test_archive_to_storage
239      */
240     public function test_extract_to_pathname_onlyfiles() {
241         global $CFG;
243         $this->resetAfterTest(false);
245         $packer = get_file_packer('application/zip');
246         $fs = get_file_storage();
247         $context = context_system::instance();
249         $target = "$CFG->tempdir/onlyfiles/";
250         $testcontent = file_get_contents($this->testfile);
252         @mkdir($target, $CFG->directorypermissions);
253         $this->assertTrue(is_dir($target));
255         $onlyfiles = array('test', 'test.test', 'Žluťoučký/Koníček.txt', 'Idontexist');
256         $willbeextracted = array_intersect(array_keys($this->files), $onlyfiles);
257         $donotextract = array_diff(array_keys($this->files), $onlyfiles);
259         $archive = "$CFG->tempdir/archive.zip";
260         $this->assertTrue(file_exists($archive));
261         $result = $packer->extract_to_pathname($archive, $target, $onlyfiles);
262         $this->assertTrue(is_array($result));
263         $this->assertEquals(count($willbeextracted), count($result));
265         foreach($willbeextracted as $file) {
266             $this->assertTrue($result[$file]);
267             $this->assertTrue(file_exists($target.$file));
268             $this->assertSame($testcontent, file_get_contents($target.$file));
269         }
270         foreach($donotextract as $file) {
271             $this->assertFalse(isset($result[$file]));
272             $this->assertFalse(file_exists($target.$file));
273         }
275     }
277     /**
278      * @depends test_archive_to_storage
279      */
280     public function test_extract_to_storage() {
281         global $CFG;
283         $this->resetAfterTest(true);
285         $packer = get_file_packer('application/zip');
286         $fs = get_file_storage();
287         $context = context_system::instance();
289         $testcontent = file_get_contents($this->testfile);
291         $archive = $fs->get_file($context->id, 'phpunit', 'test', 0, '/', 'archive.zip');
292         $this->assertNotEmpty($archive);
293         $result = $packer->extract_to_storage($archive, $context->id, 'phpunit', 'target', 0, '/');
294         $this->assertTrue(is_array($result));
295         $this->assertEquals(count($this->files), count($result));
296         foreach($this->files as $file=>$unused) {
297             $this->assertTrue($result[$file]);
298             $stored_file = $fs->get_file_by_hash(sha1("/$context->id/phpunit/target/0/$file"));
299             $this->assertInstanceOf('stored_file', $stored_file);
300             $this->assertSame($testcontent, $stored_file->get_content());
301         }
303         $archive = "$CFG->tempdir/archive.zip";
304         $this->assertTrue(file_exists($archive));
305         $result = $packer->extract_to_storage($archive, $context->id, 'phpunit', 'target', 0, '/');
306         $this->assertTrue(is_array($result));
307         $this->assertEquals(count($this->files), count($result));
308         foreach($this->files as $file=>$unused) {
309             $this->assertTrue($result[$file]);
310             $stored_file = $fs->get_file_by_hash(sha1("/$context->id/phpunit/target/0/$file"));
311             $this->assertInstanceOf('stored_file', $stored_file);
312             $this->assertSame($testcontent, $stored_file->get_content());
313         }
314     }