Unit tests MDL-24975 Completion unit tests fix
[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',
31fe61a2 11 array('delete_all_state','get_tracked_users','update_state',
4e781c7b 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
31fe61a2
SM
127 //$course=new stdClass;
128 $course=(object)array('id'=>13);
4e781c7b 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());
136
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 }
b9c639d6 151
4e781c7b 152 function test_update_state() {
153 $c=new completion_cutdown2();
49f6e5f4 154 $c->__construct((object)array('id'=>42));
4e781c7b 155 $cm=(object)array('id'=>13,'course'=>42);
b9c639d6 156
4e781c7b 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);
b9c639d6 161
4e781c7b 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);
b9c639d6 166
4e781c7b 167 $c->expectAt(0,'get_data',array($cm,false,0));
168 $c->setReturnValueAt(0,'get_data',$current);
169 $c->update_state($cm,COMPLETION_COMPLETE);
b9c639d6 170
4e781c7b 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);
b9c639d6 179
4e781c7b 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);
b9c639d6 188
4e781c7b 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,
b9c639d6 195 new TimeModifiedExpectation(array('completionstate'=>COMPLETION_INCOMPLETE))));
4e781c7b 196 $c->update_state($cm,COMPLETION_INCOMPLETE);
b9c639d6 197
4e781c7b 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,
b9c639d6 207 new TimeModifiedExpectation(array('completionstate'=>COMPLETION_COMPLETE_PASS))));
4e781c7b 208 $c->update_state($cm,COMPLETION_COMPLETE_PASS);
209
210 $c->tally();
211 }
b9c639d6 212
4e781c7b 213 function test_internal_get_state() {
214 global $DB;
b9c639d6 215
4e781c7b 216 $c=new completion_cutdown3();
49f6e5f4 217 $c->__construct((object)array('id'=>42));
4e781c7b 218 $cm=(object)array('id'=>13,'course'=>42,'completiongradeitemnumber'=>null);
b9c639d6 219
4e781c7b 220 // If view is required, but they haven't viewed it yet
221 $cm->completionview=COMPLETION_VIEW_REQUIRED;
b9c639d6 222 $current=(object)array('viewed'=>COMPLETION_NOT_VIEWED);
4e781c7b 223 $this->assertEqual(COMPLETION_INCOMPLETE,$c->internal_get_state($cm,123,$current));
b9c639d6 224
4e781c7b 225 // OK set view not required
226 $cm->completionview=COMPLETION_VIEW_NOT_REQUIRED;
b9c639d6 227
4e781c7b 228 // Test not getting module name
229 $cm->modname='label';
230 $this->assertEqual(COMPLETION_COMPLETE,$c->internal_get_state($cm,123,$current));
b9c639d6 231
4e781c7b 232 // Test getting module name
233 $cm->module=13;
234 unset($cm->modname);
235 $DB->expectOnce('get_field',array('modules','name',array('id'=>13)));
b9c639d6 236 $DB->setReturnValue('get_field','label');
4e781c7b 237 $this->assertEqual(COMPLETION_COMPLETE,$c->internal_get_state($cm,123,$current));
b9c639d6 238
4e781c7b 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
b9c639d6 242 // * the plugin_supports call is static and can't be mocked
243
4e781c7b 244 $DB->tally();
245 $c->tally();
246 }
b9c639d6 247
4e781c7b 248 function test_set_module_viewed() {
249 $c=new completion_cutdown();
49f6e5f4 250 $c->__construct((object)array('id'=>42));
4e781c7b 251 $cm=(object)array('id'=>13,'course'=>42);
b9c639d6 252
253 // Not tracking completion, should do nothing
4e781c7b 254 $cm->completionview=COMPLETION_VIEW_NOT_REQUIRED;
255 $c->set_module_viewed($cm);
b9c639d6 256
4e781c7b 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);
b9c639d6 262
4e781c7b 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);
b9c639d6 271
4e781c7b 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);
b9c639d6 282
4e781c7b 283 $c->tally();
284 }
b9c639d6 285
4e781c7b 286 function test_count_user_data() {
287 global $DB;
31fe61a2 288 $course=(object)array('id'=>13);
4e781c7b 289 $cm=(object)array('id'=>42);
b9c639d6 290 $DB->setReturnValue('get_field_sql',666);
4e781c7b 291 $DB->expectOnce('get_field_sql',array(new IgnoreWhitespaceExpectation("SELECT
292 COUNT(1)
293FROM
49f6e5f4 294 {course_modules_completion}
4e781c7b 295WHERE
296 coursemoduleid=? AND completionstate<>0"),array(42)));
31fe61a2 297 $c=new completion_info($course);
4e781c7b 298 $this->assertEqual(666,$c->count_user_data($cm));
b9c639d6 299
4e781c7b 300 $DB->tally();
301 }
b9c639d6 302
4e781c7b 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)));
b9c639d6 311 $c->delete_all_state($cm);
312
4e781c7b 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);
b9c639d6 326
4e781c7b 327 $DB->tally();
328 }
b9c639d6 329
4e781c7b 330 function test_reset_all_state() {
331 global $DB;
332 $c=new completion_cutdown();
49f6e5f4 333 $c->__construct((object)array('id'=>42));
b9c639d6 334
002f56ef 335 $cm=(object)array('id'=>13,'course'=>42,
336 'completion'=>COMPLETION_TRACKING_AUTOMATIC);
b9c639d6 337
4e781c7b 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));
31fe61a2
SM
345 $c->expectOnce('get_tracked_users',array());
346 $c->setReturnValue('get_tracked_users',array(
4e781c7b 347 (object)array('id'=>100,'firstname'=>'Woot','lastname'=>'Plugh'),
348 (object)array('id'=>201,'firstname'=>'Vroom','lastname'=>'Xyzzy'),
349 ));
b9c639d6 350
4e781c7b 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));
b9c639d6 354
4e781c7b 355 $c->reset_all_state($cm);
b9c639d6 356
4e781c7b 357 $DB->tally();
358 $c->tally();
359 }
b9c639d6 360
4e781c7b 361 function test_get_data() {
362 global $DB,$SESSION;
b9c639d6 363
364 $c=new completion_info((object)array('id'=>42));
4e781c7b 365 $cm=(object)array('id'=>13,'course'=>42);
b9c639d6 366
4e781c7b 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));
b9c639d6 375
4e781c7b 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));
b9c639d6 385
4e781c7b 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]);
b9c639d6 393 // When checking time(), allow for second overlaps
4e781c7b 394 $this->assertTrue(time()-$SESSION->completioncache[42]['updated']<2);
b9c639d6 395
396 // 4. Current user, 'whole course', but from cache
4e781c7b 397 $result=$c->get_data($cm,true);
398 $this->assertEqual($sillyrecord,$result);
b9c639d6 399
4e781c7b 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;
b9c639d6 404 $SESSION->completioncache[39]['updated']=72; // Also a long time ago
4e781c7b 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);
b9c639d6 410 // Check that updated value is right, then fudge it to make next compare
4e781c7b 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);
b9c639d6 417
4e781c7b 418 // 6. Current user, 'whole course' and record not in cache
419 unset($SESSION->completioncache);
b9c639d6 420
4e781c7b 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
b9c639d6 425 ));
4e781c7b 426 $DB->expectAt(0,'get_records_sql',array(new IgnoreWhitespaceExpectation("
427SELECT
428 cmc.*
429FROM
49f6e5f4 430 {course_modules} cm
431 INNER JOIN {course_modules_completion} cmc ON cmc.coursemoduleid=cm.id
b9c639d6 432WHERE
4e781c7b 433 cm.course=? AND cmc.userid=?"),array(42,314159)));
434
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);
b9c639d6 438
4e781c7b 439 // Check result
440 $this->assertEqual($basicrecord,$result);
b9c639d6 441
4e781c7b 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);
b9c639d6 448
4e781c7b 449 $DB->tally();
450 }
b9c639d6 451
4e781c7b 452 function test_internal_set_data() {
453 global $DB,$SESSION;
b9c639d6 454
4e781c7b 455 $cm=(object)array('course'=>42,'id'=>13);
b9c639d6 456 $c=new completion_info((object)array('id'=>42));
457
4e781c7b 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);
b9c639d6 465
4e781c7b 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));
b9c639d6 472
4e781c7b 473 $DB->tally();
474 }
b9c639d6 475
4e781c7b 476 function test_get_activities() {
477 global $DB;
b9c639d6 478
4e781c7b 479 $c=new completion_info((object)array('id'=>42));
b9c639d6 480
4e781c7b 481 // Try with no activities
482 $DB->expectAt(0,'get_records_select',array('course_modules',
4454447d 483 'course=42 AND completion<>'.COMPLETION_TRACKING_NONE));
4e781c7b 484 $DB->setReturnValueAt(0,'get_records_select',array());
485 $result=$c->get_activities();
486 $this->assertEqual(array(),$result);
b9c639d6 487
4e781c7b 488 // Try with an activity (need to fake up modinfo for it as well)
489 $DB->expectAt(1,'get_records_select',array('course_modules',
4454447d 490 'course=42 AND completion<>'.COMPLETION_TRACKING_NONE));
4e781c7b 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);
b9c639d6 499
500 $DB->tally();
4e781c7b 501 }
b9c639d6 502
31fe61a2 503 // get_tracked_users() cannot easily be tested because it uses
4e781c7b 504 // get_role_users, so skipping that
b9c639d6 505
4e781c7b 506 function test_get_progress_all() {
507 global $DB;
b9c639d6 508
4e781c7b 509 $c=new completion_cutdown();
49f6e5f4 510 $c->__construct((object)array('id'=>42));
b9c639d6 511
512 // 1) Basic usage
31fe61a2
SM
513 $c->expectAt(0,'get_tracked_users',array(false, array(), 0, '', '', ''));
514 $c->setReturnValueAt(0,'get_tracked_users',array(
4e781c7b 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("
521SELECT
522 cmc.*
523FROM
49f6e5f4 524 {course_modules} cm
525 INNER JOIN {course_modules_completion} cmc ON cm.id=cmc.coursemoduleid
4e781c7b 526WHERE
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 )));
31fe61a2 533 $this->assertEqual(array(
4e781c7b 534
002f56ef 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)),
31fe61a2 539 ),$c->get_progress_all(false));
b9c639d6 540
4e781c7b 541 // 2) With more than 1,000 results
31fe61a2 542 $c->expectAt(1,'get_tracked_users',array(true, 3, 0, '', '', ''));
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 }
31fe61a2 553 $c->setReturnValueAt(1,'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)));
4e781c7b 577 $result=$c->get_progress_all(true,3);
4e781c7b 578 $resultok=true;
31fe61a2
SM
579 $resultok = $resultok && ($ids==array_keys($result));
580
581 foreach($result as $userid => $data) {
4e781c7b 582 $resultok = $resultok && $data->firstname=='frog';
583 $resultok = $resultok && $data->lastname==$userid;
b9c639d6 584 $resultok = $resultok && $data->id==$userid;
4e781c7b 585 $cms=$data->progress;
586 $resultok= $resultok && (array(13,14)==array_keys($cms));
587 $resultok= $resultok && ((object)array('userid'=>$userid,'coursemoduleid'=>13)==$cms[13]);
b9c639d6 588 $resultok= $resultok && ((object)array('userid'=>$userid,'coursemoduleid'=>14)==$cms[14]);
4e781c7b 589 }
590 $this->assertTrue($resultok);
b9c639d6 591
592 $DB->tally();
4e781c7b 593 $c->tally();
594 }
b9c639d6 595
4e781c7b 596 function test_inform_grade_changed() {
597 $c=new completion_cutdown();
49f6e5f4 598 $c->__construct((object)array('id'=>42));
b9c639d6 599
3d99b4ac 600 $cm=(object)array('course'=>42,'id'=>13,'completion'=>0,'completiongradeitemnumber'=>null);
4e781c7b 601 $item=(object)array('itemnumber'=>3);
602 $grade=(object)array('userid'=>31337);
b9c639d6 603
4e781c7b 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);
b9c639d6 608
4e781c7b 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);
b9c639d6 613
4e781c7b 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);
b9c639d6 619
4e781c7b 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);
b9c639d6 630
4e781c7b 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);
b9c639d6 637
4e781c7b 638 $c->tally();
639 }
640
641 function test_internal_get_grade_state() {
642 $item=new stdClass;
643 $grade=new stdClass;
b9c639d6 644
4e781c7b 645 $item->gradepass=4;
646 $item->hidden=0;
647 $grade->rawgrade=4.0;
648 $grade->finalgrade=null;
b9c639d6 649
4e781c7b 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));
b9c639d6 654
4e781c7b 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));
b9c639d6 660
4e781c7b 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));
b9c639d6 666
4e781c7b 667 // Item is hidden
668 $item->hidden=1;
669 $this->assertEqual(
670 COMPLETION_COMPLETE,
671 completion_info::internal_get_grade_state($item,$grade));
b9c639d6 672
4e781c7b 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 }
680}
8926f844 681