2 // This file is part of Moodle - http://moodle.org/
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
20 * @package core_completion
22 * @copyright 2008 Sam Marshall
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26 defined('MOODLE_INTERNAL') ||
die();
29 require_once($CFG->libdir
.'/completionlib.php');
32 class completionlib_testcase
extends basic_testcase
{
34 var $realdb, $realcfg, $realsession, $realuser;
36 protected function setUp() {
37 global $DB, $CFG, $SESSION, $USER;
41 $this->realcfg
= $CFG;
42 $this->realsession
= $SESSION;
43 $this->prevuser
= $USER;
45 $DB = $this->getMock(get_class($DB));
46 $CFG = clone($this->realcfg
);
47 $CFG->prefix
= 'test_';
48 $CFG->enablecompletion
= COMPLETION_ENABLED
;
49 $SESSION = new stdClass();
50 $USER = (object)array('id' =>314159);
53 protected function tearDown() {
54 global $DB,$CFG,$SESSION,$USER;
56 $CFG = $this->realcfg
;
57 $SESSION = $this->realsession
;
58 $USER = $this->prevuser
;
63 function test_is_enabled() {
67 $CFG->enablecompletion
= COMPLETION_DISABLED
;
68 $this->assertEquals(COMPLETION_DISABLED
, completion_info
::is_enabled_for_site());
69 $CFG->enablecompletion
= COMPLETION_ENABLED
;
70 $this->assertEquals(COMPLETION_ENABLED
, completion_info
::is_enabled_for_site());
73 $course = (object)array('id' =>13);
74 $c = new completion_info($course);
75 $course->enablecompletion
= COMPLETION_DISABLED
;
76 $this->assertEquals(COMPLETION_DISABLED
, $c->is_enabled());
77 $course->enablecompletion
= COMPLETION_ENABLED
;
78 $this->assertEquals(COMPLETION_ENABLED
, $c->is_enabled());
79 $CFG->enablecompletion
= COMPLETION_DISABLED
;
80 $this->assertEquals(COMPLETION_DISABLED
, $c->is_enabled());
84 $cm->completion
= COMPLETION_TRACKING_MANUAL
;
85 $this->assertEquals(COMPLETION_DISABLED
, $c->is_enabled($cm));
86 $CFG->enablecompletion
= COMPLETION_ENABLED
;
87 $course->enablecompletion
= COMPLETION_DISABLED
;
88 $this->assertEquals(COMPLETION_DISABLED
, $c->is_enabled($cm));
89 $course->enablecompletion
= COMPLETION_ENABLED
;
90 $this->assertEquals(COMPLETION_TRACKING_MANUAL
, $c->is_enabled($cm));
91 $cm->completion
= COMPLETION_TRACKING_NONE
;
92 $this->assertEquals(COMPLETION_TRACKING_NONE
, $c->is_enabled($cm));
93 $cm->completion
= COMPLETION_TRACKING_AUTOMATIC
;
94 $this->assertEquals(COMPLETION_TRACKING_AUTOMATIC
, $c->is_enabled($cm));
97 function test_update_state() {
99 $c = $this->getMock('completion_info', array('is_enabled','get_data','internal_get_state','internal_set_data'), array((object)array('id'=>42)));
100 $cm = (object)array('id'=>13, 'course'=>42);
102 // Not enabled, should do nothing
103 $c->expects($this->at(0))
104 ->method('is_enabled')
106 ->will($this->returnValue(false));
107 $c->update_state($cm);
109 // Enabled, but current state is same as possible result, do nothing
110 $current = (object)array('completionstate'=>COMPLETION_COMPLETE
);
111 $c->expects($this->at(0))
112 ->method('is_enabled')
114 ->will($this->returnValue(true));
115 $c->expects($this->at(1))
117 ->with($cm, false, 0)
118 ->will($this->returnValue($current));
119 $c->update_state($cm, COMPLETION_COMPLETE
);
121 // Enabled, but current state is a specific one and new state is just
122 // complete, so do nothing
123 $current->completionstate
= COMPLETION_COMPLETE_PASS
;
124 $c->expects($this->at(0))
125 ->method('is_enabled')
127 ->will($this->returnValue(true));
128 $c->expects($this->at(1))
130 ->with($cm, false, 0)
131 ->will($this->returnValue($current));
132 $c->update_state($cm, COMPLETION_COMPLETE
);
134 // Manual, change state (no change)
135 $cm = (object)array('id'=>13,'course'=>42, 'completion'=>COMPLETION_TRACKING_MANUAL
);
136 $current->completionstate
=COMPLETION_COMPLETE
;
137 $c->expects($this->at(0))
138 ->method('is_enabled')
140 ->will($this->returnValue(true));
141 $c->expects($this->at(1))
143 ->with($cm, false, 0)
144 ->will($this->returnValue($current));
145 $c->update_state($cm, COMPLETION_COMPLETE
);
147 // Manual, change state (change)
148 $c->expects($this->at(0))
149 ->method('is_enabled')
151 ->will($this->returnValue(true));
152 $c->expects($this->at(1))
154 ->with($cm, false, 0)
155 ->will($this->returnValue($current));
156 $changed = clone($current);
157 $changed->timemodified
= time();
158 $changed->completionstate
= COMPLETION_INCOMPLETE
;
159 $c->expects($this->at(2))
160 ->method('internal_set_data')
161 ->with($cm, $changed);
162 $c->update_state($cm, COMPLETION_INCOMPLETE
);
164 // Auto, change state
165 $cm = (object)array('id'=>13,'course'=>42, 'completion'=>COMPLETION_TRACKING_AUTOMATIC
);
166 $current = (object)array('completionstate'=>COMPLETION_COMPLETE
);
167 $c->expects($this->at(0))
168 ->method('is_enabled')
170 ->will($this->returnValue(true));
171 $c->expects($this->at(1))
173 ->with($cm, false, 0)
174 ->will($this->returnValue($current));
175 $c->expects($this->at(2))
176 ->method('internal_get_state')
177 ->will($this->returnValue(COMPLETION_COMPLETE_PASS
));
178 $changed = clone($current);
179 $changed->timemodified
= time();
180 $changed->completionstate
= COMPLETION_COMPLETE_PASS
;
181 $c->expects($this->at(3))
182 ->method('internal_set_data')
183 ->with($cm, $changed);
184 $c->update_state($cm, COMPLETION_COMPLETE_PASS
);
187 function test_internal_get_state() {
190 $c = $this->getMock('completion_info', array('internal_get_grade_state'), array((object)array('id'=>42)));
191 $cm = (object)array('id'=>13, 'course'=>42, 'completiongradeitemnumber'=>null);
193 // If view is required, but they haven't viewed it yet
194 $cm->completionview
= COMPLETION_VIEW_REQUIRED
;
195 $current = (object)array('viewed'=>COMPLETION_NOT_VIEWED
);
196 $this->assertEquals(COMPLETION_INCOMPLETE
, $c->internal_get_state($cm, 123, $current));
198 // OK set view not required
199 $cm->completionview
= COMPLETION_VIEW_NOT_REQUIRED
;
201 // Test not getting module name
202 $cm->modname
='label';
203 $this->assertEquals(COMPLETION_COMPLETE
, $c->internal_get_state($cm, 123, $current));
205 // Test getting module name
208 /** @var $DB PHPUnit_Framework_MockObject_MockObject */
209 $DB->expects($this->once())
210 ->method('get_field')
211 ->with('modules', 'name', array('id'=>13))
212 ->will($this->returnValue('lable'));
213 $this->assertEquals(COMPLETION_COMPLETE
, $c->internal_get_state($cm, 123, $current));
215 // Note: This function is not fully tested (including kind of the main
217 // * the grade_item/grade_grade calls are static and can't be mocked
218 // * the plugin_supports call is static and can't be mocked
221 function test_set_module_viewed() {
223 $c = $this->getMock('completion_info',
224 array('delete_all_state', 'get_tracked_users', 'update_state', 'internal_get_grade_state', 'is_enabled', 'get_data', 'internal_get_state', 'internal_set_data'),
225 array((object)array('id'=>42)));
226 $cm = (object)array('id'=>13, 'course'=>42);
228 // Not tracking completion, should do nothing
229 $cm->completionview
= COMPLETION_VIEW_NOT_REQUIRED
;
230 $c->set_module_viewed($cm);
232 // Tracking completion but completion is disabled, should do nothing
233 $cm->completionview
= COMPLETION_VIEW_REQUIRED
;
234 $c->expects($this->at(0))
235 ->method('is_enabled')
237 ->will($this->returnValue(false));
238 $c->set_module_viewed($cm);
240 // Now it's enabled, we expect it to get data. If data already has
241 // viewed, still do nothing
242 $c->expects($this->at(0))
243 ->method('is_enabled')
245 ->will($this->returnValue(true));
246 $c->expects($this->at(1))
249 ->will($this->returnValue((object)array('viewed'=>COMPLETION_VIEWED
)));
250 $c->set_module_viewed($cm);
252 // OK finally one that hasn't been viewed, now it should set it viewed
254 $c->expects($this->at(0))
255 ->method('is_enabled')
257 ->will($this->returnValue(true));
258 $c->expects($this->at(1))
261 ->will($this->returnValue((object)array('viewed'=>COMPLETION_NOT_VIEWED
)));
262 $c->expects($this->at(2))
263 ->method('internal_set_data')
264 ->with($cm, (object)array('viewed'=>COMPLETION_VIEWED
));
265 $c->expects($this->at(3))
266 ->method('update_state')
267 ->with($cm, COMPLETION_COMPLETE
, 1337);
268 $c->set_module_viewed($cm, 1337);
271 function test_count_user_data() {
274 $course = (object)array('id'=>13);
275 $cm = (object)array('id'=>42);
277 /** @var $DB PHPUnit_Framework_MockObject_MockObject */
278 $DB->expects($this->at(0))
279 ->method('get_field_sql')
280 ->will($this->returnValue(666));
283 $DB->expectOnce('get_field_sql',array(new IgnoreWhitespaceExpectation("SELECT
286 {course_modules_completion}
288 coursemoduleid=? AND completionstate<>0"),array(42)));
291 $c = new completion_info($course);
292 $this->assertEquals(666, $c->count_user_data($cm));
295 function test_delete_all_state() {
296 global $DB, $SESSION;
298 $course = (object)array('id'=>13);
299 $cm = (object)array('id'=>42,'course'=>13);
300 $c = new completion_info($course);
302 // Check it works ok without data in session
303 /** @var $DB PHPUnit_Framework_MockObject_MockObject */
304 $DB->expects($this->at(0))
305 ->method('delete_records')
306 ->with('course_modules_completion', array('coursemoduleid'=>42))
307 ->will($this->returnValue(true));
308 $c->delete_all_state($cm);
310 // Build up a session to check it deletes the right bits from it
311 // (and not other bits)
312 $SESSION->completioncache
=array();
313 $SESSION->completioncache
[13]=array();
314 $SESSION->completioncache
[13][42]='foo';
315 $SESSION->completioncache
[13][43]='foo';
316 $SESSION->completioncache
[14]=array();
317 $SESSION->completioncache
[14][42]='foo';
318 $DB->expects($this->at(0))
319 ->method('delete_records')
320 ->with('course_modules_completion', array('coursemoduleid'=>42))
321 ->will($this->returnValue(true));
322 $c->delete_all_state($cm);
323 $this->assertEquals(array(13=>array(43=>'foo'), 14=>array(42=>'foo')), $SESSION->completioncache
);
326 function test_reset_all_state() {
329 $c = $this->getMock('completion_info',
330 array('delete_all_state', 'get_tracked_users','update_state', 'internal_get_grade_state', 'is_enabled', 'get_data', 'internal_get_state', 'internal_set_data'),
331 array((object)array('id'=>42)));
333 $cm = (object)array('id'=>13, 'course'=>42, 'completion'=>COMPLETION_TRACKING_AUTOMATIC
);
335 /** @var $DB PHPUnit_Framework_MockObject_MockObject */
336 $DB->expects($this->at(0))
337 ->method('get_recordset')
338 ->will($this->returnValue(
339 new completion_test_fake_recordset(array((object)array('id'=>1, 'userid'=>100),(object)array('id'=>2, 'userid'=>101)))));
341 $c->expects($this->at(0))
342 ->method('delete_all_state')
345 $c->expects($this->at(1))
346 ->method('get_tracked_users')
347 ->will($this->returnValue(array(
348 (object)array('id'=>100,'firstname'=>'Woot','lastname'=>'Plugh'),
349 (object)array('id'=>201,'firstname'=>'Vroom','lastname'=>'Xyzzy'))));
351 $c->expects($this->at(2))
352 ->method('update_state')
353 ->with($cm,COMPLETION_UNKNOWN
, 100);
354 $c->expects($this->at(3))
355 ->method('update_state')
356 ->with($cm,COMPLETION_UNKNOWN
, 101);
357 $c->expects($this->at(4))
358 ->method('update_state')
359 ->with($cm,COMPLETION_UNKNOWN
, 201);
361 $c->reset_all_state($cm);
364 function test_get_data() {
365 global $DB, $SESSION;
367 $c = new completion_info((object)array('id'=>42));
368 $cm = (object)array('id'=>13, 'course'=>42);
370 // 1. Not current user, record exists
371 $sillyrecord = (object)array('frog'=>'kermit');
373 /** @var $DB PHPUnit_Framework_MockObject_MockObject */
374 $DB->expects($this->at(0))
375 ->method('get_record')
376 ->with('course_modules_completion', array('coursemoduleid'=>13,'userid'=>123))
377 ->will($this->returnValue($sillyrecord));
378 $result = $c->get_data($cm,false,123);
379 $this->assertEquals($sillyrecord, $result);
380 $this->assertTrue(empty($SESSION->completioncache
));
382 // 2. Not current user, default record, wholecourse (ignored)
383 $DB->expects($this->at(0))
384 ->method('get_record')
385 ->with('course_modules_completion', array('coursemoduleid'=>13,'userid'=>123))
386 ->will($this->returnValue(false));
387 $result=$c->get_data($cm,true,123);
388 $this->assertEquals((object)array(
389 'id'=>'0','coursemoduleid'=>13,'userid'=>123,'completionstate'=>0,
390 'viewed'=>0,'timemodified'=>0),$result);
391 $this->assertTrue(empty($SESSION->completioncache
));
393 // 3. Current user, single record, not from cache
394 $DB->expects($this->at(0))
395 ->method('get_record')
396 ->with('course_modules_completion', array('coursemoduleid'=>13,'userid'=>314159))
397 ->will($this->returnValue($sillyrecord));
398 $result = $c->get_data($cm);
399 $this->assertEquals($sillyrecord, $result);
400 $this->assertEquals($sillyrecord, $SESSION->completioncache
[42][13]);
401 // When checking time(), allow for second overlaps
402 $this->assertTrue(time()-$SESSION->completioncache
[42]['updated']<2);
404 // 4. Current user, 'whole course', but from cache
405 $result = $c->get_data($cm, true);
406 $this->assertEquals($sillyrecord, $result);
408 // 5. Current user, single record, cache expired
409 $SESSION->completioncache
[42]['updated']=37; // Quite a long time ago
411 $SESSION->completioncache
[17]['updated']=$now;
412 $SESSION->completioncache
[39]['updated']=72; // Also a long time ago
413 $DB->expects($this->at(0))
414 ->method('get_record')
415 ->with('course_modules_completion', array('coursemoduleid'=>13,'userid'=>314159))
416 ->will($this->returnValue($sillyrecord));
417 $result = $c->get_data($cm, false);
418 $this->assertEquals($sillyrecord, $result);
420 // Check that updated value is right, then fudge it to make next compare
422 $this->assertTrue(time()-$SESSION->completioncache
[42]['updated']<2);
423 $SESSION->completioncache
[42]['updated']=$now;
424 // Check things got expired from cache
425 $this->assertEquals(array(42=>array(13=>$sillyrecord, 'updated'=>$now), 17=>array('updated'=>$now)), $SESSION->completioncache
);
427 // 6. Current user, 'whole course' and record not in cache
428 unset($SESSION->completioncache
);
430 // Scenario: Completion data exists for one CMid
431 $basicrecord = (object)array('coursemoduleid'=>13);
432 $DB->expects($this->at(0))
433 ->method('get_records_sql')
434 ->will($this->returnValue(array('1'=>$basicrecord)));
437 $DB->expectAt(0,'get_records_sql',array(new IgnoreWhitespaceExpectation("
442 INNER JOIN {course_modules_completion} cmc ON cmc.coursemoduleid=cm.id
444 cm.course=? AND cmc.userid=?"),array(42,314159)));
446 // There are two CMids in total, the one we had data for and another one
447 $modinfo = new stdClass();
448 $modinfo->cms
= array((object)array('id'=>13), (object)array('id'=>14));
449 $result = $c->get_data($cm, true, 0, $modinfo);
452 $this->assertEquals($basicrecord, $result);
454 // Check the cache contents
455 $this->assertTrue(time()-$SESSION->completioncache
[42]['updated']<2);
456 $SESSION->completioncache
[42]['updated'] = $now;
457 $this->assertEquals(array(42=>array(13=>$basicrecord, 14=>(object)array(
458 'id'=>'0', 'coursemoduleid'=>14, 'userid'=>314159, 'completionstate'=>0,
459 'viewed'=>0, 'timemodified'=>0), 'updated'=>$now)), $SESSION->completioncache
);
462 function test_internal_set_data() {
463 global $DB, $SESSION;
465 $cm = (object)array('course' => 42,'id' => 13);
466 $c = new completion_info((object)array('id' => 42));
468 // 1) Test with new data
469 $data = (object)array('id'=>0, 'userid' => 314159, 'coursemoduleid' => 99);
470 $DB->expects($this->at(0))
471 ->method('start_delegated_transaction')
472 ->will($this->returnValue($this->getMock('moodle_transaction', array(), array($DB))));
474 $DB->expects($this->at(1))
475 ->method('get_field')
476 ->with('course_modules_completion', 'id', array('coursemoduleid'=>99, 'userid'=>314159))
477 ->will($this->returnValue(false));
479 $DB->expects($this->at(2))
480 ->method('insert_record')
481 ->will($this->returnValue(4));
483 $c->internal_set_data($cm, $data);
484 $this->assertEquals(4, $data->id
);
485 $this->assertEquals(array(42 => array(13 => $data)), $SESSION->completioncache
);
487 // 2) Test with existing data and for different user (not cached)
488 unset($SESSION->completioncache
);
489 $d2 = (object)array('id' => 7, 'userid' => 17, 'coursemoduleid' => 66);
490 $DB->expects($this->at(0))
491 ->method('start_delegated_transaction')
492 ->will($this->returnValue($this->getMock('moodle_transaction', array(), array($DB))));
493 $DB->expects($this->at(1))
494 ->method('update_record')
495 ->with('course_modules_completion', $d2);
496 $c->internal_set_data($cm, $d2);
497 $this->assertFalse(isset($SESSION->completioncache
));
499 // 3) Test where it THINKS the data is new (from cache) but actually
500 // in the database it has been set since
501 // 1) Test with new data
502 $data = (object)array('id'=>0, 'userid' => 314159, 'coursemoduleid' => 99);
503 $d3 = (object)array('id' => 13, 'userid' => 314159, 'coursemoduleid' => 99);
504 $DB->expects($this->at(0))
505 ->method('start_delegated_transaction')
506 ->will($this->returnValue($this->getMock('moodle_transaction', array(), array($DB))));
507 $DB->expects($this->at(1))
508 ->method('get_field')
509 ->with('course_modules_completion', 'id', array('coursemoduleid' => 99, 'userid' => 314159))
510 ->will($this->returnValue(13));
511 $DB->expects($this->at(2))
512 ->method('update_record')
513 ->with('course_modules_completion', $d3);
514 $c->internal_set_data($cm, $data);
517 function test_get_activities() {
520 $c = new completion_info((object)array('id'=>42));
522 // Try with no activities
523 $DB->expects($this->at(0))
524 ->method('get_records_select')
525 ->with('course_modules', 'course=42 AND completion<>'.COMPLETION_TRACKING_NONE
)
526 ->will($this->returnValue(array()));
527 $result = $c->get_activities();
528 $this->assertEquals(array(), $result);
530 // Try with an activity (need to fake up modinfo for it as well)
531 $DB->expects($this->at(0))
532 ->method('get_records_select')
533 ->with('course_modules', 'course=42 AND completion<>'.COMPLETION_TRACKING_NONE
)
534 ->will($this->returnValue(array(13=>(object)array('id'=>13))));
535 $modinfo = new stdClass
;
536 $modinfo->sections
= array(array(1, 2, 3), array(12, 13, 14));
537 $modinfo->cms
[13] = (object)array('modname'=>'frog', 'name'=>'kermit');
538 $result = $c->get_activities($modinfo);
539 $this->assertEquals(array(13=>(object)array('id'=>13, 'modname'=>'frog', 'name'=>'kermit')), $result);
542 // get_tracked_users() cannot easily be tested because it uses
543 // get_role_users, so skipping that
545 function test_get_progress_all() {
548 $c = $this->getMock('completion_info',
549 array('delete_all_state', 'get_tracked_users', 'update_state', 'internal_get_grade_state', 'is_enabled', 'get_data', 'internal_get_state', 'internal_set_data'),
550 array((object)array('id'=>42)));
553 $c->expects($this->at(0))
554 ->method('get_tracked_users')
555 ->with(false, array(), 0, '', '', '', null)
556 ->will($this->returnValue(array(
557 (object)array('id'=>100, 'firstname'=>'Woot', 'lastname'=>'Plugh'),
558 (object)array('id'=>201, 'firstname'=>'Vroom', 'lastname'=>'Xyzzy'))));
559 $DB->expects($this->at(0))
560 ->method('get_in_or_equal')
561 ->with(array(100, 201))
562 ->will($this->returnValue(array(' IN (100, 201)', array())));
563 $progress1 = (object)array('userid'=>100, 'coursemoduleid'=>13);
564 $progress2 = (object)array('userid'=>201, 'coursemoduleid'=>14);
565 $DB->expects($this->at(1))
566 ->method('get_recordset_sql')
567 ->will($this->returnValue(new completion_test_fake_recordset(array($progress1, $progress2))));
570 $DB->expectAt(0, 'get_recordset_sql', array(new IgnoreWhitespaceExpectation("
575 INNER JOIN {course_modules_completion} cmc ON cm.id = cmc.coursemoduleid
577 cm.course = ? AND cmc.userid IN (100, 201)"), array(42)));
580 $this->assertEquals(array(
581 100 => (object)array('id'=>100, 'firstname'=>'Woot', 'lastname'=>'Plugh',
582 'progress'=>array(13=>$progress1)),
583 201 => (object)array('id'=>201, 'firstname'=>'Vroom', 'lastname'=>'Xyzzy',
584 'progress'=>array(14=>$progress2)),
585 ), $c->get_progress_all(false));
587 // 2) With more than 1, 000 results
591 for($i = 100;$i<2000;$i++
) {
592 $tracked[] = (object)array('id'=>$i, 'firstname'=>'frog', 'lastname'=>$i);
594 $progress[] = (object)array('userid'=>$i, 'coursemoduleid'=>13);
595 $progress[] = (object)array('userid'=>$i, 'coursemoduleid'=>14);
597 $c->expects($this->at(0))
598 ->method('get_tracked_users')
599 ->with(true, 3, 0, '', '', '', null)
600 ->will($this->returnValue($tracked));
601 $DB->expects($this->at(0))
602 ->method('get_in_or_equal')
603 ->with(array_slice($ids, 0, 1000))
604 ->will($this->returnValue(array(' IN whatever', array())));
605 $DB->expects($this->at(1))
606 ->method('get_recordset_sql')
607 ->will($this->returnValue(new completion_test_fake_recordset(array_slice($progress, 0, 1000))));
610 $DB->expectAt(1, 'get_recordset_sql', array(new IgnoreWhitespaceExpectation("
615 INNER JOIN {course_modules_completion} cmc ON cm.id = cmc.coursemoduleid
617 cm.course = ? AND cmc.userid IN whatever"), array(42)));
620 $DB->expects($this->at(2))
621 ->method('get_in_or_equal')
622 ->with(array_slice($ids, 1000))
623 ->will($this->returnValue(array(' IN whatever2', array())));
624 $DB->expects($this->at(3))
625 ->method('get_recordset_sql')
626 ->will($this->returnValue(new completion_test_fake_recordset(array_slice($progress, 1000))));
628 $result = $c->get_progress_all(true, 3);
630 $resultok = $resultok && ($ids == array_keys($result));
632 foreach($result as $userid => $data) {
633 $resultok = $resultok && $data->firstname
== 'frog';
634 $resultok = $resultok && $data->lastname
== $userid;
635 $resultok = $resultok && $data->id
== $userid;
636 $cms = $data->progress
;
637 $resultok = $resultok && (array(13, 14) == array_keys($cms));
638 $resultok = $resultok && ((object)array('userid'=>$userid, 'coursemoduleid'=>13) == $cms[13]);
639 $resultok = $resultok && ((object)array('userid'=>$userid, 'coursemoduleid'=>14) == $cms[14]);
641 $this->assertTrue($resultok);
644 function test_inform_grade_changed() {
645 $c = $this->getMock('completion_info',
646 array('delete_all_state', 'get_tracked_users', 'update_state', 'internal_get_grade_state', 'is_enabled', 'get_data', 'internal_get_state', 'internal_set_data'),
647 array((object)array('id'=>42)));
649 $cm = (object)array('course'=>42, 'id'=>13, 'completion'=>0, 'completiongradeitemnumber'=>null);
650 $item = (object)array('itemnumber'=>3, 'gradepass'=>1, 'hidden'=>0);
651 $grade = (object)array('userid'=>31337, 'finalgrade'=>0, 'rawgrade'=>0);
653 // Not enabled (should do nothing)
654 $c->expects($this->at(0))
655 ->method('is_enabled')
657 ->will($this->returnValue(false));
658 $c->inform_grade_changed($cm, $item, $grade, false);
660 // Enabled but still no grade completion required, should still do nothing
661 $c->expects($this->at(0))
662 ->method('is_enabled')
664 ->will($this->returnValue(true));
665 $c->inform_grade_changed($cm, $item, $grade, false);
667 // Enabled and completion required but item number is wrong, does nothing
668 $cm = (object)array('course'=>42, 'id'=>13, 'completion'=>0, 'completiongradeitemnumber'=>7);
669 $c->expects($this->at(0))
670 ->method('is_enabled')
672 ->will($this->returnValue(true));
673 $c->inform_grade_changed($cm, $item, $grade, false);
675 // Enabled and completion required and item number right. It is supposed
676 // to call update_state with the new potential state being obtained from
677 // internal_get_grade_state.
678 $cm = (object)array('course'=>42, 'id'=>13, 'completion'=>0, 'completiongradeitemnumber'=>3);
679 $grade = (object)array('userid'=>31337, 'finalgrade'=>1, 'rawgrade'=>0);
680 $c->expects($this->at(0))
681 ->method('is_enabled')
683 ->will($this->returnValue(true));
684 $c->expects($this->at(1))
685 ->method('update_state')
686 ->with($cm, COMPLETION_COMPLETE_PASS
, 31337)
687 ->will($this->returnValue(true));
688 $c->inform_grade_changed($cm, $item, $grade, false);
690 // Same as above but marked deleted. It is supposed to call update_state
691 // with new potential state being COMPLETION_INCOMPLETE
692 $cm = (object)array('course'=>42, 'id'=>13, 'completion'=>0, 'completiongradeitemnumber'=>3);
693 $grade = (object)array('userid'=>31337, 'finalgrade'=>1, 'rawgrade'=>0);
694 $c->expects($this->at(0))
695 ->method('is_enabled')
697 ->will($this->returnValue(true));
698 $c->expects($this->at(1))
699 ->method('update_state')
700 ->with($cm, COMPLETION_INCOMPLETE
, 31337)
701 ->will($this->returnValue(true));
702 $c->inform_grade_changed($cm, $item, $grade, true);
705 function test_internal_get_grade_state() {
706 $item = new stdClass
;
707 $grade = new stdClass
;
709 $item->gradepass
= 4;
711 $grade->rawgrade
= 4.0;
712 $grade->finalgrade
= null;
714 // Grade has pass mark and is not hidden, user passes
716 COMPLETION_COMPLETE_PASS
,
717 completion_info
::internal_get_grade_state($item, $grade));
719 // Same but user fails
720 $grade->rawgrade
= 3.9;
722 COMPLETION_COMPLETE_FAIL
,
723 completion_info
::internal_get_grade_state($item, $grade));
725 // User fails on raw grade but passes on final
726 $grade->finalgrade
= 4.0;
728 COMPLETION_COMPLETE_PASS
,
729 completion_info
::internal_get_grade_state($item, $grade));
735 completion_info
::internal_get_grade_state($item, $grade));
737 // Item isn't hidden but has no pass mark
739 $item->gradepass
= 0;
742 completion_info
::internal_get_grade_state($item, $grade));
747 class completion_test_fake_recordset
implements Iterator
{
751 function __construct($values) {
752 $this->values
= $values;
757 return $this->values
[$this->index
];
761 return $this->values
[$this->index
];
773 return count($this->values
) > $this->index
;
777 $this->closed
= true;
780 function was_closed() {
781 return $this->closed
;