<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Contains unit tests for core_completion/cm_completion_details.
*
* @package core_completion
* @copyright 2021 Jun Pataleta <jun@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
declare(strict_types = 1);
namespace core_completion;
use advanced_testcase;
use cm_info;
use completion_info;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->libdir . '/completionlib.php');
/**
* Class for unit testing core_completion/cm_completion_details.
*
* @package core_completion
* @copyright 2021 Jun Pataleta <jun@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
> * @coversDefaultClass \core_completion\cm_completion_details
*/
class cm_completion_details_test extends advanced_testcase {
/** @var completion_info A completion object. */
protected $completioninfo = null;
/**
* Fetches a mocked cm_completion_details instance.
*
* @param int|null $completion The completion tracking mode for the module.
* @param array $completionoptions Completion options (e.g. completionview, completionusegrade, etc.)
* @param object $mockcompletiondata Mock data to be returned by get_data.
* @param string $modname The modname to set in the cm if a specific one is required.
* @return cm_completion_details
*/
protected function setup_data(?int $completion, array $completionoptions = [],
object $mockcompletiondata = null, $modname = 'somenonexistentmod'): cm_completion_details {
if (is_null($completion)) {
$completion = COMPLETION_TRACKING_AUTOMATIC;
}
// Mock a completion_info instance so we can simply mock the returns of completion_info::get_data() later.
$this->completioninfo = $this->getMockBuilder(completion_info::class)
->disableOriginalConstructor()
->getMock();
// Mock return of completion_info's is_enabled() method to match the expected completion tracking for the module.
$this->completioninfo->expects($this->any())
->method('is_enabled')
->willReturn($completion);
if (!empty($mockcompletiondata)) {
$this->completioninfo->expects($this->any())
->method('get_data')
->willReturn($mockcompletiondata);
}
// Build a mock cm_info instance.
$mockcminfo = $this->getMockBuilder(cm_info::class)
->disableOriginalConstructor()
->onlyMethods(['__get'])
->getMock();
// Mock the return of the magic getter method when fetching the cm_info object's customdata and instance values.
$mockcminfo->expects($this->any())
->method('__get')
->will($this->returnValueMap([
['completion', $completion],
['instance', 1],
['modname', $modname],
['completionview', $completionoptions['completionview'] ?? COMPLETION_VIEW_NOT_REQUIRED],
['completiongradeitemnumber', $completionoptions['completionusegrade'] ?? null],
> ['completionpassgrade', $completionoptions['completionpassgrade'] ?? null],
]));
return new cm_completion_details($this->completioninfo, $mockcminfo, 2);
}
/**
* Provides data for test_has_completion().
*
* @return array[]
*/
public function has_completion_provider(): array {
return [
'Automatic' => [
COMPLETION_TRACKING_AUTOMATIC, true
],
'Manual' => [
COMPLETION_TRACKING_MANUAL, true
],
'None' => [
COMPLETION_TRACKING_NONE, false
],
];
}
/**
* Test for has_completion().
*
> * @covers ::has_completion
* @dataProvider has_completion_provider
* @param int $completion The completion tracking mode.
* @param bool $expectedresult Expected result.
*/
public function test_has_completion(int $completion, bool $expectedresult) {
$cmcompletion = $this->setup_data($completion);
$this->assertEquals($expectedresult, $cmcompletion->has_completion());
}
/**
* Provides data for test_is_automatic().
*
* @return array[]
*/
public function is_automatic_provider(): array {
return [
'Automatic' => [
COMPLETION_TRACKING_AUTOMATIC, true
],
'Manual' => [
COMPLETION_TRACKING_MANUAL, false
],
'None' => [
COMPLETION_TRACKING_NONE, false
],
];
}
/**
* Test for is_available().
*
> * @covers ::is_automatic
* @dataProvider is_automatic_provider
* @param int $completion The completion tracking mode.
* @param bool $expectedresult Expected result.
*/
public function test_is_automatic(int $completion, bool $expectedresult) {
$cmcompletion = $this->setup_data($completion);
$this->assertEquals($expectedresult, $cmcompletion->is_automatic());
}
/**
> * Provides data for test_is_manual().
* Data provider for test_get_overall_completion().
> *
* @return array[]
> * @return array[]
*/
> */
public function overall_completion_provider(): array {
> public function is_manual_provider(): array {
return [
> return [
'Complete' => [COMPLETION_COMPLETE],
> 'Automatic' => [
'Incomplete' => [COMPLETION_INCOMPLETE],
> COMPLETION_TRACKING_AUTOMATIC, false
];
> ],
}
> 'Manual' => [
> COMPLETION_TRACKING_MANUAL, true
/**
> ],
* Test for get_overall_completion().
> 'None' => [
*
> COMPLETION_TRACKING_NONE, false
* @dataProvider overall_completion_provider
> ],
* @param int $state
> ];
*/
> }
public function test_get_overall_completion(int $state) {
>
$completiondata = (object)['completionstate' => $state];
> /**
$cmcompletion = $this->setup_data(COMPLETION_TRACKING_AUTOMATIC, [], $completiondata);
> * Test for is_manual().
$this->assertEquals($state, $cmcompletion->get_overall_completion());
> *
}
> * @covers ::is_manual
> * @dataProvider is_manual_provider
/**
> * @param int $completion The completion tracking mode.
* Data provider for test_get_details().
> * @param bool $expectedresult Expected result.
* @return array[]
> */
*/
> public function test_is_manual(int $completion, bool $expectedresult) {
public function get_details_provider() {
> $cmcompletion = $this->setup_data($completion);
return [
>
'No completion tracking' => [
> $this->assertEquals($expectedresult, $cmcompletion->is_manual());
COMPLETION_TRACKING_NONE, null, null, []
> }
],
>
'Manual completion tracking' => [
> /**
COMPLETION_TRACKING_MANUAL, null, null, []
> * @covers ::get_overall_completion
],
> * Data provider for test_is_overall_complete().
'Automatic, require view, not viewed' => [
> * @return array[]
COMPLETION_TRACKING_AUTOMATIC, COMPLETION_INCOMPLETE, null, [
> */
'completionview' => (object)[
> public static function is_overall_complete_provider(): array {
'status' => COMPLETION_INCOMPLETE,
> return [
'description' => get_string('detail_desc:view', 'completion'),
> 'Automatic, require view, not viewed' => [
]
> 'expected' => false,
]
> 'completion' => COMPLETION_TRACKING_AUTOMATIC,
],
> 'completionstate' => COMPLETION_INCOMPLETE,
'Automatic, require view, viewed' => [
> 'completionview' => COMPLETION_INCOMPLETE,
COMPLETION_TRACKING_AUTOMATIC, COMPLETION_COMPLETE, null, [
> 'completiongrade' => null,
'completionview' => (object)[
> 'completionpassgrade' => null,
'status' => COMPLETION_COMPLETE,
> ],
'description' => get_string('detail_desc:view', 'completion'),
> 'Automatic, require view, viewed' => [
]
> 'expected' => true,
]
> 'completion' => COMPLETION_TRACKING_AUTOMATIC,
],
> 'completionstate' => COMPLETION_COMPLETE,
'Automatic, require grade, incomplete' => [
> 'completionview' => COMPLETION_COMPLETE,
COMPLETION_TRACKING_AUTOMATIC, null, COMPLETION_INCOMPLETE, [
> 'completiongrade' => null,
'completionusegrade' => (object)[
> 'completionpassgrade' => null,
'status' => COMPLETION_INCOMPLETE,
> ],
'description' => get_string('detail_desc:receivegrade', 'completion'),
> 'Automatic, require grade, not graded' => [
]
> 'expected' => false,
]
> 'completion' => COMPLETION_TRACKING_AUTOMATIC,
],
> 'completionstate' => COMPLETION_INCOMPLETE,
'Automatic, require grade, complete' => [
> 'completionview' => null,
COMPLETION_TRACKING_AUTOMATIC, null, COMPLETION_COMPLETE, [
> 'completiongrade' => COMPLETION_INCOMPLETE,
'completionusegrade' => (object)[
> 'completionpassgrade' => null,
'status' => COMPLETION_COMPLETE,
> ],
'description' => get_string('detail_desc:receivegrade', 'completion'),
> 'Automatic, require grade, graded with fail' => [
]
> 'expected' => true,
]
> 'completion' => COMPLETION_TRACKING_AUTOMATIC,
],
> 'completionstate' => COMPLETION_COMPLETE_FAIL,
'Automatic, require view (complete) and grade (incomplete)' => [
> 'completionview' => null,
COMPLETION_TRACKING_AUTOMATIC, COMPLETION_COMPLETE, COMPLETION_INCOMPLETE, [
> 'completiongrade' => COMPLETION_COMPLETE_FAIL,
'completionview' => (object)[
> 'completionpassgrade' => null,
'status' => COMPLETION_COMPLETE,
> ],
'description' => get_string('detail_desc:view', 'completion'),
> 'Automatic, require grade, graded with passing' => [
],
> 'expected' => true,
'completionusegrade' => (object)[
> 'completion' => COMPLETION_TRACKING_AUTOMATIC,
'status' => COMPLETION_INCOMPLETE,
> 'completionstate' => COMPLETION_COMPLETE_PASS,
'description' => get_string('detail_desc:receivegrade', 'completion'),
> 'completionview' => null,
]
> 'completiongrade' => COMPLETION_COMPLETE_PASS,
]
> 'completionpassgrade' => null,
],
> ],
'Automatic, require view (incomplete) and grade (complete)' => [
> 'Automatic, require passgrade, not graded' => [
COMPLETION_TRACKING_AUTOMATIC, COMPLETION_INCOMPLETE, COMPLETION_COMPLETE, [
> 'expected' => false,
'completionview' => (object)[
> 'completion' => COMPLETION_TRACKING_AUTOMATIC,
'status' => COMPLETION_INCOMPLETE,
> 'completionstate' => COMPLETION_INCOMPLETE,
'description' => get_string('detail_desc:view', 'completion'),
> 'completionview' => null,
],
> 'completiongrade' => null,
'completionusegrade' => (object)[
> 'completionpassgrade' => COMPLETION_INCOMPLETE,
'status' => COMPLETION_COMPLETE,
> ],
'description' => get_string('detail_desc:receivegrade', 'completion'),
> 'Automatic, require passgrade, graded with fail' => [
]
> 'expected' => false,
]
> 'completion' => COMPLETION_TRACKING_AUTOMATIC,
],
> 'completionstate' => COMPLETION_COMPLETE_FAIL,
];
> 'completionview' => null,
}
> 'completiongrade' => null,
> 'completionpassgrade' => COMPLETION_COMPLETE_FAIL,
/**
> ],
* Test for \core_completion\cm_completion_details::get_details().
> 'Automatic, require passgrade, graded with passing' => [
*
> 'expected' => true,
* @dataProvider get_details_provider
> 'completion' => COMPLETION_TRACKING_AUTOMATIC,
* @param int $completion The completion tracking mode.
> 'completionstate' => COMPLETION_COMPLETE_PASS,
* @param int|null $completionview Completion status of the "view" completion condition.
> 'completionview' => null,
* @param int|null $completiongrade Completion status of the "must receive grade" completion condition.
> 'completiongrade' => null,
* @param array $expecteddetails Expected completion details returned by get_details().
> 'completionpassgrade' => COMPLETION_COMPLETE_PASS,
*/
> ],
public function test_get_details(int $completion, ?int $completionview, ?int $completiongrade, array $expecteddetails) {
> 'Manual, incomplete' => [
$options = [];
> 'expected' => false,
$getdatareturn = (object)[
> 'completion' => COMPLETION_TRACKING_MANUAL,
'viewed' => $completionview,
> 'completionstate' => COMPLETION_INCOMPLETE,
'completiongrade' => $completiongrade,
> ],
];
> 'Manual, complete' => [
> 'expected' => true,
if (!is_null($completionview)) {
> 'completion' => COMPLETION_TRACKING_MANUAL,
$options['completionview'] = true;
> 'completionstate' => COMPLETION_COMPLETE,
}
> ],
if (!is_null($completiongrade)) {
> 'None, incomplete' => [
$options['completionusegrade'] = true;
> 'expected' => false,
}
> 'completion' => COMPLETION_TRACKING_NONE,
> 'completionstate' => COMPLETION_INCOMPLETE,
$cmcompletion = $this->setup_data($completion, $options, $getdatareturn);
> ],
$this->assertEquals($expecteddetails, $cmcompletion->get_details());
> 'None, complete' => [
}
> 'expected' => false,
> 'completion' => COMPLETION_TRACKING_NONE,
/**
> 'completionstate' => COMPLETION_COMPLETE,
* Data provider for test_get_details().
> ],
* @return array[]
> ];
*/
> }
public function get_details_custom_order_provider() {
>
return [
> /**
'Custom and view/grade standard conditions, view first and grade last' => [
> * Test for is_overall_complete().
true,
> *
true,
> * @covers ::is_overall_complete
[
> * @dataProvider is_overall_complete_provider
'completionsubmit' => true,
> * @param bool $expected Expected result returned by is_overall_complete().
],
> * @param int $completion The completion tracking mode.
'assign',
> * @param int $completionstate The overall completion state.
['completionview', 'completionsubmit', 'completionusegrade'],
> * @param int|null $completionview Completion status of the "view" completion condition.
],
> * @param int|null $completiongrade Completion status of the "must receive grade" completion condition.
'Custom and view/grade standard conditions, grade not last' => [
> * @param int|null $completionpassgrade Completion status of the "must receive passing grade" completion condition.
true,
> */
true,
> public function test_is_overall_complete(
[
> bool $expected,
'completionminattempts' => 2,
> int $completion,
'completionusegrade' => 50,
> int $completionstate,
'completionpassorattemptsexhausted' => 1,
> ?int $completionview = null,
],
> ?int $completiongrade = null,
'quiz',
> ?int $completionpassgrade = null,
['completionview', 'completionminattempts', 'completionusegrade', 'completionpassorattemptsexhausted'],
> ): void {
],
> $options = [];
'Custom and grade standard conditions only, no view condition' => [
> $getdatareturn = (object)[
false,
> 'completionstate' => $completionstate,
true,
> 'viewed' => $completionview,
[
> 'completiongrade' => $completiongrade,
'completionsubmit' => true,
> 'passgrade' => $completionpassgrade,
],
> ];
'assign',
>
['completionsubmit', 'completionusegrade'],
> if (!is_null($completionview)) {
],
> $options['completionview'] = true;
'Custom and view standard conditions only, no grade condition' => [
> }
true,
> if (!is_null($completiongrade)) {
false,
> $options['completionusegrade'] = true;
[
> }
'completionsubmit' => true
> if (!is_null($completionpassgrade)) {
],
> $options['completionpassgrade'] = true;
'assign',
> }
['completionview', 'completionsubmit'],
>
],
> $cmcompletion = $this->setup_data($completion, $options, $getdatareturn);
'View and grade conditions only, activity with no custom conditions' => [
> $this->assertEquals($expected, $cmcompletion->is_overall_complete());
true,
> }
true,
>
[
> /**
< COMPLETION_TRACKING_NONE, null, null, []
> COMPLETION_TRACKING_NONE, null, null, null, []
< COMPLETION_TRACKING_MANUAL, null, null, []
> COMPLETION_TRACKING_MANUAL, null, null, null, []
< COMPLETION_TRACKING_AUTOMATIC, COMPLETION_INCOMPLETE, null, [
> COMPLETION_TRACKING_AUTOMATIC, COMPLETION_INCOMPLETE, null, null, [
< COMPLETION_TRACKING_AUTOMATIC, COMPLETION_COMPLETE, null, [
> COMPLETION_TRACKING_AUTOMATIC, COMPLETION_COMPLETE, null, null, [
< COMPLETION_TRACKING_AUTOMATIC, null, COMPLETION_INCOMPLETE, [
> COMPLETION_TRACKING_AUTOMATIC, null, COMPLETION_INCOMPLETE, null, [
< COMPLETION_TRACKING_AUTOMATIC, null, COMPLETION_COMPLETE, [
> COMPLETION_TRACKING_AUTOMATIC, null, COMPLETION_COMPLETE, null, [
< COMPLETION_TRACKING_AUTOMATIC, COMPLETION_COMPLETE, COMPLETION_INCOMPLETE, [
> COMPLETION_TRACKING_AUTOMATIC, COMPLETION_COMPLETE, COMPLETION_INCOMPLETE, null, [
< COMPLETION_TRACKING_AUTOMATIC, COMPLETION_INCOMPLETE, COMPLETION_COMPLETE, [
> COMPLETION_TRACKING_AUTOMATIC, COMPLETION_INCOMPLETE, COMPLETION_COMPLETE, null, [
false,
> 'Automatic, require grade, require pass grade, complete' => [
[
> COMPLETION_TRACKING_AUTOMATIC, null, COMPLETION_COMPLETE, COMPLETION_COMPLETE, [
'completionview' => true,
> 'completionusegrade' => (object)[
],
> 'status' => COMPLETION_COMPLETE,
'workshop',
> 'description' => get_string('detail_desc:receivegrade', 'completion'),
['completionview'],
> ],
],
> 'completionpassgrade' => (object)[
];
> 'status' => COMPLETION_COMPLETE,
}
> 'description' => get_string('detail_desc:receivepassgrade', 'completion'),
> ],
/**
> ]
* Test custom sort order is functioning in \core_completion\cm_completion_details::get_details().
> ],
*
> 'Automatic, require grade, require pass grade, incomplete' => [
* @dataProvider get_details_custom_order_provider
> COMPLETION_TRACKING_AUTOMATIC, null, COMPLETION_COMPLETE, COMPLETION_INCOMPLETE, [
* @param bool $completionview Completion status of the "view" completion condition.
> 'completionusegrade' => (object)[
* @param bool $completiongrade Completion status of the "must receive grade" completion condition.
> 'status' => COMPLETION_COMPLETE,
* @param array $customcompletionrules Custom completion requirements, along with their values.
> 'description' => get_string('detail_desc:receivegrade', 'completion'),
* @param string $modname The name of the module having data fetched.
> ],
* @param array $expectedorder The expected order of completion conditions returned about the module.
> 'completionpassgrade' => (object)[
*/
> 'status' => COMPLETION_INCOMPLETE,
public function test_get_details_custom_order(bool $completionview, bool $completiongrade, array $customcompletionrules,
> 'description' => get_string('detail_desc:receivepassgrade', 'completion'),
string $modname, array $expectedorder) {
> ],
> ]
$options['customcompletion'] = [];
> ],
$customcompletiondata = [];
> 'Automatic, require view (complete), require grade(complete), require pass grade(complete)' => [
> COMPLETION_TRACKING_AUTOMATIC, COMPLETION_COMPLETE, COMPLETION_COMPLETE, COMPLETION_COMPLETE, [
if ($completionview) {
> 'completionview' => (object)[
$options['completionview'] = true;
> 'status' => COMPLETION_COMPLETE,
}
> 'description' => get_string('detail_desc:view', 'completion'),
> ],
if ($completiongrade) {
> 'completionusegrade' => (object)[
$options['completionusegrade'] = true;
> 'status' => COMPLETION_COMPLETE,
}
> 'description' => get_string('detail_desc:receivegrade', 'completion'),
> ],
// Set up the completion rules for the completion info.
> 'completionpassgrade' => (object)[
foreach ($customcompletionrules as $customtype => $isenabled) {
> 'status' => COMPLETION_COMPLETE,
$customcompletiondata[$customtype] = COMPLETION_COMPLETE;
> 'description' => get_string('detail_desc:receivepassgrade', 'completion'),
}
> ],
> ]
$getdatareturn = (object)[
> ],
'viewed' => $completionview ? COMPLETION_COMPLETE : COMPLETION_INCOMPLETE,
> 'Automatic, require view (incomplete), require grade(complete), require pass grade(complete)' => [
'completiongrade' => $completiongrade ? COMPLETION_COMPLETE : COMPLETION_INCOMPLETE,
> COMPLETION_TRACKING_AUTOMATIC, COMPLETION_INCOMPLETE, COMPLETION_COMPLETE, COMPLETION_COMPLETE, [
'customcompletion' => $customcompletiondata,
> 'completionview' => (object)[
];
> 'status' => COMPLETION_INCOMPLETE,
> 'description' => get_string('detail_desc:view', 'completion'),
$cmcompletion = $this->setup_data(COMPLETION_TRACKING_AUTOMATIC, $options, $getdatareturn, $modname);
> ],
> 'completionusegrade' => (object)[
$this->completioninfo->expects($this->any())
> 'status' => COMPLETION_COMPLETE,
->method('get_data')
> 'description' => get_string('detail_desc:receivegrade', 'completion'),
->willReturn($getdatareturn);
> ],
> 'completionpassgrade' => (object)[
$fetcheddetails = $cmcompletion->get_details();
> 'status' => COMPLETION_COMPLETE,
> 'description' => get_string('detail_desc:receivepassgrade', 'completion'),
// Check the expected number of items are returned, and sorted in the correct order.
> ],
$this->assertCount(count($expectedorder), $fetcheddetails);
> ]
$this->assertTrue((array_keys($fetcheddetails) === $expectedorder));
> ],
}
> * @covers ::get_details
}
> * @param int|null $completionpassgrade Completion status of the "must receive passing grade" completion condition.
< public function test_get_details(int $completion, ?int $completionview, ?int $completiongrade, array $expecteddetails) {
> public function test_get_details(int $completion, ?int $completionview,
> ?int $completiongrade, ?int $completionpassgrade, array $expecteddetails) {
> 'passgrade' => $completionpassgrade,
> if (!is_null($completionpassgrade)) {
> $options['completionpassgrade'] = true;
> }
< * Data provider for test_get_details().
> * Data provider for test_get_details_custom_order().
> * @covers ::get_details