completion is trying to modify current db, it should use test prefix, but no idea...
[moodle.git] / lib / simpletest / testcompletionlib.php
CommitLineData
4e781c7b 1<?php
2if (!defined('MOODLE_INTERNAL')) {
3 die('Direct access to this script is forbidden.');
4}
5require_once($CFG->libdir.'/completionlib.php');
6
7global $DB;
8Mock::generate(get_class($DB), 'mock_database');
9
10Mock::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'));
13Mock::generatePartial('completion_info','completion_cutdown2',
14 array('is_enabled','get_data','internal_get_state','internal_set_data'));
15Mock::generatePartial('completion_info','completion_cutdown3',
16 array('internal_get_grade_state'));
b9c639d6 17
4e781c7b 18class fake_recordset implements Iterator {
19 var $closed;
20 var $values,$index;
b9c639d6 21
4e781c7b 22 function fake_recordset($values) {
23 $this->values=$values;
24 $this->index=0;
25 }
b9c639d6 26
4e781c7b 27 function current() {
28 return $this->values[$this->index];
29 }
b9c639d6 30
4e781c7b 31 function key() {
32 return $this->values[$this->index];
33 }
b9c639d6 34
4e781c7b 35 function next() {
36 $this->index++;
37 }
b9c639d6 38
4e781c7b 39 function rewind() {
40 $this->index=0;
41 }
b9c639d6 42
4e781c7b 43 function valid() {
44 return count($this->values) > $this->index;
45 }
b9c639d6 46
4e781c7b 47 function close() {
48 $closed=true;
49 }
b9c639d6 50
4e781c7b 51 function was_closed() {
52 return $closed;
53 }
54}
55
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 */
60class TimeModifiedExpectation extends SimpleExpectation {
61 private $otherfields;
b9c639d6 62
4e781c7b 63 /**
64 * @param array $otherfields Array key=>value of required object fields
65 */
66 function TimeModifiedExpectation($otherfields) {
67 $this->otherfields=$otherfields;
68 }
b9c639d6 69
4e781c7b 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 }
b9c639d6 80
4e781c7b 81 $timedifference=time()-$thing->timemodified;
82 return ($timedifference < 2 && $timedifference>=0);
83 }
b9c639d6 84
4e781c7b 85 function testMessage($thing) {
86 return "Object does not match fields/time requirement";
87 }
88}
89
dd1a9e4c 90class completionlib_test extends UnitTestCaseUsingDatabase {
081a63a9 91
92 public static $includecoverage = array('lib/completionlib.php');
4e781c7b 93 var $realdb,$realcfg,$realsession,$realuser;
b9c639d6 94
8926f844 95 function setUp() {
4e781c7b 96 global $DB,$CFG,$SESSION,$USER;
97 $this->realdb=$DB;
98 $this->realcfg=$CFG;
e16f75bd 99 $this->realsession=$SESSION;
542797b4 100 $this->prevuser=$USER;
4e781c7b 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 }
108
109 function tearDown() {
110 global $DB,$CFG,$SESSION,$USER;
111 $DB=$this->realdb;
112 $CFG=$this->realcfg;
113 $SESSION=$this->realsession;
542797b4 114 $USER=$this->prevuser;
4e781c7b 115 }
116
117 function test_is_enabled() {
118 global $CFG;
119
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());
125
b9c639d6 126 // Course
4e781c7b 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());
135
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 }
b9c639d6 150
4e781c7b 151 function test_update_state() {
152 $c=new completion_cutdown2();
49f6e5f4 153 $c->__construct((object)array('id'=>42));
4e781c7b 154 $cm=(object)array('id'=>13,'course'=>42);
b9c639d6 155
4e781c7b 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);
b9c639d6 160
4e781c7b 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);
b9c639d6 165
4e781c7b 166 $c->expectAt(0,'get_data',array($cm,false,0));
167 $c->setReturnValueAt(0,'get_data',$current);
168 $c->update_state($cm,COMPLETION_COMPLETE);
b9c639d6 169
4e781c7b 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);
b9c639d6 178
4e781c7b 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);
b9c639d6 187
4e781c7b 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,
b9c639d6 194 new TimeModifiedExpectation(array('completionstate'=>COMPLETION_INCOMPLETE))));
4e781c7b 195 $c->update_state($cm,COMPLETION_INCOMPLETE);
b9c639d6 196
4e781c7b 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,
b9c639d6 206 new TimeModifiedExpectation(array('completionstate'=>COMPLETION_COMPLETE_PASS))));
4e781c7b 207 $c->update_state($cm,COMPLETION_COMPLETE_PASS);
208
209 $c->tally();
210 }
b9c639d6 211
4e781c7b 212 function test_internal_get_state() {
213 global $DB;
b9c639d6 214
4e781c7b 215 $c=new completion_cutdown3();
49f6e5f4 216 $c->__construct((object)array('id'=>42));
4e781c7b 217 $cm=(object)array('id'=>13,'course'=>42,'completiongradeitemnumber'=>null);
b9c639d6 218
4e781c7b 219 // If view is required, but they haven't viewed it yet
220 $cm->completionview=COMPLETION_VIEW_REQUIRED;
b9c639d6 221 $current=(object)array('viewed'=>COMPLETION_NOT_VIEWED);
4e781c7b 222 $this->assertEqual(COMPLETION_INCOMPLETE,$c->internal_get_state($cm,123,$current));
b9c639d6 223
4e781c7b 224 // OK set view not required
225 $cm->completionview=COMPLETION_VIEW_NOT_REQUIRED;
b9c639d6 226
4e781c7b 227 // Test not getting module name
228 $cm->modname='label';
229 $this->assertEqual(COMPLETION_COMPLETE,$c->internal_get_state($cm,123,$current));
b9c639d6 230
4e781c7b 231 // Test getting module name
232 $cm->module=13;
233 unset($cm->modname);
234 $DB->expectOnce('get_field',array('modules','name',array('id'=>13)));
b9c639d6 235 $DB->setReturnValue('get_field','label');
4e781c7b 236 $this->assertEqual(COMPLETION_COMPLETE,$c->internal_get_state($cm,123,$current));
b9c639d6 237
4e781c7b 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
b9c639d6 241 // * the plugin_supports call is static and can't be mocked
242
4e781c7b 243 $DB->tally();
244 $c->tally();
245 }
b9c639d6 246
4e781c7b 247 function test_set_module_viewed() {
248 $c=new completion_cutdown();
49f6e5f4 249 $c->__construct((object)array('id'=>42));
4e781c7b 250 $cm=(object)array('id'=>13,'course'=>42);
b9c639d6 251
252 // Not tracking completion, should do nothing
4e781c7b 253 $cm->completionview=COMPLETION_VIEW_NOT_REQUIRED;
254 $c->set_module_viewed($cm);
b9c639d6 255
4e781c7b 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);
b9c639d6 261
4e781c7b 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);
b9c639d6 270
4e781c7b 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);
b9c639d6 281
4e781c7b 282 $c->tally();
283 }
b9c639d6 284
4e781c7b 285 function test_count_user_data() {
286 global $DB;
287 $cm=(object)array('id'=>42);
b9c639d6 288 $DB->setReturnValue('get_field_sql',666);
4e781c7b 289 $DB->expectOnce('get_field_sql',array(new IgnoreWhitespaceExpectation("SELECT
290 COUNT(1)
291FROM
49f6e5f4 292 {course_modules_completion}
4e781c7b 293WHERE
294 coursemoduleid=? AND completionstate<>0"),array(42)));
295 $c=new completion_info(null);
296 $this->assertEqual(666,$c->count_user_data($cm));
b9c639d6 297
4e781c7b 298 $DB->tally();
299 }
b9c639d6 300
4e781c7b 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)));
b9c639d6 309 $c->delete_all_state($cm);
310
4e781c7b 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);
b9c639d6 324
4e781c7b 325 $DB->tally();
326 }
b9c639d6 327
4e781c7b 328 function test_reset_all_state() {
329 global $DB;
330 $c=new completion_cutdown();
49f6e5f4 331 $c->__construct((object)array('id'=>42));
b9c639d6 332
002f56ef 333 $cm=(object)array('id'=>13,'course'=>42,
334 'completion'=>COMPLETION_TRACKING_AUTOMATIC);
b9c639d6 335
4e781c7b 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 ));
b9c639d6 348
4e781c7b 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));
b9c639d6 352
4e781c7b 353 $c->reset_all_state($cm);
b9c639d6 354
4e781c7b 355 $DB->tally();
356 $c->tally();
357 }
b9c639d6 358
4e781c7b 359 function test_get_data() {
360 global $DB,$SESSION;
b9c639d6 361
362 $c=new completion_info((object)array('id'=>42));
4e781c7b 363 $cm=(object)array('id'=>13,'course'=>42);
b9c639d6 364
4e781c7b 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));
b9c639d6 373
4e781c7b 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));
b9c639d6 383
4e781c7b 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]);
b9c639d6 391 // When checking time(), allow for second overlaps
4e781c7b 392 $this->assertTrue(time()-$SESSION->completioncache[42]['updated']<2);
b9c639d6 393
394 // 4. Current user, 'whole course', but from cache
4e781c7b 395 $result=$c->get_data($cm,true);
396 $this->assertEqual($sillyrecord,$result);
b9c639d6 397
4e781c7b 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;
b9c639d6 402 $SESSION->completioncache[39]['updated']=72; // Also a long time ago
4e781c7b 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);
b9c639d6 408 // Check that updated value is right, then fudge it to make next compare
4e781c7b 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);
b9c639d6 415
4e781c7b 416 // 6. Current user, 'whole course' and record not in cache
417 unset($SESSION->completioncache);
b9c639d6 418
4e781c7b 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
b9c639d6 423 ));
4e781c7b 424 $DB->expectAt(0,'get_records_sql',array(new IgnoreWhitespaceExpectation("
425SELECT
426 cmc.*
427FROM
49f6e5f4 428 {course_modules} cm
429 INNER JOIN {course_modules_completion} cmc ON cmc.coursemoduleid=cm.id
b9c639d6 430WHERE
4e781c7b 431 cm.course=? AND cmc.userid=?"),array(42,314159)));
432
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);
b9c639d6 436
4e781c7b 437 // Check result
438 $this->assertEqual($basicrecord,$result);
b9c639d6 439
4e781c7b 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);
b9c639d6 446
4e781c7b 447 $DB->tally();
448 }
b9c639d6 449
4e781c7b 450 function test_internal_set_data() {
451 global $DB,$SESSION;
b9c639d6 452
4e781c7b 453 $cm=(object)array('course'=>42,'id'=>13);
b9c639d6 454 $c=new completion_info((object)array('id'=>42));
455
4e781c7b 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);
b9c639d6 463
4e781c7b 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));
b9c639d6 470
4e781c7b 471 $DB->tally();
472 }
b9c639d6 473
4e781c7b 474 function test_get_activities() {
475 global $DB;
b9c639d6 476
4e781c7b 477 $c=new completion_info((object)array('id'=>42));
b9c639d6 478
4e781c7b 479 // Try with no activities
480 $DB->expectAt(0,'get_records_select',array('course_modules',
4454447d 481 'course=42 AND completion<>'.COMPLETION_TRACKING_NONE));
4e781c7b 482 $DB->setReturnValueAt(0,'get_records_select',array());
483 $result=$c->get_activities();
484 $this->assertEqual(array(),$result);
b9c639d6 485
4e781c7b 486 // Try with an activity (need to fake up modinfo for it as well)
487 $DB->expectAt(1,'get_records_select',array('course_modules',
4454447d 488 'course=42 AND completion<>'.COMPLETION_TRACKING_NONE));
4e781c7b 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);
b9c639d6 497
498 $DB->tally();
4e781c7b 499 }
b9c639d6 500
501 // internal_get_tracked_users() cannot easily be tested because it uses
4e781c7b 502 // get_role_users, so skipping that
b9c639d6 503
4e781c7b 504 function test_get_progress_all() {
505 global $DB;
b9c639d6 506
4e781c7b 507 $c=new completion_cutdown();
49f6e5f4 508 $c->__construct((object)array('id'=>42));
b9c639d6 509
510 // 1) Basic usage
4e781c7b 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("
519SELECT
520 cmc.*
521FROM
49f6e5f4 522 {course_modules} cm
523 INNER JOIN {course_modules_completion} cmc ON cm.id=cmc.coursemoduleid
4e781c7b 524WHERE
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 )));
531
002f56ef 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));
b9c639d6 540
4e781c7b 541 // 2) With more than 1,000 results
542 $c->expectAt(1,'internal_get_tracked_users',array(true,3));
b9c639d6 543
4e781c7b 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);
b9c639d6 554
4e781c7b 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("
558SELECT
559 cmc.*
560FROM
49f6e5f4 561 {course_modules} cm
562 INNER JOIN {course_modules_completion} cmc ON cm.id=cmc.coursemoduleid
4e781c7b 563WHERE
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("
569SELECT
570 cmc.*
571FROM
49f6e5f4 572 {course_modules} cm
573 INNER JOIN {course_modules_completion} cmc ON cm.id=cmc.coursemoduleid
4e781c7b 574WHERE
575 cm.course=? AND cmc.userid IN whatever2"),array(42)));
576 $DB->setReturnValueAt(2,'get_recordset_sql',new fake_recordset(array_slice($progress,1000)));
b9c639d6 577
4e781c7b 578 $result=$c->get_progress_all(true,3);
b9c639d6 579
4e781c7b 580 $resultok=true;
002f56ef 581 $resultok = $resultok && ($ids==array_keys($result->users));
582 foreach($result->users as $userid => $data) {
4e781c7b 583 $resultok = $resultok && $data->firstname=='frog';
584 $resultok = $resultok && $data->lastname==$userid;
b9c639d6 585 $resultok = $resultok && $data->id==$userid;
4e781c7b 586 $cms=$data->progress;
587 $resultok= $resultok && (array(13,14)==array_keys($cms));
588 $resultok= $resultok && ((object)array('userid'=>$userid,'coursemoduleid'=>13)==$cms[13]);
b9c639d6 589 $resultok= $resultok && ((object)array('userid'=>$userid,'coursemoduleid'=>14)==$cms[14]);
4e781c7b 590 }
591 $this->assertTrue($resultok);
b9c639d6 592
593 $DB->tally();
4e781c7b 594 $c->tally();
595 }
b9c639d6 596
4e781c7b 597 function test_inform_grade_changed() {
598 $c=new completion_cutdown();
49f6e5f4 599 $c->__construct((object)array('id'=>42));
b9c639d6 600
3d99b4ac 601 $cm=(object)array('course'=>42,'id'=>13,'completion'=>0,'completiongradeitemnumber'=>null);
4e781c7b 602 $item=(object)array('itemnumber'=>3);
603 $grade=(object)array('userid'=>31337);
b9c639d6 604
4e781c7b 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);
b9c639d6 609
4e781c7b 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);
b9c639d6 614
4e781c7b 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);
b9c639d6 620
4e781c7b 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);
b9c639d6 631
4e781c7b 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);
b9c639d6 638
4e781c7b 639 $c->tally();
640 }
641
642 function test_internal_get_grade_state() {
643 $item=new stdClass;
644 $grade=new stdClass;
b9c639d6 645
4e781c7b 646 $item->gradepass=4;
647 $item->hidden=0;
648 $grade->rawgrade=4.0;
649 $grade->finalgrade=null;
b9c639d6 650
4e781c7b 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));
b9c639d6 655
4e781c7b 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));
b9c639d6 661
4e781c7b 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));
b9c639d6 667
4e781c7b 668 // Item is hidden
669 $item->hidden=1;
670 $this->assertEqual(
671 COMPLETION_COMPLETE,
672 completion_info::internal_get_grade_state($item,$grade));
b9c639d6 673
4e781c7b 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 }
681}
8926f844 682