completion is trying to modify current db, it should use test prefix, but no idea...
[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','internal_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         $c=new completion_info($course);
129         $course->enablecompletion=COMPLETION_DISABLED;
130         $this->assertEqual(COMPLETION_DISABLED,$c->is_enabled());
131         $course->enablecompletion=COMPLETION_ENABLED;
132         $this->assertEqual(COMPLETION_ENABLED,$c->is_enabled());
133         $CFG->enablecompletion=COMPLETION_DISABLED;
134         $this->assertEqual(COMPLETION_DISABLED,$c->is_enabled());
136         // Course and CM
137         $cm=new stdClass;
138         $cm->completion=COMPLETION_TRACKING_MANUAL;
139         $this->assertEqual(COMPLETION_DISABLED,$c->is_enabled($cm));
140         $CFG->enablecompletion=COMPLETION_ENABLED;
141         $course->enablecompletion=COMPLETION_DISABLED;
142         $this->assertEqual(COMPLETION_DISABLED,$c->is_enabled($cm));
143         $course->enablecompletion=COMPLETION_ENABLED;
144         $this->assertEqual(COMPLETION_TRACKING_MANUAL,$c->is_enabled($cm));
145         $cm->completion=COMPLETION_TRACKING_NONE;
146         $this->assertEqual(COMPLETION_TRACKING_NONE,$c->is_enabled($cm));
147         $cm->completion=COMPLETION_TRACKING_AUTOMATIC;
148         $this->assertEqual(COMPLETION_TRACKING_AUTOMATIC,$c->is_enabled($cm));
149     }
151     function test_update_state() {
152         $c=new completion_cutdown2();
153         $c->__construct((object)array('id'=>42));
154         $cm=(object)array('id'=>13,'course'=>42);
156         // Not enabled, should do nothing
157         $c->expectAt(0,'is_enabled',array($cm));
158         $c->setReturnValueAt(0,'is_enabled',false);
159         $c->update_state($cm);
161         // Enabled, but current state is same as possible result, do nothing
162         $current=(object)array('completionstate'=>COMPLETION_COMPLETE);
163         $c->expectAt(1,'is_enabled',array($cm));
164         $c->setReturnValueAt(1,'is_enabled',true);
166         $c->expectAt(0,'get_data',array($cm,false,0));
167         $c->setReturnValueAt(0,'get_data',$current);
168         $c->update_state($cm,COMPLETION_COMPLETE);
170         // Enabled, but current state is a specific one and new state is just
171         // omplete, so do nothing
172         $current->completionstate=COMPLETION_COMPLETE_PASS;
173         $c->expectAt(2,'is_enabled',array($cm));
174         $c->setReturnValueAt(2,'is_enabled',true);
175         $c->expectAt(1,'get_data',array($cm,false,0));
176         $c->setReturnValueAt(1,'get_data',$current);
177         $c->update_state($cm,COMPLETION_COMPLETE);
179         // Manual, change state (no change)
180         $cm->completion=COMPLETION_TRACKING_MANUAL;
181         $current->completionstate=COMPLETION_COMPLETE;
182         $c->expectAt(3,'is_enabled',array($cm));
183         $c->setReturnValueAt(3,'is_enabled',true);
184         $c->expectAt(2,'get_data',array($cm,false,0));
185         $c->setReturnValueAt(2,'get_data',$current);
186         $c->update_state($cm,COMPLETION_COMPLETE);
188         // Manual, change state (change)
189         $c->expectAt(4,'is_enabled',array($cm));
190         $c->setReturnValueAt(4,'is_enabled',true);
191         $c->expectAt(3,'get_data',array($cm,false,0));
192         $c->setReturnValueAt(3,'get_data',$current);
193         $c->expectAt(0,'internal_set_data',array($cm,
194             new TimeModifiedExpectation(array('completionstate'=>COMPLETION_INCOMPLETE))));
195         $c->update_state($cm,COMPLETION_INCOMPLETE);
197         // Auto, change state
198         $cm->completion=COMPLETION_TRACKING_AUTOMATIC;
199         $c->expectAt(5,'is_enabled',array($cm));
200         $c->setReturnValueAt(5,'is_enabled',true);
201         $c->expectAt(4,'get_data',array($cm,false,0));
202         $c->setReturnValueAt(4,'get_data',$current);
203         $c->expectAt(0,'internal_get_state',array($cm,0,$current));
204         $c->setReturnValueAt(0,'internal_get_state',COMPLETION_COMPLETE_PASS);
205         $c->expectAt(1,'internal_set_data',array($cm,
206             new TimeModifiedExpectation(array('completionstate'=>COMPLETION_COMPLETE_PASS))));
207         $c->update_state($cm,COMPLETION_COMPLETE_PASS);
209         $c->tally();
210     }
212     function test_internal_get_state() {
213         global $DB;
215         $c=new completion_cutdown3();
216         $c->__construct((object)array('id'=>42));
217         $cm=(object)array('id'=>13,'course'=>42,'completiongradeitemnumber'=>null);
219         // If view is required, but they haven't viewed it yet
220         $cm->completionview=COMPLETION_VIEW_REQUIRED;
221         $current=(object)array('viewed'=>COMPLETION_NOT_VIEWED);
222         $this->assertEqual(COMPLETION_INCOMPLETE,$c->internal_get_state($cm,123,$current));
224         // OK set view not required
225         $cm->completionview=COMPLETION_VIEW_NOT_REQUIRED;
227         // Test not getting module name
228         $cm->modname='label';
229         $this->assertEqual(COMPLETION_COMPLETE,$c->internal_get_state($cm,123,$current));
231         // Test getting module name
232         $cm->module=13;
233         unset($cm->modname);
234         $DB->expectOnce('get_field',array('modules','name',array('id'=>13)));
235         $DB->setReturnValue('get_field','label');
236         $this->assertEqual(COMPLETION_COMPLETE,$c->internal_get_state($cm,123,$current));
238         // Note: This function is not fully tested (including kind of the main
239         // part) because:
240         // * the grade_item/grade_grade calls are static and can't be mocked
241         // * the plugin_supports call is static and can't be mocked
243         $DB->tally();
244         $c->tally();
245     }
247     function test_set_module_viewed() {
248         $c=new completion_cutdown();
249         $c->__construct((object)array('id'=>42));
250         $cm=(object)array('id'=>13,'course'=>42);
252         // Not tracking completion, should do nothing
253         $cm->completionview=COMPLETION_VIEW_NOT_REQUIRED;
254         $c->set_module_viewed($cm);
256         // Tracking completion but completion is disabled, should do nothing
257         $cm->completionview=COMPLETION_VIEW_REQUIRED;
258         $c->expectAt(0,'is_enabled',array($cm));
259         $c->setReturnValueAt(0,'is_enabled',false);
260         $c->set_module_viewed($cm);
262         // Now it's enabled, we expect it to get data. If data already has
263         // viewed, still do nothing
264         $c->expectAt(1,'is_enabled',array($cm));
265         $c->setReturnValueAt(1,'is_enabled',true);
266         $c->expectAt(0,'get_data',array($cm,0));
267         $hasviewed=(object)array('viewed'=>COMPLETION_VIEWED);
268         $c->setReturnValueAt(0,'get_data',$hasviewed);
269         $c->set_module_viewed($cm);
271         // OK finally one that hasn't been viewed, now it should set it viewed
272         // and update state
273         $c->expectAt(2,'is_enabled',array($cm));
274         $c->setReturnValueAt(2,'is_enabled',true);
275         $notviewed=(object)array('viewed'=>COMPLETION_NOT_VIEWED);
276         $c->expectAt(1,'get_data',array($cm,1337));
277         $c->setReturnValueAt(1,'get_data',$notviewed);
278         $c->expectOnce('internal_set_data',array($cm,$hasviewed));
279         $c->expectOnce('update_state',array($cm,COMPLETION_COMPLETE,1337));
280         $c->set_module_viewed($cm,1337);
282         $c->tally();
283     }
285     function test_count_user_data() {
286         global $DB;
287         $cm=(object)array('id'=>42);
288         $DB->setReturnValue('get_field_sql',666);
289         $DB->expectOnce('get_field_sql',array(new IgnoreWhitespaceExpectation("SELECT
290     COUNT(1)
291 FROM
292     {course_modules_completion}
293 WHERE
294     coursemoduleid=? AND completionstate<>0"),array(42)));
295         $c=new completion_info(null);
296         $this->assertEqual(666,$c->count_user_data($cm));
298         $DB->tally();
299     }
301     function test_delete_all_state() {
302         global $DB,$SESSION;
303         $course=(object)array('id'=>13);
304         $cm=(object)array('id'=>42,'course'=>13);
305         $c=new completion_info($course);
306         // Check it works ok without data in session
307         $DB->expectAt(0,'delete_records',
308             array('course_modules_completion',array('coursemoduleid'=>42)));
309         $c->delete_all_state($cm);
311         // Build up a session to check it deletes the right bits from it
312         // (and not other bits)
313         $SESSION->completioncache=array();
314         $SESSION->completioncache[13]=array();
315         $SESSION->completioncache[13][42]='foo';
316         $SESSION->completioncache[13][43]='foo';
317         $SESSION->completioncache[14]=array();
318         $SESSION->completioncache[14][42]='foo';
319         $DB->expectAt(1,'delete_records',
320             array('course_modules_completion',array('coursemoduleid'=>42)));
321         $c->delete_all_state($cm);
322         $this->assertEqual(array(13=>array(43=>'foo'),14=>array(42=>'foo')),
323             $SESSION->completioncache);
325         $DB->tally();
326     }
328     function test_reset_all_state() {
329         global $DB;
330         $c=new completion_cutdown();
331         $c->__construct((object)array('id'=>42));
333         $cm=(object)array('id'=>13,'course'=>42,
334             'completion'=>COMPLETION_TRACKING_AUTOMATIC);
336         $DB->setReturnValue('get_recordset',new fake_recordset(array(
337             (object)array('id'=>1,'userid'=>100),
338             (object)array('id'=>2,'userid'=>101),
339         )));
340         $DB->expectOnce('get_recordset',array('course_modules_completion',
341             array('coursemoduleid'=>13),'','userid'));
342         $c->expectOnce('delete_all_state',array($cm));
343         $c->expectOnce('internal_get_tracked_users',array(false));
344         $c->setReturnValue('internal_get_tracked_users',array(
345             (object)array('id'=>100,'firstname'=>'Woot','lastname'=>'Plugh'),
346             (object)array('id'=>201,'firstname'=>'Vroom','lastname'=>'Xyzzy'),
347             ));
349         $c->expectAt(0,'update_state',array($cm,COMPLETION_UNKNOWN,100));
350         $c->expectAt(1,'update_state',array($cm,COMPLETION_UNKNOWN,101));
351         $c->expectAt(2,'update_state',array($cm,COMPLETION_UNKNOWN,201));
353         $c->reset_all_state($cm);
355         $DB->tally();
356         $c->tally();
357     }
359     function test_get_data() {
360         global $DB,$SESSION;
362         $c=new completion_info((object)array('id'=>42));
363         $cm=(object)array('id'=>13,'course'=>42);
365         // 1. Not current user, record exists
366         $sillyrecord=(object)array('frog'=>'kermit');
367         $DB->expectAt(0,'get_record',array('course_modules_completion',
368             array('coursemoduleid'=>13,'userid'=>123)));
369         $DB->setReturnValueAt(0,'get_record',$sillyrecord);
370         $result=$c->get_data($cm,false,123);
371         $this->assertEqual($sillyrecord,$result);
372         $this->assertTrue(empty($SESSION->completioncache));
374         // 2. Not current user, default record, wholecourse (ignored)
375         $DB->expectAt(1,'get_record',array('course_modules_completion',
376             array('coursemoduleid'=>13,'userid'=>123)));
377         $DB->setReturnValueAt(1,'get_record',false);
378         $result=$c->get_data($cm,true,123);
379         $this->assertEqual((object)array(
380             'id'=>'0','coursemoduleid'=>13,'userid'=>123,'completionstate'=>0,
381             'viewed'=>0,'timemodified'=>0),$result);
382         $this->assertTrue(empty($SESSION->completioncache));
384         // 3. Current user, single record, not from cache
385         $DB->expectAt(2,'get_record',array('course_modules_completion',
386             array('coursemoduleid'=>13,'userid'=>314159)));
387         $DB->setReturnValueAt(2,'get_record',$sillyrecord);
388         $result=$c->get_data($cm);
389         $this->assertEqual($sillyrecord,$result);
390         $this->assertEqual($sillyrecord,$SESSION->completioncache[42][13]);
391         // When checking time(), allow for second overlaps
392         $this->assertTrue(time()-$SESSION->completioncache[42]['updated']<2);
394         // 4. Current user, 'whole course', but from cache
395         $result=$c->get_data($cm,true);
396         $this->assertEqual($sillyrecord,$result);
398         // 5. Current user, single record, cache expired
399         $SESSION->completioncache[42]['updated']=37; // Quite a long time ago
400         $now=time();
401         $SESSION->completioncache[17]['updated']=$now;
402         $SESSION->completioncache[39]['updated']=72; // Also a long time ago
403         $DB->expectAt(3,'get_record',array('course_modules_completion',
404             array('coursemoduleid'=>13,'userid'=>314159)));
405         $DB->setReturnValueAt(3,'get_record',$sillyrecord);
406         $result=$c->get_data($cm,false);
407         $this->assertEqual($sillyrecord,$result);
408         // Check that updated value is right, then fudge it to make next compare
409         // work
410         $this->assertTrue(time()-$SESSION->completioncache[42]['updated']<2);
411         $SESSION->completioncache[42]['updated']=$now;
412         // Check things got expired from cache
413         $this->assertEqual(array(42=>array(13=>$sillyrecord,'updated'=>$now),
414             17=>array('updated'=>$now)),$SESSION->completioncache);
416         // 6. Current user, 'whole course' and record not in cache
417         unset($SESSION->completioncache);
419         // Scenario: Completion data exists for one CMid
420         $basicrecord=(object)array('coursemoduleid'=>13);
421         $DB->setReturnValueAt(0,'get_records_sql',array(
422             1=>$basicrecord
423         ));
424         $DB->expectAt(0,'get_records_sql',array(new IgnoreWhitespaceExpectation("
425 SELECT
426     cmc.*
427 FROM
428     {course_modules} cm
429     INNER JOIN {course_modules_completion} cmc ON cmc.coursemoduleid=cm.id
430 WHERE
431     cm.course=? AND cmc.userid=?"),array(42,314159)));
433         // There are two CMids in total, the one we had data for and another one
434         $modinfo->cms=array((object)array('id'=>13),(object)array('id'=>14));
435         $result=$c->get_data($cm,true,0,$modinfo);
437         // Check result
438         $this->assertEqual($basicrecord,$result);
440         // Check the cache contents
441         $this->assertTrue(time()-$SESSION->completioncache[42]['updated']<2);
442         $SESSION->completioncache[42]['updated']=$now;
443         $this->assertEqual(array(42=>array(13=>$basicrecord,14=>(object)array(
444             'id'=>'0','coursemoduleid'=>14,'userid'=>314159,'completionstate'=>0,
445             'viewed'=>0,'timemodified'=>0),'updated'=>$now)),$SESSION->completioncache);
447         $DB->tally();
448     }
450     function test_internal_set_data() {
451         global $DB,$SESSION;
453         $cm=(object)array('course'=>42,'id'=>13);
454         $c=new completion_info((object)array('id'=>42));
456         // 1) Test with new data
457         $data=(object)array('id'=>0,'userid'=>314159);
458         $DB->setReturnValueAt(0,'insert_record',4);
459         $DB->expectAt(0,'insert_record',array('course_modules_completion',$data));
460         $c->internal_set_data($cm,$data);
461         $this->assertEqual(4,$data->id);
462         $this->assertEqual(array(42=>array(13=>$data)),$SESSION->completioncache);
464         // 2) Test with existing data and for different user (not cached)
465         unset($SESSION->completioncache);
466         $d2=(object)array('id'=>7,'userid'=>17);
467         $DB->expectAt(0,'update_record',array('course_modules_completion',$d2));
468         $c->internal_set_data($cm,$d2);
469         $this->assertFalse(isset($SESSION->completioncache));
471         $DB->tally();
472     }
474     function test_get_activities() {
475         global $DB;
477         $c=new completion_info((object)array('id'=>42));
479         // Try with no activities
480         $DB->expectAt(0,'get_records_select',array('course_modules',
481               'course=42 AND completion<>'.COMPLETION_TRACKING_NONE));
482         $DB->setReturnValueAt(0,'get_records_select',array());
483         $result=$c->get_activities();
484         $this->assertEqual(array(),$result);
486         // Try with an activity (need to fake up modinfo for it as well)
487         $DB->expectAt(1,'get_records_select',array('course_modules',
488               'course=42 AND completion<>'.COMPLETION_TRACKING_NONE));
489         $DB->setReturnValueAt(1,'get_records_select',array(
490             13=>(object)array('id'=>13)
491         ));
492         $modinfo=new stdClass;
493         $modinfo->sections=array(array(1,2,3),array(12,13,14));
494         $modinfo->cms[13]=(object)array('modname'=>'frog','name'=>'kermit');
495         $result=$c->get_activities($modinfo);
496         $this->assertEqual(array(13=>(object)array('id'=>13,'modname'=>'frog','name'=>'kermit')),$result);
498         $DB->tally();
499     }
501     // internal_get_tracked_users() cannot easily be tested because it uses
502     // get_role_users, so skipping that
504     function test_get_progress_all() {
505         global $DB;
507         $c=new completion_cutdown();
508         $c->__construct((object)array('id'=>42));
510         // 1) Basic usage
511         $c->expectAt(0,'internal_get_tracked_users',array(false,0));
512         $c->setReturnValueAt(0,'internal_get_tracked_users',array(
513             (object)array('id'=>100,'firstname'=>'Woot','lastname'=>'Plugh'),
514             (object)array('id'=>201,'firstname'=>'Vroom','lastname'=>'Xyzzy'),
515             ));
516         $DB->expectAt(0,'get_in_or_equal',array(array(100,201)));
517         $DB->setReturnValueAt(0,'get_in_or_equal',array(' IN (100,201)',array()));
518         $DB->expectAt(0,'get_recordset_sql',array(new IgnoreWhitespaceExpectation("
519 SELECT
520     cmc.*
521 FROM
522     {course_modules} cm
523     INNER JOIN {course_modules_completion} cmc ON cm.id=cmc.coursemoduleid
524 WHERE
525     cm.course=? AND cmc.userid IN (100,201)"),array(42)));
526         $progress1=(object)array('userid'=>100,'coursemoduleid'=>13);
527         $progress2=(object)array('userid'=>201,'coursemoduleid'=>14);
528         $DB->setReturnValueAt(0,'get_recordset_sql',new fake_recordset(array(
529             $progress1,$progress2
530         )));
532         $this->assertEqual((object)array(
533             'start'=>0,'total'=>2,
534             'users'=>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,'internal_get_tracked_users',array(true,3));
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,'internal_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)));
578         $result=$c->get_progress_all(true,3);
580         $resultok=true;
581         $resultok = $resultok && ($ids==array_keys($result->users));
582         foreach($result->users as $userid => $data) {
583             $resultok = $resultok && $data->firstname=='frog';
584             $resultok = $resultok && $data->lastname==$userid;
585             $resultok = $resultok && $data->id==$userid;
586             $cms=$data->progress;
587             $resultok= $resultok && (array(13,14)==array_keys($cms));
588             $resultok= $resultok && ((object)array('userid'=>$userid,'coursemoduleid'=>13)==$cms[13]);
589             $resultok= $resultok && ((object)array('userid'=>$userid,'coursemoduleid'=>14)==$cms[14]);
590         }
591         $this->assertTrue($resultok);
593         $DB->tally();
594         $c->tally();
595     }
597     function test_inform_grade_changed() {
598         $c=new completion_cutdown();
599         $c->__construct((object)array('id'=>42));
601         $cm=(object)array('course'=>42,'id'=>13,'completion'=>0,'completiongradeitemnumber'=>null);
602         $item=(object)array('itemnumber'=>3);
603         $grade=(object)array('userid'=>31337);
605         // Not enabled (should do nothing)
606         $c->setReturnValueAt(0,'is_enabled',false);
607         $c->expectAt(0,'is_enabled',array($cm));
608         $c->inform_grade_changed($cm,$item,$grade,false);
610         // Enabled but still no grade completion required, should still do nothing
611         $c->setReturnValueAt(1,'is_enabled',true);
612         $c->expectAt(1,'is_enabled',array($cm));
613         $c->inform_grade_changed($cm,$item,$grade,false);
615         // Enabled and completion required but item number is wrong, does nothing
616         $cm->completiongradeitemnumber=7;
617         $c->setReturnValueAt(2,'is_enabled',true);
618         $c->expectAt(2,'is_enabled',array($cm));
619         $c->inform_grade_changed($cm,$item,$grade,false);
621         // Enabled and completion required and item number right. It is supposed
622         // to call update_state with the new potential state being obtained from
623         // internal_get_grade_state.
624         $cm->completiongradeitemnumber=3;
625         $c->setReturnValueAt(3,'is_enabled',true);
626         $c->expectAt(3,'is_enabled',array($cm));
627         $c->expectAt(0,'internal_get_grade_state',array($item,$grade));
628         $c->setReturnValueAt(0,'internal_get_grade_state',COMPLETION_COMPLETE_PASS);
629         $c->expectAt(0,'update_state',array($cm,COMPLETION_COMPLETE_PASS,31337));
630         $c->inform_grade_changed($cm,$item,$grade,false);
632         // Same as above but marked deleted. It is supposed to call update_state
633         // with new potential state being COMPLETION_INCOMPLETE
634         $c->setReturnValueAt(4,'is_enabled',false);
635         $c->expectAt(4,'is_enabled',array($cm));
636         $c->expectAt(1,'update_state',array($cm,COMPLETION_INCOMPLETE,31337));
637         $c->inform_grade_changed($cm,$item,$grade,false);
639         $c->tally();
640     }
642     function test_internal_get_grade_state() {
643         $item=new stdClass;
644         $grade=new stdClass;
646         $item->gradepass=4;
647         $item->hidden=0;
648         $grade->rawgrade=4.0;
649         $grade->finalgrade=null;
651         // Grade has pass mark and is not hidden, user passes
652         $this->assertEqual(
653             COMPLETION_COMPLETE_PASS,
654             completion_info::internal_get_grade_state($item,$grade));
656         // Same but user fails
657         $grade->rawgrade=3.9;
658         $this->assertEqual(
659             COMPLETION_COMPLETE_FAIL,
660             completion_info::internal_get_grade_state($item,$grade));
662         // User fails on raw grade but passes on final
663         $grade->finalgrade=4.0;
664         $this->assertEqual(
665             COMPLETION_COMPLETE_PASS,
666             completion_info::internal_get_grade_state($item,$grade));
668         // Item is hidden
669         $item->hidden=1;
670         $this->assertEqual(
671             COMPLETION_COMPLETE,
672             completion_info::internal_get_grade_state($item,$grade));
674         // Item isn't hidden but has no pass mark
675         $item->hidden=0;
676         $item->gradepass=0;
677         $this->assertEqual(
678             COMPLETION_COMPLETE,
679             completion_info::internal_get_grade_state($item,$grade));
680     }