5ef73658095ff0b0e88a3a4ea0a9b6392eab2e75
[moodle.git] / lib / tests / conditionlib_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 conditional activities
19  *
20  * @package    core
21  * @category   phpunit
22  * @copyright  &copy; 2008 The Open University
23  * @author     Sam Marshall
24  * @license    http://www.gnu.org/copyleft/gpl.html GNU Public License
25  */
27 defined('MOODLE_INTERNAL') || die();
29 global $CFG;
30 require_once($CFG->dirroot . '/lib/conditionlib.php');
33 class conditionlib_testcase extends advanced_testcase {
34     private $oldcfg, $olduser;
36     protected function setUp() {
37         global $CFG, $USER, $DB;
38         parent::setUp();
40         $this->resetAfterTest(true);
42         $CFG->enableavailability = 1;
43         $CFG->enablecompletion = 1;
44         $user = $this->getDataGenerator()->create_user();
45         $this->setUser($user);
46     }
48     function test_constructor() {
49         global $DB,$CFG;
50         $cm=new stdClass;
52         // Test records
53         $id=$DB->insert_record('course_modules',(object)array(
54             'showavailability'=>1,'availablefrom'=>17,'availableuntil'=>398,'course'=>64));
56         // no ID
57         try {
58             $test=new condition_info($cm);
59             $this->fail();
60         } catch(coding_exception $e) {
61         }
63         // no other data
64         $cm->id=$id;
65         $test=new condition_info($cm,CONDITION_MISSING_EVERYTHING);
66         $this->assertEquals(
67             (object)array('id'=>$id,'showavailability'=>1,
68                 'availablefrom'=>17,'availableuntil'=>398,'course'=>64,
69                 'conditionsgrade'=>array(), 'conditionscompletion'=>array(),
70                 'visible' => 1, 'conditionsfield' => array()),
71             $test->get_full_course_module());
73         // just the course_modules stuff; check it doesn't request that from db
74         $cm->showavailability=0;
75         $cm->availablefrom=2;
76         $cm->availableuntil=74;
77         $cm->course=38;
78         $cm->visible = 1;
79         $test=new condition_info($cm,CONDITION_MISSING_EXTRATABLE);
80         $this->assertEquals(
81             (object)array('id'=>$id,'showavailability'=>0,
82                 'availablefrom'=>2,'availableuntil'=>74,'course'=>38,
83                 'conditionsgrade' => array(), 'conditionscompletion' => array(),
84                 'visible' => 1, 'conditionsfield' => array()),
85             $test->get_full_course_module());
87         // Now let's add some actual grade/completion conditions
88         $DB->insert_record('course_modules_availability',(object)array(
89             'coursemoduleid'=>$id,
90             'sourcecmid'=>42,
91             'requiredcompletion'=>2
92         ));
93         $DB->insert_record('course_modules_availability',(object)array(
94             'coursemoduleid'=>$id,
95             'sourcecmid'=>666,
96             'requiredcompletion'=>1
97         ));
98         $DB->insert_record('course_modules_availability',(object)array(
99             'coursemoduleid'=>$id,
100             'gradeitemid'=>37,
101             'grademin'=>5.5
102         ));
104         $cm=(object)array('id'=>$id);
105         $test=new condition_info($cm,CONDITION_MISSING_EVERYTHING);
106         $fullcm=$test->get_full_course_module();
107         $this->assertEquals(array(42=>2,666=>1),$fullcm->conditionscompletion);
108         $this->assertEquals(array(37=>(object)array('min'=>5.5,'max'=>null,'name'=>'!missing')),
109             $fullcm->conditionsgrade);
110     }
112     /**
113      * Same as above test but for course_sections instead of course_modules.
114      */
115     public function test_section_constructor() {
116         global $DB, $CFG;
118         // Test records
119         $id = $DB->insert_record('course_sections', (object)array(
120                 'showavailability' => 1, 'availablefrom' => 17,
121                 'availableuntil' => 398, 'course' => 64, 'groupingid' => 13));
123         // No ID
124         $section = new stdClass;
125         try {
126             $test = new condition_info_section($section);
127             $this->fail();
128         } catch (coding_exception $e) {
129             // Do nothing
130         }
132         // No other data
133         $section->id = $id;
134         $test = new condition_info_section($section, CONDITION_MISSING_EVERYTHING);
135         $this->assertEquals(
136                 (object)array('id' => $id, 'showavailability' => 1, 'groupingid' => 13,
137                     'availablefrom' => 17, 'availableuntil' => 398, 'course' => 64,
138                     'conditionsgrade' => array(), 'conditionscompletion' => array(),
139                     'visible' => 1, 'conditionsfield' => array()),
140                 $test->get_full_section());
142         // Just the course_sections stuff; check it doesn't request that from db
143         // (by using fake values and ensuring it still has those)
144         $section->showavailability = 0;
145         $section->availablefrom = 2;
146         $section->availableuntil = 74;
147         $section->course = 38;
148         $section->groupingid = 99;
149         $section->visible = 1;
150         $test = new condition_info_section($section, CONDITION_MISSING_EXTRATABLE);
151         $this->assertEquals(
152                 (object)array('id' => $id, 'showavailability' => 0, 'groupingid' => 99,
153                     'availablefrom' => 2, 'availableuntil' => 74, 'course' => 38,
154                     'conditionsgrade' => array(), 'conditionscompletion' => array(),
155                     'visible' => 1, 'conditionsfield' => array()),
156                 $test->get_full_section());
158         // Now let's add some actual grade/completion conditions
159         $DB->insert_record('course_sections_availability', (object)array(
160                 'coursesectionid' => $id,
161                 'sourcecmid' => 42,
162                 'requiredcompletion' => 2
163         ));
164         $DB->insert_record('course_sections_availability', (object)array(
165                 'coursesectionid' => $id,
166                 'sourcecmid' => 666,
167                 'requiredcompletion' => 1
168         ));
169         $DB->insert_record('course_sections_availability', (object)array(
170                 'coursesectionid' => $id,
171                 'gradeitemid' => 37,
172                 'grademin' => 5.5
173         ));
175         $section = (object)array('id' => $id);
176         $test = new condition_info_section($section, CONDITION_MISSING_EVERYTHING);
177         $fullsection = $test->get_full_section();
178         $this->assertEquals(array(42 => 2, 666 => 1), $fullsection->conditionscompletion);
179         $this->assertEquals(array(37 => (object)array('min' => 5.5, 'max' => null, 'name' => '!missing')),
180                 $fullsection->conditionsgrade);
181     }
183     private function make_course() {
184         $category = $this->getDataGenerator()->create_category(array('name' => 'conditionlibtest'));
185         $course = $this->getDataGenerator()->create_course(
186                 array('fullname' => 'Condition test',
187                     'shortname' => 'CT1',
188                     'category' => $category->id,
189                     'enablecompletion' => 1));
190         context_course::instance($course->id);
191         return $course->id;
192     }
194     private function make_course_module($courseid,$params=array()) {
195         global $DB;
196         static $moduleid=0;
197         if(!$moduleid) {
198             $moduleid=$DB->get_field('modules','id',array('name'=>'resource'));
199         }
201         $rid=$DB->insert_record('resource',(object)array('course'=>$courseid,
202             'name'=>'xxx','alltext'=>'','popup'=>''));
203         $settings=(object)array(
204             'course'=>$courseid,'module'=>$moduleid,'instance'=>$rid);
205         foreach($params as $name=>$value) {
206             $settings->{$name}=$value;
207         }
208         $cmid = $DB->insert_record('course_modules', $settings);
209         rebuild_course_cache($courseid, true);
210         return $cmid;
211     }
213     private function make_section($courseid, $cmids, $sectionnum=0, $params=array()) {
214         global $DB;
215         $record = (object)array(
216             'course' => $courseid,
217             'sequence' => implode(',', $cmids),
218             'section' => $sectionnum);
219         foreach ($params as $name => $value) {
220             $record->{$name} = $value;
221         }
222         $sectionid = $DB->insert_record('course_sections', $record);
223         rebuild_course_cache($courseid, true);
224         return $sectionid;
225     }
227     private function make_grouping($courseid, $name) {
228         global $CFG;
229         require_once($CFG->dirroot . '/group/lib.php');
230         return groups_create_grouping((object)array('courseid' => $courseid,
231                 'name' => $name));
232     }
234     private function make_group($courseid, $name, $groupingid=0) {
235         global $CFG;
236         require_once($CFG->dirroot . '/group/lib.php');
237         $groupid = groups_create_group((object)array('courseid' => $courseid,
238                 'name' => $name));
239         if ($groupingid) {
240             groups_assign_grouping($groupingid, $groupid);
241         }
242         return $groupid;
243     }
245     function test_modinfo() {
246         global $DB;
248         // Let's make a course
249         $courseid=$this->make_course();
251         // Now let's make a couple modules on that course
252         $cmid1=$this->make_course_module($courseid,array(
253             'showavailability'=>1,'availablefrom'=>17,'availableuntil'=>398,
254             'completion'=>COMPLETION_TRACKING_MANUAL));
255         $cmid2=$this->make_course_module($courseid,array(
256             'showavailability'=>0,'availablefrom'=>0,'availableuntil'=>0));
257         $this->make_section($courseid, array($cmid1, $cmid2), 1);
259         // Add a fake grade item
260         $gradeitemid=$DB->insert_record('grade_items',(object)array(
261             'courseid'=>$courseid,'itemname'=>'frog'));
263         // One of the modules has grade and completion conditions, other doesn't
264         $DB->insert_record('course_modules_availability',(object)array(
265             'coursemoduleid'=>$cmid2,
266             'sourcecmid'=>$cmid1,
267             'requiredcompletion'=>1
268         ));
269         $DB->insert_record('course_modules_availability',(object)array(
270             'coursemoduleid'=>$cmid2,
271             'gradeitemid'=>$gradeitemid,
272             'grademin'=>5.5
273         ));
275         // Okay sweet, now get modinfo
276         $modinfo=get_fast_modinfo($courseid);
278         // Test basic data
279         $this->assertEquals(1,$modinfo->cms[$cmid1]->showavailability);
280         $this->assertEquals(17,$modinfo->cms[$cmid1]->availablefrom);
281         $this->assertEquals(398,$modinfo->cms[$cmid1]->availableuntil);
282         $this->assertEquals(0,$modinfo->cms[$cmid2]->showavailability);
283         $this->assertEquals(0,$modinfo->cms[$cmid2]->availablefrom);
284         $this->assertEquals(0,$modinfo->cms[$cmid2]->availableuntil);
286         // Test condition arrays
287         $this->assertEquals(array(),$modinfo->cms[$cmid1]->conditionscompletion);
288         $this->assertEquals(array(),$modinfo->cms[$cmid1]->conditionsgrade);
289         $this->assertEquals(array($cmid1=>1),
290             $modinfo->cms[$cmid2]->conditionscompletion);
291         $this->assertEquals(array($gradeitemid=>(object)array('min'=>5.5,'max'=>null,'name'=>'frog')),
292             $modinfo->cms[$cmid2]->conditionsgrade);
293     }
295     public function test_section_modinfo() {
296         global $DB;
298         // Let's make a course
299         $courseid = $this->make_course();
301         // Now let's make a couple sections on that course, one of which has a cm
302         $cmid = $this->make_course_module($courseid);
303         $sectionid1 = $this->make_section($courseid, array($cmid), 1, array(
304                 'showavailability' => 1, 'availablefrom' => 17,
305                 'availableuntil' => 398, 'groupingid' => 13));
306         $sectionid2 = $this->make_section($courseid, array(), 2);
308         // Add a fake grade item
309         $gradeitemid = $DB->insert_record('grade_items', (object)array(
310                 'courseid' => $courseid, 'itemname' => 'frog'));
312         // One of the sections has grade and completion conditions, other doesn't
313         $DB->insert_record('course_sections_availability', (object)array(
314             'coursesectionid' => $sectionid2,
315             'sourcecmid' => $cmid,
316             'requiredcompletion'=>1
317         ));
318         $DB->insert_record('course_sections_availability', (object)array(
319             'coursesectionid' => $sectionid2,
320             'gradeitemid' => $gradeitemid,
321             'grademin' => 5.5
322         ));
324         rebuild_course_cache($courseid, true);
325         // Okay sweet, now get modinfo
326         $modinfo = get_fast_modinfo($courseid);
328         // Test basic data
329         $section1 = $modinfo->get_section_info(1);
330         $this->assertEquals(1, $section1->showavailability);
331         $this->assertEquals(17, $section1->availablefrom);
332         $this->assertEquals(398, $section1->availableuntil);
333         $this->assertEquals(13, $section1->groupingid);
334         $section2 = $modinfo->get_section_info(2);
335         $this->assertEquals(0, $section2->showavailability);
336         $this->assertEquals(0, $section2->availablefrom);
337         $this->assertEquals(0, $section2->availableuntil);
338         $this->assertEquals(0, $section2->groupingid);
340         // Test condition arrays
341         $this->assertEquals(array(), $section1->conditionscompletion);
342         $this->assertEquals(array(), $section1->conditionsgrade);
343         $this->assertEquals(array($cmid => 1),
344                 $section2->conditionscompletion);
345         $this->assertEquals(array($gradeitemid => (object)array('min' => 5.5, 'max' => null, 'name' => 'frog')),
346                 $section2->conditionsgrade);
347     }
349     function test_add_and_remove() {
350         global $DB;
351         // Make course and module
352         $courseid=$this->make_course();
353         $cmid=$this->make_course_module($courseid,array(
354             'showavailability'=>0,'availablefrom'=>0,'availableuntil'=>0));
355         $this->make_section($courseid, array($cmid), 1);
357         // Check it has no conditions
358         $test1=new condition_info((object)array('id'=>$cmid),
359             CONDITION_MISSING_EVERYTHING);
360         $cm=$test1->get_full_course_module();
361         $this->assertEquals(array(),$cm->conditionscompletion);
362         $this->assertEquals(array(),$cm->conditionsgrade);
364         // Add conditions of each type
365         $test1->add_completion_condition(13,3);
366         $this->assertEquals(array(13=>3),$cm->conditionscompletion);
367         $test1->add_grade_condition(666,0.4,null,true);
368         $this->assertEquals(array(666=>(object)array('min'=>0.4,'max'=>null,'name'=>'!missing')),
369             $cm->conditionsgrade);
371         // Check they were really added in db
372         $test2=new condition_info((object)array('id'=>$cmid),
373             CONDITION_MISSING_EVERYTHING);
374         $cm=$test2->get_full_course_module();
375         $this->assertEquals(array(13=>3),$cm->conditionscompletion);
376         $this->assertEquals(array(666=>(object)array('min'=>0.4,'max'=>null,'name'=>'!missing')),
377             $cm->conditionsgrade);
379         // Wipe conditions
380         $test2->wipe_conditions();
381         $this->assertEquals(array(),$cm->conditionscompletion);
382         $this->assertEquals(array(),$cm->conditionsgrade);
384         // Check they were really wiped
385         $test3=new condition_info((object)array('id'=>$cmid),
386             CONDITION_MISSING_EVERYTHING);
387         $cm=$test3->get_full_course_module();
388         $this->assertEquals(array(),$cm->conditionscompletion);
389         $this->assertEquals(array(),$cm->conditionsgrade);
390     }
392     public function test_section_add_and_remove() {
393         global $DB;
395         // Make course and module
396         $courseid = $this->make_course();
397         $cmid = $this->make_course_module($courseid);
398         $sectionid = $this->make_section($courseid, array($cmid), 1);
400         // Check it has no conditions
401         $test1 = new condition_info_section((object)array('id'=>$sectionid),
402                 CONDITION_MISSING_EVERYTHING);
403         $section = $test1->get_full_section();
404         $this->assertEquals(array(), $section->conditionscompletion);
405         $this->assertEquals(array(), $section->conditionsgrade);
407         // Add conditions of each type
408         $test1->add_completion_condition(13, 3);
409         $this->assertEquals(array(13 => 3), $section->conditionscompletion);
410         $test1->add_grade_condition(666, 0.4, null, true);
411         $this->assertEquals(array(666 => (object)array('min' => 0.4, 'max' => null, 'name' => '!missing')),
412                 $section->conditionsgrade);
414         // Check they were really added in db
415         $test2 = new condition_info_section((object)array('id' => $sectionid),
416                 CONDITION_MISSING_EVERYTHING);
417         $section = $test2->get_full_section();
418         $this->assertEquals(array(13 => 3), $section->conditionscompletion);
419         $this->assertEquals(array(666 => (object)array('min' => 0.4, 'max' => null, 'name' => '!missing')),
420                 $section->conditionsgrade);
422         // Wipe conditions
423         $test2->wipe_conditions();
424         $this->assertEquals(array(), $section->conditionscompletion);
425         $this->assertEquals(array(), $section->conditionsgrade);
427         // Check they were really wiped
428         $test3 = new condition_info_section((object)array('id' => $cmid),
429                 CONDITION_MISSING_EVERYTHING);
430         $section = $test3->get_full_section();
431         $this->assertEquals(array(), $section->conditionscompletion);
432         $this->assertEquals(array(), $section->conditionsgrade);
433     }
435     function test_is_available() {
436         global $DB,$USER;
437         $courseid=$this->make_course();
439         // No conditions
440         $cmid=$this->make_course_module($courseid);
441         $ci=new condition_info((object)array('id'=>$cmid),
442             CONDITION_MISSING_EVERYTHING);
443         $this->assertTrue($ci->is_available($text,false,0));
444         $this->assertEquals('',$text);
446         // Time (from)
447         $time=time()+100;
448         $cmid=$this->make_course_module($courseid,array('availablefrom'=>$time));
449         $ci=new condition_info((object)array('id'=>$cmid),
450             CONDITION_MISSING_EVERYTHING);
451         $this->assertFalse($ci->is_available($text));
452         $this->assertRegExp('/'.preg_quote(userdate($time,get_string('strftimedate','langconfig'))).'/',$text);
454         $time=time()-100;
455         $cmid=$this->make_course_module($courseid,array('availablefrom'=>$time));
456         $ci=new condition_info((object)array('id'=>$cmid),
457             CONDITION_MISSING_EVERYTHING);
458         $this->assertTrue($ci->is_available($text));
459         $this->assertEquals('',$text);
460         $this->assertRegExp('/'.preg_quote(userdate($time,get_string('strftimedate','langconfig'))).'/',$ci->get_full_information());
462         // Time (until)
463         $cmid=$this->make_course_module($courseid,array('availableuntil'=>time()-100));
464         $ci=new condition_info((object)array('id'=>$cmid),
465             CONDITION_MISSING_EVERYTHING);
466         $this->assertFalse($ci->is_available($text));
467         $this->assertEquals('',$text);
469         // Completion
470         $oldid=$cmid;
471         $cmid=$this->make_course_module($courseid);
472         $this->make_section($courseid, array($oldid, $cmid), 1);
473         $oldcm=$DB->get_record('course_modules',array('id'=>$oldid));
474         $oldcm->completion=COMPLETION_TRACKING_MANUAL;
475         $DB->update_record('course_modules',$oldcm);
477         // Need to reset modinfo after changing the options
478         rebuild_course_cache($courseid);
480         $ci=new condition_info((object)array('id'=>$cmid),CONDITION_MISSING_EVERYTHING);
481         $ci->add_completion_condition($oldid,COMPLETION_COMPLETE);
482         condition_info::wipe_session_cache();
484         $this->assertFalse($ci->is_available($text,false));
485         $this->assertEquals(get_string('requires_completion_1','condition','xxx'),$text);
486         completion_info::wipe_session_cache();
487         $completion=new completion_info($DB->get_record('course',array('id'=>$courseid)));
488         $completion->update_state($oldcm,COMPLETION_COMPLETE);
489         completion_info::wipe_session_cache();
490         condition_info::wipe_session_cache();
492         $this->assertTrue($ci->is_available($text));
493         $this->assertFalse($ci->is_available($text,false,$USER->id+1));
494         completion_info::wipe_session_cache();
495         condition_info::wipe_session_cache();
496         $completion=new completion_info($DB->get_record('course',array('id'=>$courseid)));
497         $completion->update_state($oldcm,COMPLETION_INCOMPLETE);
498         $this->assertFalse($ci->is_available($text));
500         $ci->wipe_conditions();
501         $ci->add_completion_condition($oldid,COMPLETION_INCOMPLETE);
502         condition_info::wipe_session_cache();
503         $this->assertTrue($ci->is_available($text));
504         $this->assertTrue($ci->is_available($text,false,$USER->id+1));
506         condition_info::wipe_session_cache();
507         $this->assertTrue($ci->is_available($text,true));
509         // Grade
510         $ci->wipe_conditions();
511         // Add a fake grade item
512         $gradeitemid=$DB->insert_record('grade_items',(object)array(
513             'courseid'=>$courseid,'itemname'=>'frog'));
514         // Add a condition on a value existing...
515         $ci->add_grade_condition($gradeitemid,null,null,true);
516         $this->assertFalse($ci->is_available($text));
517         $this->assertEquals(get_string('requires_grade_any','condition','frog'),$text);
519         // Fake it existing
520         $DB->insert_record('grade_grades',(object)array(
521             'itemid'=>$gradeitemid,'userid'=>$USER->id,'finalgrade'=>3.78));
522         condition_info::wipe_session_cache();
523         $this->assertTrue($ci->is_available($text));
525         condition_info::wipe_session_cache();
526         $this->assertTrue($ci->is_available($text,true));
528         // Now require that user gets more than 3.78001
529         $ci->wipe_conditions();
530         $ci->add_grade_condition($gradeitemid,3.78001,null,true);
531         condition_info::wipe_session_cache();
532         $this->assertFalse($ci->is_available($text));
533         $this->assertEquals(get_string('requires_grade_min','condition','frog'),$text);
535         // ...just on 3.78...
536         $ci->wipe_conditions();
537         $ci->add_grade_condition($gradeitemid,3.78,null,true);
538         condition_info::wipe_session_cache();
539         $this->assertTrue($ci->is_available($text));
541         // ...less than 3.78
542         $ci->wipe_conditions();
543         $ci->add_grade_condition($gradeitemid,null,3.78,true);
544         condition_info::wipe_session_cache();
545         $this->assertFalse($ci->is_available($text));
546         $this->assertEquals(get_string('requires_grade_max','condition','frog'),$text);
548         // ...less than 3.78001
549         $ci->wipe_conditions();
550         $ci->add_grade_condition($gradeitemid,null,3.78001,true);
551         condition_info::wipe_session_cache();
552         $this->assertTrue($ci->is_available($text));
554         // ...in a range that includes it
555         $ci->wipe_conditions();
556         $ci->add_grade_condition($gradeitemid,3,4,true);
557         condition_info::wipe_session_cache();
558         $this->assertTrue($ci->is_available($text));
560         // ...in a range that doesn't include it
561         $ci->wipe_conditions();
562         $ci->add_grade_condition($gradeitemid,4,5,true);
563         condition_info::wipe_session_cache();
564         $this->assertFalse($ci->is_available($text));
565         $this->assertEquals(get_string('requires_grade_range','condition','frog'),$text);
566     }
568     public function test_section_is_available() {
569         global $DB, $USER;
570         $courseid = $this->make_course();
572         // Enrol user (needed for groups)
573         $enrolplugin = enrol_get_plugin('manual');
574         $course = $DB->get_record('course', array('id' => $courseid));
575         $enrolplugin->add_instance($course);
576         $enrolinstances = enrol_get_instances($courseid, false);
577         foreach ($enrolinstances as $enrolinstance) {
578             if ($enrolinstance->enrol === 'manual') {
579                 break;
580             }
581         }
582         $enrolplugin->enrol_user($enrolinstance, $USER->id);
584         // Module for conditions later
585         $cmid = $this->make_course_module($courseid);
587         // No conditions
588         $sectionid = $this->make_section($courseid, array($cmid), 1);
589         $ci = new condition_info_section((object)array('id' => $sectionid),
590                 CONDITION_MISSING_EVERYTHING);
591         $this->assertTrue($ci->is_available($text, false, 0));
592         $this->assertEquals('', $text);
594         // Time (from)
595         $time = time() + 100;
596         $sectionid = $this->make_section($courseid, array(), 2, array('availablefrom' => $time));
597         $ci = new condition_info_section((object)array('id' => $sectionid),
598                 CONDITION_MISSING_EVERYTHING);
599         $this->assertFalse($ci->is_available($text));
600         $timetext = userdate($time, get_string('strftimedate', 'langconfig'));
601         $this->assertRegExp('~' . preg_quote($timetext) . '~', $text);
603         $time=time()-100;
604         $sectionid = $this->make_section($courseid, array(), 3, array('availablefrom' => $time));
605         $ci = new condition_info_section((object)array('id' => $sectionid),
606                 CONDITION_MISSING_EVERYTHING);
607         $this->assertTrue($ci->is_available($text));
608         $this->assertEquals('', $text);
609         $timetext = userdate($time, get_string('strftimedate', 'langconfig'));
610         $this->assertRegExp('~' . preg_quote($timetext) . '~', $ci->get_full_information());
612         // Time (until)
613         $sectionid = $this->make_section($courseid, array(), 4, array('availableuntil' => time() - 100));
614         $ci = new condition_info_section((object)array('id' => $sectionid),
615             CONDITION_MISSING_EVERYTHING);
616         $this->assertFalse($ci->is_available($text));
617         $this->assertEquals('', $text);
619         // Completion: first set up cm
620         $sectionid = $this->make_section($courseid, array(), 5);
621         $cm = $DB->get_record('course_modules', array('id' => $cmid));
622         $cm->completion = COMPLETION_TRACKING_MANUAL;
623         $DB->update_record('course_modules', $cm);
625         // Completion: Reset modinfo after changing the options
626         rebuild_course_cache($courseid);
628         // Completion: Add condition
629         $ci = new condition_info_section((object)array('id' => $sectionid),
630                 CONDITION_MISSING_EVERYTHING);
631         $ci->add_completion_condition($cmid, COMPLETION_COMPLETE);
632         condition_info_section::wipe_session_cache();
634         // Completion: Check
635         $this->assertFalse($ci->is_available($text, false));
636         $this->assertEquals(get_string('requires_completion_1', 'condition', 'xxx'), $text);
637         completion_info::wipe_session_cache();
638         $completion = new completion_info($DB->get_record('course', array('id' => $courseid)));
639         $completion->update_state($cm, COMPLETION_COMPLETE);
640         completion_info::wipe_session_cache();
641         condition_info_section::wipe_session_cache();
642         $this->assertTrue($ci->is_available($text));
643         $this->assertFalse($ci->is_available($text, false, $USER->id + 1));
645         // Completion: Uncheck
646         completion_info::wipe_session_cache();
647         condition_info_section::wipe_session_cache();
648         $completion = new completion_info($DB->get_record('course', array('id' => $courseid)));
649         $completion->update_state($cm, COMPLETION_INCOMPLETE);
650         $this->assertFalse($ci->is_available($text));
652         // Completion: Incomplete condition
653         $ci->wipe_conditions();
654         $ci->add_completion_condition($cmid, COMPLETION_INCOMPLETE);
655         condition_info_section::wipe_session_cache();
656         $this->assertTrue($ci->is_available($text));
657         $this->assertTrue($ci->is_available($text, false, $USER->id + 1));
658         condition_info_section::wipe_session_cache();
659         $this->assertTrue($ci->is_available($text, true));
661         // Grade: Add a fake grade item
662         $gradeitemid = $DB->insert_record('grade_items', (object)array(
663             'courseid' => $courseid, 'itemname' => 'frog'));
665         // Grade: Add a condition on a value existing
666         $ci->wipe_conditions();
667         $ci->add_grade_condition($gradeitemid, null, null, true);
668         $this->assertFalse($ci->is_available($text));
669         $this->assertEquals(get_string('requires_grade_any', 'condition', 'frog'), $text);
671         // Grade: Fake it existing
672         $DB->insert_record('grade_grades', (object)array(
673             'itemid' => $gradeitemid, 'userid' => $USER->id, 'finalgrade' => 3.78));
674         condition_info_section::wipe_session_cache();
675         $this->assertTrue($ci->is_available($text));
676         condition_info_section::wipe_session_cache();
677         $this->assertTrue($ci->is_available($text, true));
679         // Grade: Now require that user gets more than 3.78001
680         $ci->wipe_conditions();
681         $ci->add_grade_condition($gradeitemid, 3.78001, null, true);
682         condition_info_section::wipe_session_cache();
683         $this->assertFalse($ci->is_available($text));
684         $this->assertEquals(get_string('requires_grade_min', 'condition', 'frog'), $text);
686         // Grade: ...just on 3.78...
687         $ci->wipe_conditions();
688         $ci->add_grade_condition($gradeitemid, 3.78, null, true);
689         condition_info_section::wipe_session_cache();
690         $this->assertTrue($ci->is_available($text));
692         // Grade: ...less than 3.78
693         $ci->wipe_conditions();
694         $ci->add_grade_condition($gradeitemid, null, 3.78, true);
695         condition_info_section::wipe_session_cache();
696         $this->assertFalse($ci->is_available($text));
697         $this->assertEquals(get_string('requires_grade_max', 'condition', 'frog'), $text);
699         // Grade: ...less than 3.78001
700         $ci->wipe_conditions();
701         $ci->add_grade_condition($gradeitemid, null, 3.78001, true);
702         condition_info_section::wipe_session_cache();
703         $this->assertTrue($ci->is_available($text));
705         // Grade: ...in a range that includes it
706         $ci->wipe_conditions();
707         $ci->add_grade_condition($gradeitemid, 3, 4, true);
708         condition_info_section::wipe_session_cache();
709         $this->assertTrue($ci->is_available($text));
711         // Grade: ...in a range that doesn't include it
712         $ci->wipe_conditions();
713         $ci->add_grade_condition($gradeitemid, 4, 5, true);
714         condition_info_section::wipe_session_cache();
715         $this->assertFalse($ci->is_available($text));
716         $this->assertEquals(get_string('requires_grade_range', 'condition', 'frog'), $text);
718         // Grouping: Not member
719         $groupingid = $this->make_grouping($courseid, 'Grouping');
720         $groupid = $this->make_group($courseid, 'Group', $groupingid);
721         $sectionid = $this->make_section($courseid, array(), 6, array('groupingid' => $groupingid));
722         $ci = new condition_info_section((object)array('id' => $sectionid),
723                 CONDITION_MISSING_EVERYTHING);
724         $this->assertFalse($ci->is_available($text));
725         $this->assertEquals(trim(get_string('groupingnoaccess', 'condition')), $text);
727         // Grouping: Member
728         $this->assertTrue(groups_add_member($groupid, $USER->id));
730         condition_info_section::init_global_cache();
731         $this->assertTrue($ci->is_available($text));
732         $this->assertEquals('', $text);
733         $this->assertTrue($ci->is_available($text, true));
735         // Grouping: Somebody else
736         $this->assertFalse($ci->is_available($text, false, $USER->id + 1));
737         $this->assertFalse($ci->is_available($text, true, $USER->id + 1));
738     }
740     /**
741      * Tests user fields to ensure that the list of provided fields includes only
742      * fields which the equivalent function can be used to obtain the value of.
743      */
744     public function test_condition_user_fields() {
745         global $CFG, $DB, $USER;
747         // Set up basic data.
748         $courseid = $this->make_course();
749         $cmid = $this->make_course_module($courseid);
750         $ci = new condition_info_testwrapper(
751                 (object)array('id' => $cmid), CONDITION_MISSING_EVERYTHING);
753         // Add a custom user profile field. Unfortunately there is no back-end
754         // API for adding profile fields without having an actual form and doing
755         // redirects and stuff! These are the default text field parameters.
756         require_once($CFG->dirroot . '/user/profile/lib.php');
757         $field = (object)array(
758                 'shortname' => 'myfield', 'name' => 'My field', 'required' => 0,
759                 'locked' => 0, 'forceunique' => 0, 'signup' => 0,
760                 'visible' => PROFILE_VISIBLE_ALL,
761                 'datatype' => 'text', 'description' => 'A field of mine',
762                 'descriptionformat' => FORMAT_HTML, 'defaultdata' => '',
763                 'defaultdataformat' => FORMAT_HTML, 'param1' => 30, 'param2' => 2048,
764                 'param3' => 0, 'param4' => '', 'param5' => '');
765         $customfieldid = $DB->insert_record('user_info_field', $field);
767         // Get list of condition user fields.
768         $fields = condition_info::get_condition_user_fields();
770         // Check custom field is included.
771         $this->assertEquals('My field', $fields[$customfieldid]);
773         // For all other fields, check it actually works to get data from them.
774         foreach ($fields as $fieldid => $name) {
775             // Not checking the result, just that it's possible to get it
776             // without error.
777             $ci->get_cached_user_profile_field($USER->id, $fieldid);
778         }
779     }
783 /**
784  * Test wrapper used only to make protected functions public so they can be
785  * tested.
786  */
787 class condition_info_testwrapper extends condition_info {
788     public function get_cached_user_profile_field($userid, $fieldid) {
789         return parent::get_cached_user_profile_field($userid, $fieldid);
790     }