MDL-57023 tool_usertours: Ensure that the tour key is unique
[moodle.git] / admin / tool / usertours / tests / tour_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  * Tests for tour.
19  *
20  * @package    tool_usertours
21  * @copyright  2016 Andrew Nicols <andrew@nicols.co.uk>
22  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23  */
25 defined('MOODLE_INTERNAL') || die();
27 global $CFG;
28 require_once($CFG->libdir . '/formslib.php');
30 use tool_usertours\tour;
32 /**
33  * Tests for tour.
34  *
35  * @package    tool_usertours
36  * @copyright  2016 Andrew Nicols <andrew@nicols.co.uk>
37  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
38  */
39 class tour_testcase extends advanced_testcase {
41     /**
42      * @var moodle_database
43      */
44     protected $db;
46     /**
47      * Setup to store the DB reference.
48      */
49     public function setUp() {
50         global $DB;
52         $this->db = $DB;
53     }
55     /**
56      * Tear down to restore the original DB reference.
57      */
58     public function tearDown() {
59         global $DB;
61         $DB = $this->db;
62     }
64     /**
65      * Helper to mock the database.
66      *
67      * @return moodle_database
68      */
69     public function mock_database() {
70         global $DB;
72         $DB = $this->getMockBuilder(\moodle_database::class)
73             ->getMock()
74             ;
76         return $DB;
77     }
79     /**
80      * Data provider for the dirty value tester.
81      *
82      * @return array
83      */
84     public function dirty_value_provider() {
85         return [
86                 'name' => [
87                         'name',
88                         ['Lorem'],
89                     ],
90                 'description' => [
91                         'description',
92                         ['Lorem'],
93                     ],
94                 'pathmatch' => [
95                         'pathmatch',
96                         ['Lorem'],
97                     ],
98                 'enabled' => [
99                         'enabled',
100                         ['Lorem'],
101                     ],
102                 'sortorder' => [
103                         'sortorder',
104                         [1],
105                     ],
106                 'config' => [
107                         'config',
108                         ['key', 'value'],
109                     ],
110             ];
111     }
113     /**
114      * Test that setters mark things as dirty.
115      *
116      * @dataProvider dirty_value_provider
117      * @param   string  $name           The name of the key being tested
118      * @param   mixed   $value          The value being set
119      */
120     public function test_dirty_values($name, $value) {
121         $tour = new \tool_usertours\tour();
122         $method = 'set_' . $name;
123         call_user_func_array([$tour, $method], $value);
125         $rc = new \ReflectionClass(\tool_usertours\tour::class);
126         $rcp = $rc->getProperty('dirty');
127         $rcp->setAccessible(true);
129         $this->assertTrue($rcp->getValue($tour));
130     }
132     /**
133      * Data provider for the get_ tests.
134      *
135      * @return array
136      */
137     public function getter_provider() {
138         return [
139                 'id' => [
140                         'id',
141                         rand(1, 100),
142                     ],
143                 'name' => [
144                         'name',
145                         'Lorem',
146                     ],
147                 'description' => [
148                         'description',
149                         'Lorem',
150                     ],
151                 'pathmatch' => [
152                         'pathmatch',
153                         'Lorem',
154                     ],
155                 'enabled' => [
156                         'enabled',
157                         'Lorem',
158                     ],
159                 'sortorder' => [
160                         'sortorder',
161                         rand(1, 100),
162                     ],
163                 'config' => [
164                         'config',
165                         ['key', 'value'],
166                     ],
167             ];
168     }
170     /**
171      * Test that getters return the configured value.
172      *
173      * @dataProvider getter_provider
174      * @param   string  $key            The name of the key being tested
175      * @param   mixed   $value          The value being set
176      */
177     public function test_getters($key, $value) {
178         $tour = new \tool_usertours\tour();
180         $rc = new \ReflectionClass(tour::class);
182         $rcp = $rc->getProperty($key);
183         $rcp->setAccessible(true);
184         $rcp->setValue($tour, $value);
186         $getter = 'get_' . $key;
188         $this->assertEquals($value, $tour->$getter());
189     }
191     /**
192      * Ensure that non-dirty tours are not persisted.
193      */
194     public function test_persist_non_dirty() {
195         $tour = $this->getMockBuilder(tour::class)
196             ->setMethods(['to_record'])
197             ->getMock()
198             ;
200         $tour->expects($this->never())
201             ->method('to_record')
202             ;
204         $this->assertSame($tour, $tour->persist());
205     }
207     /**
208      * Ensure that new dirty tours are persisted.
209      */
210     public function test_persist_dirty_new() {
211         // Mock the database.
212         $DB = $this->mock_database();
214         $DB->expects($this->never())
215             ->method('update_record')
216             ;
218         $id = rand(1, 100);
219         $DB->expects($this->once())
220             ->method('insert_record')
221             ->willReturn($id)
222             ;
224         // Mock the tour.
225         $tour = $this->getMockBuilder(tour::class)
226             ->setMethods([
227                     'to_record',
228                     'reload',
229                 ])
230             ->getMock()
231             ;
233         $tour->expects($this->once())
234             ->method('to_record')
235             ;
237         $tour->expects($this->once())
238             ->method('reload')
239             ;
241         $rc = new \ReflectionClass(tour::class);
243         $rcp = $rc->getProperty('dirty');
244         $rcp->setAccessible(true);
245         $rcp->setValue($tour, true);
247         $this->assertSame($tour, $tour->persist());
249         $rcp = $rc->getProperty('id');
250         $rcp->setAccessible(true);
251         $this->assertEquals($id, $rcp->getValue($tour));
252     }
254     /**
255      * Ensure that non-dirty, forced tours are persisted.
256      */
257     public function test_persist_force_new() {
258         global $DB;
260         // Mock the database.
261         $DB = $this->mock_database();
263         $DB->expects($this->never())
264             ->method('update_record')
265             ;
267         $id = rand(1, 100);
268         $DB->expects($this->once())
269             ->method('insert_record')
270             ->willReturn($id)
271             ;
273         // Mock the tour.
274         $tour = $this->getMockBuilder(tour::class)
275             ->setMethods([
276                     'to_record',
277                     'reload',
278                 ])
279             ->getMock()
280             ;
282         $tour->expects($this->once())
283             ->method('to_record')
284             ;
286         $tour->expects($this->once())
287             ->method('reload')
288             ;
290         $this->assertSame($tour, $tour->persist(true));
292         $rc = new \ReflectionClass(tour::class);
293         $rcp = $rc->getProperty('id');
294         $rcp->setAccessible(true);
295         $this->assertEquals($id, $rcp->getValue($tour));
296     }
298     /**
299      * Ensure that dirty tours are persisted.
300      */
301     public function test_persist_dirty_existing() {
302         // Mock the database.
303         $DB = $this->mock_database();
304         $DB->expects($this->once())
305             ->method('update_record')
306             ->willReturn($this->returnSelf())
307             ;
309         $DB->expects($this->never())
310             ->method('insert_record')
311             ;
313         // Mock the tour.
314         $tour = $this->getMockBuilder(tour::class)
315             ->setMethods([
316                     'to_record',
317                     'reload',
318                 ])
319             ->getMock()
320             ;
322         $tour->expects($this->once())
323             ->method('to_record')
324             ;
326         $tour->expects($this->once())
327             ->method('reload')
328             ;
330         $rc = new \ReflectionClass(tour::class);
332         $rcp = $rc->getProperty('id');
333         $rcp->setAccessible(true);
334         $rcp->setValue($tour, 42);
336         $rcp = $rc->getProperty('dirty');
337         $rcp->setAccessible(true);
338         $rcp->setValue($tour, true);
340         $this->assertSame($tour, $tour->persist());
341     }
343     /**
344      * Ensure that non-dirty, forced tours are persisted.
345      */
346     public function test_persist_force() {
347         global $DB;
349         // Mock the database.
350         $DB = $this->mock_database();
352         $DB->expects($this->once())
353             ->method('update_record')
354             ->willReturn($this->returnSelf())
355             ;
357         $DB->expects($this->never())
358             ->method('insert_record')
359             ;
361         // Mock the tour.
362         $tour = $this->getMockBuilder(tour::class)
363             ->setMethods([
364                     'to_record',
365                     'reload',
366                 ])
367             ->getMock()
368             ;
370         $tour->expects($this->once())
371             ->method('to_record')
372             ;
374         $tour->expects($this->once())
375             ->method('reload')
376             ;
378         $rc = new \ReflectionClass(tour::class);
380         $rcp = $rc->getProperty('id');
381         $rcp->setAccessible(true);
382         $rcp->setValue($tour, 42);
384         $rcp = $rc->getProperty('dirty');
385         $rcp->setAccessible(true);
386         $rcp->setValue($tour, true);
388         $this->assertSame($tour, $tour->persist(true));
389     }
391     /**
392      * Test setting config.
393      */
394     public function test_set_config() {
395         $tour = new \tool_usertours\tour();
397         $tour->set_config('key', 'value');
398         $tour->set_config('another', [
399                 'foo' => 'bar',
400             ]);
402         $rc = new \ReflectionClass(tour::class);
403         $rcp = $rc->getProperty('config');
404         $rcp->setAccessible(true);
405         $this->assertEquals((object) [
406                 'key' => 'value',
407                 'another' => [
408                     'foo' => 'bar',
409                 ],
410             ], $rcp->getValue($tour));
411     }
413     /**
414      * Test get_config with no keys provided.
415      */
416     public function test_get_config_no_keys() {
417         $tour = new \tool_usertours\tour();
419         $rc = new \ReflectionClass(tour::class);
420         $rcp = $rc->getProperty('config');
421         $rcp->setAccessible(true);
423         $allvalues = (object) [
424                 'some' => 'value',
425                 'another' => 42,
426                 'key' => [
427                     'somethingelse',
428                 ],
429             ];
431         $rcp->setValue($tour, $allvalues);
433         $this->assertEquals($allvalues, $tour->get_config());
434     }
436     /**
437      * Data provider for get_config.
438      *
439      * @return array
440      */
441     public function get_config_provider() {
442         $allvalues = (object) [
443                 'some' => 'value',
444                 'another' => 42,
445                 'key' => [
446                     'somethingelse',
447                 ],
448             ];
450         return [
451                 'No nitial config' => [
452                         null,
453                         null,
454                         null,
455                         (object) [],
456                     ],
457                 'All values' => [
458                         $allvalues,
459                         null,
460                         null,
461                         $allvalues,
462                     ],
463                 'Valid string value' => [
464                         $allvalues,
465                         'some',
466                         null,
467                         'value',
468                     ],
469                 'Valid array value' => [
470                         $allvalues,
471                         'key',
472                         null,
473                         ['somethingelse'],
474                     ],
475                 'Invalid value' => [
476                         $allvalues,
477                         'notavalue',
478                         null,
479                         null,
480                     ],
481                 'Configuration value' => [
482                         $allvalues,
483                         'placement',
484                         null,
485                         \tool_usertours\configuration::get_default_value('placement'),
486                     ],
487                 'Invalid value with default' => [
488                         $allvalues,
489                         'notavalue',
490                         'somedefault',
491                         'somedefault',
492                     ],
493             ];
494     }
496     /**
497      * Test get_config with valid keys provided.
498      *
499      * @dataProvider get_config_provider
500      * @param   object  $values     The config values
501      * @param   string  $key        The key
502      * @param   mixed   $default    The default value
503      * @param   mixed   $expected   The expected value
504      */
505     public function test_get_config_valid_keys($values, $key, $default, $expected) {
506         $tour = new \tool_usertours\tour();
508         $rc = new \ReflectionClass(tour::class);
509         $rcp = $rc->getProperty('config');
510         $rcp->setAccessible(true);
511         $rcp->setValue($tour, $values);
513         $this->assertEquals($expected, $tour->get_config($key, $default));
514     }
516     /**
517      * Check that a tour which has never been persisted is removed correctly.
518      */
519     public function test_remove_non_persisted() {
520         $tour = $this->getMockBuilder(tour::class)
521             ->setMethods([
522                     'get_steps',
523                 ])
524             ->getMock()
525             ;
527         $tour->expects($this->never())
528             ->method('get_steps')
529             ;
531         // Mock the database.
532         $DB = $this->mock_database();
533         $DB->expects($this->never())
534             ->method('delete_records')
535             ;
537         $this->assertNull($tour->remove());
538     }
540     /**
541      * Check that a tour which has been persisted is removed correctly.
542      */
543     public function test_remove_persisted() {
544         $id = rand(1, 100);
546         $tour = $this->getMockBuilder(tour::class)
547             ->setMethods([
548                     'get_steps',
549                 ])
550             ->getMock()
551             ;
553         $rc = new \ReflectionClass(tour::class);
554         $rcp = $rc->getProperty('id');
555         $rcp->setAccessible(true);
556         $rcp->setValue($tour, $id);
558         $step = $this->getMockBuilder(\tool_usertours\step::class)
559             ->setMethods([
560                     'remove',
561                 ])
562             ->getMock()
563             ;
565         $tour->expects($this->once())
566             ->method('get_steps')
567             ->willReturn([$step])
568             ;
570         // Mock the database.
571         $DB = $this->mock_database();
572         $DB->expects($this->once())
573             ->method('delete_records')
574             ->with($this->equalTo('tool_usertours_tours'), $this->equalTo(['id' => $id]))
575             ->willReturn(null)
576             ;
578         $DB->expects($this->once())
579             ->method('get_records')
580             ->with($this->equalTo('tool_usertours_tours'), $this->equalTo(null))
581             ->willReturn([])
582             ;
584         $this->assertNull($tour->remove());
585     }
587     /**
588      * Teset that sortorder is reset according to sortorder with values from 0.
589      */
590     public function test_reset_step_sortorder() {
591         $tour = new \tool_usertours\tour();
593         $mockdata = [];
594         for ($i = 4; $i >= 0; $i--) {
595             $id = rand($i * 10, ($i * 10) + 9);
596             $mockdata[] = (object) ['id' => $id];
597             $expectations[] = [$this->equalTo('tool_usertours_steps'), $this->equalTo('sortorder'), 4 - $i, ['id' => $id]];
598         }
600         // Mock the database.
601         $DB = $this->mock_database();
602         $DB->expects($this->once())
603             ->method('get_records')
604             ->willReturn($mockdata)
605             ;
607         $setfield = $DB->expects($this->exactly(5))
608             ->method('set_field')
609             ;
610         call_user_func_array([$setfield, 'withConsecutive'], $expectations);
612         $tour->reset_step_sortorder();
613     }
615     /**
616      * Test that a disabled tour should never be shown to users.
617      */
618     public function test_should_show_for_user_disabled() {
619         $tour = new \tool_usertours\tour();
620         $tour->set_enabled(false);
622         $this->assertFalse($tour->should_show_for_user());
623     }
625     /**
626      * Provider for should_show_for_user.
627      *
628      * @return array
629      */
630     public function should_show_for_user_provider() {
631         $time = time();
632         return [
633                 'Not seen by user at all' => [
634                         null,
635                         null,
636                         null,
637                         true,
638                     ],
639                 'Completed by user before majorupdatetime' => [
640                         $time - DAYSECS,
641                         null,
642                         $time,
643                         true,
644                     ],
645                 'Completed by user since majorupdatetime' => [
646                         $time,
647                         null,
648                         $time - DAYSECS,
649                         false,
650                     ],
651                 'Requested by user before current completion' => [
652                         $time,
653                         $time - DAYSECS,
654                         null,
655                         false,
656                     ],
657                 'Requested by user since completion' => [
658                         $time - DAYSECS,
659                         $time,
660                         null,
661                         true,
662                     ],
663             ];
664     }
666     /**
667      * Test that a disabled tour should never be shown to users.
668      *
669      * @dataProvider should_show_for_user_provider
670      * @param   mixed   $completiondate The user's completion date for this tour
671      * @param   mixed   $requesteddate  The user's last requested date for this tour
672      * @param   mixed   $updateddate    The date this tour was last updated
673      * @param   string  $expectation    The expected tour key
674      */
675     public function test_should_show_for_user($completiondate, $requesteddate, $updateddate, $expectation) {
676         // Uses user preferences so we must be in a user context.
677         $this->resetAfterTest();
678         $this->setAdminUser();
680         $tour = $this->getMockBuilder(tour::class)
681             ->setMethods([
682                     'get_id',
683                     'get_config',
684                     'is_enabled',
685                 ])
686             ->getMock()
687             ;
689         $tour->method('is_enabled')
690             ->willReturn(true)
691             ;
693         $id = rand(1, 100);
694         $tour->method('get_id')
695             ->willReturn($id)
696             ;
698         if ($completiondate !== null) {
699             set_user_preference(\tool_usertours\tour::TOUR_LAST_COMPLETED_BY_USER . $id, $completiondate);
700         }
702         if ($requesteddate !== null) {
703             set_user_preference(\tool_usertours\tour::TOUR_REQUESTED_BY_USER . $id, $requesteddate);
704         }
706         if ($updateddate !== null) {
707             $tour->expects($this->once())
708                 ->method('get_config')
709                 ->willReturn($updateddate)
710                 ;
711         }
713         $this->assertEquals($expectation, $tour->should_show_for_user());
714     }
716     /**
717      * Provider for get_tour_key.
718      *
719      * @return array
720      */
721     public function get_tour_key_provider() {
722         $id = rand(1, 100);
723         $time = time();
725         return [
726             'No initial values' => [
727                     $id,
728                     [null, $time],
729                     $this->greaterThanOrEqual($time),
730                     true,
731                     null,
732                     sprintf('tool_usertours_\d_%d_%s', $id, $time),
733                 ],
735             'Initial tour time, no user pref' => [
736                     $id,
737                     [$time],
738                     null,
739                     false,
740                     null,
741                     sprintf('tool_usertours_\d_%d_%s', $id, $time),
742                 ],
743             'Initial tour time, with user reset lower' => [
744                     $id,
745                     [$time],
746                     null,
747                     false,
748                     $time - DAYSECS,
749                     sprintf('tool_usertours_\d_%d_%s', $id, $time),
750                 ],
751             'Initial tour time, with user reset higher' => [
752                     $id,
753                     [$time],
754                     null,
755                     false,
756                     $time + DAYSECS,
757                     sprintf('tool_usertours_\d_%d_%s', $id, $time + DAYSECS),
758                 ],
759         ];
760     }
762     /**
763      * Test that get_tour_key provides the anticipated unique keys.
764      *
765      * @dataProvider get_tour_key_provider
766      * @param   int     $id             The tour ID
767      * @param   array   $getconfig      The mocked values for get_config calls
768      * @param   array   $setconfig      The mocked values for set_config calls
769      * @param   bool    $willpersist    Whether a persist is expected
770      * @param   mixed   $userpref       The value to set for the user preference
771      * @param   string  $expectation    The expected tour key
772      */
773     public function test_get_tour_key($id, $getconfig, $setconfig, $willpersist, $userpref, $expectation) {
774         // Uses user preferences so we must be in a user context.
775         $this->resetAfterTest();
776         $this->setAdminUser();
778         $tour = $this->getMockBuilder(tour::class)
779             ->setMethods([
780                     'get_config',
781                     'set_config',
782                     'get_id',
783                     'persist',
784                 ])
785             ->getMock()
786             ;
788         if ($getconfig) {
789             $tour->expects($this->exactly(count($getconfig)))
790                 ->method('get_config')
791                 ->will(call_user_func_array([$this, 'onConsecutiveCalls'], $getconfig))
792                 ;
793         }
795         if ($setconfig) {
796             $tour->expects($this->once())
797                 ->method('set_config')
798                 ->with($this->equalTo('majorupdatetime'), $setconfig)
799                 ->will($this->returnSelf())
800                 ;
801         } else {
802             $tour->expects($this->never())
803                 ->method('set_config')
804                 ;
805         }
807         if ($willpersist) {
808             $tour->expects($this->once())
809                 ->method('persist')
810                 ;
811         } else {
812             $tour->expects($this->never())
813                 ->method('persist')
814                 ;
815         }
817         $tour->expects($this->any())
818             ->method('get_id')
819             ->willReturn($id)
820             ;
822         if ($userpref !== null) {
823             set_user_preference(\tool_usertours\tour::TOUR_REQUESTED_BY_USER . $id, $userpref);
824         }
826         $this->assertRegExp(
827                 '/' . $expectation . '/',
828                 $tour->get_tour_key()
829             );
830     }
832     /**
833      * Ensure that the request_user_reset function sets an appropriate value for the tour.
834      */
835     public function test_requested_user_reset() {
836         $tour = $this->getMockBuilder(tour::class)
837             ->setMethods([
838                     'get_id',
839                 ])
840             ->getMock()
841             ;
843         $id = rand(1, 100);
844         $time = time();
846         $tour->expects($this->once())
847             ->method('get_id')
848             ->willReturn($id)
849             ;
851         $tour->request_user_reset();
853         $this->assertGreaterThanOrEqual($time, get_user_preferences(\tool_usertours\tour::TOUR_REQUESTED_BY_USER . $id));
854     }
856     /**
857      * Ensure that the request_user_reset function sets an appropriate value for the tour.
858      */
859     public function test_mark_user_completed() {
860         $tour = $this->getMockBuilder(tour::class)
861             ->setMethods([
862                     'get_id',
863                 ])
864             ->getMock()
865             ;
867         $id = rand(1, 100);
868         $time = time();
870         $tour->expects($this->once())
871             ->method('get_id')
872             ->willReturn($id)
873             ;
875         $tour->mark_user_completed();
877         $this->assertGreaterThanOrEqual($time, get_user_preferences(\tool_usertours\tour::TOUR_LAST_COMPLETED_BY_USER . $id));
878     }
880     /**
881      * Provider for the is_first_tour and is_last_tour tests.
882      *
883      * @return array
884      */
885     public function sortorder_first_last_provider() {
886         $topcount = rand(10, 100);
887         return [
888                 'Only tour => first + last' => [
889                         0,
890                         true,
891                         1,
892                         true,
893                     ],
894                 'First tour of many' => [
895                         0,
896                         true,
897                         $topcount,
898                         false,
899                     ],
900                 'Last tour of many' => [
901                         $topcount - 1,
902                         false,
903                         $topcount,
904                         true,
905                     ],
906                 'Middle tour of many' => [
907                         5,
908                         false,
909                         $topcount,
910                         false,
911                     ],
912             ];
913     }
915     /**
916      * Test the is_first_tour() function.
917      *
918      * @dataProvider sortorder_first_last_provider
919      * @param   int     $sortorder      The new sort order
920      * @param   bool    $isfirst        Whether this is the first tour
921      * @param   int     $total          The number of tours
922      * @param   bool    $islast         Whether this is the last tour
923      */
924     public function test_is_first_tour($sortorder, $isfirst, $total, $islast) {
925         $tour = new \tool_usertours\tour();
927         $rc = new \ReflectionClass(tour::class);
928         $rcp = $rc->getProperty('sortorder');
929         $rcp->setAccessible(true);
930         $rcp->setValue($tour, $sortorder);
932         $this->assertEquals($isfirst, $tour->is_first_tour());
933     }
935     /**
936      * Test the is_last_tour() function.
937      *
938      * @dataProvider sortorder_first_last_provider
939      * @param   int     $sortorder      The new sort order
940      * @param   bool    $isfirst        Whether this is the first tour
941      * @param   int     $total          The number of tours
942      * @param   bool    $islast         Whether this is the last tour
943      */
944     public function test_is_last_tour_calculated($sortorder, $isfirst, $total, $islast) {
945         $tour = new \tool_usertours\tour();
947         $rc = new \ReflectionClass(tour::class);
948         $rcp = $rc->getProperty('sortorder');
949         $rcp->setAccessible(true);
950         $rcp->setValue($tour, $sortorder);
952         // The total will be calculated.
953         $DB = $this->mock_database();
954         $DB->expects($this->once())
955             ->method('count_records')
956             ->willReturn($total)
957             ;
958         $this->assertEquals($islast, $tour->is_last_tour());
959     }
961     /**
962      * Test the is_last_tour() function.
963      *
964      * @dataProvider sortorder_first_last_provider
965      * @param   int     $sortorder      The new sort order
966      * @param   bool    $isfirst        Whether this is the first tour
967      * @param   int     $total          The number of tours
968      * @param   bool    $islast         Whether this is the last tour
969      */
970     public function test_is_last_tour_provided($sortorder, $isfirst, $total, $islast) {
971         $tour = new \tool_usertours\tour();
973         $rc = new \ReflectionClass(tour::class);
974         $rcp = $rc->getProperty('sortorder');
975         $rcp->setAccessible(true);
976         $rcp->setValue($tour, $sortorder);
978         // The total is provided.
979         // No DB calls expected.
980         $DB = $this->mock_database();
981         $DB->expects($this->never())
982             ->method('count_records')
983             ->willReturn(0)
984             ;
985         $this->assertEquals($islast, $tour->is_last_tour($total));
986     }
988     /**
989      * Data provider for the get_filter_values tests.
990      *
991      * @return array
992      */
993     public function get_filter_values_provider() {
994         $cheese = ['cheddar', 'boursin', 'mozzarella'];
995         $horses = ['coolie', 'dakota', 'leo', 'twiggy'];
996         return [
997             'No config' => [
998                 [],
999                 'cheese',
1000                 [],
1001             ],
1002             'Some config for another filter' => [
1003                 [
1004                     'horses' => $horses,
1005                 ],
1006                 'cheese',
1007                 [],
1008             ],
1009             'Some config for this filter' => [
1010                 [
1011                     'horses' => $horses,
1012                 ],
1013                 'horses',
1014                 $horses,
1015             ],
1016             'Some config for several filters' => [
1017                 [
1018                     'horses' => $horses,
1019                     'cheese' => $cheese
1020                 ],
1021                 'horses',
1022                 $horses,
1023             ],
1024         ];
1025     }
1027     /**
1028      * Tests for the get_filter_values function.
1029      *
1030      * @dataProvider get_filter_values_provider
1031      * @param   array       $fullconfig     The config value being tested
1032      * @param   string      $filtername     The name of the filter being tested
1033      * @param   array       $expectedvalues The expected result
1034      */
1035     public function test_get_filter_values($fullconfig, $filtername, $expectedvalues) {
1036         $tour = $this->getMockBuilder(tour::class)
1037             ->setMethods(['get_config'])
1038             ->getMock();
1040         $tour->expects($this->once())
1041             ->method('get_config')
1042             ->will($this->returnValue($fullconfig));
1044         $this->assertEquals($expectedvalues, $tour->get_filter_values($filtername));
1045     }
1047     /**
1048      * Data provider for set_filter_values tests.
1049      *
1050      * @return  array
1051      */
1052     public function set_filter_values_provider() {
1053         $cheese = ['cheddar', 'boursin', 'mozzarella'];
1054         $horses = ['coolie', 'dakota', 'leo', 'twiggy'];
1056         return [
1057             'No initial value' => [
1058                 [],
1059                 'cheese',
1060                 $cheese,
1061                 ['cheese' => $cheese],
1062             ],
1063             'Existing filter merged' => [
1064                 ['horses' => $horses],
1065                 'cheese',
1066                 $cheese,
1067                 ['horses' => $horses, 'cheese' => $cheese],
1068             ],
1069             'Existing filter updated' => [
1070                 ['cheese' => $cheese],
1071                 'cheese',
1072                 ['cheddar'],
1073                 ['cheese' => ['cheddar']],
1074             ],
1075             'Existing filter updated with merge' => [
1076                 ['horses' => $horses, 'cheese' => $cheese],
1077                 'cheese',
1078                 ['cheddar'],
1079                 ['horses' => $horses, 'cheese' => ['cheddar']],
1080             ],
1081         ];
1082     }
1084     /**
1085      * Base tests for set_filter_values.
1086      *
1087      * @dataProvider set_filter_values_provider
1088      * @param   array       $currentvalues  The current value
1089      * @param   string      $filtername     The name of the filter to add to
1090      * @param   array       $newvalues      The new values to store
1091      * @param   array       $expectedvalues The combined values
1092      */
1093     public function test_set_filter_values_merge($currentvalues, $filtername, $newvalues, $expectedvalues) {
1094         $tour = $this->getMockBuilder(tour::class)
1095             ->setMethods(['get_config', 'set_config'])
1096             ->getMock();
1098         $tour->expects($this->once())
1099             ->method('get_config')
1100             ->will($this->returnValue($currentvalues));
1102         $tour->expects($this->once())
1103             ->method('set_config')
1104             ->with(
1105                 $this->equalTo('filtervalues'),
1106                 $this->equalTo($expectedvalues)
1107             );
1109         $tour->set_filter_values($filtername, $newvalues);
1110     }