Proyectos de Subversion Moodle

Rev

Ir a la última revisión | | 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
/**
18
 * Unit tests for /lib/filestorage/file_storage.php
19
 *
20
 * @package   core
21
 * @category  test
22
 * @copyright 2012 David Mudrak <david@moodle.com>
23
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24
 */
25
 
26
namespace core;
27
 
28
use file_exception;
29
use file_reference_exception;
30
use repository;
31
use stored_file;
32
use stored_file_creation_exception;
33
 
34
defined('MOODLE_INTERNAL') || die();
35
 
36
global $CFG;
37
require_once($CFG->libdir . '/filelib.php');
38
require_once($CFG->dirroot . '/repository/lib.php');
39
require_once($CFG->libdir . '/filestorage/stored_file.php');
40
 
41
/**
42
 * Unit tests for /lib/filestorage/file_storage.php
43
 *
44
 * @package   core
45
 * @category  test
46
 * @copyright 2012 David Mudrak <david@moodle.com>
47
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
48
 * @coversDefaultClass \file_storage
49
 */
50
class file_storage_test extends \advanced_testcase {
51
 
52
    /**
53
     * Files can be created from strings.
54
     *
55
     * @covers ::create_file_from_string
56
     */
57
    public function test_create_file_from_string() {
58
        global $DB;
59
 
60
        $this->resetAfterTest(true);
61
 
62
        // Number of files installed in the database on a fresh Moodle site.
63
        $installedfiles = $DB->count_records('files', array());
64
 
65
        $content = 'abcd';
66
        $syscontext = \context_system::instance();
67
        $filerecord = array(
68
            'contextid' => $syscontext->id,
69
            'component' => 'core',
70
            'filearea'  => 'unittest',
71
            'itemid'    => 0,
72
            'filepath'  => '/images/',
73
            'filename'  => 'testfile.txt',
74
        );
75
        $pathhash = sha1('/'.$filerecord['contextid'].'/'.$filerecord['component'].'/'.$filerecord['filearea'].'/'.$filerecord['itemid'].$filerecord['filepath'].$filerecord['filename']);
76
 
77
        $fs = get_file_storage();
78
        $file = $fs->create_file_from_string($filerecord, $content);
79
 
80
        $this->assertInstanceOf('stored_file', $file);
81
        $this->assertTrue($file->compare_to_string($content));
82
        $this->assertSame($pathhash, $file->get_pathnamehash());
83
 
84
        $this->assertTrue($DB->record_exists('files', array('pathnamehash'=>$pathhash)));
85
 
86
        $filesystem = $fs->get_file_system();
87
        $location = $filesystem->get_local_path_from_storedfile($file, true);
88
 
89
        $this->assertFileExists($location);
90
 
91
        // Verify the dir placeholder files are created.
92
        $this->assertEquals($installedfiles + 3, $DB->count_records('files', array()));
93
        $this->assertTrue($DB->record_exists('files', array('pathnamehash'=>sha1('/'.$filerecord['contextid'].'/'.$filerecord['component'].'/'.$filerecord['filearea'].'/'.$filerecord['itemid'].'/.'))));
94
        $this->assertTrue($DB->record_exists('files', array('pathnamehash'=>sha1('/'.$filerecord['contextid'].'/'.$filerecord['component'].'/'.$filerecord['filearea'].'/'.$filerecord['itemid'].$filerecord['filepath'].'.'))));
95
 
96
        // Tests that missing content file is recreated.
97
 
98
        unlink($location);
99
        $this->assertFileDoesNotExist($location);
100
 
101
        $filerecord['filename'] = 'testfile2.txt';
102
        $file2 = $fs->create_file_from_string($filerecord, $content);
103
        $this->assertInstanceOf('stored_file', $file2);
104
        $this->assertSame($file->get_contenthash(), $file2->get_contenthash());
105
        $this->assertFileExists($location);
106
 
107
        $this->assertEquals($installedfiles + 4, $DB->count_records('files', array()));
108
 
109
        // Test that borked content file is recreated.
110
 
111
        $this->assertSame(2, file_put_contents($location, 'xx'));
112
 
113
        $filerecord['filename'] = 'testfile3.txt';
114
        $file3 = $fs->create_file_from_string($filerecord, $content);
115
        $this->assertInstanceOf('stored_file', $file3);
116
        $this->assertSame($file->get_contenthash(), $file3->get_contenthash());
117
        $this->assertFileExists($location);
118
 
119
        $this->assertSame($content, file_get_contents($location));
120
        $this->assertDebuggingCalled();
121
 
122
        $this->assertEquals($installedfiles + 5, $DB->count_records('files', array()));
123
    }
124
 
125
    /**
126
     * Local files can be added to the filepool
127
     *
128
     * @covers ::create_file_from_pathname
129
     */
130
    public function test_create_file_from_pathname() {
131
        global $CFG, $DB;
132
 
133
        $this->resetAfterTest(true);
134
 
135
        // Number of files installed in the database on a fresh Moodle site.
136
        $installedfiles = $DB->count_records('files', array());
137
 
138
        $filepath = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
139
        $syscontext = \context_system::instance();
140
        $filerecord = array(
141
            'contextid' => $syscontext->id,
142
            'component' => 'core',
143
            'filearea'  => 'unittest',
144
            'itemid'    => 0,
145
            'filepath'  => '/images/',
146
            'filename'  => 'testimage.jpg',
147
        );
148
        $pathhash = sha1('/'.$filerecord['contextid'].'/'.$filerecord['component'].'/'.$filerecord['filearea'].'/'.$filerecord['itemid'].$filerecord['filepath'].$filerecord['filename']);
149
 
150
        $fs = get_file_storage();
151
        $file = $fs->create_file_from_pathname($filerecord, $filepath);
152
 
153
        $this->assertInstanceOf('stored_file', $file);
154
        $this->assertTrue($file->compare_to_path($filepath));
155
 
156
        $this->assertTrue($DB->record_exists('files', array('pathnamehash'=>$pathhash)));
157
 
158
        $filesystem = $fs->get_file_system();
159
        $location = $filesystem->get_local_path_from_storedfile($file, true);
160
 
161
        $this->assertFileExists($location);
162
 
163
        // Verify the dir placeholder files are created.
164
        $this->assertEquals($installedfiles + 3, $DB->count_records('files', array()));
165
        $this->assertTrue($DB->record_exists('files', array('pathnamehash'=>sha1('/'.$filerecord['contextid'].'/'.$filerecord['component'].'/'.$filerecord['filearea'].'/'.$filerecord['itemid'].'/.'))));
166
        $this->assertTrue($DB->record_exists('files', array('pathnamehash'=>sha1('/'.$filerecord['contextid'].'/'.$filerecord['component'].'/'.$filerecord['filearea'].'/'.$filerecord['itemid'].$filerecord['filepath'].'.'))));
167
 
168
        // Tests that missing content file is recreated.
169
 
170
        unlink($location);
171
        $this->assertFileDoesNotExist($location);
172
 
173
        $filerecord['filename'] = 'testfile2.jpg';
174
        $file2 = $fs->create_file_from_pathname($filerecord, $filepath);
175
        $this->assertInstanceOf('stored_file', $file2);
176
        $this->assertSame($file->get_contenthash(), $file2->get_contenthash());
177
        $this->assertFileExists($location);
178
 
179
        $this->assertEquals($installedfiles + 4, $DB->count_records('files', array()));
180
 
181
        // Test that borked content file is recreated.
182
 
183
        $this->assertSame(2, file_put_contents($location, 'xx'));
184
 
185
        $filerecord['filename'] = 'testfile3.jpg';
186
        $file3 = $fs->create_file_from_pathname($filerecord, $filepath);
187
        $this->assertInstanceOf('stored_file', $file3);
188
        $this->assertSame($file->get_contenthash(), $file3->get_contenthash());
189
        $this->assertFileExists($location);
190
 
191
        $this->assertSame(file_get_contents($filepath), file_get_contents($location));
192
        $this->assertDebuggingCalled();
193
 
194
        $this->assertEquals($installedfiles + 5, $DB->count_records('files', array()));
195
 
196
        // Test invalid file creation.
197
 
198
        $filerecord['filename'] = 'testfile4.jpg';
199
        try {
200
            $fs->create_file_from_pathname($filerecord, $filepath.'nonexistent');
201
            $this->fail('Exception expected when trying to add non-existent stored file.');
202
        } catch (\Exception $e) {
203
            $this->assertInstanceOf('file_exception', $e);
204
        }
205
    }
206
 
207
    /**
208
     * Tests get get file.
209
     *
210
     * @covers ::get_file
211
     */
212
    public function test_get_file() {
213
        global $CFG;
214
 
215
        $this->resetAfterTest(false);
216
 
217
        $filepath = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
218
        $syscontext = \context_system::instance();
219
        $filerecord = array(
220
            'contextid' => $syscontext->id,
221
            'component' => 'core',
222
            'filearea'  => 'unittest',
223
            'itemid'    => 0,
224
            'filepath'  => '/images/',
225
            'filename'  => 'testimage.jpg',
226
        );
227
        $pathhash = sha1('/'.$filerecord['contextid'].'/'.$filerecord['component'].'/'.$filerecord['filearea'].'/'.$filerecord['itemid'].$filerecord['filepath'].$filerecord['filename']);
228
 
229
        $fs = get_file_storage();
230
        $file = $fs->create_file_from_pathname($filerecord, $filepath);
231
 
232
        $this->assertInstanceOf('stored_file', $file);
233
        $this->assertEquals($syscontext->id, $file->get_contextid());
234
        $this->assertEquals('core', $file->get_component());
235
        $this->assertEquals('unittest', $file->get_filearea());
236
        $this->assertEquals(0, $file->get_itemid());
237
        $this->assertEquals('/images/', $file->get_filepath());
238
        $this->assertEquals('testimage.jpg', $file->get_filename());
239
        $this->assertEquals(filesize($filepath), $file->get_filesize());
240
        $this->assertEquals($pathhash, $file->get_pathnamehash());
241
 
242
        return $file;
243
    }
244
 
245
    /**
246
     * Local images can be added to the filepool and their preview can be obtained
247
     *
248
     * @param stored_file $file
249
     * @depends test_get_file
250
     * @covers ::get_file_preview
251
     */
252
    public function test_get_file_preview(stored_file $file) {
253
        global $CFG;
254
 
255
        $this->resetAfterTest();
256
        $fs = get_file_storage();
257
 
258
        $previewtinyicon = $fs->get_file_preview($file, 'tinyicon');
259
        $this->assertInstanceOf('stored_file', $previewtinyicon);
260
        $this->assertEquals('6b9864ae1536a8eeef54e097319175a8be12f07c', $previewtinyicon->get_filename());
261
 
262
        $previewtinyicon = $fs->get_file_preview($file, 'thumb');
263
        $this->assertInstanceOf('stored_file', $previewtinyicon);
264
        $this->assertEquals('6b9864ae1536a8eeef54e097319175a8be12f07c', $previewtinyicon->get_filename());
265
 
266
        $this->expectException('file_exception');
267
        $fs->get_file_preview($file, 'amodewhichdoesntexist');
268
    }
269
 
270
    /**
271
     * Tests for get_file_preview without an image.
272
     *
273
     * @covers ::get_file_preview
274
     */
275
    public function test_get_file_preview_nonimage() {
276
        $this->resetAfterTest(true);
277
        $syscontext = \context_system::instance();
278
        $filerecord = array(
279
            'contextid' => $syscontext->id,
280
            'component' => 'core',
281
            'filearea'  => 'unittest',
282
            'itemid'    => 0,
283
            'filepath'  => '/textfiles/',
284
            'filename'  => 'testtext.txt',
285
        );
286
 
287
        $fs = get_file_storage();
288
        $fs->create_file_from_string($filerecord, 'text contents');
289
        $textfile = $fs->get_file($syscontext->id, $filerecord['component'], $filerecord['filearea'],
290
            $filerecord['itemid'], $filerecord['filepath'], $filerecord['filename']);
291
 
292
        $preview = $fs->get_file_preview($textfile, 'thumb');
293
        $this->assertFalse($preview);
294
    }
295
 
296
    /**
297
     * Make sure renaming is working
298
     *
299
     * @copyright 2012 Dongsheng Cai {@link http://dongsheng.org}
300
     * @covers \stored_file::rename
301
     */
302
    public function test_file_renaming() {
303
        global $CFG;
304
 
305
        $this->resetAfterTest();
306
        $fs = get_file_storage();
307
        $syscontext = \context_system::instance();
308
        $component = 'core';
309
        $filearea  = 'unittest';
310
        $itemid    = 0;
311
        $filepath  = '/';
312
        $filename  = 'test.txt';
313
 
314
        $filerecord = array(
315
            'contextid' => $syscontext->id,
316
            'component' => $component,
317
            'filearea'  => $filearea,
318
            'itemid'    => $itemid,
319
            'filepath'  => $filepath,
320
            'filename'  => $filename,
321
        );
322
 
323
        $originalfile = $fs->create_file_from_string($filerecord, 'Test content');
324
        $this->assertInstanceOf('stored_file', $originalfile);
325
        $contenthash = $originalfile->get_contenthash();
326
        $newpath = '/test/';
327
        $newname = 'newtest.txt';
328
 
329
        // This should work.
330
        $originalfile->rename($newpath, $newname);
331
        $file = $fs->get_file($syscontext->id, $component, $filearea, $itemid, $newpath, $newname);
332
        $this->assertInstanceOf('stored_file', $file);
333
        $this->assertEquals($contenthash, $file->get_contenthash());
334
 
335
        // Try break it.
336
        $this->expectException('file_exception');
337
        $this->expectExceptionMessage('Cannot create file 1/core/unittest/0/test/newtest.txt (file exists, cannot rename)');
338
        // This shall throw exception.
339
        $originalfile->rename($newpath, $newname);
340
    }
341
 
342
    /**
343
     * Create file from reference tests
344
     *
345
     * @copyright 2012 Dongsheng Cai {@link http://dongsheng.org}
346
     * @covers ::create_file_from_reference
347
     */
348
    public function test_create_file_from_reference() {
349
        global $CFG, $DB;
350
 
351
        $this->resetAfterTest();
352
        // Create user.
353
        $generator = $this->getDataGenerator();
354
        $user = $generator->create_user();
355
        $this->setUser($user);
356
        $usercontext = \context_user::instance($user->id);
357
        $syscontext = \context_system::instance();
358
 
359
        $fs = get_file_storage();
360
 
361
        $repositorypluginname = 'user';
362
        // Override repository permission.
363
        $capability = 'repository/' . $repositorypluginname . ':view';
364
        $guestroleid = $DB->get_field('role', 'id', array('shortname' => 'guest'));
365
        assign_capability($capability, CAP_ALLOW, $guestroleid, $syscontext->id, true);
366
 
367
        $args = array();
368
        $args['type'] = $repositorypluginname;
369
        $repos = repository::get_instances($args);
370
        $userrepository = reset($repos);
371
        $this->assertInstanceOf('repository', $userrepository);
372
 
373
        $component = 'user';
374
        $filearea  = 'private';
375
        $itemid    = 0;
376
        $filepath  = '/';
377
        $filename  = 'userfile.txt';
378
 
379
        $filerecord = array(
380
            'contextid' => $usercontext->id,
381
            'component' => $component,
382
            'filearea'  => $filearea,
383
            'itemid'    => $itemid,
384
            'filepath'  => $filepath,
385
            'filename'  => $filename,
386
        );
387
 
388
        $content = 'Test content';
389
        $originalfile = $fs->create_file_from_string($filerecord, $content);
390
        $this->assertInstanceOf('stored_file', $originalfile);
391
 
392
        $newfilerecord = array(
393
            'contextid' => $syscontext->id,
394
            'component' => 'core',
395
            'filearea'  => 'phpunit',
396
            'itemid'    => 0,
397
            'filepath'  => $filepath,
398
            'filename'  => $filename,
399
        );
400
        $ref = $fs->pack_reference($filerecord);
401
        $newstoredfile = $fs->create_file_from_reference($newfilerecord, $userrepository->id, $ref);
402
        $this->assertInstanceOf('stored_file', $newstoredfile);
403
        $this->assertEquals($userrepository->id, $newstoredfile->get_repository_id());
404
        $this->assertEquals($originalfile->get_contenthash(), $newstoredfile->get_contenthash());
405
        $this->assertEquals($originalfile->get_filesize(), $newstoredfile->get_filesize());
406
        $this->assertMatchesRegularExpression('#' . $filename. '$#', $newstoredfile->get_reference_details());
407
 
408
        // Test looking for references.
409
        $count = $fs->get_references_count_by_storedfile($originalfile);
410
        $this->assertEquals(1, $count);
411
        $files = $fs->get_references_by_storedfile($originalfile);
412
        $file = reset($files);
413
        $this->assertEquals($file, $newstoredfile);
414
 
415
        // Look for references by repository ID.
416
        $files = $fs->get_external_files($userrepository->id);
417
        $file = reset($files);
418
        $this->assertEquals($file, $newstoredfile);
419
 
420
        // Try convert reference to local file.
421
        $importedfile = $fs->import_external_file($newstoredfile);
422
        $this->assertFalse($importedfile->is_external_file());
423
        $this->assertInstanceOf('stored_file', $importedfile);
424
        // Still readable?
425
        $this->assertEquals($content, $importedfile->get_content());
426
    }
427
 
428
    /**
429
     * Create file from reference tests
430
     *
431
     * @copyright 2012 Dongsheng Cai {@link http://dongsheng.org}
432
     * @covers ::create_file_from_reference
433
     */
434
    public function test_create_file_from_reference_with_content_hash() {
435
        global $CFG, $DB;
436
 
437
        $this->resetAfterTest();
438
        // Create user.
439
        $generator = $this->getDataGenerator();
440
        $user = $generator->create_user();
441
        $this->setUser($user);
442
        $usercontext = \context_user::instance($user->id);
443
        $syscontext = \context_system::instance();
444
 
445
        $fs = get_file_storage();
446
 
447
        $repositorypluginname = 'user';
448
        // Override repository permission.
449
        $capability = 'repository/' . $repositorypluginname . ':view';
450
        $guestroleid = $DB->get_field('role', 'id', array('shortname' => 'guest'));
451
        assign_capability($capability, CAP_ALLOW, $guestroleid, $syscontext->id, true);
452
 
453
        $args = array();
454
        $args['type'] = $repositorypluginname;
455
        $repos = repository::get_instances($args);
456
        $userrepository = reset($repos);
457
        $this->assertInstanceOf('repository', $userrepository);
458
 
459
        $component = 'user';
460
        $filearea = 'private';
461
        $itemid = 0;
462
        $filepath = '/';
463
        $filename = 'userfile.txt';
464
 
465
        $filerecord = array(
466
                'contextid' => $usercontext->id,
467
                'component' => $component,
468
                'filearea' => $filearea,
469
                'itemid' => $itemid,
470
                'filepath' => $filepath,
471
                'filename' => $filename,
472
        );
473
 
474
        $content = 'Test content';
475
        $originalfile = $fs->create_file_from_string($filerecord, $content);
476
        $this->assertInstanceOf('stored_file', $originalfile);
477
 
478
        $otherfilerecord = $filerecord;
479
        $otherfilerecord['filename'] = 'other-filename.txt';
480
        $otherfilewithsamecontents = $fs->create_file_from_string($otherfilerecord, $content);
481
        $this->assertInstanceOf('stored_file', $otherfilewithsamecontents);
482
 
483
        $newfilerecord = array(
484
                'contextid' => $syscontext->id,
485
                'component' => 'core',
486
                'filearea' => 'phpunit',
487
                'itemid' => 0,
488
                'filepath' => $filepath,
489
                'filename' => $filename,
490
                'contenthash' => $originalfile->get_contenthash(),
491
        );
492
        $ref = $fs->pack_reference($filerecord);
493
        $newstoredfile = $fs->create_file_from_reference($newfilerecord, $userrepository->id, $ref);
494
        $this->assertInstanceOf('stored_file', $newstoredfile);
495
        $this->assertEquals($userrepository->id, $newstoredfile->get_repository_id());
496
        $this->assertEquals($originalfile->get_contenthash(), $newstoredfile->get_contenthash());
497
        $this->assertEquals($originalfile->get_filesize(), $newstoredfile->get_filesize());
498
        $this->assertMatchesRegularExpression('#' . $filename . '$#', $newstoredfile->get_reference_details());
499
    }
500
 
501
    private function setup_three_private_files() {
502
 
503
        $this->resetAfterTest();
504
 
505
        $generator = $this->getDataGenerator();
506
        $user = $generator->create_user();
507
        $this->setUser($user->id);
508
        $usercontext = \context_user::instance($user->id);
509
        // Create a user private file.
510
        $file1 = new \stdClass;
511
        $file1->contextid = $usercontext->id;
512
        $file1->component = 'user';
513
        $file1->filearea  = 'private';
514
        $file1->itemid    = 0;
515
        $file1->filepath  = '/';
516
        $file1->filename  = '1.txt';
517
        $file1->source    = 'test';
518
 
519
        $fs = get_file_storage();
520
        $userfile1 = $fs->create_file_from_string($file1, 'file1 content');
521
        $this->assertInstanceOf('stored_file', $userfile1);
522
 
523
        $file2 = clone($file1);
524
        $file2->filename = '2.txt';
525
        $userfile2 = $fs->create_file_from_string($file2, 'file2 content longer');
526
        $this->assertInstanceOf('stored_file', $userfile2);
527
 
528
        $file3 = clone($file1);
529
        $file3->filename = '3.txt';
530
        $userfile3 = $fs->create_file_from_storedfile($file3, $userfile2);
531
        $this->assertInstanceOf('stored_file', $userfile3);
532
 
533
        $user->ctxid = $usercontext->id;
534
 
535
        return $user;
536
    }
537
 
538
    /**
539
     * Tests for get_area_files
540
     *
541
     * @covers ::get_area_files
542
     */
543
    public function test_get_area_files() {
544
        $user = $this->setup_three_private_files();
545
        $fs = get_file_storage();
546
 
547
        // Get area files with default options.
548
        $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
549
 
550
        // Should be the two files we added plus the folder.
551
        $this->assertEquals(4, count($areafiles));
552
 
553
        // Verify structure.
554
        foreach ($areafiles as $key => $file) {
555
            $this->assertInstanceOf('stored_file', $file);
556
            $this->assertEquals($key, $file->get_pathnamehash());
557
        }
558
 
559
        // Get area files without a folder.
560
        $folderlessfiles = $fs->get_area_files($user->ctxid, 'user', 'private', false, 'sortorder', false);
561
        // Should be the two files without folder.
562
        $this->assertEquals(3, count($folderlessfiles));
563
 
564
        // Verify structure.
565
        foreach ($folderlessfiles as $key => $file) {
566
            $this->assertInstanceOf('stored_file', $file);
567
            $this->assertEquals($key, $file->get_pathnamehash());
568
        }
569
 
570
        // Get area files ordered by id.
571
        $filesbyid  = $fs->get_area_files($user->ctxid, 'user', 'private', false, 'id', false);
572
        // Should be the two files without folder.
573
        $this->assertEquals(3, count($filesbyid));
574
 
575
        // Verify structure.
576
        foreach ($filesbyid as $key => $file) {
577
            $this->assertInstanceOf('stored_file', $file);
578
            $this->assertEquals($key, $file->get_pathnamehash());
579
        }
580
 
581
        // Test the limit feature to retrieve each individual file.
582
        $limited = $fs->get_area_files($user->ctxid, 'user', 'private', false, 'filename', false,
583
                0, 0, 1);
584
        $mapfunc = function($f) {
585
            return $f->get_filename();
586
        };
587
        $this->assertEquals(array('1.txt'), array_values(array_map($mapfunc, $limited)));
588
        $limited = $fs->get_area_files($user->ctxid, 'user', 'private', false, 'filename', false,
589
                0, 1, 50);
590
        $this->assertEquals(array('2.txt', '3.txt'), array_values(array_map($mapfunc, $limited)));
591
 
592
        // Test with an itemid with no files.
593
        $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private', 666, 'sortorder', false);
594
        // Should be none.
595
        $this->assertEmpty($areafiles);
596
    }
597
 
598
    /**
599
     * Tests for get_area_tree
600
     *
601
     * @covers ::get_area_tree
602
     */
603
    public function test_get_area_tree() {
604
        $user = $this->setup_three_private_files();
605
        $fs = get_file_storage();
606
 
607
        // Get area files with default options.
608
        $areatree = $fs->get_area_tree($user->ctxid, 'user', 'private', 0);
609
        $this->assertEmpty($areatree['subdirs']);
610
        $this->assertNotEmpty($areatree['files']);
611
        $this->assertCount(3, $areatree['files']);
612
 
613
        // Ensure an empty try with a fake itemid.
614
        $emptytree = $fs->get_area_tree($user->ctxid, 'user', 'private', 666);
615
        $this->assertEmpty($emptytree['subdirs']);
616
        $this->assertEmpty($emptytree['files']);
617
 
618
        // Create a subdir.
619
        $dir = $fs->create_directory($user->ctxid, 'user', 'private', 0, '/testsubdir/');
620
        $this->assertInstanceOf('stored_file', $dir);
621
 
622
        // Add a file to the subdir.
623
        $filerecord = array(
624
            'contextid' => $user->ctxid,
625
            'component' => 'user',
626
            'filearea'  => 'private',
627
            'itemid'    => 0,
628
            'filepath'  => '/testsubdir/',
629
            'filename'  => 'test-get-area-tree.txt',
630
        );
631
 
632
        $directoryfile = $fs->create_file_from_string($filerecord, 'Test content');
633
        $this->assertInstanceOf('stored_file', $directoryfile);
634
 
635
        $areatree = $fs->get_area_tree($user->ctxid, 'user', 'private', 0);
636
 
637
        // At the top level there should still be 3 files.
638
        $this->assertCount(3, $areatree['files']);
639
 
640
        // There should now be a subdirectory.
641
        $this->assertCount(1, $areatree['subdirs']);
642
 
643
        // The test subdir is named testsubdir.
644
        $subdir = $areatree['subdirs']['testsubdir'];
645
        $this->assertNotEmpty($subdir);
646
        // It should have one file we added.
647
        $this->assertCount(1, $subdir['files']);
648
        // And no subdirs itself.
649
        $this->assertCount(0, $subdir['subdirs']);
650
 
651
        // Verify the file is the one we added.
652
        $subdirfile = reset($subdir['files']);
653
        $this->assertInstanceOf('stored_file', $subdirfile);
654
        $this->assertEquals($filerecord['filename'], $subdirfile->get_filename());
655
    }
656
 
657
    /**
658
     * Tests for get_file_by_id
659
     *
660
     * @covers ::get_file_by_id
661
     */
662
    public function test_get_file_by_id() {
663
        $user = $this->setup_three_private_files();
664
        $fs = get_file_storage();
665
 
666
        $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
667
 
668
        // Test get_file_by_id.
669
        $filebyid = reset($areafiles);
670
        $shouldbesame = $fs->get_file_by_id($filebyid->get_id());
671
        $this->assertEquals($filebyid->get_contenthash(), $shouldbesame->get_contenthash());
672
 
673
        // Test an id which doens't exist.
674
        $doesntexist = $fs->get_file_by_id(99999);
675
        $this->assertFalse($doesntexist);
676
    }
677
 
678
    /**
679
     * Tests for get_file_by_hash
680
     *
681
     * @covers ::get_file_by_hash
682
     */
683
    public function test_get_file_by_hash() {
684
        $user = $this->setup_three_private_files();
685
        $fs = get_file_storage();
686
 
687
        $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
688
        // Test get_file_by_hash.
689
        $filebyhash = reset($areafiles);
690
        $shouldbesame = $fs->get_file_by_hash($filebyhash->get_pathnamehash());
691
        $this->assertEquals($filebyhash->get_id(), $shouldbesame->get_id());
692
 
693
        // Test an hash which doens't exist.
694
        $doesntexist = $fs->get_file_by_hash('DOESNTEXIST');
695
        $this->assertFalse($doesntexist);
696
    }
697
 
698
    /**
699
     * Tests for get_external_files
700
     *
701
     * @covers ::get_external_files
702
     */
703
    public function test_get_external_files() {
704
        $user = $this->setup_three_private_files();
705
        $fs = get_file_storage();
706
 
707
        $repos = repository::get_instances(array('type'=>'user'));
708
        $userrepository = reset($repos);
709
        $this->assertInstanceOf('repository', $userrepository);
710
 
711
        // No aliases yet.
712
        $exfiles = $fs->get_external_files($userrepository->id, 'id');
713
        $this->assertEquals(array(), $exfiles);
714
 
715
        // Create three aliases linking the same original: $aliasfile1 and $aliasfile2 are
716
        // created via create_file_from_reference(), $aliasfile3 created from $aliasfile2.
717
        /** @var \stored_file $originalfile */
718
        $originalfile = null;
719
        foreach ($fs->get_area_files($user->ctxid, 'user', 'private') as $areafile) {
720
            if (!$areafile->is_directory()) {
721
                $originalfile = $areafile;
722
                break;
723
            }
724
        }
725
        $this->assertInstanceOf('stored_file', $originalfile);
726
        $originalrecord = array(
727
            'contextid' => $originalfile->get_contextid(),
728
            'component' => $originalfile->get_component(),
729
            'filearea'  => $originalfile->get_filearea(),
730
            'itemid'    => $originalfile->get_itemid(),
731
            'filepath'  => $originalfile->get_filepath(),
732
            'filename'  => $originalfile->get_filename(),
733
        );
734
 
735
        $aliasrecord = $this->generate_file_record();
736
        $aliasrecord->filepath = '/foo/';
737
        $aliasrecord->filename = 'one.txt';
738
 
739
        $ref = $fs->pack_reference($originalrecord);
740
        $aliasfile1 = $fs->create_file_from_reference($aliasrecord, $userrepository->id, $ref);
741
 
742
        $aliasrecord->filepath = '/bar/';
743
        $aliasrecord->filename = 'uno.txt';
744
        // Change the order of the items in the array to make sure that it does not matter.
745
        ksort($originalrecord);
746
        $ref = $fs->pack_reference($originalrecord);
747
        $aliasfile2 = $fs->create_file_from_reference($aliasrecord, $userrepository->id, $ref);
748
 
749
        $aliasrecord->filepath = '/bar/';
750
        $aliasrecord->filename = 'jedna.txt';
751
        $aliasfile3 = $fs->create_file_from_storedfile($aliasrecord, $aliasfile2);
752
 
753
        // Make sure we get three aliases now.
754
        $exfiles = $fs->get_external_files($userrepository->id, 'id');
755
        $this->assertEquals(3, count($exfiles));
756
        foreach ($exfiles as $exfile) {
757
            $this->assertTrue($exfile->is_external_file());
758
        }
759
        // Make sure they all link the same original (thence that all are linked with the same
760
        // record in {files_reference}).
761
        $this->assertEquals($aliasfile1->get_referencefileid(), $aliasfile2->get_referencefileid());
762
        $this->assertEquals($aliasfile3->get_referencefileid(), $aliasfile2->get_referencefileid());
763
    }
764
 
765
    /**
766
     * Tests for create_directory with a negative contextid.
767
     *
768
     * @covers ::create_directory
769
     */
770
    public function test_create_directory_contextid_negative() {
771
        $fs = get_file_storage();
772
 
773
        $this->expectException('file_exception');
774
        $fs->create_directory(-1, 'core', 'unittest', 0, '/');
775
    }
776
 
777
    /**
778
     * Tests for create_directory with an invalid contextid.
779
     *
780
     * @covers ::create_directory
781
     */
782
    public function test_create_directory_contextid_invalid() {
783
        $fs = get_file_storage();
784
 
785
        $this->expectException('file_exception');
786
        $fs->create_directory('not an int', 'core', 'unittest', 0, '/');
787
    }
788
 
789
    /**
790
     * Tests for create_directory with an invalid component.
791
     *
792
     * @covers ::create_directory
793
     */
794
    public function test_create_directory_component_invalid() {
795
        $fs = get_file_storage();
796
        $syscontext = \context_system::instance();
797
 
798
        $this->expectException('file_exception');
799
        $fs->create_directory($syscontext->id, 'bad/component', 'unittest', 0, '/');
800
    }
801
 
802
    /**
803
     * Tests for create_directory with an invalid filearea.
804
     *
805
     * @covers ::create_directory
806
     */
807
    public function test_create_directory_filearea_invalid() {
808
        $fs = get_file_storage();
809
        $syscontext = \context_system::instance();
810
 
811
        $this->expectException('file_exception');
812
        $fs->create_directory($syscontext->id, 'core', 'bad-filearea', 0, '/');
813
    }
814
 
815
    /**
816
     * Tests for create_directory with a negative itemid
817
     *
818
     * @covers ::create_directory
819
     */
820
    public function test_create_directory_itemid_negative() {
821
        $fs = get_file_storage();
822
        $syscontext = \context_system::instance();
823
 
824
        $this->expectException('file_exception');
825
        $fs->create_directory($syscontext->id, 'core', 'unittest', -1, '/');
826
    }
827
 
828
    /**
829
     * Tests for create_directory with an invalid itemid
830
     *
831
     * @covers ::create_directory
832
     */
833
    public function test_create_directory_itemid_invalid() {
834
        $fs = get_file_storage();
835
        $syscontext = \context_system::instance();
836
 
837
        $this->expectException('file_exception');
838
        $fs->create_directory($syscontext->id, 'core', 'unittest', 'notanint', '/');
839
    }
840
 
841
    /**
842
     * Tests for create_directory with an invalid filepath
843
     *
844
     * @covers ::create_directory
845
     */
846
    public function test_create_directory_filepath_invalid() {
847
        $fs = get_file_storage();
848
        $syscontext = \context_system::instance();
849
 
850
        $this->expectException('file_exception');
851
        $fs->create_directory($syscontext->id, 'core', 'unittest', 0, '/not-with-trailing/or-leading-slash');
852
    }
853
 
854
    /**
855
     * Tests for get_directory_files.
856
     *
857
     * @covers ::get_directory_files
858
     */
859
    public function test_get_directory_files() {
860
        $user = $this->setup_three_private_files();
861
        $fs = get_file_storage();
862
 
863
        $dir = $fs->create_directory($user->ctxid, 'user', 'private', 0, '/testsubdir/');
864
        $this->assertInstanceOf('stored_file', $dir);
865
 
866
        // Add a file to the subdir.
867
        $filerecord = array(
868
            'contextid' => $user->ctxid,
869
            'component' => 'user',
870
            'filearea'  => 'private',
871
            'itemid'    => 0,
872
            'filepath'  => '/testsubdir/',
873
            'filename'  => 'test-get-area-tree.txt',
874
        );
875
 
876
        $directoryfile = $fs->create_file_from_string($filerecord, 'Test content');
877
        $this->assertInstanceOf('stored_file', $directoryfile);
878
 
879
        // Don't recurse without dirs.
880
        $files = $fs->get_directory_files($user->ctxid, 'user', 'private', 0, '/', false, false, 'id');
881
        // 3 files only.
882
        $this->assertCount(3, $files);
883
        foreach ($files as $key => $file) {
884
            $this->assertInstanceOf('stored_file', $file);
885
            $this->assertEquals($key, $file->get_pathnamehash());
886
        }
887
 
888
        // Don't recurse with dirs.
889
        $files = $fs->get_directory_files($user->ctxid, 'user', 'private', 0, '/', false, true, 'id');
890
        // 3 files + 1 directory.
891
        $this->assertCount(4, $files);
892
        foreach ($files as $key => $file) {
893
            $this->assertInstanceOf('stored_file', $file);
894
            $this->assertEquals($key, $file->get_pathnamehash());
895
        }
896
 
897
        // Recurse with dirs.
898
        $files = $fs->get_directory_files($user->ctxid, 'user', 'private', 0, '/', true, true, 'id');
899
        // 3 files + 1 directory +  1 subdir file.
900
        $this->assertCount(5, $files);
901
        foreach ($files as $key => $file) {
902
            $this->assertInstanceOf('stored_file', $file);
903
            $this->assertEquals($key, $file->get_pathnamehash());
904
        }
905
 
906
        // Recurse without dirs.
907
        $files = $fs->get_directory_files($user->ctxid, 'user', 'private', 0, '/', true, false, 'id');
908
        // 3 files +  1 subdir file.
909
        $this->assertCount(4, $files);
910
        foreach ($files as $key => $file) {
911
            $this->assertInstanceOf('stored_file', $file);
912
            $this->assertEquals($key, $file->get_pathnamehash());
913
        }
914
    }
915
 
916
    /**
917
     * Tests for search_references.
918
     *
919
     * @covers ::search_references
920
     */
921
    public function test_search_references() {
922
        $user = $this->setup_three_private_files();
923
        $fs = get_file_storage();
924
        $repos = repository::get_instances(array('type'=>'user'));
925
        $repo = reset($repos);
926
 
927
        $alias1 = array(
928
            'contextid' => $user->ctxid,
929
            'component' => 'user',
930
            'filearea'  => 'private',
931
            'itemid'    => 0,
932
            'filepath'  => '/aliases/',
933
            'filename'  => 'alias-to-1.txt'
934
        );
935
 
936
        $alias2 = array(
937
            'contextid' => $user->ctxid,
938
            'component' => 'user',
939
            'filearea'  => 'private',
940
            'itemid'    => 0,
941
            'filepath'  => '/aliases/',
942
            'filename'  => 'another-alias-to-1.txt'
943
        );
944
 
945
        $reference = \file_storage::pack_reference(array(
946
            'contextid' => $user->ctxid,
947
            'component' => 'user',
948
            'filearea'  => 'private',
949
            'itemid'    => 0,
950
            'filepath'  => '/',
951
            'filename'  => '1.txt'
952
        ));
953
 
954
        // There are no aliases now.
955
        $result = $fs->search_references($reference);
956
        $this->assertEquals(array(), $result);
957
 
958
        $result = $fs->search_references_count($reference);
959
        $this->assertSame($result, 0);
960
 
961
        // Create two aliases and make sure they are returned.
962
        $fs->create_file_from_reference($alias1, $repo->id, $reference);
963
        $fs->create_file_from_reference($alias2, $repo->id, $reference);
964
 
965
        $result = $fs->search_references($reference);
966
        $this->assertTrue(is_array($result));
967
        $this->assertEquals(count($result), 2);
968
        foreach ($result as $alias) {
969
            $this->assertTrue($alias instanceof stored_file);
970
        }
971
 
972
        $result = $fs->search_references_count($reference);
973
        $this->assertSame($result, 2);
974
 
975
        // The method can't be used for references to files outside the filepool.
976
        $exceptionthrown = false;
977
        try {
978
            $fs->search_references('http://dl.dropbox.com/download/1234567/naked-dougiamas.jpg');
979
        } catch (file_reference_exception $e) {
980
            $exceptionthrown = true;
981
        }
982
        $this->assertTrue($exceptionthrown);
983
 
984
        $exceptionthrown = false;
985
        try {
986
            $fs->search_references_count('http://dl.dropbox.com/download/1234567/naked-dougiamas.jpg');
987
        } catch (file_reference_exception $e) {
988
            $exceptionthrown = true;
989
        }
990
        $this->assertTrue($exceptionthrown);
991
    }
992
 
993
    /**
994
     * Tests for delete_area_files.
995
     *
996
     * @covers ::delete_area_files
997
     */
998
    public function test_delete_area_files() {
999
        $user = $this->setup_three_private_files();
1000
        $fs = get_file_storage();
1001
 
1002
        // Get area files with default options.
1003
        $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
1004
        // Should be the two files we added plus the folder.
1005
        $this->assertEquals(4, count($areafiles));
1006
        $fs->delete_area_files($user->ctxid, 'user', 'private');
1007
 
1008
        $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
1009
        // Should be the two files we added plus the folder.
1010
        $this->assertEquals(0, count($areafiles));
1011
    }
1012
 
1013
    /**
1014
     * Tests for delete_area_files using an itemid.
1015
     *
1016
     * @covers ::delete_area_files
1017
     */
1018
    public function test_delete_area_files_itemid() {
1019
        $user = $this->setup_three_private_files();
1020
        $fs = get_file_storage();
1021
 
1022
        // Get area files with default options.
1023
        $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
1024
        // Should be the two files we added plus the folder.
1025
        $this->assertEquals(4, count($areafiles));
1026
        $fs->delete_area_files($user->ctxid, 'user', 'private', 9999);
1027
 
1028
        $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
1029
        $this->assertEquals(4, count($areafiles));
1030
    }
1031
 
1032
    /**
1033
     * Tests for delete_area_files_select.
1034
     *
1035
     * @covers ::delete_area_files_select
1036
     */
1037
    public function test_delete_area_files_select() {
1038
        $user = $this->setup_three_private_files();
1039
        $fs = get_file_storage();
1040
 
1041
        // Get area files with default options.
1042
        $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
1043
        // Should be the two files we added plus the folder.
1044
        $this->assertEquals(4, count($areafiles));
1045
        $fs->delete_area_files_select($user->ctxid, 'user', 'private', '!= :notitemid', array('notitemid'=>9999));
1046
 
1047
        $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
1048
        // Should be the two files we added plus the folder.
1049
        $this->assertEquals(0, count($areafiles));
1050
    }
1051
 
1052
    /**
1053
     * Tests for delete_component_files.
1054
     *
1055
     * @covers ::delete_component_files
1056
     */
1057
    public function test_delete_component_files() {
1058
        $user = $this->setup_three_private_files();
1059
        $fs = get_file_storage();
1060
 
1061
        $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
1062
        $this->assertEquals(4, count($areafiles));
1063
        $fs->delete_component_files('user');
1064
        $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
1065
        $this->assertEquals(0, count($areafiles));
1066
    }
1067
 
1068
    /**
1069
     * Tests for create_file_from_url.
1070
     *
1071
     * @covers ::create_file_from_url
1072
     */
1073
    public function test_create_file_from_url() {
1074
        $this->resetAfterTest(true);
1075
 
1076
        $syscontext = \context_system::instance();
1077
        $filerecord = array(
1078
            'contextid' => $syscontext->id,
1079
            'component' => 'core',
1080
            'filearea'  => 'unittest',
1081
            'itemid'    => 0,
1082
            'filepath'  => '/downloadtest/',
1083
        );
1084
        $url = $this->getExternalTestFileUrl('/test.html');
1085
 
1086
        $fs = get_file_storage();
1087
 
1088
        // Test creating file without filename.
1089
        $file1 = $fs->create_file_from_url($filerecord, $url);
1090
        $this->assertInstanceOf('stored_file', $file1);
1091
 
1092
        // Set filename.
1093
        $filerecord['filename'] = 'unit-test-filename.html';
1094
        $file2 = $fs->create_file_from_url($filerecord, $url);
1095
        $this->assertInstanceOf('stored_file', $file2);
1096
 
1097
        // Use temporary file.
1098
        $filerecord['filename'] = 'unit-test-with-temp-file.html';
1099
        $file3 = $fs->create_file_from_url($filerecord, $url, null, true);
1100
        $file3 = $this->assertInstanceOf('stored_file', $file3);
1101
    }
1102
 
1103
    /**
1104
     * Tests for cron.
1105
     *
1106
     * @covers ::cron
1107
     */
1108
    public function test_cron() {
1109
        $this->resetAfterTest(true);
1110
 
1111
        // Note: this is only testing DB compatibility atm, rather than
1112
        // that work is done.
1113
        $fs = get_file_storage();
1114
 
1115
        $this->expectOutputRegex('/Cleaning up/');
1116
        $fs->cron();
1117
    }
1118
 
1119
    /**
1120
     * Tests for is_area_empty.
1121
     *
1122
     * @covers ::is_area_empty
1123
     */
1124
    public function test_is_area_empty() {
1125
        $user = $this->setup_three_private_files();
1126
        $fs = get_file_storage();
1127
 
1128
        $this->assertFalse($fs->is_area_empty($user->ctxid, 'user', 'private'));
1129
 
1130
        // File area with madeup itemid should be empty.
1131
        $this->assertTrue($fs->is_area_empty($user->ctxid, 'user', 'private', 9999));
1132
        // Still empty with dirs included.
1133
        $this->assertTrue($fs->is_area_empty($user->ctxid, 'user', 'private', 9999, false));
1134
    }
1135
 
1136
    /**
1137
     * Tests for move_area_files_to_new_context.
1138
     *
1139
     * @covers ::move_area_files_to_new_context
1140
     */
1141
    public function test_move_area_files_to_new_context() {
1142
        $this->resetAfterTest(true);
1143
 
1144
        // Create a course with a page resource.
1145
        $course = $this->getDataGenerator()->create_course();
1146
        $page1 = $this->getDataGenerator()->create_module('page', array('course'=>$course->id));
1147
        $page1context = \context_module::instance($page1->cmid);
1148
 
1149
        // Add a file to the page.
1150
        $fs = get_file_storage();
1151
        $filerecord = array(
1152
            'contextid' => $page1context->id,
1153
            'component' => 'mod_page',
1154
            'filearea'  => 'content',
1155
            'itemid'    => 0,
1156
            'filepath'  => '/',
1157
            'filename'  => 'unit-test-file.txt',
1158
        );
1159
 
1160
        $originalfile = $fs->create_file_from_string($filerecord, 'Test content');
1161
        $this->assertInstanceOf('stored_file', $originalfile);
1162
 
1163
        $pagefiles = $fs->get_area_files($page1context->id, 'mod_page', 'content', 0, 'sortorder', false);
1164
        // Should be one file in filearea.
1165
        $this->assertFalse($fs->is_area_empty($page1context->id, 'mod_page', 'content'));
1166
 
1167
        // Create a new page.
1168
        $page2 = $this->getDataGenerator()->create_module('page', array('course'=>$course->id));
1169
        $page2context = \context_module::instance($page2->cmid);
1170
 
1171
        // Newly created page area is empty.
1172
        $this->assertTrue($fs->is_area_empty($page2context->id, 'mod_page', 'content'));
1173
 
1174
        // Move the files.
1175
        $fs->move_area_files_to_new_context($page1context->id, $page2context->id, 'mod_page', 'content');
1176
 
1177
        // Page2 filearea should no longer be empty.
1178
        $this->assertFalse($fs->is_area_empty($page2context->id, 'mod_page', 'content'));
1179
 
1180
        // Page1 filearea should now be empty.
1181
        $this->assertTrue($fs->is_area_empty($page1context->id, 'mod_page', 'content'));
1182
 
1183
        $page2files = $fs->get_area_files($page2context->id, 'mod_page', 'content', 0, 'sortorder', false);
1184
        $movedfile = reset($page2files);
1185
 
1186
        // The two files should have the same content hash.
1187
        $this->assertEquals($movedfile->get_contenthash(), $originalfile->get_contenthash());
1188
    }
1189
 
1190
    /**
1191
     * Tests for convert_image.
1192
     *
1193
     * @covers ::convert_image
1194
     */
1195
    public function test_convert_image() {
1196
        global $CFG;
1197
 
1198
        $this->resetAfterTest(false);
1199
 
1200
        $filepath = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
1201
        $syscontext = \context_system::instance();
1202
        $filerecord = array(
1203
            'contextid' => $syscontext->id,
1204
            'component' => 'core',
1205
            'filearea'  => 'unittest',
1206
            'itemid'    => 0,
1207
            'filepath'  => '/images/',
1208
            'filename'  => 'testimage.jpg',
1209
        );
1210
 
1211
        $fs = get_file_storage();
1212
        $original = $fs->create_file_from_pathname($filerecord, $filepath);
1213
 
1214
        $filerecord['filename'] = 'testimage-converted-10x10.jpg';
1215
        $converted = $fs->convert_image($filerecord, $original, 10, 10, true, 100);
1216
        $this->assertInstanceOf('stored_file', $converted);
1217
 
1218
        $filerecord['filename'] = 'testimage-convereted-nosize.jpg';
1219
        $converted = $fs->convert_image($filerecord, $original);
1220
        $this->assertInstanceOf('stored_file', $converted);
1221
    }
1222
 
1223
    /**
1224
     * Tests for convert_image with a PNG.
1225
     *
1226
     * @covers ::convert_image
1227
     */
1228
    public function test_convert_image_png() {
1229
        global $CFG;
1230
 
1231
        $this->resetAfterTest(false);
1232
 
1233
        $filepath = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.png';
1234
        $syscontext = \context_system::instance();
1235
        $filerecord = array(
1236
            'contextid' => $syscontext->id,
1237
            'component' => 'core',
1238
            'filearea'  => 'unittest',
1239
            'itemid'    => 0,
1240
            'filepath'  => '/images/',
1241
            'filename'  => 'testimage.png',
1242
        );
1243
 
1244
        $fs = get_file_storage();
1245
        $original = $fs->create_file_from_pathname($filerecord, $filepath);
1246
 
1247
        // Vanilla test.
1248
        $filerecord['filename'] = 'testimage-converted-nosize.png';
1249
        $vanilla = $fs->convert_image($filerecord, $original);
1250
        $this->assertInstanceOf('stored_file', $vanilla);
1251
        // Assert that byte 25 has the ascii value 6 for PNG-24.
1252
        $this->assertTrue(ord(substr($vanilla->get_content(), 25, 1)) == 6);
1253
 
1254
        // 10x10 resize test; also testing for a ridiculous quality setting, which
1255
        // we should if necessary scale to the 0 - 9 range.
1256
        $filerecord['filename'] = 'testimage-converted-10x10.png';
1257
        $converted = $fs->convert_image($filerecord, $original, 10, 10, true, 100);
1258
        $this->assertInstanceOf('stored_file', $converted);
1259
        // Assert that byte 25 has the ascii value 6 for PNG-24.
1260
        $this->assertTrue(ord(substr($converted->get_content(), 25, 1)) == 6);
1261
 
1262
        // Transparency test.
1263
        $filerecord['filename'] = 'testimage-converted-102x31.png';
1264
        $converted = $fs->convert_image($filerecord, $original, 102, 31, true, 9);
1265
        $this->assertInstanceOf('stored_file', $converted);
1266
        // Assert that byte 25 has the ascii value 6 for PNG-24.
1267
        $this->assertTrue(ord(substr($converted->get_content(), 25, 1)) == 6);
1268
 
1269
        $originalfile = imagecreatefromstring($original->get_content());
1270
        $convertedfile = imagecreatefromstring($converted->get_content());
1271
        $vanillafile = imagecreatefromstring($vanilla->get_content());
1272
 
1273
        $originalcolors = imagecolorsforindex($originalfile, imagecolorat($originalfile, 0, 0));
1274
        $convertedcolors = imagecolorsforindex($convertedfile, imagecolorat($convertedfile, 0, 0));
1275
        $vanillacolors = imagecolorsforindex($vanillafile, imagecolorat($vanillafile, 0, 0));
1276
        $this->assertEquals(count($originalcolors), 4);
1277
        $this->assertEquals(count($convertedcolors), 4);
1278
        $this->assertEquals(count($vanillacolors), 4);
1279
        $this->assertEquals($originalcolors['red'], $convertedcolors['red']);
1280
        $this->assertEquals($originalcolors['green'], $convertedcolors['green']);
1281
        $this->assertEquals($originalcolors['blue'], $convertedcolors['blue']);
1282
        $this->assertEquals($originalcolors['alpha'], $convertedcolors['alpha']);
1283
        $this->assertEquals($originalcolors['red'], $vanillacolors['red']);
1284
        $this->assertEquals($originalcolors['green'], $vanillacolors['green']);
1285
        $this->assertEquals($originalcolors['blue'], $vanillacolors['blue']);
1286
        $this->assertEquals($originalcolors['alpha'], $vanillacolors['alpha']);
1287
        $this->assertEquals($originalcolors['alpha'], 127);
1288
 
1289
    }
1290
 
1291
    private function generate_file_record() {
1292
        $syscontext = \context_system::instance();
1293
        $filerecord = new \stdClass();
1294
        $filerecord->contextid = $syscontext->id;
1295
        $filerecord->component = 'core';
1296
        $filerecord->filearea = 'phpunit';
1297
        $filerecord->filepath = '/';
1298
        $filerecord->filename = 'testfile.txt';
1299
        $filerecord->itemid = 0;
1300
 
1301
        return $filerecord;
1302
    }
1303
 
1304
    /**
1305
     * @covers ::create_file_from_storedfile
1306
     */
1307
    public function test_create_file_from_storedfile_file_invalid() {
1308
        $this->resetAfterTest(true);
1309
 
1310
        $filerecord = $this->generate_file_record();
1311
 
1312
        $fs = get_file_storage();
1313
 
1314
        // Create a file from a file id which doesn't exist.
1315
        $this->expectException(file_exception::class);
1316
        $fs->create_file_from_storedfile($filerecord,  9999);
1317
    }
1318
 
1319
    /**
1320
     * @covers ::create_file_from_storedfile
1321
     */
1322
    public function test_create_file_from_storedfile_contextid_invalid() {
1323
        $this->resetAfterTest(true);
1324
 
1325
        $filerecord = $this->generate_file_record();
1326
 
1327
        $fs = get_file_storage();
1328
        $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1329
        $this->assertInstanceOf('stored_file', $file1);
1330
 
1331
        $filerecord->filename = 'invalid.txt';
1332
        $filerecord->contextid = 'invalid';
1333
 
1334
        $this->expectException(file_exception::class);
1335
        $this->expectExceptionMessage('Invalid contextid');
1336
        $fs->create_file_from_storedfile($filerecord, $file1->get_id());
1337
    }
1338
 
1339
    /**
1340
     * @covers ::create_file_from_storedfile
1341
     */
1342
    public function test_create_file_from_storedfile_component_invalid() {
1343
        $this->resetAfterTest(true);
1344
 
1345
        $filerecord = $this->generate_file_record();
1346
 
1347
        $fs = get_file_storage();
1348
        $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1349
        $this->assertInstanceOf('stored_file', $file1);
1350
 
1351
        $filerecord->filename = 'invalid.txt';
1352
        $filerecord->component = 'bad/component';
1353
 
1354
        $this->expectException(file_exception::class);
1355
        $this->expectExceptionMessage('Invalid component');
1356
        $fs->create_file_from_storedfile($filerecord, $file1->get_id());
1357
    }
1358
 
1359
    /**
1360
     * @covers ::create_file_from_storedfile
1361
     */
1362
    public function test_create_file_from_storedfile_filearea_invalid() {
1363
        $this->resetAfterTest(true);
1364
 
1365
        $filerecord = $this->generate_file_record();
1366
 
1367
        $fs = get_file_storage();
1368
        $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1369
        $this->assertInstanceOf('stored_file', $file1);
1370
 
1371
        $filerecord->filename = 'invalid.txt';
1372
        $filerecord->filearea = 'bad-filearea';
1373
 
1374
        $this->expectException(file_exception::class);
1375
        $this->expectExceptionMessage('Invalid filearea');
1376
        $fs->create_file_from_storedfile($filerecord, $file1->get_id());
1377
    }
1378
 
1379
    /**
1380
     * @covers ::create_file_from_storedfile
1381
     */
1382
    public function test_create_file_from_storedfile_itemid_invalid() {
1383
        $this->resetAfterTest(true);
1384
 
1385
        $filerecord = $this->generate_file_record();
1386
 
1387
        $fs = get_file_storage();
1388
        $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1389
        $this->assertInstanceOf('stored_file', $file1);
1390
 
1391
        $filerecord->filename = 'invalid.txt';
1392
        $filerecord->itemid = 'bad-itemid';
1393
 
1394
        $this->expectException(file_exception::class);
1395
        $this->expectExceptionMessage('Invalid itemid');
1396
        $fs->create_file_from_storedfile($filerecord, $file1->get_id());
1397
    }
1398
 
1399
    /**
1400
     * @covers ::create_file_from_storedfile
1401
     */
1402
    public function test_create_file_from_storedfile_filepath_invalid() {
1403
        $this->resetAfterTest(true);
1404
 
1405
        $filerecord = $this->generate_file_record();
1406
 
1407
        $fs = get_file_storage();
1408
        $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1409
        $this->assertInstanceOf('stored_file', $file1);
1410
 
1411
        $filerecord->filename = 'invalid.txt';
1412
        $filerecord->filepath = 'a-/bad/-filepath';
1413
 
1414
        $this->expectException(file_exception::class);
1415
        $this->expectExceptionMessage('Invalid file path');
1416
        $fs->create_file_from_storedfile($filerecord, $file1->get_id());
1417
    }
1418
 
1419
    /**
1420
     * @covers ::create_file_from_storedfile
1421
     */
1422
    public function test_create_file_from_storedfile_filename_invalid() {
1423
        $this->resetAfterTest(true);
1424
 
1425
        $filerecord = $this->generate_file_record();
1426
 
1427
        $fs = get_file_storage();
1428
        $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1429
        $this->assertInstanceOf('stored_file', $file1);
1430
 
1431
        $filerecord->filename = '';
1432
 
1433
        $this->expectException(file_exception::class);
1434
        $this->expectExceptionMessage('Invalid file name');
1435
        $fs->create_file_from_storedfile($filerecord, $file1->get_id());
1436
    }
1437
 
1438
    /**
1439
     * @covers ::create_file_from_storedfile
1440
     */
1441
    public function test_create_file_from_storedfile_timecreated_invalid() {
1442
        $this->resetAfterTest(true);
1443
 
1444
        $filerecord = $this->generate_file_record();
1445
 
1446
        $fs = get_file_storage();
1447
        $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1448
        $this->assertInstanceOf('stored_file', $file1);
1449
 
1450
        $filerecord->filename = 'invalid.txt';
1451
        $filerecord->timecreated = 'today';
1452
 
1453
        $this->expectException(file_exception::class);
1454
        $this->expectExceptionMessage('Invalid file timecreated');
1455
        $fs->create_file_from_storedfile($filerecord, $file1->get_id());
1456
    }
1457
 
1458
    /**
1459
     * @covers ::create_file_from_storedfile
1460
     */
1461
    public function test_create_file_from_storedfile_timemodified_invalid() {
1462
        $this->resetAfterTest(true);
1463
 
1464
        $filerecord = $this->generate_file_record();
1465
 
1466
        $fs = get_file_storage();
1467
        $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1468
        $this->assertInstanceOf('stored_file', $file1);
1469
 
1470
        $filerecord->filename = 'invalid.txt';
1471
        $filerecord->timemodified  = 'today';
1472
 
1473
        $this->expectException(file_exception::class);
1474
        $this->expectExceptionMessage('Invalid file timemodified');
1475
        $fs->create_file_from_storedfile($filerecord, $file1->get_id());
1476
    }
1477
 
1478
    /**
1479
     * @covers ::create_file_from_storedfile
1480
     */
1481
    public function test_create_file_from_storedfile_duplicate() {
1482
        $this->resetAfterTest(true);
1483
 
1484
        $filerecord = $this->generate_file_record();
1485
 
1486
        $fs = get_file_storage();
1487
        $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1488
        $this->assertInstanceOf('stored_file', $file1);
1489
 
1490
        // Creating a file validating unique constraint.
1491
        $this->expectException(stored_file_creation_exception::class);
1492
        $this->expectExceptionMessage('Cannot create file 1/core/phpunit/0/testfile.txt');
1493
        $fs->create_file_from_storedfile($filerecord, $file1->get_id());
1494
    }
1495
 
1496
    /**
1497
     * Tests for create_file_from_storedfile.
1498
     *
1499
     * @covers ::create_file_from_storedfile
1500
     */
1501
    public function test_create_file_from_storedfile() {
1502
        $this->resetAfterTest(true);
1503
 
1504
        $syscontext = \context_system::instance();
1505
 
1506
        $filerecord = new \stdClass();
1507
        $filerecord->contextid = $syscontext->id;
1508
        $filerecord->component = 'core';
1509
        $filerecord->filearea = 'phpunit';
1510
        $filerecord->filepath = '/';
1511
        $filerecord->filename = 'testfile.txt';
1512
        $filerecord->itemid = 0;
1513
 
1514
        $fs = get_file_storage();
1515
 
1516
        $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1517
        $this->assertInstanceOf('stored_file', $file1);
1518
 
1519
        $filerecord->filename = 'test-create-file-from-storedfile.txt';
1520
        $file2 = $fs->create_file_from_storedfile($filerecord, $file1->get_id());
1521
        $this->assertInstanceOf('stored_file', $file2);
1522
 
1523
        // These will be normalised to current time..
1524
        $filerecord->timecreated = -100;
1525
        $filerecord->timemodified= -100;
1526
        $filerecord->filename = 'test-create-file-from-storedfile-bad-dates.txt';
1527
 
1528
        $file3 = $fs->create_file_from_storedfile($filerecord, $file1->get_id());
1529
        $this->assertInstanceOf('stored_file', $file3);
1530
 
1531
        $this->assertNotEquals($file3->get_timemodified(), $filerecord->timemodified);
1532
        $this->assertNotEquals($file3->get_timecreated(), $filerecord->timecreated);
1533
    }
1534
 
1535
    /**
1536
     * @covers ::create_file_from_string
1537
     */
1538
    public function test_create_file_from_string_contextid_invalid() {
1539
        $this->resetAfterTest(true);
1540
 
1541
        $filerecord = $this->generate_file_record();
1542
        $fs = get_file_storage();
1543
 
1544
        $filerecord->contextid = 'invalid';
1545
 
1546
        $this->expectException(file_exception::class);
1547
        $this->expectExceptionMessage('Invalid contextid');
1548
        $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1549
    }
1550
 
1551
    /**
1552
     * @covers ::create_file_from_string
1553
     */
1554
    public function test_create_file_from_string_component_invalid() {
1555
        $this->resetAfterTest(true);
1556
 
1557
        $filerecord = $this->generate_file_record();
1558
        $fs = get_file_storage();
1559
 
1560
        $filerecord->component = 'bad/component';
1561
 
1562
        $this->expectException(file_exception::class);
1563
        $this->expectExceptionMessage('Invalid component');
1564
        $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1565
    }
1566
 
1567
    /**
1568
     * @covers ::create_file_from_string
1569
     */
1570
    public function test_create_file_from_string_filearea_invalid() {
1571
        $this->resetAfterTest(true);
1572
 
1573
        $filerecord = $this->generate_file_record();
1574
        $fs = get_file_storage();
1575
 
1576
        $filerecord->filearea = 'bad-filearea';
1577
 
1578
        $this->expectException(file_exception::class);
1579
        $this->expectExceptionMessage('Invalid filearea');
1580
        $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1581
    }
1582
 
1583
    /**
1584
     * @covers ::create_file_from_string
1585
     */
1586
    public function test_create_file_from_string_itemid_invalid() {
1587
        $this->resetAfterTest(true);
1588
 
1589
        $filerecord = $this->generate_file_record();
1590
        $fs = get_file_storage();
1591
 
1592
        $filerecord->itemid = 'bad-itemid';
1593
 
1594
        $this->expectException(file_exception::class);
1595
        $this->expectExceptionMessage('Invalid itemid');
1596
        $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1597
    }
1598
 
1599
    /**
1600
     * @covers ::create_file_from_string
1601
     */
1602
    public function test_create_file_from_string_filepath_invalid() {
1603
        $this->resetAfterTest(true);
1604
 
1605
        $filerecord = $this->generate_file_record();
1606
        $fs = get_file_storage();
1607
 
1608
        $filerecord->filepath = 'a-/bad/-filepath';
1609
 
1610
        $this->expectException(file_exception::class);
1611
        $this->expectExceptionMessage('Invalid file path');
1612
        $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1613
    }
1614
 
1615
    /**
1616
     * @covers ::create_file_from_string
1617
     */
1618
    public function test_create_file_from_string_filename_invalid() {
1619
        $this->resetAfterTest(true);
1620
 
1621
        $filerecord = $this->generate_file_record();
1622
        $fs = get_file_storage();
1623
 
1624
        $filerecord->filename = '';
1625
 
1626
        $this->expectException(file_exception::class);
1627
        $this->expectExceptionMessage('Invalid file name');
1628
        $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1629
    }
1630
 
1631
    /**
1632
     * @covers ::create_file_from_string
1633
     */
1634
    public function test_create_file_from_string_timecreated_invalid() {
1635
        $this->resetAfterTest(true);
1636
 
1637
        $filerecord = $this->generate_file_record();
1638
        $fs = get_file_storage();
1639
 
1640
        $filerecord->timecreated = 'today';
1641
 
1642
        $this->expectException('file_exception');
1643
        $this->expectExceptionMessage('Invalid file timecreated');
1644
        $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1645
    }
1646
 
1647
    /**
1648
     * @covers ::create_file_from_string
1649
     */
1650
    public function test_create_file_from_string_timemodified_invalid() {
1651
        $this->resetAfterTest(true);
1652
 
1653
        $filerecord = $this->generate_file_record();
1654
        $fs = get_file_storage();
1655
 
1656
        $filerecord->timemodified  = 'today';
1657
 
1658
        $this->expectException(file_exception::class);
1659
        $this->expectExceptionMessage('Invalid file timemodified');
1660
        $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1661
    }
1662
 
1663
    /**
1664
     * Tests for create_file_from_string with a duplicate string.
1665
     * @covers ::create_file_from_string
1666
     */
1667
    public function test_create_file_from_string_duplicate() {
1668
        $this->resetAfterTest(true);
1669
 
1670
        $filerecord = $this->generate_file_record();
1671
        $fs = get_file_storage();
1672
 
1673
        $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1674
 
1675
        // Creating a file validating unique constraint.
1676
        $this->expectException('stored_file_creation_exception');
1677
        $file2 = $fs->create_file_from_string($filerecord, 'text contents');
1678
    }
1679
 
1680
    /**
1681
     * @covers ::create_file_from_pathname
1682
     */
1683
    public function test_create_file_from_pathname_contextid_invalid() {
1684
        global $CFG;
1685
        $path = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
1686
 
1687
        $this->resetAfterTest(true);
1688
 
1689
        $filerecord = $this->generate_file_record();
1690
        $fs = get_file_storage();
1691
 
1692
        $filerecord->contextid = 'invalid';
1693
 
1694
        $this->expectException(file_exception::class);
1695
        $this->expectExceptionMessage('Invalid contextid');
1696
        $file1 = $fs->create_file_from_pathname($filerecord, $path);
1697
    }
1698
 
1699
    /**
1700
     * @covers ::create_file_from_pathname
1701
     */
1702
    public function test_create_file_from_pathname_component_invalid() {
1703
        global $CFG;
1704
        $path = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
1705
 
1706
        $this->resetAfterTest(true);
1707
 
1708
        $filerecord = $this->generate_file_record();
1709
        $fs = get_file_storage();
1710
 
1711
        $filerecord->component = 'bad/component';
1712
 
1713
        $this->expectException(file_exception::class);
1714
        $this->expectExceptionMessage('Invalid component');
1715
        $file1 = $fs->create_file_from_pathname($filerecord, $path);
1716
    }
1717
 
1718
    /**
1719
     * @covers ::create_file_from_pathname
1720
     */
1721
    public function test_create_file_from_pathname_filearea_invalid() {
1722
        global $CFG;
1723
        $path = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
1724
 
1725
        $this->resetAfterTest(true);
1726
 
1727
        $filerecord = $this->generate_file_record();
1728
        $fs = get_file_storage();
1729
 
1730
        $filerecord->filearea = 'bad-filearea';
1731
 
1732
        $this->expectException(file_exception::class);
1733
        $this->expectExceptionMessage('Invalid filearea');
1734
        $file1 = $fs->create_file_from_pathname($filerecord, $path);
1735
    }
1736
 
1737
    /**
1738
     * @covers ::create_file_from_pathname
1739
     */
1740
    public function test_create_file_from_pathname_itemid_invalid() {
1741
        global $CFG;
1742
        $path = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
1743
 
1744
        $this->resetAfterTest(true);
1745
 
1746
        $filerecord = $this->generate_file_record();
1747
        $fs = get_file_storage();
1748
 
1749
        $filerecord->itemid = 'bad-itemid';
1750
 
1751
        $this->expectException(file_exception::class);
1752
        $this->expectExceptionMessage('Invalid itemid');
1753
        $file1 = $fs->create_file_from_pathname($filerecord, $path);
1754
    }
1755
 
1756
    /**
1757
     * @covers ::create_file_from_pathname
1758
     */
1759
    public function test_create_file_from_pathname_filepath_invalid() {
1760
        global $CFG;
1761
        $path = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
1762
 
1763
        $this->resetAfterTest(true);
1764
 
1765
        $filerecord = $this->generate_file_record();
1766
        $fs = get_file_storage();
1767
 
1768
        $filerecord->filepath = 'a-/bad/-filepath';
1769
 
1770
        $this->expectException(file_exception::class);
1771
        $this->expectExceptionMessage('Invalid file path');
1772
        $file1 = $fs->create_file_from_pathname($filerecord, $path);
1773
    }
1774
 
1775
    /**
1776
     * @covers ::create_file_from_pathname
1777
     */
1778
    public function test_create_file_from_pathname_filename_invalid() {
1779
        global $CFG;
1780
        $path = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
1781
 
1782
        $this->resetAfterTest(true);
1783
 
1784
        $filerecord = $this->generate_file_record();
1785
        $fs = get_file_storage();
1786
 
1787
        $filerecord->filename = '';
1788
 
1789
        $this->expectException(file_exception::class);
1790
        $this->expectExceptionMessage('Invalid file name');
1791
        $file1 = $fs->create_file_from_pathname($filerecord, $path);
1792
    }
1793
 
1794
    /**
1795
     * @covers ::create_file_from_pathname
1796
     */
1797
    public function test_create_file_from_pathname_timecreated_invalid() {
1798
        global $CFG;
1799
        $path = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
1800
 
1801
        $this->resetAfterTest(true);
1802
 
1803
        $filerecord = $this->generate_file_record();
1804
        $fs = get_file_storage();
1805
 
1806
        $filerecord->timecreated = 'today';
1807
 
1808
        $this->expectException(file_exception::class);
1809
        $this->expectExceptionMessage('Invalid file timecreated');
1810
        $file1 = $fs->create_file_from_pathname($filerecord, $path);
1811
    }
1812
 
1813
    /**
1814
     * @covers ::create_file_from_pathname
1815
     */
1816
    public function test_create_file_from_pathname_timemodified_invalid() {
1817
        global $CFG;
1818
        $path = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
1819
 
1820
        $this->resetAfterTest(true);
1821
 
1822
        $filerecord = $this->generate_file_record();
1823
        $fs = get_file_storage();
1824
 
1825
        $filerecord->timemodified  = 'today';
1826
 
1827
        $this->expectException(file_exception::class);
1828
        $this->expectExceptionMessage('Invalid file timemodified');
1829
        $file1 = $fs->create_file_from_pathname($filerecord, $path);
1830
    }
1831
 
1832
    /**
1833
     * @covers ::create_file_from_pathname
1834
     */
1835
    public function test_create_file_from_pathname_duplicate_file() {
1836
        global $CFG;
1837
        $this->resetAfterTest(true);
1838
 
1839
        $path = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
1840
 
1841
        $filerecord = $this->generate_file_record();
1842
        $fs = get_file_storage();
1843
 
1844
        $file1 = $fs->create_file_from_pathname($filerecord, $path);
1845
        $this->assertInstanceOf('stored_file', $file1);
1846
 
1847
        // Creating a file validating unique constraint.
1848
        $this->expectException(stored_file_creation_exception::class);
1849
        $this->expectExceptionMessage('Cannot create file 1/core/phpunit/0/testfile.txt');
1850
        $file2 = $fs->create_file_from_pathname($filerecord, $path);
1851
    }
1852
 
1853
    /**
1854
     * Calling \stored_file::delete_reference() on a non-reference file throws coding_exception
1855
     *
1856
     * @covers \stored_file::delete_reference
1857
     */
1858
    public function test_delete_reference_on_nonreference() {
1859
 
1860
        $this->resetAfterTest(true);
1861
        $user = $this->setup_three_private_files();
1862
        $fs = get_file_storage();
1863
        $repos = repository::get_instances(array('type'=>'user'));
1864
        $repo = reset($repos);
1865
 
1866
        /** @var \stored_file $file */
1867
        $file = null;
1868
        foreach ($fs->get_area_files($user->ctxid, 'user', 'private') as $areafile) {
1869
            if (!$areafile->is_directory()) {
1870
                $file = $areafile;
1871
                break;
1872
            }
1873
        }
1874
        $this->assertInstanceOf('stored_file', $file);
1875
        $this->assertFalse($file->is_external_file());
1876
 
1877
        $this->expectException('coding_exception');
1878
        $file->delete_reference();
1879
    }
1880
 
1881
    /**
1882
     * Calling \stored_file::delete_reference() on a reference file does not affect other
1883
     * symlinks to the same original
1884
     *
1885
     * @covers \stored_file::delete_reference
1886
     */
1887
    public function test_delete_reference_one_symlink_does_not_rule_them_all() {
1888
 
1889
        $this->resetAfterTest(true);
1890
        $user = $this->setup_three_private_files();
1891
        $fs = get_file_storage();
1892
        $repos = repository::get_instances(array('type'=>'user'));
1893
        $repo = reset($repos);
1894
 
1895
        // Create two aliases linking the same original.
1896
 
1897
        /** @var \stored_file $originalfile */
1898
        $originalfile = null;
1899
        foreach ($fs->get_area_files($user->ctxid, 'user', 'private') as $areafile) {
1900
            if (!$areafile->is_directory()) {
1901
                $originalfile = $areafile;
1902
                break;
1903
            }
1904
        }
1905
        $this->assertInstanceOf('stored_file', $originalfile);
1906
 
1907
        // Calling delete_reference() on a non-reference file.
1908
 
1909
        $originalrecord = array(
1910
            'contextid' => $originalfile->get_contextid(),
1911
            'component' => $originalfile->get_component(),
1912
            'filearea'  => $originalfile->get_filearea(),
1913
            'itemid'    => $originalfile->get_itemid(),
1914
            'filepath'  => $originalfile->get_filepath(),
1915
            'filename'  => $originalfile->get_filename(),
1916
        );
1917
 
1918
        $aliasrecord = $this->generate_file_record();
1919
        $aliasrecord->filepath = '/A/';
1920
        $aliasrecord->filename = 'symlink.txt';
1921
 
1922
        $ref = $fs->pack_reference($originalrecord);
1923
        $aliasfile1 = $fs->create_file_from_reference($aliasrecord, $repo->id, $ref);
1924
 
1925
        $aliasrecord->filepath = '/B/';
1926
        $aliasrecord->filename = 'symlink.txt';
1927
        $ref = $fs->pack_reference($originalrecord);
1928
        $aliasfile2 = $fs->create_file_from_reference($aliasrecord, $repo->id, $ref);
1929
 
1930
        // Refetch A/symlink.txt file.
1931
        $symlink1 = $fs->get_file($aliasrecord->contextid, $aliasrecord->component,
1932
            $aliasrecord->filearea, $aliasrecord->itemid, '/A/', 'symlink.txt');
1933
        $this->assertTrue($symlink1->is_external_file());
1934
 
1935
        // Unlink the A/symlink.txt file.
1936
        $symlink1->delete_reference();
1937
        $this->assertFalse($symlink1->is_external_file());
1938
 
1939
        // Make sure that B/symlink.txt has not been affected.
1940
        $symlink2 = $fs->get_file($aliasrecord->contextid, $aliasrecord->component,
1941
            $aliasrecord->filearea, $aliasrecord->itemid, '/B/', 'symlink.txt');
1942
        $this->assertTrue($symlink2->is_external_file());
1943
    }
1944
 
1945
    /**
1946
     * Make sure that when internal file is updated all references to it are
1947
     * updated immediately. When it is deleted, the references are converted
1948
     * to true copies.
1949
     */
1950
    public function test_update_reference_internal() {
1951
        purge_all_caches();
1952
        $this->resetAfterTest(true);
1953
        $user = $this->setup_three_private_files();
1954
        $fs = get_file_storage();
1955
        $repos = repository::get_instances(array('type' => 'user'));
1956
        $repo = reset($repos);
1957
 
1958
        // Create two aliases linking the same original.
1959
 
1960
        $areafiles = array_values($fs->get_area_files($user->ctxid, 'user', 'private', false, 'filename', false));
1961
 
1962
        $originalfile = $areafiles[0];
1963
        $this->assertInstanceOf('stored_file', $originalfile);
1964
        $contenthash = $originalfile->get_contenthash();
1965
        $filesize = $originalfile->get_filesize();
1966
 
1967
        $substitutefile = $areafiles[1];
1968
        $this->assertInstanceOf('stored_file', $substitutefile);
1969
        $newcontenthash = $substitutefile->get_contenthash();
1970
        $newfilesize = $substitutefile->get_filesize();
1971
 
1972
        $originalrecord = array(
1973
            'contextid' => $originalfile->get_contextid(),
1974
            'component' => $originalfile->get_component(),
1975
            'filearea'  => $originalfile->get_filearea(),
1976
            'itemid'    => $originalfile->get_itemid(),
1977
            'filepath'  => $originalfile->get_filepath(),
1978
            'filename'  => $originalfile->get_filename(),
1979
        );
1980
 
1981
        $aliasrecord = $this->generate_file_record();
1982
        $aliasrecord->filepath = '/A/';
1983
        $aliasrecord->filename = 'symlink.txt';
1984
 
1985
        $ref = $fs->pack_reference($originalrecord);
1986
        $symlink1 = $fs->create_file_from_reference($aliasrecord, $repo->id, $ref);
1987
        // Make sure created alias is a reference and has the same size and contenthash as source.
1988
        $this->assertEquals($contenthash, $symlink1->get_contenthash());
1989
        $this->assertEquals($filesize, $symlink1->get_filesize());
1990
        $this->assertEquals($repo->id, $symlink1->get_repository_id());
1991
        $this->assertNotEmpty($symlink1->get_referencefileid());
1992
        $referenceid = $symlink1->get_referencefileid();
1993
 
1994
        $aliasrecord->filepath = '/B/';
1995
        $aliasrecord->filename = 'symlink.txt';
1996
        $ref = $fs->pack_reference($originalrecord);
1997
        $symlink2 = $fs->create_file_from_reference($aliasrecord, $repo->id, $ref);
1998
        // Make sure created alias is a reference and has the same size and contenthash as source.
1999
        $this->assertEquals($contenthash, $symlink2->get_contenthash());
2000
        $this->assertEquals($filesize, $symlink2->get_filesize());
2001
        $this->assertEquals($repo->id, $symlink2->get_repository_id());
2002
        // Make sure both aliases have the same reference id.
2003
        $this->assertEquals($referenceid, $symlink2->get_referencefileid());
2004
 
2005
        // Overwrite ofiginal file.
2006
        $originalfile->replace_file_with($substitutefile);
2007
        $this->assertEquals($newcontenthash, $originalfile->get_contenthash());
2008
        $this->assertEquals($newfilesize, $originalfile->get_filesize());
2009
 
2010
        // References to the internal files must be synchronised immediately.
2011
        // Refetch A/symlink.txt file.
2012
        $symlink1 = $fs->get_file($aliasrecord->contextid, $aliasrecord->component,
2013
            $aliasrecord->filearea, $aliasrecord->itemid, '/A/', 'symlink.txt');
2014
        $this->assertTrue($symlink1->is_external_file());
2015
        $this->assertEquals($newcontenthash, $symlink1->get_contenthash());
2016
        $this->assertEquals($newfilesize, $symlink1->get_filesize());
2017
        $this->assertEquals($repo->id, $symlink1->get_repository_id());
2018
        $this->assertEquals($referenceid, $symlink1->get_referencefileid());
2019
 
2020
        // Refetch B/symlink.txt file.
2021
        $symlink2 = $fs->get_file($aliasrecord->contextid, $aliasrecord->component,
2022
            $aliasrecord->filearea, $aliasrecord->itemid, '/B/', 'symlink.txt');
2023
        $this->assertTrue($symlink2->is_external_file());
2024
        $this->assertEquals($newcontenthash, $symlink2->get_contenthash());
2025
        $this->assertEquals($newfilesize, $symlink2->get_filesize());
2026
        $this->assertEquals($repo->id, $symlink2->get_repository_id());
2027
        $this->assertEquals($referenceid, $symlink2->get_referencefileid());
2028
 
2029
        // Remove original file.
2030
        $originalfile->delete();
2031
 
2032
        // References must be converted to independend files.
2033
        // Refetch A/symlink.txt file.
2034
        $symlink1 = $fs->get_file($aliasrecord->contextid, $aliasrecord->component,
2035
            $aliasrecord->filearea, $aliasrecord->itemid, '/A/', 'symlink.txt');
2036
        $this->assertFalse($symlink1->is_external_file());
2037
        $this->assertEquals($newcontenthash, $symlink1->get_contenthash());
2038
        $this->assertEquals($newfilesize, $symlink1->get_filesize());
2039
        $this->assertNull($symlink1->get_repository_id());
2040
        $this->assertNull($symlink1->get_referencefileid());
2041
 
2042
        // Refetch B/symlink.txt file.
2043
        $symlink2 = $fs->get_file($aliasrecord->contextid, $aliasrecord->component,
2044
            $aliasrecord->filearea, $aliasrecord->itemid, '/B/', 'symlink.txt');
2045
        $this->assertFalse($symlink2->is_external_file());
2046
        $this->assertEquals($newcontenthash, $symlink2->get_contenthash());
2047
        $this->assertEquals($newfilesize, $symlink2->get_filesize());
2048
        $this->assertNull($symlink2->get_repository_id());
2049
        $this->assertNull($symlink2->get_referencefileid());
2050
    }
2051
 
2052
    /**
2053
     * Tests for get_unused_filename.
2054
     *
2055
     * @covers ::get_unused_filename
2056
     */
2057
    public function test_get_unused_filename() {
2058
        global $USER;
2059
        $this->resetAfterTest(true);
2060
 
2061
        $fs = get_file_storage();
2062
        $this->setAdminUser();
2063
        $contextid = \context_user::instance($USER->id)->id;
2064
        $component = 'user';
2065
        $filearea = 'private';
2066
        $itemid = 0;
2067
        $filepath = '/';
2068
 
2069
        // Create some private files.
2070
        $file = new \stdClass;
2071
        $file->contextid = $contextid;
2072
        $file->component = 'user';
2073
        $file->filearea  = 'private';
2074
        $file->itemid    = 0;
2075
        $file->filepath  = '/';
2076
        $file->source    = 'test';
2077
        $filenames = array('foo.txt', 'foo (1).txt', 'foo (20).txt', 'foo (999)', 'bar.jpg', 'What (a cool file).jpg',
2078
                'Hurray! (1).php', 'Hurray! (2).php', 'Hurray! (9a).php', 'Hurray! (abc).php');
2079
        foreach ($filenames as $key => $filename) {
2080
            $file->filename = $filename;
2081
            $userfile = $fs->create_file_from_string($file, "file $key $filename content");
2082
            $this->assertInstanceOf('stored_file', $userfile);
2083
        }
2084
 
2085
        // Asserting new generated names.
2086
        $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'unused.txt');
2087
        $this->assertEquals('unused.txt', $newfilename);
2088
        $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'foo.txt');
