MDL-67673 phpunit: Introduce a new, lightweight phpunit_dataset
[moodle.git] / lib / phpunit / tests / phpunit_dataset.test.php
1 <?php
2 // This file is part of Moodle - http://moodle.org/
3 //
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
17 /**
18  * Test phpunit_dataset features.
19  *
20  * @package    core
21  * @category   tests
22  * @copyright  2020 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
23  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  */
26 declare(strict_types=1);
28 use org\bovigo\vfs\vfsStream;
30 /**
31  * Test phpunit_dataset features.
32  *
33  * @coversDefaultClass phpunit_dataset
34  */
35 class core_phpunit_dataset_testcase extends advanced_testcase {
38     /**
39      * @covers ::from_files
40      */
41     public function test_from_files() {
43         $ds = new phpunit_dataset();
45         $files = [
46             __DIR__ . '/fixtures/sample_dataset.xml',
47             'user2' => __DIR__ . '/fixtures/sample_dataset.csv',
48         ];
50         // We need public properties to check the basis.
51         $dsref = new ReflectionClass($ds);
52         $dstables = $dsref->getProperty('tables');
53         $dstables->setAccessible(true);
54         $dscolumns = $dsref->getProperty('columns');
55         $dscolumns->setAccessible(true);
56         $dsrows = $dsref->getProperty('rows');
57         $dsrows->setAccessible(true);
59         // Expectations.
60         $exptables = ['user', 'user2'];
61         $expcolumns = ['id', 'username', 'email'];
62         $exprows = [
63             ['id' => 5, 'username' => 'bozka.novakova', 'email' => 'bozka@example.com'],
64             ['id' => 7, 'username' => 'pepa.novak', 'email' => 'pepa@example.com'],
65         ];
67         $ds->from_files($files);
69         $this->assertIsArray($dstables->getValue($ds));
70         $this->assertSame($exptables, $dstables->getValue($ds));
71         $this->assertIsArray($dscolumns->getValue($ds));
72         $this->assertSame($expcolumns, $dscolumns->getValue($ds)['user']);
73         $this->assertSame($expcolumns, $dscolumns->getValue($ds)['user2']);
74         $this->assertIsArray($dsrows->getValue($ds));
75         $this->assertEquals($exprows, $dsrows->getValue($ds)['user']); // Equals because of stringified integers on load.
76         $this->assertEquals($exprows, $dsrows->getValue($ds)['user2']); // Equals because of stringified integers on load.
77     }
79     /**
80      * test_from_file() data provider.
81      */
82     public function from_file_provider() {
83         // Create an unreadable file with vfsStream.
84         $vfsfile = vfsStream::newFile('unreadable', 0222);
85         vfsStream::setup('root')->addChild($vfsfile);
87         return [
88             'file not found' => [
89                 'fullpath' => '/this/does/not/exist',
90                 'tablename' => 'user',
91                 'exception' => 'from_file, file not found: /this/does/not/exist',
92                 'tables' => [],
93                 'columns' => [],
94                 'rows' => [],
95             ],
96             'file not readable' => [
97                 'fullpath' => $vfsfile->url(),
98                 'tablename' => 'user',
99                 'exception' => 'from_file, file not readable: ' . $vfsfile->url(),
100                 'tables' => [],
101                 'columns' => [],
102                 'rows' => [],
103             ],
104             'wrong extension' => [
105                 'fullpath' => __DIR__ . '/fixtures/sample_dataset.txt',
106                 'tablename' => 'user',
107                 'exception' => 'from_file, cannot handle files with extension: txt',
108                 'tables' => [],
109                 'columns' => [],
110                 'rows' => [],
111             ],
112             'csv loads ok' => [
113                 'fullpath' => __DIR__ . '/fixtures/sample_dataset.csv',
114                 'tablename' => 'user',
115                 'exception' => null,
116                 'tables' => ['user'],
117                 'columns' => ['user' =>
118                     ['id', 'username', 'email']
119                 ],
120                 'rows' => ['user' =>
121                     [
122                         ['id' => 5, 'username' => 'bozka.novakova', 'email' => 'bozka@example.com'],
123                         ['id' => 7, 'username' => 'pepa.novak', 'email' => 'pepa@example.com'],
124                     ]
125                 ],
126             ],
127             'xml loads ok' => [
128                 'fullpath' => __DIR__ . '/fixtures/sample_dataset.xml',
129                 'tablename' => 'user',
130                 'exception' => null,
131                 'tables' => ['user'],
132                 'columns' => ['user' =>
133                     ['id', 'username', 'email']
134                 ],
135                 'rows' => ['user' =>
136                     [
137                         ['id' => 5, 'username' => 'bozka.novakova', 'email' => 'bozka@example.com'],
138                         ['id' => 7, 'username' => 'pepa.novak', 'email' => 'pepa@example.com'],
139                     ]
140                 ],
141             ],
142         ];
143     }
145     /**
146      * @dataProvider from_file_provider
147      * @covers ::from_file
148      */
149     public function test_from_file(string $fullpath, string $tablename, ?string $exception,
150         array $tables, array $columns, array $rows) {
152         $ds = new phpunit_dataset();
154         // We need public properties to check the basis.
155         $dsref = new ReflectionClass($ds);
156         $dstables = $dsref->getProperty('tables');
157         $dstables->setAccessible(true);
158         $dscolumns = $dsref->getProperty('columns');
159         $dscolumns->setAccessible(true);
160         $dsrows = $dsref->getProperty('rows');
161         $dsrows->setAccessible(true);
163         // We are expecting an exception.
164         if (!empty($exception)) {
165             $this->expectException('coding_exception');
166             $this->expectExceptionMessage($exception);
167         }
169         $ds->from_file($fullpath, $tablename);
171         $this->assertIsArray($dstables->getValue($ds));
172         $this->assertSame($tables, $dstables->getValue($ds));
173         $this->assertIsArray($dscolumns->getValue($ds));
174         $this->assertSame($columns, $dscolumns->getValue($ds));
175         $this->assertIsArray($dsrows->getValue($ds));
176         $this->assertEquals($rows, $dsrows->getValue($ds)); // Equals because of stringified integers on load.
177     }
179     /**
180      * test_from_string() data provider.
181      */
182     public function from_string_provider() {
184         return [
185             'wrong type' => [
186                 'content' => file_get_contents(__DIR__ . '/fixtures/sample_dataset.xml'),
187                 'type' => 'txt',
188                 'tablename' => 'user',
189                 'exception' => 'from_string, cannot handle contents of type: txt',
190                 'tables' => [],
191                 'columns' => [],
192                 'rows' => [],
193             ],
194             'missing cvs table' => [
195                 'content' => file_get_contents(__DIR__ . '/fixtures/sample_dataset.csv'),
196                 'type' => 'csv',
197                 'tablename' => '',
198                 'exception' => 'from_string, contents of type "cvs" require a $table to be passed, none found',
199                 'tables' => [],
200                 'columns' => [],
201                 'rows' => [],
202             ],
203             'csv loads ok' => [
204                 'fullpath' => file_get_contents(__DIR__ . '/fixtures/sample_dataset.csv'),
205                 'type' => 'csv',
206                 'tablename' => 'user',
207                 'exception' => null,
208                 'tables' => ['user'],
209                 'columns' => ['user' =>
210                     ['id', 'username', 'email']
211                 ],
212                 'rows' => ['user' =>
213                     [
214                         ['id' => 5, 'username' => 'bozka.novakova', 'email' => 'bozka@example.com'],
215                         ['id' => 7, 'username' => 'pepa.novak', 'email' => 'pepa@example.com'],
216                     ]
217                 ],
218             ],
219             'xml loads ok' => [
220                 'fullpath' => file_get_contents(__DIR__ . '/fixtures/sample_dataset.xml'),
221                 'type' => 'xml',
222                 'tablename' => 'user',
223                 'exception' => null,
224                 'tables' => ['user'],
225                 'columns' => ['user' =>
226                     ['id', 'username', 'email']
227                 ],
228                 'rows' => ['user' =>
229                     [
230                         ['id' => 5, 'username' => 'bozka.novakova', 'email' => 'bozka@example.com'],
231                         ['id' => 7, 'username' => 'pepa.novak', 'email' => 'pepa@example.com'],
232                     ]
233                 ],
234             ],
235         ];
236     }
238     /**
239      * @dataProvider from_string_provider
240      * @covers ::from_string
241      */
242     public function test_from_string(string $content, string $type, string $tablename, ?string $exception,
243         array $tables, array $columns, array $rows) {
245         $ds = new phpunit_dataset();
247         // We need public properties to check the basis.
248         $dsref = new ReflectionClass($ds);
249         $dstables = $dsref->getProperty('tables');
250         $dstables->setAccessible(true);
251         $dscolumns = $dsref->getProperty('columns');
252         $dscolumns->setAccessible(true);
253         $dsrows = $dsref->getProperty('rows');
254         $dsrows->setAccessible(true);
256         // We are expecting an exception.
257         if (!empty($exception)) {
258             $this->expectException('coding_exception');
259             $this->expectExceptionMessage($exception);
260         }
262         $ds->from_string($content, $type, $tablename);
264         $this->assertIsArray($dstables->getValue($ds));
265         $this->assertSame($tables, $dstables->getValue($ds));
266         $this->assertIsArray($dscolumns->getValue($ds));
267         $this->assertSame($columns, $dscolumns->getValue($ds));
268         $this->assertIsArray($dsrows->getValue($ds));
269         $this->assertEquals($rows, $dsrows->getValue($ds)); // Equals because of stringified integers on load.
270     }
272     /**
273      * test_from_array() data provider.
274      */
275     public function from_array_provider() {
276         return [
277             'repeated array table many structures' => [
278                 'structure' => [
279                     'user' => [
280                         ['id' => 5, 'name' => 'John'],
281                         ['id' => 6, 'name' => 'Jane'],
282                     ],
283                 ],
284                 'exception' => 'from_array, table already added to dataset: user',
285                 'tables' => [],
286                 'columns' => [],
287                 'rows' => [],
288                 'repeated' => true, // To force the table already exists exception.
289             ],
290             'wrong number of columns' => [
291                 'structure' => [
292                     'user' => [
293                         ['id' => 5, 'name' => 'John'],
294                         ['id' => 6],
295                     ],
296                 ],
297                 'exception' => 'from_array, number of columns must match number of values, found: 2 vs 1',
298                 'tables' => [],
299                 'columns' => [],
300                 'rows' => [],
301             ],
302             'wrong not matching names of columns' => [
303                 'structure' => [
304                     'user' => [
305                         ['id' => 5, 'name' => 'John'],
306                         ['id' => 6, 'noname' => 'Jane'],
307                     ],
308                 ],
309                 'exception' => 'from_array, columns in all elements must match first one, found: id, noname',
310                 'tables' => [],
311                 'columns' => [],
312                 'rows' => [],
313             ],
314             'ok non-associative format' => [
315                 'structure' => [
316                     'user' => [
317                         ['id', 'name'],
318                         [5, 'John'],
319                         [6, 'Jane'],
320                     ],
321                 ],
322                 'exception' => null,
323                 'tables' => ['user'],
324                 'columns' => ['user' =>
325                     ['id', 'name'],
326                 ],
327                 'rows' => ['user' =>
328                     [
329                         ['id' => 5, 'name' => 'John'],
330                         ['id' => 6, 'name' => 'Jane'],
331                     ],
332                 ],
333             ],
334             'ok associative format' => [
335                 'structure' => [
336                     'user' => [
337                         ['id' => 5, 'name' => 'John'],
338                         ['id' => 6, 'name' => 'Jane'],
339                     ],
340                 ],
341                 'exception' => null,
342                 'tables' => ['user'],
343                 'columns' => ['user' =>
344                     ['id', 'name'],
345                 ],
346                 'rows' => ['user' =>
347                     [
348                         ['id' => 5, 'name' => 'John'],
349                         ['id' => 6, 'name' => 'Jane'],
350                     ],
351                 ],
352             ],
353             'ok multiple' => [
354                 'structure' => [
355                     'user' => [
356                         ['id' => 5, 'name' => 'John'],
357                         ['id' => 6, 'name' => 'Jane'],
358                     ],
359                     'course' => [
360                         ['id' => 7, 'name' => '101'],
361                         ['id' => 8, 'name' => '102'],
362                     ],
363                 ],
364                 'exception' => null,
365                 'tables' => ['user', 'course'],
366                 'columns' => [
367                     'user' => ['id', 'name'],
368                     'course' => ['id', 'name'],
369                 ],
370                 'rows' => [
371                     'user' => [
372                         ['id' => 5, 'name' => 'John'],
373                         ['id' => 6, 'name' => 'Jane'],
374                     ],
375                     'course' => [
376                         ['id' => 7, 'name' => '101'],
377                         ['id' => 8, 'name' => '102'],
378                     ],
379                 ],
380             ],
381         ];
382     }
384     /**
385      * @dataProvider from_array_provider
386      * @covers ::from_array
387      */
388     public function test_from_array(array $structure, ?string $exception,
389         array $tables, array $columns, array $rows, ?bool $repeated = false) {
391         $ds = new phpunit_dataset();
393         // We need public properties to check the basis.
394         $dsref = new ReflectionClass($ds);
395         $dstables = $dsref->getProperty('tables');
396         $dstables->setAccessible(true);
397         $dscolumns = $dsref->getProperty('columns');
398         $dscolumns->setAccessible(true);
399         $dsrows = $dsref->getProperty('rows');
400         $dsrows->setAccessible(true);
402         // We are expecting an exception.
403         if (!empty($exception)) {
404             $this->expectException('coding_exception');
405             $this->expectExceptionMessage($exception);
406         }
408         $ds->from_array($structure);
409         if ($repeated) {
410             $ds->from_array($structure);
411         }
413         $this->assertIsArray($dstables->getValue($ds));
414         $this->assertSame($tables, $dstables->getValue($ds));
415         $this->assertIsArray($dscolumns->getValue($ds));
416         $this->assertSame($columns, $dscolumns->getValue($ds));
417         $this->assertIsArray($dsrows->getValue($ds));
418         $this->assertEquals($rows, $dsrows->getValue($ds)); // Equals because of stringified integers on load.
419     }
421     /**
422      * test_load_csv() data provider.
423      */
424     public function load_csv_provider() {
426         return [
427             'repeated csv table many files' => [
428                 'files' => [
429                     __DIR__ . '/fixtures/sample_dataset.xml',
430                     'user' => __DIR__ . '/fixtures/sample_dataset.csv',
431                 ],
432                 'exception' => 'csv_dataset_format, table already added to dataset: user',
433                 'tables' => [],
434                 'columns' => [],
435                 'rows' => [],
436             ],
437             'ok one csv file' => [
438                 'files' => [
439                     'user' => __DIR__ . '/fixtures/sample_dataset.csv',
440                 ],
441                 'exception' => null,
442                 'tables' => ['user'],
443                 'columns' => ['user' =>
444                     ['id', 'username', 'email']
445                 ],
446                 'rows' => ['user' =>
447                     [
448                         ['id' => 5, 'username' => 'bozka.novakova', 'email' => 'bozka@example.com'],
449                         ['id' => 7, 'username' => 'pepa.novak', 'email' => 'pepa@example.com'],
450                     ]
451                 ],
452             ],
453             'ok multiple csv files' => [
454                 'files' => [
455                     'user1' => __DIR__ . '/fixtures/sample_dataset.csv',
456                     'user2' => __DIR__ . '/fixtures/sample_dataset.csv',
457                 ],
458                 'exception' => null,
459                 'tables' => ['user1', 'user2'],
460                 'columns' => [
461                     'user1' => ['id', 'username', 'email'],
462                     'user2' => ['id', 'username', 'email'],
463                 ],
464                 'rows' => [
465                     'user1' => [
466                         ['id' => 5, 'username' => 'bozka.novakova', 'email' => 'bozka@example.com'],
467                         ['id' => 7, 'username' => 'pepa.novak', 'email' => 'pepa@example.com'],
468                     ],
469                     'user2' => [
470                         ['id' => 5, 'username' => 'bozka.novakova', 'email' => 'bozka@example.com'],
471                         ['id' => 7, 'username' => 'pepa.novak', 'email' => 'pepa@example.com'],
472                     ],
473                 ],
474             ],
475         ];
476     }
478     /**
479      * @dataProvider load_csv_provider
480      * @covers ::load_csv
481      */
482     public function test_load_csv(array $files, ?string $exception,
483         array $tables, array $columns, array $rows) {
485         $ds = new phpunit_dataset();
487         // We need public properties to check the basis.
488         $dsref = new ReflectionClass($ds);
489         $dstables = $dsref->getProperty('tables');
490         $dstables->setAccessible(true);
491         $dscolumns = $dsref->getProperty('columns');
492         $dscolumns->setAccessible(true);
493         $dsrows = $dsref->getProperty('rows');
494         $dsrows->setAccessible(true);
496         // We are expecting an exception.
497         if (!empty($exception)) {
498             $this->expectException('coding_exception');
499             $this->expectExceptionMessage($exception);
500         }
502         $ds->from_files($files);
504         $this->assertIsArray($dstables->getValue($ds));
505         $this->assertSame($tables, $dstables->getValue($ds));
506         $this->assertIsArray($dscolumns->getValue($ds));
507         $this->assertSame($columns, $dscolumns->getValue($ds));
508         $this->assertIsArray($dsrows->getValue($ds));
509         $this->assertEquals($rows, $dsrows->getValue($ds)); // Equals because of stringified integers on load.
510     }
512     /**
513      * test_load_xml() data provider.
514      */
515     public function load_xml_provider() {
517         return [
518             'repeated xml table multiple files' => [
519                 'files' => [
520                     'user' => __DIR__ . '/fixtures/sample_dataset.csv',
521                     __DIR__ . '/fixtures/sample_dataset.xml',
522                 ],
523                 'exception' => 'xml_dataset_format, table already added to dataset: user',
524                 'tables' => [],
525                 'columns' => [],
526                 'rows' => [],
527             ],
528             'repeated xml table one file' => [
529                 'files' => [__DIR__ . '/fixtures/sample_dataset_repeated.xml'],
530                 'exception' => 'xml_dataset_format, table already added to dataset: user',
531                 'tables' => [],
532                 'columns' => [],
533                 'rows' => [],
534             ],
535             'wrong dataset element' => [
536                 'files' => [__DIR__ . '/fixtures/sample_dataset_wrong_dataset.xml'],
537                 'exception' => 'xml_dataset_format, main xml element must be "dataset", found: nodataset',
538                 'tables' => [],
539                 'columns' => [],
540                 'rows' => [],
541             ],
542             'wrong table element' => [
543                 'files' => [__DIR__ . '/fixtures/sample_dataset_wrong_table.xml'],
544                 'exception' => 'xml_dataset_format, only "table" elements allowed, found: notable',
545                 'tables' => [],
546                 'columns' => [],
547                 'rows' => [],
548             ],
549             'wrong table name attribute' => [
550                 'files' => [__DIR__ . '/fixtures/sample_dataset_wrong_attribute.xml'],
551                 'exception' => 'xml_dataset_format, "table" element only allows "name" attribute',
552                 'tables' => [],
553                 'columns' => [],
554                 'rows' => [],
555             ],
556             'only col and row allowed' => [
557                 'files' => [__DIR__ . '/fixtures/sample_dataset_only_colrow.xml'],
558                 'exception' => 'xml_dataset_format, only "column or "row" elements allowed, found: nocolumn',
559                 'tables' => [],
560                 'columns' => [],
561                 'rows' => [],
562             ],
563             'wrong value element' => [
564                 'files' => [__DIR__ . '/fixtures/sample_dataset_wrong_value.xml'],
565                 'exception' => 'xml_dataset_format, only "value" elements allowed, found: novalue',
566                 'tables' => [],
567                 'columns' => [],
568                 'rows' => [],
569             ],
570             'column before row' => [
571                 'files' => [__DIR__ . '/fixtures/sample_dataset_col_before_row.xml'],
572                 'exception' => 'xml_dataset_format, "column" elements always must be before "row" ones',
573                 'tables' => [],
574                 'columns' => [],
575                 'rows' => [],
576             ],
577             'row after column' => [
578                 'files' => [__DIR__ . '/fixtures/sample_dataset_row_after_col.xml'],
579                 'exception' => 'xml_dataset_format, "row" elements always must be after "column" ones',
580                 'tables' => [],
581                 'columns' => [],
582                 'rows' => [],
583             ],
584             'number of columns' => [
585                 'files' => [__DIR__ . '/fixtures/sample_dataset_number_of_columns.xml'],
586                 'exception' => 'xml_dataset_format, number of columns must match number of values, found: 4 vs 3',
587                 'tables' => [],
588                 'columns' => [],
589                 'rows' => [],
590             ],
591             'ok one xml file' => [
592                 'files' => [__DIR__ . '/fixtures/sample_dataset.xml'],
593                 'exception' => null,
594                 'tables' => ['user'],
595                 'columns' => ['user' =>
596                     ['id', 'username', 'email']
597                 ],
598                 'rows' => ['user' =>
599                     [
600                         ['id' => 5, 'username' => 'bozka.novakova', 'email' => 'bozka@example.com'],
601                         ['id' => 7, 'username' => 'pepa.novak', 'email' => 'pepa@example.com'],
602                     ]
603                 ],
604             ],
605             'ok multiple xml files' => [
606                 'files' => [
607                     'user1' => __DIR__ . '/fixtures/sample_dataset.csv',
608                     __DIR__ . '/fixtures/sample_dataset.xml',
609                 ],
610                 'exception' => null,
611                 'tables' => ['user1', 'user'],
612                 'columns' => [
613                     'user1' => ['id', 'username', 'email'],
614                     'user' => ['id', 'username', 'email'],
615                 ],
616                 'rows' => [
617                     'user1' => [
618                         ['id' => 5, 'username' => 'bozka.novakova', 'email' => 'bozka@example.com'],
619                         ['id' => 7, 'username' => 'pepa.novak', 'email' => 'pepa@example.com'],
620                     ],
621                     'user' => [
622                         ['id' => 5, 'username' => 'bozka.novakova', 'email' => 'bozka@example.com'],
623                         ['id' => 7, 'username' => 'pepa.novak', 'email' => 'pepa@example.com'],
624                     ],
625                 ],
626             ],
627             'ok many tables in one xml' => [
628                 'files' => [__DIR__ . '/fixtures/sample_dataset_many.xml'],
629                 'exception' => null,
630                 'tables' => ['user', 'course'],
631                 'columns' => [
632                     'user' => ['id', 'username', 'email'],
633                     'course' => ['id', 'shortname', 'fullname'],
634                 ],
635                 'rows' => [
636                     'user' => [
637                         ['id' => 5, 'username' => 'bozka.novakova', 'email' => 'bozka@example.com'],
638                         ['id' => 7, 'username' => 'pepa.novak', 'email' => 'pepa@example.com'],
639                     ],
640                     'course' => [
641                         ['id' => 6, 'shortname' => '101', 'fullname' => '1-0-1'],
642                         ['id' => 8, 'shortname' => '202', 'fullname' => '2-0-2'],
643                     ],
644                 ],
645             ],
646         ];
647     }
649     /**
650      * @dataProvider load_xml_provider
651      * @covers ::load_xml
652      */
653     public function test_load_xml(array $files, ?string $exception,
654         array $tables, array $columns, array $rows) {
656         $ds = new phpunit_dataset();
658         // We need public properties to check the basis.
659         $dsref = new ReflectionClass($ds);
660         $dstables = $dsref->getProperty('tables');
661         $dstables->setAccessible(true);
662         $dscolumns = $dsref->getProperty('columns');
663         $dscolumns->setAccessible(true);
664         $dsrows = $dsref->getProperty('rows');
665         $dsrows->setAccessible(true);
667         // We are expecting an exception.
668         if (!empty($exception)) {
669             $this->expectException('coding_exception');
670             $this->expectExceptionMessage($exception);
671         }
673         $ds->from_files($files);
675         $this->assertIsArray($dstables->getValue($ds));
676         $this->assertSame($tables, $dstables->getValue($ds));
677         $this->assertIsArray($dscolumns->getValue($ds));
678         $this->assertSame($columns, $dscolumns->getValue($ds));
679         $this->assertIsArray($dsrows->getValue($ds));
680         $this->assertEquals($rows, $dsrows->getValue($ds)); // Equals because of stringified integers on load.
681     }
683     /**
684      * test_to_database() data provider.
685      */
686     public function to_database_provider() {
688         return [
689             'wrong table requested' => [
690                 'files' => [__DIR__ . '/fixtures/sample_dataset_insert.xml'],
691                 'filter' => ['wrongtable'],
692                 'exception' => 'dataset_to_database, table is not in the dataset: wrongtable',
693                 'columns' => [],
694                 'rows' => [],
695             ],
696             'one table insert' => [
697                 'files' => [__DIR__ . '/fixtures/sample_dataset_insert.xml'],
698                 'filter' => [],
699                 'exception' => null,
700                 'columns' => [
701                     'user' => ['username', 'email'],
702                 ],
703                 'rows' => ['user' =>
704                     [
705                         (object)['username' => 'bozka.novakova', 'email' => 'bozka@example.com'],
706                         (object)['username' => 'pepa.novak', 'email' => 'pepa@example.com'],
707                     ]
708                 ],
709             ],
710             'one table import' => [
711                 'files' => [__DIR__ . '/fixtures/sample_dataset.xml'],
712                 'filter' => [],
713                 'exception' => null,
714                 'columns' => [
715                     'user' => ['id', 'username', 'email'],
716                 ],
717                 'rows' => ['user' =>
718                     [
719                         (object)['id' => 5, 'username' => 'bozka.novakova', 'email' => 'bozka@example.com'],
720                         (object)['id' => 7, 'username' => 'pepa.novak', 'email' => 'pepa@example.com'],
721                     ]
722                 ],
723             ],
724             'multiple table many files import' => [
725                 'files' => [
726                     __DIR__ . '/fixtures/sample_dataset.xml',
727                     __DIR__ . '/fixtures/sample_dataset2.xml',
728                 ],
729                 'filter' => [],
730                 'exception' => null,
731                 'columns' => [
732                     'user' => ['id', 'username', 'email'],
733                     'course' => ['id', 'shortname', 'fullname'],
734                 ],
735                 'rows' => [
736                     'user' => [
737                         (object)['id' => 5, 'username' => 'bozka.novakova', 'email' => 'bozka@example.com'],
738                         (object)['id' => 7, 'username' => 'pepa.novak', 'email' => 'pepa@example.com'],
739                     ],
740                     'course' => [
741                         (object)['id' => 6, 'shortname' => '101', 'fullname' => '1-0-1'],
742                         (object)['id' => 8, 'shortname' => '202', 'fullname' => '2-0-2'],
743                     ],
744                 ],
745             ],
746             'multiple table one file import' => [
747                 'files' => [__DIR__ . '/fixtures/sample_dataset_many.xml'],
748                 'filter' => [],
749                 'exception' => null,
750                 'columns' => [
751                     'user' => ['id', 'username', 'email'],
752                     'course' => ['id', 'shortname', 'fullname'],
753                 ],
754                 'rows' => [
755                     'user' => [
756                         (object)['id' => 5, 'username' => 'bozka.novakova', 'email' => 'bozka@example.com'],
757                         (object)['id' => 7, 'username' => 'pepa.novak', 'email' => 'pepa@example.com'],
758                     ],
759                     'course' => [
760                         (object)['id' => 6, 'shortname' => '101', 'fullname' => '1-0-1'],
761                         (object)['id' => 8, 'shortname' => '202', 'fullname' => '2-0-2'],
762                     ],
763                 ],
764             ],
765             'filtering tables' => [
766                 'files' => [__DIR__ . '/fixtures/sample_dataset_many.xml'],
767                 'filter' => ['course'],
768                 'exception' => null,
769                 'columns' => [
770                     'user' => ['id', 'username', 'email'],
771                     'course' => ['id', 'shortname', 'fullname'],
772                 ],
773                 'rows' => [
774                     'user' => [], // Table user is being excluded via filter, expect no rows sent to database.
775                     'course' => [
776                         (object)['id' => 6, 'shortname' => '101', 'fullname' => '1-0-1'],
777                         (object)['id' => 8, 'shortname' => '202', 'fullname' => '2-0-2'],
778                     ],
779                 ],
780             ],
781         ];
782     }
784     /**
785      * @dataProvider to_database_provider
786      * @covers ::to_database
787      */
788     public function test_to_database(array $files, ?array $filter = [], ?string $exception, array $columns, array $rows) {
789         global $DB;
791         $this->resetAfterTest();
793         // Grab the status before loading to database.
794         $before = [];
795         foreach ($columns as $tablename => $tablecolumns) {
796             if (!isset($before[$tablename])) {
797                 $before[$tablename] = [];
798             }
799             $before[$tablename] = $DB->get_records($tablename, null, '', implode(', ', $tablecolumns));
800         }
802         $ds = new phpunit_dataset();
804         // We are expecting an exception.
805         if (!empty($exception)) {
806             $this->expectException('coding_exception');
807             $this->expectExceptionMessage($exception);
808         }
810         $ds->from_files($files);
811         $ds->to_database($filter);
813         // Grab the status after loading to database.
814         $after = [];
815         foreach ($columns as $tablename => $tablecolumns) {
816             if (!isset($after[$tablename])) {
817                 $after[$tablename] = [];
818             }
819             $sortandcol = implode(', ', $tablecolumns);
820             $after[$tablename] = $DB->get_records($tablename, null, $sortandcol, $sortandcol);
821         }
823         // Differences must match the expectations.
824         foreach ($rows as $tablename => $expectedrows) {
825             $changes = array_udiff($after[$tablename], $before[$tablename], function ($b, $a) {
826                 if ((array)$b > (array)$a) {
827                     return 1;
828                 } else if ((array)$b < (array)$a) {
829                     return -1;
830                 } else {
831                     return 0;
832                 }
833             });
834             $this->assertEquals(array_values($expectedrows), array_values($changes));
835         }
836     }
838     /**
839      * test_get_rows() data provider.
840      */
841     public function get_rows_provider() {
843         return [
844             'wrong table requested' => [
845                 'files' => [__DIR__ . '/fixtures/sample_dataset_many.xml'],
846                 'filter' => ['wrongtable'],
847                 'exception' => 'dataset_get_rows, table is not in the dataset: wrongtable',
848                 'rows' => [],
849             ],
850             'ok get rows from empty tables' => [
851                 'files' => [__DIR__ . '/fixtures/sample_dataset_many_with_empty.xml'],
852                 'filter' => ['empty1', 'empty2'],
853                 'exception' => null,
854                 'rows' => [
855                     'empty1' => [],
856                     'empty2' => [],
857                 ],
858             ],
859             'ok get rows from one table' => [
860                 'files' => [__DIR__ . '/fixtures/sample_dataset_many_with_empty.xml'],
861                 'filter' => ['user'],
862                 'exception' => null,
863                 'rows' => [
864                     'user' => [
865                         ['id' => 5, 'username' => 'bozka.novakova', 'email' => 'bozka@example.com'],
866                         ['id' => 7, 'username' => 'pepa.novak', 'email' => 'pepa@example.com'],
867                     ],
868                 ],
869             ],
870             'ok get rows from two tables' => [
871                 'files' => [__DIR__ . '/fixtures/sample_dataset_many_with_empty.xml'],
872                 'filter' => ['user', 'course'],
873                 'exception' => null,
874                 'rows' => [
875                     'user' => [
876                         ['id' => 5, 'username' => 'bozka.novakova', 'email' => 'bozka@example.com'],
877                         ['id' => 7, 'username' => 'pepa.novak', 'email' => 'pepa@example.com'],
878                     ],
879                     'course' => [
880                         ['id' => 6, 'shortname' => '101', 'fullname' => '1-0-1'],
881                         ['id' => 8, 'shortname' => '202', 'fullname' => '2-0-2'],
882                     ],
883                 ],
884             ],
885             'ok get rows from three tables' => [
886                 'files' => [__DIR__ . '/fixtures/sample_dataset_many_with_empty.xml'],
887                 'filter' => ['user', 'empty1', 'course'],
888                 'exception' => null,
889                 'rows' => [
890                     'user' => [
891                         ['id' => 5, 'username' => 'bozka.novakova', 'email' => 'bozka@example.com'],
892                         ['id' => 7, 'username' => 'pepa.novak', 'email' => 'pepa@example.com'],
893                     ],
894                     'empty1' => [],
895                     'course' => [
896                         ['id' => 6, 'shortname' => '101', 'fullname' => '1-0-1'],
897                         ['id' => 8, 'shortname' => '202', 'fullname' => '2-0-2'],
898                     ],
899                 ],
900             ],
901             'ok no filter returns all' => [
902                 'files' => [__DIR__ . '/fixtures/sample_dataset_many_with_empty.xml'],
903                 'filter' => [],
904                 'exception' => null,
905                 'rows' => [
906                     'user' => [
907                         ['id' => 5, 'username' => 'bozka.novakova', 'email' => 'bozka@example.com'],
908                         ['id' => 7, 'username' => 'pepa.novak', 'email' => 'pepa@example.com'],
909                     ],
910                     'empty1' => [],
911                     'empty2' => [],
912                     'course' => [
913                         ['id' => 6, 'shortname' => '101', 'fullname' => '1-0-1'],
914                         ['id' => 8, 'shortname' => '202', 'fullname' => '2-0-2'],
915                     ],
916                 ],
917             ],
918         ];
919     }
921     /**
922      * @dataProvider get_rows_provider
923      * @covers ::get_rows
924      */
925     public function test_get_rows(array $files, array $filter, ?string $exception, array $rows) {
927         $ds = new phpunit_dataset();
929         // We are expecting an exception.
930         if (!empty($exception)) {
931             $this->expectException('coding_exception');
932             $this->expectExceptionMessage($exception);
933         }
935         $ds->from_files($files);
936         $this->assertEquals($rows, $ds->get_rows($filter));
937     }