Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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
namespace core;
18
 
19
use file_progress;
20
use tgz_packer;
21
 
22
defined('MOODLE_INTERNAL') || die();
23
 
24
global $CFG;
25
require_once($CFG->libdir . '/filestorage/file_progress.php');
26
 
27
/**
28
 * Unit tests for /lib/filestorage/tgz_packer.php and tgz_extractor.php.
29
 *
30
 * @package core
31
 * @copyright 2013 The Open University
32
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
33
 */
34
class tgz_packer_test extends \advanced_testcase implements file_progress {
35
    /**
36
     * @var array Progress information passed to the progress reporter
37
     */
38
    protected $progress;
39
 
40
    /**
41
     * Puts contents with specified time.
42
     *
43
     * @param string $path File path
44
     * @param string $contents Contents of file
45
     * @param int $mtime Time modified
46
     */
47
    protected static function file_put_contents_at_time($path, $contents, $mtime) {
48
        file_put_contents($path, $contents);
49
        touch($path, $mtime);
50
    }
51
 
52
    /**
53
     * Set up some files to be archived.
54
     *
55
     * @return array Array listing files of all types
56
     */
57
    protected function prepare_file_list() {
58
        global $CFG;
59
        $this->resetAfterTest(true);
60
 
61
        // Make array listing files to archive.
62
        $filelist = array();
63
 
64
        // Normal file.
65
        self::file_put_contents_at_time($CFG->tempdir . '/file1.txt', 'File 1', 1377993601);
66
        $filelist['out1.txt'] = $CFG->tempdir . '/file1.txt';
67
 
68
        // Recursive directory w/ file and directory with file.
69
        check_dir_exists($CFG->tempdir . '/dir1/dir2');
70
        self::file_put_contents_at_time($CFG->tempdir . '/dir1/file2.txt', 'File 2', 1377993602);
71
        self::file_put_contents_at_time($CFG->tempdir . '/dir1/dir2/file3.txt', 'File 3', 1377993603);
72
        $filelist['out2'] = $CFG->tempdir . '/dir1';
73
 
74
        // Moodle stored_file.
75
        $context = \context_system::instance();
76
        $filerecord = array('contextid' => $context->id, 'component' => 'phpunit',
77
                'filearea' => 'data', 'itemid' => 0, 'filepath' => '/',
78
                'filename' => 'file4.txt', 'timemodified' => 1377993604);
79
        $fs = get_file_storage();
80
        $sf = $fs->create_file_from_string($filerecord, 'File 4');
81
        $filelist['out3.txt'] = $sf;
82
 
83
         // Moodle stored_file directory.
84
        $filerecord['itemid'] = 1;
85
        $filerecord['filepath'] = '/dir1/';
86
        $filerecord['filename'] = 'file5.txt';
87
        $filerecord['timemodified'] = 1377993605;
88
        $fs->create_file_from_string($filerecord, 'File 5');
89
        $filerecord['filepath'] = '/dir1/dir2/';
90
        $filerecord['filename'] = 'file6.txt';
91
        $filerecord['timemodified'] = 1377993606;
92
        $fs->create_file_from_string($filerecord, 'File 6');
93
        $filerecord['filepath'] = '/';
94
        $filerecord['filename'] = 'excluded.txt';
95
        $fs->create_file_from_string($filerecord, 'Excluded');
96
        $filelist['out4'] = $fs->get_file($context->id, 'phpunit', 'data', 1, '/dir1/', '.');
97
 
98
        // File stored as raw content.
99
        $filelist['out5.txt'] = array('File 7');
100
 
101
        // File where there's just an empty directory.
102
        $filelist['out6'] = null;
103
 
104
        return $filelist;
105
    }
106
 
107
    /**
108
     * Tests getting the item.
109
     */
11 efrain 110
    public function test_get_packer(): void {
1 efrain 111
        $packer = get_file_packer('application/x-gzip');
112
        $this->assertInstanceOf('tgz_packer', $packer);
113
    }
114
 
115
    /**
116
     * Tests basic archive and extract to file paths.
117
     */
11 efrain 118
    public function test_to_normal_files(): void {
1 efrain 119
        global $CFG;
120
        $packer = get_file_packer('application/x-gzip');
121
 
122
        // Archive files.
123
        $files = $this->prepare_file_list();
124
        $archivefile = $CFG->tempdir . '/test.tar.gz';
125
        $packer->archive_to_pathname($files, $archivefile);
126
 
127
        // Extract same files.
128
        $outdir = $CFG->tempdir . '/out';
129
        check_dir_exists($outdir);
130
        $result = $packer->extract_to_pathname($archivefile, $outdir);
131
 
132
        // The result array should have file entries + directory entries for
133
        // all implicit directories + entry for the explicit directory.
134
        $expectedpaths = array('out1.txt', 'out2/', 'out2/dir2/', 'out2/dir2/file3.txt',
135
                'out2/file2.txt', 'out3.txt', 'out4/', 'out4/dir2/', 'out4/file5.txt',
136
                'out4/dir2/file6.txt', 'out5.txt', 'out6/');
137
        sort($expectedpaths);
138
        $actualpaths = array_keys($result);
139
        sort($actualpaths);
140
        $this->assertEquals($expectedpaths, $actualpaths);
141
        foreach ($result as $path => $booleantrue) {
142
            $this->assertTrue($booleantrue);
143
        }
144
 
145
        // Check the files are as expected.
146
        $this->assertEquals('File 1', file_get_contents($outdir . '/out1.txt'));
147
        $this->assertEquals('File 2', file_get_contents($outdir . '/out2/file2.txt'));
148
        $this->assertEquals('File 3', file_get_contents($outdir . '/out2/dir2/file3.txt'));
149
        $this->assertEquals('File 4', file_get_contents($outdir . '/out3.txt'));
150
        $this->assertEquals('File 5', file_get_contents($outdir . '/out4/file5.txt'));
151
        $this->assertEquals('File 6', file_get_contents($outdir . '/out4/dir2/file6.txt'));
152
        $this->assertEquals('File 7', file_get_contents($outdir . '/out5.txt'));
153
        $this->assertTrue(is_dir($outdir . '/out6'));
154
    }
155
 
156
    /**
157
     * Tests archive and extract to Moodle file system.
158
     */
11 efrain 159
    public function test_to_stored_files(): void {
1 efrain 160
        global $CFG;
161
        $packer = get_file_packer('application/x-gzip');
162
 
163
        // Archive files.
164
        $files = $this->prepare_file_list();
165
        $archivefile = $CFG->tempdir . '/test.tar.gz';
166
        $context = \context_system::instance();
167
        $sf = $packer->archive_to_storage($files,
168
                $context->id, 'phpunit', 'archive', 1, '/', 'archive.tar.gz');
169
        $this->assertInstanceOf('stored_file', $sf);
170
 
171
        // Extract (from storage) to disk.
172
        $outdir = $CFG->tempdir . '/out';
173
        check_dir_exists($outdir);
174
        $packer->extract_to_pathname($sf, $outdir);
175
 
176
        // Check the files are as expected.
177
        $this->assertEquals('File 1', file_get_contents($outdir . '/out1.txt'));
178
        $this->assertEquals('File 2', file_get_contents($outdir . '/out2/file2.txt'));
179
        $this->assertEquals('File 3', file_get_contents($outdir . '/out2/dir2/file3.txt'));
180
        $this->assertEquals('File 4', file_get_contents($outdir . '/out3.txt'));
181
        $this->assertEquals('File 5', file_get_contents($outdir . '/out4/file5.txt'));
182
        $this->assertEquals('File 6', file_get_contents($outdir . '/out4/dir2/file6.txt'));
183
        $this->assertEquals('File 7', file_get_contents($outdir . '/out5.txt'));
184
        $this->assertTrue(is_dir($outdir . '/out6'));
185
 
186
        // Extract to Moodle storage.
187
        $packer->extract_to_storage($sf, $context->id, 'phpunit', 'data', 2, '/out/');
188
        $fs = get_file_storage();
189
        $out = $fs->get_file($context->id, 'phpunit', 'data', 2, '/out/', 'out1.txt');
190
        $this->assertNotEmpty($out);
191
        $this->assertEquals('File 1', $out->get_content());
192
        $out = $fs->get_file($context->id, 'phpunit', 'data', 2, '/out/out2/', 'file2.txt');
193
        $this->assertNotEmpty($out);
194
        $this->assertEquals('File 2', $out->get_content());
195
        $out = $fs->get_file($context->id, 'phpunit', 'data', 2, '/out/out2/dir2/', 'file3.txt');
196
        $this->assertNotEmpty($out);
197
        $this->assertEquals('File 3', $out->get_content());
198
        $out = $fs->get_file($context->id, 'phpunit', 'data', 2, '/out/', 'out3.txt');
199
        $this->assertNotEmpty($out);
200
        $this->assertEquals('File 4', $out->get_content());
201
        $out = $fs->get_file($context->id, 'phpunit', 'data', 2, '/out/out4/', 'file5.txt');
202
        $this->assertNotEmpty($out);
203
        $this->assertEquals('File 5', $out->get_content());
204
        $out = $fs->get_file($context->id, 'phpunit', 'data', 2, '/out/out4/dir2/', 'file6.txt');
205
        $this->assertNotEmpty($out);
206
        $this->assertEquals('File 6', $out->get_content());
207
        $out = $fs->get_file($context->id, 'phpunit', 'data', 2, '/out/', 'out5.txt');
208
        $this->assertNotEmpty($out);
209
        $this->assertEquals('File 7', $out->get_content());
210
        $out = $fs->get_file($context->id, 'phpunit', 'data', 2, '/out/out6/', '.');
211
        $this->assertNotEmpty($out);
212
        $this->assertTrue($out->is_directory());
213
 
214
        // These functions are supposed to overwrite existing files; test they
215
        // don't give errors when run twice.
216
        $sf = $packer->archive_to_storage($files,
217
                $context->id, 'phpunit', 'archive', 1, '/', 'archive.tar.gz');
218
        $this->assertInstanceOf('stored_file', $sf);
219
        $packer->extract_to_storage($sf, $context->id, 'phpunit', 'data', 2, '/out/');
220
    }
221
 
222
    /**
223
     * Tests extracting with a list of specified files.
224
     */
11 efrain 225
    public function test_only_specified_files(): void {
1 efrain 226
        global $CFG;
227
        $packer = get_file_packer('application/x-gzip');
228
 
229
        // Archive files.
230
        $files = $this->prepare_file_list();
231
        $archivefile = $CFG->tempdir . '/test.tar.gz';
232
        $packer->archive_to_pathname($files, $archivefile);
233
 
234
        // Extract same files.
235
        $outdir = $CFG->tempdir . '/out';
236
        check_dir_exists($outdir);
237
        $result = $packer->extract_to_pathname($archivefile, $outdir,
238
                array('out3.txt', 'out6/', 'out4/file5.txt'));
239
 
240
        // Check result reporting only includes specified files.
241
        $expectedpaths = array('out3.txt', 'out4/file5.txt', 'out6/');
242
        sort($expectedpaths);
243
        $actualpaths = array_keys($result);
244
        sort($actualpaths);
245
        $this->assertEquals($expectedpaths, $actualpaths);
246
 
247
        // Check the files are as expected.
248
        $this->assertFalse(file_exists($outdir . '/out1.txt'));
249
        $this->assertEquals('File 4', file_get_contents($outdir . '/out3.txt'));
250
        $this->assertEquals('File 5', file_get_contents($outdir . '/out4/file5.txt'));
251
        $this->assertTrue(is_dir($outdir . '/out6'));
252
    }
253
 
254
    /**
255
     * Tests extracting files returning only a boolean state with success.
256
     */
11 efrain 257
    public function test_extract_to_pathname_returnvalue_successful(): void {
1 efrain 258
        $packer = get_file_packer('application/x-gzip');
259
 
260
        // Prepare files.
261
        $files = $this->prepare_file_list();
262
        $archivefile = make_request_directory() . '/test.tgz';
263
        $packer->archive_to_pathname($files, $archivefile);
264
 
265
        // Extract same files.
266
        $outdir = make_request_directory();
267
        $result = $packer->extract_to_pathname($archivefile, $outdir, null, null, true);
268
 
269
        $this->assertTrue($result);
270
    }
271
 
272
    /**
273
     * Tests extracting files returning only a boolean state with failure.
274
     */
11 efrain 275
    public function test_extract_to_pathname_returnvalue_failure(): void {
1 efrain 276
        $packer = get_file_packer('application/x-gzip');
277
 
278
        // Create sample files.
279
        $archivefile = make_request_directory() . '/test.tgz';
280
        file_put_contents($archivefile, '');
281
 
282
        // Extract same files.
283
        $outdir = make_request_directory();
284
 
285
        $result = $packer->extract_to_pathname($archivefile, $outdir, null, null, true);
286
 
287
        $this->assertFalse($result);
288
    }
289
 
290
    /**
291
     * Tests the progress reporting.
292
     */
11 efrain 293
    public function test_file_progress(): void {
1 efrain 294
        global $CFG;
295
 
296
        // Set up.
297
        $filelist = $this->prepare_file_list();
298
        $packer = get_file_packer('application/x-gzip');
299
        $archive = "$CFG->tempdir/archive.tgz";
300
        $context = \context_system::instance();
301
 
302
        // Archive to pathname.
303
        $this->progress = array();
304
        $result = $packer->archive_to_pathname($filelist, $archive, true, $this);
305
        $this->assertTrue($result);
306
        // Should send progress at least once per file.
307
        $this->assertTrue(count($this->progress) >= count($filelist));
308
        // Progress should obey some restrictions.
309
        $this->check_progress_toward_max();
310
 
311
        // Archive to storage.
312
        $this->progress = array();
313
        $archivefile = $packer->archive_to_storage($filelist, $context->id,
314
                'phpunit', 'test', 0, '/', 'archive.tgz', null, true, $this);
315
        $this->assertInstanceOf('stored_file', $archivefile);
316
        $this->assertTrue(count($this->progress) >= count($filelist));
317
        $this->check_progress_toward_max();
318
 
319
        // Extract to pathname.
320
        $this->progress = array();
321
        $target = "$CFG->tempdir/test/";
322
        check_dir_exists($target);
323
        $result = $packer->extract_to_pathname($archive, $target, null, $this);
324
        remove_dir($target);
325
        // We only output progress once per block, and this is kind of a small file.
326
        $this->assertTrue(count($this->progress) >= 1);
327
        $this->check_progress_toward_max();
328
 
329
        // Extract to storage (from storage).
330
        $this->progress = array();
331
        $result = $packer->extract_to_storage($archivefile, $context->id,
332
                'phpunit', 'target', 0, '/', null, $this);
333
        $this->assertTrue(count($this->progress) >= 1);
334
        $this->check_progress_toward_max();
335
 
336
        // Extract to storage (from path).
337
        $this->progress = array();
338
        $result = $packer->extract_to_storage($archive, $context->id,
339
                'phpunit', 'target', 0, '/', null, $this);
340
        $this->assertTrue(count($this->progress) >= 1);
341
        $this->check_progress_toward_max();
342
 
343
        // Wipe created disk file.
344
        unlink($archive);
345
    }
346
 
347
    /**
348
     * Tests the list_files function with and without an index file.
349
     */
11 efrain 350
    public function test_list_files(): void {
1 efrain 351
        global $CFG;
352
 
353
        // Set up.
354
        $filelist = $this->prepare_file_list();
355
        $packer = get_file_packer('application/x-gzip');
356
        $archive = "$CFG->tempdir/archive.tgz";
357
 
358
        // Archive with an index (default).
359
        $packer = get_file_packer('application/x-gzip');
360
        $result = $packer->archive_to_pathname($filelist, $archive, true, $this);
361
        $this->assertTrue($result);
362
        $hashwith = \file_storage::hash_from_path($archive);
363
 
364
        // List files.
365
        $files = $packer->list_files($archive);
366
 
367
        // Check they match expected.
368
        $expectedinfo = array(
369
            array('out1.txt', 1377993601, false, 6),
370
            array('out2/', tgz_packer::DEFAULT_TIMESTAMP, true, 0),
371
            array('out2/dir2/', tgz_packer::DEFAULT_TIMESTAMP, true, 0),
372
            array('out2/dir2/file3.txt', 1377993603, false, 6),
373
            array('out2/file2.txt', 1377993602, false, 6),
374
            array('out3.txt', 1377993604, false, 6),
375
            array('out4/', tgz_packer::DEFAULT_TIMESTAMP, true, 0),
376
            array('out4/dir2/', tgz_packer::DEFAULT_TIMESTAMP, true, 0),
377
            array('out4/dir2/file6.txt', 1377993606, false, 6),
378
            array('out4/file5.txt', 1377993605, false, 6),
379
            array('out5.txt', tgz_packer::DEFAULT_TIMESTAMP, false, 6),
380
            array('out6/', tgz_packer::DEFAULT_TIMESTAMP, true, 0),
381
        );
382
        $this->assertEquals($expectedinfo, self::convert_info_for_assert($files));
383
 
384
        // Archive with no index. Should have same result.
385
        $this->progress = array();
386
        $packer->set_include_index(false);
387
        $result = $packer->archive_to_pathname($filelist, $archive, true, $this);
388
        $this->assertTrue($result);
389
        $hashwithout = \file_storage::hash_from_path($archive);
390
        $files = $packer->list_files($archive);
391
        $this->assertEquals($expectedinfo, self::convert_info_for_assert($files));
392
 
393
        // Check it actually is different (does have index in)!
394
        $this->assertNotEquals($hashwith, $hashwithout);
395
 
396
        // Put the index back on in case of future tests.
397
        $packer->set_include_index(true);
398
    }
399
 
400
    /**
401
     * Utility function to convert the file info array into a simpler format
402
     * for making comparisons.
403
     *
404
     * @param array $files Array from list_files result
405
     */
406
    protected static function convert_info_for_assert(array $files) {
407
        $actualinfo = array();
408
        foreach ($files as $file) {
409
            $actualinfo[] = array($file->pathname, $file->mtime, $file->is_directory, $file->size);
410
        }
411
        usort($actualinfo, function($a, $b) {
412
            return strcmp($a[0], $b[0]);
413
        });
414
        return $actualinfo;
415
    }
416
 
11 efrain 417
    public function test_is_tgz_file(): void {
1 efrain 418
        global $CFG;
419
 
420
        // Set up.
421
        $filelist = $this->prepare_file_list();
422
        $packer1 = get_file_packer('application/x-gzip');
423
        $packer2 = get_file_packer('application/zip');
424
        $archive2 = "$CFG->tempdir/archive.zip";
425
 
426
        // Archive in tgz and zip format.
427
        $context = \context_system::instance();
428
        $archive1 = $packer1->archive_to_storage($filelist, $context->id,
429
                'phpunit', 'test', 0, '/', 'archive.tgz', null, true, $this);
430
        $this->assertInstanceOf('stored_file', $archive1);
431
        $result = $packer2->archive_to_pathname($filelist, $archive2);
432
        $this->assertTrue($result);
433
 
434
        // Use is_tgz_file to detect which is which. First check is from storage,
435
        // second check is from filesystem.
436
        $this->assertTrue(tgz_packer::is_tgz_file($archive1));
437
        $this->assertFalse(tgz_packer::is_tgz_file($archive2));
438
    }
439
 
440
    /**
441
     * Checks that progress reported is numeric rather than indeterminate,
442
     * and follows the progress reporting rules.
443
     */
444
    protected function check_progress_toward_max() {
445
        $lastvalue = -1; $lastmax = -1;
446
        foreach ($this->progress as $progressitem) {
447
            list($value, $max) = $progressitem;
448
            if ($lastmax != -1) {
449
                $this->assertEquals($max, $lastmax);
450
            } else {
451
                $lastmax = $max;
452
            }
453
            $this->assertTrue(is_integer($value));
454
            $this->assertTrue(is_integer($max));
455
            $this->assertNotEquals(file_progress::INDETERMINATE, $max);
456
            $this->assertTrue($value <= $max);
457
            $this->assertTrue($value >= $lastvalue);
458
            $lastvalue = $value;
459
        }
460
    }
461
 
462
    /**
463
     * Handles file_progress interface.
464
     *
465
     * @param int $progress
466
     * @param int $max
467
     */
468
    public function progress($progress = file_progress::INDETERMINATE, $max = file_progress::INDETERMINATE) {
469
        $this->progress[] = array($progress, $max);
470
    }
471
}