2089
        $this->assertEquals('foo (21).txt', $newfilename);
2090
        $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'foo (1).txt');
2091
        $this->assertEquals('foo (21).txt', $newfilename);
2092
        $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'foo (2).txt');
2093
        $this->assertEquals('foo (2).txt', $newfilename);
2094
        $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'foo (20).txt');
2095
        $this->assertEquals('foo (21).txt', $newfilename);
2096
        $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'foo');
2097
        $this->assertEquals('foo', $newfilename);
2098
        $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'foo (123)');
2099
        $this->assertEquals('foo (123)', $newfilename);
2100
        $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'foo (999)');
2101
        $this->assertEquals('foo (1000)', $newfilename);
2102
        $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'bar.png');
2103
        $this->assertEquals('bar.png', $newfilename);
2104
        $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'bar (12).png');
2105
        $this->assertEquals('bar (12).png', $newfilename);
2106
        $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'bar.jpg');
2107
        $this->assertEquals('bar (1).jpg', $newfilename);
2108
        $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'bar (1).jpg');
2109
        $this->assertEquals('bar (1).jpg', $newfilename);
2110
        $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'What (a cool file).jpg');
2111
        $this->assertEquals('What (a cool file) (1).jpg', $newfilename);
2112
        $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'Hurray! (1).php');
