Proyectos de Subversion Moodle

Rev

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

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