Unit tests MDL-24975 Completion unit tests fix
[moodle.git] / lib / simpletest / testcompletionlib.php
1 <?php
2 if (!defined('MOODLE_INTERNAL')) {
3     die('Direct access to this script is forbidden.');
4 }
5 require_once($CFG->libdir.'/completionlib.php');
7 global $DB;
8 Mock::generate(get_class($DB), 'mock_database');
10 Mock::generatePartial('completion_info','completion_cutdown',
11     array('delete_all_state','get_tracked_users','update_state',
12         'internal_get_grade_state','is_enabled','get_data','internal_get_state','internal_set_data'));
13 Mock::generatePartial('completion_info','completion_cutdown2',
14     array('is_enabled','get_data','internal_get_state','internal_set_data'));
15 Mock::generatePartial('completion_info','completion_cutdown3',
16     array('internal_get_grade_state'));
18 class fake_recordset implements Iterator {
19     var $closed;
20     var $values,$index;
22     function fake_recordset($values) {
23         $this->values=$values;
24         $this->index=0;
25     }
27     function current() {
28         return $this->values[$this->index];
29     }
31     function key() {
32         return $this->values[$this->index];
33     }
35     function next() {
36         $this->index++;
37     }
39     function rewind() {
40         $this->index=0;
41     }
43     function valid() {
44         return count($this->values) > $this->index;
45     }
47     function close() {
48         $closed=true;
49     }
51     function was_closed() {
52         return $closed;
53     }
54 }
56 /**
57  * Expectation that checks an object for given values (normal equality test)
58  * plus a 'timemodified' field that is current (last second or two).
59  */
60 class TimeModifiedExpectation extends SimpleExpectation {
61     private $otherfields;
63     /**
64      * @param array $otherfields Array key=>value of required object fields
65      */
66     function TimeModifiedExpectation($otherfields) {
67         $this->otherfields=$otherfields;
68     }
70     function test($thing) {
71         $thingfields=(array)$thing;
72         foreach($this->otherfields as $key=>$value) {
73             if(!array_key_exists($key,$thingfields)) {
74                 return false;
75             }
76             if($thingfields[$key]!=$value) {
77                 return false;
78             }
79         }
81         $timedifference=time()-$thing->timemodified;
82         return ($timedifference < 2 && $timedifference>=0);
83     }
85     function testMessage($thing) {
86         return "Object does not match fields/time requirement";
87     }
88 }
90 class completionlib_test extends UnitTestCaseUsingDatabase {
92     public static $includecoverage = array('lib/completionlib.php');
93     var $realdb,$realcfg,$realsession,$realuser;
95     function setUp() {
96         global $DB,$CFG,$SESSION,$USER;
97         $this->realdb=$DB;
98         $this->realcfg=$CFG;
99         $this->realsession=$SESSION;
100         $this->prevuser=$USER;
101         $DB=new mock_database();
102         $CFG=clone($this->realcfg);
103         $CFG->prefix='test_';
104         $CFG->enablecompletion=COMPLETION_ENABLED;
105         $SESSION=new stdClass();
106         $USER=(object)array('id'=>314159);
107     }
109     function tearDown() {
110         global $DB,$CFG,$SESSION,$USER;
111         $DB=$this->realdb;
112         $CFG=$this->realcfg;
113         $SESSION=$this->realsession;
114         $USER=$this->prevuser;
115     }
117     function test_is_enabled() {
118         global $CFG;
120         // Config alone
121         $CFG->enablecompletion=COMPLETION_DISABLED;
122         $this->assertEqual(COMPLETION_DISABLED,completion_info::is_enabled_for_site());
123         $CFG->enablecompletion=COMPLETION_ENABLED;
124         $this->assertEqual(COMPLETION_ENABLED,completion_info::is_enabled_for_site());
126         // Course
127         //$course=new stdClass;
128         $course=(object)array('id'=>13);
129         $c=new completion_info($course);
130         $course->enablecompletion=COMPLETION_DISABLED;
131         $this->assertEqual(COMPLETION_DISABLED,$c->is_enabled());
132         $course->enablecompletion=COMPLETION_ENABLED;
133         $this->assertEqual(COMPLETION_ENABLED,$c->is_enabled());
134         $CFG->enablecompletion=COMPLETION_DISABLED;
135         $this->assertEqual(COMPLETION_DISABLED,$c->is_enabled());
137         // Course and CM
138         $cm=new stdClass;
139         $cm->completion=COMPLETION_TRACKING_MANUAL;
140         $this->assertEqual(COMPLETION_DISABLED,$c->is_enabled($cm));
141         $CFG->enablecompletion=COMPLETION_ENABLED;
142         $course->enablecompletion=COMPLETION_DISABLED;
143         $this->assertEqual(COMPLETION_DISABLED,$c->is_enabled($cm));
144         $course->enablecompletion=COMPLETION_ENABLED;
145         $this->assertEqual(COMPLETION_TRACKING_MANUAL,$c->is_enabled($cm));
146         $cm->completion=COMPLETION_TRACKING_NONE;
147         $this->assertEqual(COMPLETION_TRACKING_NONE,$c->is_enabled($cm));
148         $cm->completion=COMPLETION_TRACKING_AUTOMATIC;
149         $this->assertEqual(COMPLETION_TRACKING_AUTOMATIC,$c->is_enabled($cm));
150     }
152     function test_update_state() {
153         $c=new completion_cutdown2();
154         $c->__construct((object)array('id'=>42));
155         $cm=(object)array('id'=>13,'course'=>42);
157         // Not enabled, should do nothing
158         $c->expectAt(0,'is_enabled',array($cm));
159         $c->setReturnValueAt(0,'is_enabled',false);
160         $c->update_state($cm);
162         // Enabled, but current state is same as possible result, do nothing
163         $current=(object)array('completionstate'=>COMPLETION_COMPLETE);
164         $c->expectAt(1,'is_enabled',array($cm));
165         $c->setReturnValueAt(1,'is_enabled',true);
167         $c->expectAt(0,'get_data',array($cm,false,0));
168         $c->setReturnValueAt(0,'get_data',$current);
169         $c->update_state($cm,COMPLETION_COMPLETE);
171         // Enabled, but current state is a specific one and new state is just
172         // omplete, so do nothing
173         $current->completionstate=COMPLETION_COMPLETE_PASS;
174         $c->expectAt(2,'is_enabled',array($cm));
175         $c->setReturnValueAt(2,'is_enabled',true);
176         $c->expectAt(1,'get_data',array($cm,false,0));
177         $c->setReturnValueAt(1,'get_data',$current);
178         $c->update_state($cm,COMPLETION_COMPLETE);
180         // Manual, change state (no change)
181         $cm->completion=COMPLETION_TRACKING_MANUAL;
182         $current->completionstate=COMPLETION_COMPLETE;
183         $c->expectAt(3,'is_enabled',array($cm));
184         $c->setReturnValueAt(3,'is_enabled',true);
185         $c->expectAt(2,'get_data',array($cm,false,0));
186         $c->setReturnValueAt(2,'get_data',$current);
187         $c->update_state($cm,COMPLETION_COMPLETE);
189         // Manual, change state (change)
190         $c->expectAt(4,'is_enabled',array($cm));
191         $c->setReturnValueAt(4,'is_enabled',true);
192         $c->expectAt(3,'get_data',array($cm,false,0));
193         $c->setReturnValueAt(3,'get_data',$current);
194         $c->expectAt(0,'internal_set_data',array($cm,
195             new TimeModifiedExpectation(array('completionstate'=>COMPLETION_INCOMPLETE))));
196         $c->update_state($cm,COMPLETION_INCOMPLETE);
198         // Auto, change state
199         $cm->completion=COMPLETION_TRACKING_AUTOMATIC;
200         $c->expectAt(5,'is_enabled',array($cm));
201         $c->setReturnValueAt(5,'is_enabled',true);
202         $c->expectAt(4,'get_data',array($cm,false,0));
203         $c->setReturnValueAt(4,'get_data',$current);
204         $c->expectAt(0,'internal_get_state',array($cm,0,$current));
205         $c->setReturnValueAt(0,'internal_get_state',COMPLETION_COMPLETE_PASS);
206         $c->expectAt(1,'internal_set_data',array($cm,
207             new TimeModifiedExpectation(array('completionstate'=>COMPLETION_COMPLETE_PASS))));
208         $c->update_state($cm,COMPLETION_COMPLETE_PASS);
210         $c->tally();
211     }
213     function test_internal_get_state() {
214         global $DB;
216         $c=new completion_cutdown3();
217         $c->__construct((object)array('id'=>42));
218         $cm=(object)array('id'=>13,'course'=>42,'completiongradeitemnumber'=>null);
220         // If view is required, but they haven't viewed it yet
221         $cm->completionview=COMPLETION_VIEW_REQUIRED;
222         $current=(object)array('viewed'=>COMPLETION_NOT_VIEWED);
223         $this->assertEqual(COMPLETION_INCOMPLETE,$c->internal_get_state($cm,123,$current));
225         // OK set view not required
226         $cm->completionview=COMPLETION_VIEW_NOT_REQUIRED;
228         // Test not getting module name
229         $cm->modname='label';
230         $this->assertEqual(COMPLETION_COMPLETE,$c->internal_get_state($cm,123,$current));
232         // Test getting module name
233         $cm->module=13;
234         unset($cm->modname);
235         $DB->expectOnce('get_field',array('modules','name',array('id'=>13)));
236         $DB->setReturnValue('get_field','label');
237         $this->assertEqual(COMPLETION_COMPLETE,$c->internal_get_state($cm,123,$current));
239         // Note: This function is not fully tested (including kind of the main
240         // part) because:
241         // * the grade_item/grade_grade calls are static and can't be mocked
242         // * the plugin_supports call is static and can't be mocked
244         $DB->tally();
245         $c->tally();
246     }
248     function test_set_module_viewed() {
249         $c=new completion_cutdown();
250         $c->__construct((object)array('id'=>42));
251         $cm=(object)array('id'=>13,'course'=>42);
253         // Not tracking completion, should do nothing
254         $cm->completionview=COMPLETION_VIEW_NOT_REQUIRED;
255         $c->set_module_viewed($cm);
257         // Tracking completion but completion is disabled, should do nothing
258         $cm->completionview=COMPLETION_VIEW_REQUIRED;
259         $c->expectAt(0,'is_enabled',array($cm));
260         $c->setReturnValueAt(0,'is_enabled',false);
261         $c->set_module_viewed($cm);
263         // Now it's enabled, we expect it to get data. If data already has
264         // viewed, still do nothing
265         $c->expectAt(1,'is_enabled',array($cm));
266         $c->setReturnValueAt(1,'is_enabled',true);
267         $c->expectAt(0,'get_data',array($cm,0));
268         $hasviewed=(object)array('viewed'=>COMPLETION_VIEWED);
269         $c->setReturnValueAt(0,'get_data',$hasviewed);
270         $c->set_module_viewed($cm);
272         // OK finally one that hasn't been viewed, now it should set it viewed
273         // and update state
274         $c->expectAt(2,'is_enabled',array($cm));
275         $c->setReturnValueAt(2,'is_enabled',true);
276         $notviewed=(object)array('viewed'=>COMPLETION_NOT_VIEWED);
277         $c->expectAt(1,'get_data',array($cm,1337));
278         $c->setReturnValueAt(1,'get_data',$notviewed);
279         $c->expectOnce('internal_set_data',array($cm,$hasviewed));
280         $c->expectOnce('update_state',array($cm,COMPLETION_COMPLETE,1337));
281         $c->set_module_viewed($cm,1337);
283         $c->tally();
284     }
286     function test_count_user_data() {
287         global $DB;
288         $course=(object)array('id'=>13);
289         $cm=(object)array('id'=>42);
290         $DB->setReturnValue('get_field_sql',666);
291         $DB->expectOnce('get_field_sql',array(new IgnoreWhitespaceExpectation("SELECT
292     COUNT(1)
293 FROM
294     {course_modules_completion}
295 WHERE
296     coursemoduleid=? AND completionstate<>0"),array(42)));
297         $c=new completion_info($course);
298         $this->assertEqual(666,$c->count_user_data($cm));
300         $DB->tally();
301     }
303     function test_delete_all_state() {
304         global $DB,$SESSION;
305         $course=(object)array('id'=>13);
306         $cm=(object)array('id'=>42,'course'=>13);
307         $c=new completion_info($course);
308         // Check it works ok without data in session
309         $DB->expectAt(0,'delete_records',
310             array('course_modules_completion',array('coursemoduleid'=>42)));
311         $c->delete_all_state($cm);
313         // Build up a session to check it deletes the right bits from it
314         // (and not other bits)
315         $SESSION->completioncache=array();
316         $SESSION->completioncache[13]=array();
317         $SESSION->completioncache[13][42]='foo';
318         $SESSION->completioncache[13][43]='foo';
319         $SESSION->completioncache[14]=array();
320         $SESSION->completioncache[14][42]='foo';
321         $DB->expectAt(1,'delete_records',
322             array('course_modules_completion',array('coursemoduleid'=>42)));
323         $c->delete_all_state($cm);
324         $this->assertEqual(array(13=>array(43=>'foo'),14=>array(42=>'foo')),
325             $SESSION->completioncache);
327         $DB->tally();
328     }
330     function test_reset_all_state() {
331         global $DB;
332         $c=new completion_cutdown();
333         $c->__construct((object)array('id'=>42));
335         $cm=(object)array('id'=>13,'course'=>42,
336             'completion'=>COMPLETION_TRACKING_AUTOMATIC);
338         $DB->setReturnValue('get_recordset',new fake_recordset(array(
339             (object)array('id'=>1,'userid'=>100),
340             (object)array('id'=>2,'userid'=>101),
341         )));
342         $DB->expectOnce('get_recordset',array('course_modules_completion',
343             array('coursemoduleid'=>13),'','userid'));
344         $c->expectOnce('delete_all_state',array($cm));
345         $c->expectOnce('get_tracked_users',array());
346         $c->setReturnValue('get_tracked_users',array(
347             (object)array('id'=>100,'firstname'=>'Woot','lastname'=>'Plugh'),
348             (object)array('id'=>201,'firstname'=>'Vroom','lastname'=>'Xyzzy'),
349             ));
351         $c->expectAt(0,'update_state',array($cm,COMPLETION_UNKNOWN,100));
352         $c->expectAt(1,'update_state',array($cm,COMPLETION_UNKNOWN,101));
353         $c->expectAt(2,'update_state',array($cm,COMPLETION_UNKNOWN,201));
355         $c->reset_all_state($cm);
357         $DB->tally();
358         $c->tally();
359     }
361     function test_get_data() {
362         global $DB,$SESSION;
364         $c=new completion_info((object)array('id'=>42));
365         $cm=(object)array('id'=>13,'course'=>42);
367         // 1. Not current user, record exists
368         $sillyrecord=(object)array('frog'=>'kermit');
369         $DB->expectAt(0,'get_record',array('course_modules_completion',
370             array('coursemoduleid'=>13,'userid'=>123)));
371         $DB->setReturnValueAt(0,'get_record',$sillyrecord);
372         $result=$c->get_data($cm,false,123);
373         $this->assertEqual($sillyrecord,$result);
374         $this->assertTrue(empty($SESSION->completioncache));
376         // 2. Not current user, default record, wholecourse (ignored)
377         $DB->expectAt(1,'get_record',array('course_modules_completion',
378             array('coursemoduleid'=>13,'userid'=>123)));
379         $DB->setReturnValueAt(1,'get_record',false);
380         $result=$c->get_data($cm,true,123);
381         $this->assertEqual((object)array(
382             'id'=>'0','coursemoduleid'=>13,'userid'=>123,'completionstate'=>0,
383             'viewed'=>0,'timemodified'=>0),$result);
384         $this->assertTrue(empty($SESSION->completioncache));
386         // 3. Current user, single record, not from cache
387         $DB->expectAt(2,'get_record',array('course_modules_completion',
388             array('coursemoduleid'=>13,'userid'=>314159)));
389         $DB->setReturnValueAt(2,'get_record',$sillyrecord);
390         $result=$c->get_data($cm);
391         $this->assertEqual($sillyrecord,$result);
392         $this->assertEqual($sillyrecord,$SESSION->completioncache[42][13]);
393         // When checking time(), allow for second overlaps
394         $this->assertTrue(time()-$SESSION->completioncache[42]['updated']<2);
396         // 4. Current user, 'whole course', but from cache
397         $result=$c->get_data($cm,true);
398         $this->assertEqual($sillyrecord,$result);
400         // 5. Current user, single record, cache expired
401         $SESSION->completioncache[42]['updated']=37; // Quite a long time ago
402         $now=time();
403         $SESSION->completioncache[17]['updated']=$now;
404         $SESSION->completioncache[39]['updated']=72; // Also a long time ago
405         $DB->expectAt(3,'get_record',array('course_modules_completion',
406             array('coursemoduleid'=>13,'userid'=>314159)));
407         $DB->setReturnValueAt(3,'get_record',$sillyrecord);
408         $result=$c->get_data($cm,false);
409         $this->assertEqual($sillyrecord,$result);
410         // Check that updated value is right, then fudge it to make next compare
411         // work
412         $this->assertTrue(time()-$SESSION->completioncache[42]['updated']<2);
413         $SESSION->completioncache[42]['updated']=$now;
414         // Check things got expired from cache
415         $this->assertEqual(array(42=>array(13=>$sillyrecord,'updated'=>$now),
416             17=>array('updated'=>$now)),$SESSION->completioncache);
418         // 6. Current user, 'whole course' and record not in cache
419         unset($SESSION->completioncache);
421         // Scenario: Completion data exists for one CMid
422         $basicrecord=(object)array('coursemoduleid'=>13);
423         $DB->setReturnValueAt(0,'get_records_sql',array(
424             1=>$basicrecord
425         ));
426         $DB->expectAt(0,'get_records_sql',array(new IgnoreWhitespaceExpectation("
427 SELECT
428     cmc.*
429 FROM
430     {course_modules} cm
431     INNER JOIN {course_modules_completion} cmc ON cmc.coursemoduleid=cm.id
432 WHERE
433     cm.course=? AND cmc.userid=?"),array(42,314159)));
435         // There are two CMids in total, the one we had data for and another one
436         $modinfo->cms=array((object)array('id'=>13),(object)array('id'=>14));
437         $result=$c->get_data($cm,true,0,$modinfo);
439         // Check result
440         $this->assertEqual($basicrecord,$result);
442         // Check the cache contents
443         $this->assertTrue(time()-$SESSION->completioncache[42]['updated']<2);
444         $SESSION->completioncache[42]['updated']=$now;
445         $this->assertEqual(array(42=>array(13=>$basicrecord,14=>(object)array(
446             'id'=>'0','coursemoduleid'=>14,'userid'=>314159,'completionstate'=>0,
447             'viewed'=>0,'timemodified'=>0),'updated'=>$now)),$SESSION->completioncache);
449         $DB->tally();
450     }
452     function test_internal_set_data() {
453         global $DB,$SESSION;
455         $cm=(object)array('course'=>42,'id'=>13);
456         $c=new completion_info((object)array('id'=>42));
458         // 1) Test with new data
459         $data=(object)array('id'=>0,'userid'=>314159);
460         $DB->setReturnValueAt(0,'insert_record',4);
461         $DB->expectAt(0,'insert_record',array('course_modules_completion',$data));
462         $c->internal_set_data($cm,$data);
463         $this->assertEqual(4,$data->id);
464         $this->assertEqual(array(42=>array(13=>$data)),$SESSION->completioncache);
466         // 2) Test with existing data and for different user (not cached)
467         unset($SESSION->completioncache);
468         $d2=(object)array('id'=>7,'userid'=>17);
469         $DB->expectAt(0,'update_record',array('course_modules_completion',$d2));
470         $c->internal_set_data($cm,$d2);
471         $this->assertFalse(isset($SESSION->completioncache));
473         $DB->tally();
474     }
476     function test_get_activities() {
477         global $DB;
479         $c=new completion_info((object)array('id'=>42));
481         // Try with no activities
482         $DB->expectAt(0,'get_records_select',array('course_modules',
483               'course=42 AND completion<>'.COMPLETION_TRACKING_NONE));
484         $DB->setReturnValueAt(0,'get_records_select',array());
485         $result=$c->get_activities();
486         $this->assertEqual(array(),$result);
488         // Try with an activity (need to fake up modinfo for it as well)
489         $DB->expectAt(1,'get_records_select',array('course_modules',
490               'course=42 AND completion<>'.COMPLETION_TRACKING_NONE));
491         $DB->setReturnValueAt(1,'get_records_select',array(
492             13=>(object)array('id'=>13)
493         ));
494         $modinfo=new stdClass;
495         $modinfo->sections=array(array(1,2,3),array(12,13,14));
496         $modinfo->cms[13]=(object)array('modname'=>'frog','name'=>'kermit');
497         $result=$c->get_activities($modinfo);
498         $this->assertEqual(array(13=>(object)array('id'=>13,'modname'=>'frog','name'=>'kermit')),$result);
500         $DB->tally();
501     }
503     // get_tracked_users() cannot easily be tested because it uses
504     // get_role_users, so skipping that
506     function test_get_progress_all() {
507         global $DB;
509         $c=new completion_cutdown();
510         $c->__construct((object)array('id'=>42));
512         // 1) Basic usage
513         $c->expectAt(0,'get_tracked_users',array(false, array(), 0, '', '', ''));
514         $c->setReturnValueAt(0,'get_tracked_users',array(
515             (object)array('id'=>100,'firstname'=>'Woot','lastname'=>'Plugh'),
516             (object)array('id'=>201,'firstname'=>'Vroom','lastname'=>'Xyzzy'),
517             ));
518         $DB->expectAt(0,'get_in_or_equal',array(array(100,201)));
519         $DB->setReturnValueAt(0,'get_in_or_equal',array(' IN (100,201)',array()));
520         $DB->expectAt(0,'get_recordset_sql',array(new IgnoreWhitespaceExpectation("
521 SELECT
522     cmc.*
523 FROM
524     {course_modules} cm
525     INNER JOIN {course_modules_completion} cmc ON cm.id=cmc.coursemoduleid
526 WHERE
527     cm.course=? AND cmc.userid IN (100,201)"),array(42)));
528         $progress1=(object)array('userid'=>100,'coursemoduleid'=>13);
529         $progress2=(object)array('userid'=>201,'coursemoduleid'=>14);
530         $DB->setReturnValueAt(0,'get_recordset_sql',new fake_recordset(array(
531             $progress1,$progress2
532         )));
533         $this->assertEqual(array(
535                 100 => (object)array('id'=>100,'firstname'=>'Woot','lastname'=>'Plugh',
536                     'progress'=>array(13=>$progress1)),
537                 201 => (object)array('id'=>201,'firstname'=>'Vroom','lastname'=>'Xyzzy',
538                     'progress'=>array(14=>$progress2)),
539             ),$c->get_progress_all(false));
541         // 2) With more than 1,000 results
542         $c->expectAt(1,'get_tracked_users',array(true, 3, 0, '', '', ''));
544         $tracked=array();
545         $ids=array();
546         $progress=array();
547         for($i=100;$i<2000;$i++) {
548             $tracked[]=(object)array('id'=>$i,'firstname'=>'frog','lastname'=>$i);
549             $ids[]=$i;
550             $progress[]=(object)array('userid'=>$i,'coursemoduleid'=>13);
551             $progress[]=(object)array('userid'=>$i,'coursemoduleid'=>14);
552         }
553         $c->setReturnValueAt(1,'get_tracked_users',$tracked);
555         $DB->expectAt(1,'get_in_or_equal',array(array_slice($ids,0,1000)));
556         $DB->setReturnValueAt(1,'get_in_or_equal',array(' IN whatever',array()));
557         $DB->expectAt(1,'get_recordset_sql',array(new IgnoreWhitespaceExpectation("
558 SELECT
559     cmc.*
560 FROM
561     {course_modules} cm
562     INNER JOIN {course_modules_completion} cmc ON cm.id=cmc.coursemoduleid
563 WHERE
564     cm.course=? AND cmc.userid IN whatever"),array(42)));
565         $DB->setReturnValueAt(1,'get_recordset_sql',new fake_recordset(array_slice($progress,0,1000)));
566         $DB->expectAt(2,'get_in_or_equal',array(array_slice($ids,1000)));
567         $DB->setReturnValueAt(2,'get_in_or_equal',array(' IN whatever2',array()));
568         $DB->expectAt(2,'get_recordset_sql',array(new IgnoreWhitespaceExpectation("
569 SELECT
570     cmc.*
571 FROM
572     {course_modules} cm
573     INNER JOIN {course_modules_completion} cmc ON cm.id=cmc.coursemoduleid
574 WHERE
575     cm.course=? AND cmc.userid IN whatever2"),array(42)));
576         $DB->setReturnValueAt(2,'get_recordset_sql',new fake_recordset(array_slice($progress,1000)));
577         $result=$c->get_progress_all(true,3);
578         $resultok=true;
579         $resultok = $resultok && ($ids==array_keys($result));
581         foreach($result as $userid => $data) {
582             $resultok = $resultok && $data->firstname=='frog';
583             $resultok = $resultok && $data->lastname==$userid;
584             $resultok = $resultok && $data->id==$userid;
585             $cms=$data->progress;
586             $resultok= $resultok && (array(13,14)==array_keys($cms));
587             $resultok= $resultok && ((object)array('userid'=>$userid,'coursemoduleid'=>13)==$cms[13]);
588             $resultok= $resultok && ((object)array('userid'=>$userid,'coursemoduleid'=>14)==$cms[14]);
589         }
590         $this->assertTrue($resultok);
592         $DB->tally();
593         $c->tally();
594     }
596     function test_inform_grade_changed() {
597         $c=new completion_cutdown();
598         $c->__construct((object)array('id'=>42));
600         $cm=(object)array('course'=>42,'id'=>13,'completion'=>0,'completiongradeitemnumber'=>null);
601         $item=(object)array('itemnumber'=>3);
602         $grade=(object)array('userid'=>31337);
604         // Not enabled (should do nothing)
605         $c->setReturnValueAt(0,'is_enabled',false);
606         $c->expectAt(0,'is_enabled',array($cm));
607         $c->inform_grade_changed($cm,$item,$grade,false);
609         // Enabled but still no grade completion required, should still do nothing
610         $c->setReturnValueAt(1,'is_enabled',true);
611         $c->expectAt(1,'is_enabled',array($cm));
612         $c->inform_grade_changed($cm,$item,$grade,false);
614         // Enabled and completion required but item number is wrong, does nothing
615         $cm->completiongradeitemnumber=7;
616         $c->setReturnValueAt(2,'is_enabled',true);
617         $c->expectAt(2,'is_enabled',array($cm));
618         $c->inform_grade_changed($cm,$item,$grade,false);
620         // Enabled and completion required and item number right. It is supposed
621         // to call update_state with the new potential state being obtained from
622         // internal_get_grade_state.
623         $cm->completiongradeitemnumber=3;
624         $c->setReturnValueAt(3,'is_enabled',true);
625         $c->expectAt(3,'is_enabled',array($cm));
626         $c->expectAt(0,'internal_get_grade_state',array($item,$grade));
627         $c->setReturnValueAt(0,'internal_get_grade_state',COMPLETION_COMPLETE_PASS);
628         $c->expectAt(0,'update_state',array($cm,COMPLETION_COMPLETE_PASS,31337));
629         $c->inform_grade_changed($cm,$item,$grade,false);
631         // Same as above but marked deleted. It is supposed to call update_state
632         // with new potential state being COMPLETION_INCOMPLETE
633         $c->setReturnValueAt(4,'is_enabled',false);
634         $c->expectAt(4,'is_enabled',array($cm));
635         $c->expectAt(1,'update_state',array($cm,COMPLETION_INCOMPLETE,31337));
636         $c->inform_grade_changed($cm,$item,$grade,false);
638         $c->tally();
639     }
641     function test_internal_get_grade_state() {
642         $item=new stdClass;
643         $grade=new stdClass;
645         $item->gradepass=4;
646         $item->hidden=0;
647         $grade->rawgrade=4.0;
648         $grade->finalgrade=null;
650         // Grade has pass mark and is not hidden, user passes
651         $this->assertEqual(
652             COMPLETION_COMPLETE_PASS,
653             completion_info::internal_get_grade_state($item,$grade));
655         // Same but user fails
656         $grade->rawgrade=3.9;
657         $this->assertEqual(
658             COMPLETION_COMPLETE_FAIL,
659             completion_info::internal_get_grade_state($item,$grade));
661         // User fails on raw grade but passes on final
662         $grade->finalgrade=4.0;
663         $this->assertEqual(
664             COMPLETION_COMPLETE_PASS,
665             completion_info::internal_get_grade_state($item,$grade));
667         // Item is hidden
668         $item->hidden=1;
669         $this->assertEqual(
670             COMPLETION_COMPLETE,
671             completion_info::internal_get_grade_state($item,$grade));
673         // Item isn't hidden but has no pass mark
674         $item->hidden=0;
675         $item->gradepass=0;
676         $this->assertEqual(
677             COMPLETION_COMPLETE,
678             completion_info::internal_get_grade_state($item,$grade));
679     }