2113
        $this->assertEquals('Hurray! (3).php', $newfilename);
2114
 
2115
        $this->expectException('coding_exception');
2116
        $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, '');
2117
    }
2118
 
2119
    /**
2120
     * Test that mimetype_from_file returns appropriate output when the
2121
     * file could not be found.
2122
     *
2123
     * @covers ::mimetype
2124
     */
2125
    public function test_mimetype_not_found() {
2126
        $mimetype = \file_storage::mimetype('/path/to/nonexistent/file');
2127
        $this->assertEquals('document/unknown', $mimetype);
2128
    }
2129
 
2130
    /**
2131
     * Data provider to return fixture files and their expected mimetype
2132
     *
2133
     * @return array[]
2134
     */
2135
    public function filepath_mimetype_provider(): array {
2136
        return [
2137
            [__DIR__ . '/fixtures/testimage.jpg', 'image/jpeg'],
2138
            [__DIR__ . '/fixtures/testimage.svg', 'image/svg+xml'],
2139
            [__DIR__ . '/fixtures/testimage_basic.svg', 'image/svg+xml'],
2140
        ];
2141
    }
2142
 
2143
    /**
2144
     * Test that mimetype returns appropriate output for a known file.
2145
     *
2146
     * Note: this is not intended to check that functions outside of this
2147
     * file works. It is intended to validate the codepath contains no
2148
     * errors and behaves as expected.
2149
     *
2150
     * @covers ::mimetype
2151
     *
2152
     * @param string $filepath
2153
     * @param string $expectedmimetype
2154
     *
2155
     * @dataProvider filepath_mimetype_provider
2156
     */
2157
    public function test_mimetype_known(string $filepath, string $expectedmimetype): void {
2158
        $mimetype = \file_storage::mimetype($filepath);
2159
        $this->assertEquals($expectedmimetype, $mimetype);
2160
    }
2161
 
2162
    /**
2163
     * Test that mimetype_from_file returns appropriate output when the
2164
     * file could not be found.
2165
     *
2166
     * @covers ::mimetype_from_file
2167
     */
2168
    public function test_mimetype_from_file_not_found() {
2169
        $mimetype = \file_storage::mimetype_from_file('/path/to/nonexistent/file');
2170
        $this->assertEquals('document/unknown', $mimetype);
2171
    }
2172
 
2173
    /**
2174
     * Test that mimetype_from_file returns appropriate output for a known
2175
     * file.
2176
     *
2177
     * Note: this is not intended to check that functions outside of this
2178
     * file works. It is intended to validate the codepath contains no
2179
     * errors and behaves as expected.
2180
     *
2181
     * @covers ::mimetype_from_file
2182
     *
2183
     * @param string $filepath
2184
     * @param string $expectedmimetype
2185
     *
2186
     * @dataProvider filepath_mimetype_provider
2187
     */
2188
    public function test_mimetype_from_file_known(string $filepath, string $expectedmimetype): void {
2189
        $mimetype = \file_storage::mimetype_from_file($filepath);
2190
        $this->assertEquals($expectedmimetype, $mimetype);
2191
    }
2192
 
2193
    /**
2194
     * Test that get_pathname_hash returns the same file hash for pathnames
2195
     * with and without trailing / leading slash.
2196
     *
2197
     * @covers ::get_pathname_hash
2198
     *
2199
     */
2200
    public function test_get_pathname_hash(): void {
2201
        $contextid = 2;
2202
        $component = 'mod_test';
2203
        $filearea = 'data';
2204
        $itemid = 0;
2205
        $filepath1 = '/path';
2206
        $filepath2 = '/path/';
2207
        $filepath3 = 'path/';
2208
        $filename = 'example.jpg';
2209
        $hash1 = \file_storage::get_pathname_hash($contextid, $component, $filearea, $itemid, $filepath1, $filename);
2210
        $hash2 = \file_storage::get_pathname_hash($contextid, $component, $filearea, $itemid, $filepath2, $filename);
2211
        $hash3 = \file_storage::get_pathname_hash($contextid, $component, $filearea, $itemid, $filepath3, $filename);
2212
        $this->assertEquals($hash1, $hash2);
2213
        $this->assertEquals($hash2, $hash3);
2214
    }
2215
 
2216
}
2217
 
2218
class test_stored_file_inspection extends stored_file {
2219
    public static function get_pretected_pathname(stored_file $file) {
2220
        return $file->get_pathname_by_contenthash();
2221
    }
2222
}