Merge branch 'w16_MDL-32400_m23_phpunit4' of git://github.com/skodak/moodle

This commit is contained in:
Dan Poltawski 2012-04-16 16:12:35 +08:00
commit 54e4d1b3a1
24 changed files with 1075 additions and 222 deletions

View File

@ -1,22 +0,0 @@
@ECHO OFF
ECHO Initialising Moodle PHPUnit test environment...
CALL php %~dp0\util.php --diag > NUL 2>&1
IF ERRORLEVEL 133 GOTO drop
IF ERRORLEVEL 132 GOTO install
IF ERRORLEVEL 1 GOTO unknown
GOTO done
:drop
CALL php %~dp0\util.php --drop
IF ERRORLEVEL 1 GOTO done
:install
CALL php %~dp0\util.php --install
GOTO done
:unknown
CALL php %~dp0\util.php --diag
:done

View File

@ -43,13 +43,13 @@ exec("php util.php --diag", $output, $code);
if ($code == 0) {
// everything is ready
} else if ($code == 132) {
} else if ($code == PHPUNIT_EXITCODE_INSTALL) {
passthru("php util.php --install", $code);
if ($code != 0) {
exit($code);
}
} else if ($code == 133) {
} else if ($code == PHPUNIT_EXITCODE_REINSTALL) {
passthru("php util.php --drop", $code);
passthru("php util.php --install", $code);
if ($code != 0) {

View File

@ -1,31 +0,0 @@
#!/bin/bash
SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done
CLIDIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
UTIL="$CLIDIR/util.php"
echo "Initialising Moodle PHPUnit test environment..."
DIGERROR=`php $UTIL --diag`
DIAG=$?
if [ $DIAG -eq 132 ] ; then
php $UTIL --install
else
if [ $DIAG -eq 133 ] ; then
php $UTIL --drop
RESULT=$?
if [ $RESULT -gt 0 ] ; then
exit $RESULT
fi
php $UTIL --install
else
if [ $DIAG -gt 0 ] ; then
echo $DIGERROR
exit $DIAG
fi
fi
fi
php $UTIL --buildconfig

View File

@ -17,14 +17,7 @@
/**
* PHPUnit related utilities.
*
* Exit codes:
* 0 - success
* 1 - general error
* 130 - missing PHPUnit library error
* 131 - configuration problem
* 132 - install new test database
* 133 - drop existing data before installing
* 134 - can not create main phpunit.xml
* Exit codes: {@see phpunit_bootstrap_error()}
*
* @package tool_phpunit
* @copyright 2012 Petr Skoda {@link http://skodak.org}
@ -72,12 +65,8 @@ if ($options['phpunitdir']) {
}
// verify PHPUnit libs are loaded
if (!@include_once('PHPUnit/Autoload.php')) {
phpunit_bootstrap_error(130);
}
if (!@include_once('PHPUnit/Extensions/Database/Autoload.php')) {
phpunit_bootstrap_error(130);
if (!include_once('PHPUnit/Autoload.php')) {
phpunit_bootstrap_error(PHPUNIT_EXITCODE_PHPUNITMISSING);
}
if ($options['run']) {
@ -147,7 +136,7 @@ if ($diag) {
if (phpunit_util::build_config_file()) {
exit(0);
} else {
phpunit_bootstrap_error(134);
phpunit_bootstrap_error(PHPUNIT_EXITCODE_CONFIGWARNING, 'Can not create phpunit.xml configuration file, verify dirroot permissions');
}
} else if ($drop) {

View File

@ -67,7 +67,7 @@ if ($execute) {
if ($code == 0) {
// everything is ready
} else if ($code == 132) {
} else if ($code == PHPUNIT_EXITCODE_INSTALL) {
tool_phpunit_header();
echo $OUTPUT->box_start('generalbox');
echo '<pre>';
@ -87,7 +87,7 @@ if ($execute) {
echo $OUTPUT->footer();
die();
} else if ($code == 133) {
} else if ($code == PHPUNIT_EXITCODE_REINSTALL) {
tool_phpunit_header();
echo $OUTPUT->box_start('generalbox');
echo '<pre>';

View File

@ -262,6 +262,9 @@ class grade_base_testcase extends advanced_testcase {
protected function load_grade_items() {
global $DB;
// purge all items created by module generators
$DB->delete_records('grade_items', array('itemtype'=>'mod'));
$course_category = grade_category::fetch_course_category($this->course->id);
// id = 0

View File

@ -18,13 +18,7 @@
* Prepares PHPUnit environment, the phpunit.xml configuration
* must specify this file as bootstrap.
*
* Exit codes:
* 0 - success
* 1 - general error
* 130 - missing PHPUnit library error
* 131 - configuration problem
* 132 - install new test database
* 133 - drop existing data before installing
* Exit codes: {@see phpunit_bootstrap_error()}
*
* @package core
* @category phpunit
@ -63,10 +57,14 @@ $phpunitversion = PHPUnit_Runner_Version::id();
if ($phpunitversion === '@package_version@') {
// library checked out from git, let's hope dev knows that 3.6.0 is required
} else if (version_compare($phpunitversion, '3.6.0', 'lt')) {
phpunit_bootstrap_error(129, $phpunitversion);
phpunit_bootstrap_error(PHPUNIT_EXITCODE_PHPUNITWRONG, $phpunitversion);
}
unset($phpunitversion);
if (!include_once('PHPUnit/Extensions/Database/Autoload.php')) {
phpunit_bootstrap_error(PHPUNIT_EXITCODE_PHPUNITEXTMISSING, 'phpunit/DbUnit');
}
define('NO_OUTPUT_BUFFERING', true);
// only load CFG from config.php, stop ASAP in lib/setup.php
@ -93,16 +91,16 @@ if (isset($CFG->phpunit_directorypermissions)) {
}
$CFG->filepermissions = ($CFG->directorypermissions & 0666);
if (!isset($CFG->phpunit_dataroot)) {
phpunit_bootstrap_error(131, 'Missing $CFG->phpunit_dataroot in config.php, can not run tests!');
phpunit_bootstrap_error(PHPUNIT_EXITCODE_CONFIGERROR, 'Missing $CFG->phpunit_dataroot in config.php, can not run tests!');
}
if (isset($CFG->dataroot) and $CFG->phpunit_dataroot === $CFG->dataroot) {
phpunit_bootstrap_error(131, '$CFG->dataroot and $CFG->phpunit_dataroot must not be identical, can not run tests!');
phpunit_bootstrap_error(PHPUNIT_EXITCODE_CONFIGERROR, '$CFG->dataroot and $CFG->phpunit_dataroot must not be identical, can not run tests!');
}
if (!file_exists($CFG->phpunit_dataroot)) {
mkdir($CFG->phpunit_dataroot, $CFG->directorypermissions);
}
if (!is_dir($CFG->phpunit_dataroot)) {
phpunit_bootstrap_error(131, '$CFG->phpunit_dataroot directory can not be created, can not run tests!');
phpunit_bootstrap_error(PHPUNIT_EXITCODE_CONFIGERROR, '$CFG->phpunit_dataroot directory can not be created, can not run tests!');
}
if (!is_writable($CFG->phpunit_dataroot)) {
@ -115,7 +113,7 @@ if (!is_writable($CFG->phpunit_dataroot)) {
}
}
if (!is_writable($CFG->phpunit_dataroot)) {
phpunit_bootstrap_error(131, '$CFG->phpunit_dataroot directory is not writable, can not run tests!');
phpunit_bootstrap_error(PHPUNIT_EXITCODE_CONFIGERROR, '$CFG->phpunit_dataroot directory is not writable, can not run tests!');
}
}
if (!file_exists("$CFG->phpunit_dataroot/phpunittestdir.txt")) {
@ -124,7 +122,7 @@ if (!file_exists("$CFG->phpunit_dataroot/phpunittestdir.txt")) {
if ($file === 'phpunit' or $file === '.' or $file === '..' or $file === '.DS_Store') {
continue;
}
phpunit_bootstrap_error(131, '$CFG->phpunit_dataroot directory is not empty, can not run tests! Is it used for anything else?');
phpunit_bootstrap_error(PHPUNIT_EXITCODE_CONFIGERROR, '$CFG->phpunit_dataroot directory is not empty, can not run tests! Is it used for anything else?');
}
closedir($dh);
unset($dh);
@ -137,13 +135,13 @@ if (!file_exists("$CFG->phpunit_dataroot/phpunittestdir.txt")) {
// verify db prefix
if (!isset($CFG->phpunit_prefix)) {
phpunit_bootstrap_error(131, 'Missing $CFG->phpunit_prefix in config.php, can not run tests!');
phpunit_bootstrap_error(PHPUNIT_EXITCODE_CONFIGERROR, 'Missing $CFG->phpunit_prefix in config.php, can not run tests!');
}
if ($CFG->phpunit_prefix === '') {
phpunit_bootstrap_error(131, '$CFG->phpunit_prefix can not be empty, can not run tests!');
phpunit_bootstrap_error(PHPUNIT_EXITCODE_CONFIGERROR, '$CFG->phpunit_prefix can not be empty, can not run tests!');
}
if (isset($CFG->prefix) and $CFG->prefix === $CFG->phpunit_prefix) {
phpunit_bootstrap_error(131, '$CFG->prefix and $CFG->phpunit_prefix must not be identical, can not run tests!');
phpunit_bootstrap_error(PHPUNIT_EXITCODE_CONFIGERROR, '$CFG->prefix and $CFG->phpunit_prefix must not be identical, can not run tests!');
}
// override CFG settings if necessary and throw away extra CFG settings

View File

@ -25,6 +25,14 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define('PHPUNIT_EXITCODE_PHPUNITMISSING', 129);
define('PHPUNIT_EXITCODE_PHPUNITWRONG', 130);
define('PHPUNIT_EXITCODE_PHPUNITEXTMISSING', 131);
define('PHPUNIT_EXITCODE_CONFIGERROR', 135);
define('PHPUNIT_EXITCODE_CONFIGWARNING', 136);
define('PHPUNIT_EXITCODE_INSTALL', 140);
define('PHPUNIT_EXITCODE_REINSTALL', 141);
/**
* Print error and stop execution
* @param int $errorcode The exit error code
@ -39,35 +47,35 @@ function phpunit_bootstrap_error($errorcode, $text = '') {
case 1:
$text = 'Error: '.$text;
break;
case 129:
case PHPUNIT_EXITCODE_PHPUNITMISSING:
$text = "Moodle can not find PHPUnit PEAR library";
break;
case PHPUNIT_EXITCODE_PHPUNITWRONG:
$text = 'Moodle requires PHPUnit 3.6.x, '.$text.' is not compatible';
break;
case 130:
$text = 'Moodle can not find PHPUnit PEAR library or necessary PHPUnit extension';
case PHPUNIT_EXITCODE_PHPUNITEXTMISSING:
$text = 'Moodle can not find required PHPUnit extension '.$text;
break;
case 131:
$text = 'Moodle configuration problem: '.$text;
case PHPUNIT_EXITCODE_CONFIGERROR:
$text = "Moodle PHPUnit environment configuration error:\n".$text;
break;
case 132:
$text = "Moodle PHPUnit environment is not initialised, please use:\n php admin/tool/phpunit/cli/util.php --install";
case PHPUNIT_EXITCODE_CONFIGWARNING:
$text = "Moodle PHPUnit environment configuration warning:\n".$text;
break;
case 133:
$text = "Moodle PHPUnit environment was initialised for different version, please use:\n php admin/tool/phpunit/cli/util.php --drop\n php admin/tool/phpunit/cli/util.php --install";
case PHPUNIT_EXITCODE_INSTALL:
$text = "Moodle PHPUnit environment is not initialised, please use:\n php admin/tool/phpunit/cli/init.php";
break;
case 134:
$text = 'Moodle can not create PHPUnit configuration file, please verify dirroot permissions';
case PHPUNIT_EXITCODE_REINSTALL:
$text = "Moodle PHPUnit environment was initialised for different version, please use:\n php admin/tool/phpunit/cli/init.php";
break;
default:
$text = empty($text) ? '' : ': '.$text;
$text = 'Unknown error '.$errorcode.$text;
break;
}
if (defined('PHPUNIT_UTIL') and PHPUNIT_UTIL) {
// do not write to error stream because we need the error message in PHP exec result from web ui
echo($text."\n");
} else {
fwrite(STDERR, $text."\n");
}
// do not write to error stream because we need the error message in PHP exec result from web ui
echo($text."\n");
exit($errorcode);
}

View File

@ -593,20 +593,20 @@ abstract class phpunit_module_generator {
/**
* Create course module and link it to course
* @param stdClass $instance
* @param int $courseid
* @param array $options: section, visible
* @return stdClass $cm instance
* @return int $cm instance id
*/
protected function create_course_module(stdClass $instance, array $options) {
protected function precreate_course_module($courseid, array $options) {
global $DB, $CFG;
require_once("$CFG->dirroot/course/lib.php");
$modulename = $this->get_modulename();
$cm = new stdClass();
$cm->course = $instance->course;
$cm->course = $courseid;
$cm->module = $DB->get_field('modules', 'id', array('name'=>$modulename));
$cm->instance = $instance->id;
$cm->instance = 0;
$cm->section = isset($options['section']) ? $options['section'] : 0;
$cm->idnumber = isset($options['idnumber']) ? $options['idnumber'] : 0;
$cm->added = time();
@ -627,11 +627,28 @@ abstract class phpunit_module_generator {
add_mod_to_section($cm);
$cm = get_coursemodule_from_id($modulename, $cm->id, $cm->course, true, MUST_EXIST);
return $cm->id;
}
/**
* Called after *_add_instance()
* @param int $id
* @param int $cmid
* @return stdClass module instance
*/
protected function post_add_instance($id, $cmid) {
global $DB;
$DB->set_field('course_modules', 'instance', $id, array('id'=>$cmid));
$instance = $DB->get_record($this->get_modulename(), array('id'=>$id), '*', MUST_EXIST);
$cm = get_coursemodule_from_id($this->get_modulename(), $cmid, $instance->course, true, MUST_EXIST);
context_module::instance($cm->id);
return $cm;
$instance->cmid = $cm->id;
return $instance;
}
/**

View File

@ -36,34 +36,25 @@ require_once 'PHPUnit/Extensions/Database/Autoload.php';
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class phpunit_util {
/**
* @var array original content of all database tables
*/
/** @var string current version hash from php files */
protected static $versionhash = null;
/** @var array original content of all database tables*/
protected static $tabledata = null;
/**
* @var array original structure of all database tables
*/
/** @var array original structure of all database tables */
protected static $tablestructure = null;
/**
* @var array An array of original globals, restored after each test
*/
/** @var array An array of original globals, restored after each test */
protected static $globals = array();
/**
* @var int last value of db writes counter, used for db resetting
*/
/** @var int last value of db writes counter, used for db resetting */
public static $lastdbwrites = null;
/**
* @var phpunit_data_generator
*/
/** @var phpunit_data_generator */
protected static $generator = null;
/**
* @var resource used for prevention of parallel test execution
*/
/** @var resource used for prevention of parallel test execution */
protected static $lockhandle = null;
/**
@ -116,6 +107,30 @@ class phpunit_util {
}
}
/**
* Load global $CFG;
* @internal
* @static
* @return void
*/
public static function initialise_cfg() {
global $DB;
$dbhash = false;
try {
$dbhash = $DB->get_field('config', 'value', array('name'=>'phpunittest'));
} catch (Exception $e) {
// not installed yet
initialise_cfg();
return;
}
if ($dbhash !== phpunit_util::get_version_hash()) {
// do not set CFG - the only way forward is to drop and reinstall
return;
}
// standard CFG init
initialise_cfg();
}
/**
* Get data generator
* @static
@ -625,26 +640,31 @@ class phpunit_util {
if (!self::is_test_site()) {
// dataroot was verified in bootstrap, so it must be DB
return array(131, 'Can not use test database, try changing prefix');
return array(PHPUNIT_EXITCODE_CONFIGERROR, 'Can not use database for testing, try different prefix');
}
if (empty($tables)) {
return array(132, '');
return array(PHPUNIT_EXITCODE_INSTALL, '');
}
if (!file_exists("$CFG->dataroot/phpunit/tabledata.ser") or !file_exists("$CFG->dataroot/phpunit/tablestructure.ser")) {
return array(133, '');
return array(PHPUNIT_EXITCODE_REINSTALL, '');
}
if (!file_exists("$CFG->dataroot/phpunit/versionshash.txt")) {
return array(133, '');
return array(PHPUNIT_EXITCODE_REINSTALL, '');
}
$hash = phpunit_util::get_version_hash();
$oldhash = file_get_contents("$CFG->dataroot/phpunit/versionshash.txt");
if ($hash !== $oldhash) {
return array(133, '');
return array(PHPUNIT_EXITCODE_REINSTALL, '');
}
$dbhash = get_config('core', 'phpunittest');
if ($hash !== $dbhash) {
return array(PHPUNIT_EXITCODE_REINSTALL, '');
}
return array(0, '');
@ -662,7 +682,7 @@ class phpunit_util {
global $DB, $CFG;
if (!self::is_test_site()) {
phpunit_bootstrap_error(131, 'Can not drop non-test site!!');
phpunit_bootstrap_error(PHPUNIT_EXITCODE_CONFIGERROR, 'Can not drop non-test site!!');
}
// purge dataroot
@ -707,13 +727,13 @@ class phpunit_util {
global $DB, $CFG;
if (!self::is_test_site()) {
phpunit_bootstrap_error(131, 'Can not install on non-test site!!');
phpunit_bootstrap_error(PHPUNIT_EXITCODE_CONFIGERROR, 'Can not install on non-test site!!');
}
if ($DB->get_tables()) {
list($errorcode, $message) = phpunit_util::testing_ready_problem();
if ($errorcode) {
phpunit_bootstrap_error(133, 'Database tables already present, Moodle PHPUnit test environment can not be initialised');
phpunit_bootstrap_error(PHPUNIT_EXITCODE_REINSTALL, 'Database tables already present, Moodle PHPUnit test environment can not be initialised');
} else {
phpunit_bootstrap_error(0, 'Moodle PHPUnit test environment is already initialised');
}
@ -731,7 +751,8 @@ class phpunit_util {
update_timezone_records($timezones);
// add test db flag
set_config('phpunittest', 'phpunittest');
$hash = phpunit_util::get_version_hash();
set_config('phpunittest', $hash);
// store data for all tables
$data = array();
@ -756,7 +777,6 @@ class phpunit_util {
phpunit_boostrap_fix_file_permissions("$CFG->dataroot/phpunit/tablestructure.ser");
// hash all plugin versions - helps with very fast detection of db structure changes
$hash = phpunit_util::get_version_hash();
file_put_contents("$CFG->dataroot/phpunit/versionshash.txt", $hash);
phpunit_boostrap_fix_file_permissions("$CFG->dataroot/phpunit/versionshash.txt", $hash);
}
@ -769,6 +789,10 @@ class phpunit_util {
public static function get_version_hash() {
global $CFG;
if (self::$versionhash) {
return self::$versionhash;
}
$versions = array();
// main version first
@ -801,9 +825,9 @@ class phpunit_util {
}
}
$hash = sha1(serialize($versions));
self::$versionhash = sha1(serialize($versions));
return $hash;
return self::$versionhash;
}
/**
@ -831,7 +855,7 @@ class phpunit_util {
if (!file_exists("$fullplug/tests/")) {
continue;
}
$dir = preg_replace("|$CFG->dirroot/|", '', $fullplug, 1);
$dir = substr($fullplug, strlen($CFG->dirroot)+1);
$dir .= '/tests';
$component = $type.'_'.$plug;
@ -850,9 +874,12 @@ class phpunit_util {
phpunit_boostrap_fix_file_permissions("$CFG->dirroot/phpunit.xml");
}
}
// relink - it seems that xml:base does not work in phpunit xml files, remove this nasty hack if you find a way to set xml base for relative refs
$data = str_replace('lib/phpunit/', "$CFG->dirroot/lib/phpunit/", $data);
$data = preg_replace('|<directory suffix="_test.php">([^<]+)</directory>|', '<directory suffix="_test.php">'.$CFG->dirroot.'/$1</directory>', $data);
$data = str_replace('lib/phpunit/', $CFG->dirroot.DIRECTORY_SEPARATOR.'lib'.DIRECTORY_SEPARATOR.'phpunit'.DIRECTORY_SEPARATOR, $data);
$data = preg_replace('|<directory suffix="_test.php">([^<]+)</directory>|',
'<directory suffix="_test.php">'.$CFG->dirroot.(DIRECTORY_SEPARATOR === '\\' ? '\\\\' : DIRECTORY_SEPARATOR).'$1</directory>',
$data);
file_put_contents("$CFG->dataroot/phpunit/webrunner.xml", $data);
phpunit_boostrap_fix_file_permissions("$CFG->dataroot/phpunit/webrunner.xml");

View File

@ -13,7 +13,7 @@ Installation
1. install PEAR package manager - see [PEAR Manual](http://pear.php.net/manual/en/installation.php)
2. install PHPUnit package and phpunit/DbUnit extension - see [PHPUnit installation documentation](http://www.phpunit.de/manual/current/en/installation.html)
3. edit main config.php - add `$CFG->phpunit_prefix` and `$CFG->phpunit_dataroot` - see config-dist.php
4. execute `admin/tool/phpunit/cli/init.sh` to initialise the test environemnt, repeat it after every upgrade or installation of plugins
4. execute `php admin/tool/phpunit/cli/init.php` to initialise the test environemnt, repeat it after every upgrade or installation of plugins
Test execution
@ -29,7 +29,7 @@ How to add more tests?
2. add `local/mytest/tests/my_test.php` file with `local_my_testcase` class that extends `basic_testcase` or `advanced_testcase`
3. add some test_*() methods
4. execute your new test case `phpunit local_my_testcase local/mytest/tests/my_test.php`
5. execute `admin/tool/phpunit/cli/init.sh` to get the plugin tests included in main phpunit.xml configuration file
5. execute `php admin/tool/phpunit/cli/init.php` to get the plugin tests included in main phpunit.xml configuration file
How to convert existing tests?

View File

@ -479,7 +479,12 @@ if (isset($CFG->debug)) {
}
// Load up any configuration from the config table
initialise_cfg();
if (PHPUNIT_TEST) {
phpunit_util::initialise_cfg();
} else {
initialise_cfg();
}
// Verify upgrade is not running unless we are in a script that needs to execute in any case
if (!defined('NO_UPGRADE_CHECK') and isset($CFG->upgraderunning)) {

View File

@ -0,0 +1,783 @@
<?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/>.
/**
* Completion tests
*
* @package core_completion
* @category phpunit
* @copyright 2008 Sam Marshall
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->libdir.'/completionlib.php');
class completionlib_testcase extends basic_testcase {
var $realdb, $realcfg, $realsession, $realuser;
protected function setUp() {
global $DB, $CFG, $SESSION, $USER;
parent::setUp();
$this->realdb = $DB;
$this->realcfg = $CFG;
$this->realsession = $SESSION;
$this->prevuser = $USER;
$DB = $this->getMock(get_class($DB));
$CFG = clone($this->realcfg);
$CFG->prefix = 'test_';
$CFG->enablecompletion = COMPLETION_ENABLED;
$SESSION = new stdClass();
$USER = (object)array('id' =>314159);
}
protected function tearDown() {
global $DB,$CFG,$SESSION,$USER;
$DB = $this->realdb;
$CFG = $this->realcfg;
$SESSION = $this->realsession;
$USER = $this->prevuser;
parent::tearDown();
}
function test_is_enabled() {
global $CFG;
// Config alone
$CFG->enablecompletion = COMPLETION_DISABLED;
$this->assertEquals(COMPLETION_DISABLED, completion_info::is_enabled_for_site());
$CFG->enablecompletion = COMPLETION_ENABLED;
$this->assertEquals(COMPLETION_ENABLED, completion_info::is_enabled_for_site());
// Course
$course = (object)array('id' =>13);
$c = new completion_info($course);
$course->enablecompletion = COMPLETION_DISABLED;
$this->assertEquals(COMPLETION_DISABLED, $c->is_enabled());
$course->enablecompletion = COMPLETION_ENABLED;
$this->assertEquals(COMPLETION_ENABLED, $c->is_enabled());
$CFG->enablecompletion = COMPLETION_DISABLED;
$this->assertEquals(COMPLETION_DISABLED, $c->is_enabled());
// Course and CM
$cm = new stdClass();
$cm->completion = COMPLETION_TRACKING_MANUAL;
$this->assertEquals(COMPLETION_DISABLED, $c->is_enabled($cm));
$CFG->enablecompletion = COMPLETION_ENABLED;
$course->enablecompletion = COMPLETION_DISABLED;
$this->assertEquals(COMPLETION_DISABLED, $c->is_enabled($cm));
$course->enablecompletion = COMPLETION_ENABLED;
$this->assertEquals(COMPLETION_TRACKING_MANUAL, $c->is_enabled($cm));
$cm->completion = COMPLETION_TRACKING_NONE;
$this->assertEquals(COMPLETION_TRACKING_NONE, $c->is_enabled($cm));
$cm->completion = COMPLETION_TRACKING_AUTOMATIC;
$this->assertEquals(COMPLETION_TRACKING_AUTOMATIC, $c->is_enabled($cm));
}
function test_update_state() {
$c = $this->getMock('completion_info', array('is_enabled','get_data','internal_get_state','internal_set_data'), array((object)array('id'=>42)));
$cm = (object)array('id'=>13, 'course'=>42);
// Not enabled, should do nothing
$c->expects($this->at(0))
->method('is_enabled')
->with($cm)
->will($this->returnValue(false));
$c->update_state($cm);
// Enabled, but current state is same as possible result, do nothing
$current = (object)array('completionstate'=>COMPLETION_COMPLETE);
$c->expects($this->at(0))
->method('is_enabled')
->with($cm)
->will($this->returnValue(true));
$c->expects($this->at(1))
->method('get_data')
->with($cm, false, 0)
->will($this->returnValue($current));
$c->update_state($cm, COMPLETION_COMPLETE);
// Enabled, but current state is a specific one and new state is just
// complete, so do nothing
$current->completionstate = COMPLETION_COMPLETE_PASS;
$c->expects($this->at(0))
->method('is_enabled')
->with($cm)
->will($this->returnValue(true));
$c->expects($this->at(1))
->method('get_data')
->with($cm, false, 0)
->will($this->returnValue($current));
$c->update_state($cm, COMPLETION_COMPLETE);
// Manual, change state (no change)
$cm = (object)array('id'=>13,'course'=>42, 'completion'=>COMPLETION_TRACKING_MANUAL);
$current->completionstate=COMPLETION_COMPLETE;
$c->expects($this->at(0))
->method('is_enabled')
->with($cm)
->will($this->returnValue(true));
$c->expects($this->at(1))
->method('get_data')
->with($cm, false, 0)
->will($this->returnValue($current));
$c->update_state($cm, COMPLETION_COMPLETE);
// Manual, change state (change)
$c->expects($this->at(0))
->method('is_enabled')
->with($cm)
->will($this->returnValue(true));
$c->expects($this->at(1))
->method('get_data')
->with($cm, false, 0)
->will($this->returnValue($current));
$changed = clone($current);
$changed->timemodified = time();
$changed->completionstate = COMPLETION_INCOMPLETE;
$c->expects($this->at(2))
->method('internal_set_data')
->with($cm, $changed);
$c->update_state($cm, COMPLETION_INCOMPLETE);
// Auto, change state
$cm = (object)array('id'=>13,'course'=>42, 'completion'=>COMPLETION_TRACKING_AUTOMATIC);
$current = (object)array('completionstate'=>COMPLETION_COMPLETE);
$c->expects($this->at(0))
->method('is_enabled')
->with($cm)
->will($this->returnValue(true));
$c->expects($this->at(1))
->method('get_data')
->with($cm, false, 0)
->will($this->returnValue($current));
$c->expects($this->at(2))
->method('internal_get_state')
->will($this->returnValue(COMPLETION_COMPLETE_PASS));
$changed = clone($current);
$changed->timemodified = time();
$changed->completionstate = COMPLETION_COMPLETE_PASS;
$c->expects($this->at(3))
->method('internal_set_data')
->with($cm, $changed);
$c->update_state($cm, COMPLETION_COMPLETE_PASS);
}
function test_internal_get_state() {
global $DB;
$c = $this->getMock('completion_info', array('internal_get_grade_state'), array((object)array('id'=>42)));
$cm = (object)array('id'=>13, 'course'=>42, 'completiongradeitemnumber'=>null);
// If view is required, but they haven't viewed it yet
$cm->completionview = COMPLETION_VIEW_REQUIRED;
$current = (object)array('viewed'=>COMPLETION_NOT_VIEWED);
$this->assertEquals(COMPLETION_INCOMPLETE, $c->internal_get_state($cm, 123, $current));
// OK set view not required
$cm->completionview = COMPLETION_VIEW_NOT_REQUIRED;
// Test not getting module name
$cm->modname='label';
$this->assertEquals(COMPLETION_COMPLETE, $c->internal_get_state($cm, 123, $current));
// Test getting module name
$cm->module = 13;
unset($cm->modname);
/** @var $DB PHPUnit_Framework_MockObject_MockObject */
$DB->expects($this->once())
->method('get_field')
->with('modules', 'name', array('id'=>13))
->will($this->returnValue('lable'));
$this->assertEquals(COMPLETION_COMPLETE, $c->internal_get_state($cm, 123, $current));
// Note: This function is not fully tested (including kind of the main
// part) because:
// * the grade_item/grade_grade calls are static and can't be mocked
// * the plugin_supports call is static and can't be mocked
}
function test_set_module_viewed() {
$c = $this->getMock('completion_info',
array('delete_all_state', 'get_tracked_users', 'update_state', 'internal_get_grade_state', 'is_enabled', 'get_data', 'internal_get_state', 'internal_set_data'),
array((object)array('id'=>42)));
$cm = (object)array('id'=>13, 'course'=>42);
// Not tracking completion, should do nothing
$cm->completionview = COMPLETION_VIEW_NOT_REQUIRED;
$c->set_module_viewed($cm);
// Tracking completion but completion is disabled, should do nothing
$cm->completionview = COMPLETION_VIEW_REQUIRED;
$c->expects($this->at(0))
->method('is_enabled')
->with($cm)
->will($this->returnValue(false));
$c->set_module_viewed($cm);
// Now it's enabled, we expect it to get data. If data already has
// viewed, still do nothing
$c->expects($this->at(0))
->method('is_enabled')
->with($cm)
->will($this->returnValue(true));
$c->expects($this->at(1))
->method('get_data')
->with($cm, 0)
->will($this->returnValue((object)array('viewed'=>COMPLETION_VIEWED)));
$c->set_module_viewed($cm);
// OK finally one that hasn't been viewed, now it should set it viewed
// and update state
$c->expects($this->at(0))
->method('is_enabled')
->with($cm)
->will($this->returnValue(true));
$c->expects($this->at(1))
->method('get_data')
->with($cm, 1337)
->will($this->returnValue((object)array('viewed'=>COMPLETION_NOT_VIEWED)));
$c->expects($this->at(2))
->method('internal_set_data')
->with($cm, (object)array('viewed'=>COMPLETION_VIEWED));
$c->expects($this->at(3))
->method('update_state')
->with($cm, COMPLETION_COMPLETE, 1337);
$c->set_module_viewed($cm, 1337);
}
function test_count_user_data() {
global $DB;
$course = (object)array('id'=>13);
$cm = (object)array('id'=>42);
/** @var $DB PHPUnit_Framework_MockObject_MockObject */
$DB->expects($this->at(0))
->method('get_field_sql')
->will($this->returnValue(666));
/*
$DB->expectOnce('get_field_sql',array(new IgnoreWhitespaceExpectation("SELECT
COUNT(1)
FROM
{course_modules_completion}
WHERE
coursemoduleid=? AND completionstate<>0"),array(42)));
*/
$c = new completion_info($course);
$this->assertEquals(666, $c->count_user_data($cm));
}
function test_delete_all_state() {
global $DB, $SESSION;
$course = (object)array('id'=>13);
$cm = (object)array('id'=>42,'course'=>13);
$c = new completion_info($course);
// Check it works ok without data in session
/** @var $DB PHPUnit_Framework_MockObject_MockObject */
$DB->expects($this->at(0))
->method('delete_records')
->with('course_modules_completion', array('coursemoduleid'=>42))
->will($this->returnValue(true));
$c->delete_all_state($cm);
// Build up a session to check it deletes the right bits from it
// (and not other bits)
$SESSION->completioncache=array();
$SESSION->completioncache[13]=array();
$SESSION->completioncache[13][42]='foo';
$SESSION->completioncache[13][43]='foo';
$SESSION->completioncache[14]=array();
$SESSION->completioncache[14][42]='foo';
$DB->expects($this->at(0))
->method('delete_records')
->with('course_modules_completion', array('coursemoduleid'=>42))
->will($this->returnValue(true));
$c->delete_all_state($cm);
$this->assertEquals(array(13=>array(43=>'foo'), 14=>array(42=>'foo')), $SESSION->completioncache);
}
function test_reset_all_state() {
global $DB;
$c = $this->getMock('completion_info',
array('delete_all_state', 'get_tracked_users','update_state', 'internal_get_grade_state', 'is_enabled', 'get_data', 'internal_get_state', 'internal_set_data'),
array((object)array('id'=>42)));
$cm = (object)array('id'=>13, 'course'=>42, 'completion'=>COMPLETION_TRACKING_AUTOMATIC);
/** @var $DB PHPUnit_Framework_MockObject_MockObject */
$DB->expects($this->at(0))
->method('get_recordset')
->will($this->returnValue(
new completion_test_fake_recordset(array((object)array('id'=>1, 'userid'=>100),(object)array('id'=>2, 'userid'=>101)))));
$c->expects($this->at(0))
->method('delete_all_state')
->with($cm);
$c->expects($this->at(1))
->method('get_tracked_users')
->will($this->returnValue(array(
(object)array('id'=>100,'firstname'=>'Woot','lastname'=>'Plugh'),
(object)array('id'=>201,'firstname'=>'Vroom','lastname'=>'Xyzzy'))));
$c->expects($this->at(2))
->method('update_state')
->with($cm,COMPLETION_UNKNOWN, 100);
$c->expects($this->at(3))
->method('update_state')
->with($cm,COMPLETION_UNKNOWN, 101);
$c->expects($this->at(4))
->method('update_state')
->with($cm,COMPLETION_UNKNOWN, 201);
$c->reset_all_state($cm);
}
function test_get_data() {
global $DB, $SESSION;
$c = new completion_info((object)array('id'=>42));
$cm = (object)array('id'=>13, 'course'=>42);
// 1. Not current user, record exists
$sillyrecord = (object)array('frog'=>'kermit');
/** @var $DB PHPUnit_Framework_MockObject_MockObject */
$DB->expects($this->at(0))
->method('get_record')
->with('course_modules_completion', array('coursemoduleid'=>13,'userid'=>123))
->will($this->returnValue($sillyrecord));
$result = $c->get_data($cm,false,123);
$this->assertEquals($sillyrecord, $result);
$this->assertTrue(empty($SESSION->completioncache));
// 2. Not current user, default record, wholecourse (ignored)
$DB->expects($this->at(0))
->method('get_record')
->with('course_modules_completion', array('coursemoduleid'=>13,'userid'=>123))
->will($this->returnValue(false));
$result=$c->get_data($cm,true,123);
$this->assertEquals((object)array(
'id'=>'0','coursemoduleid'=>13,'userid'=>123,'completionstate'=>0,
'viewed'=>0,'timemodified'=>0),$result);
$this->assertTrue(empty($SESSION->completioncache));
// 3. Current user, single record, not from cache
$DB->expects($this->at(0))
->method('get_record')
->with('course_modules_completion', array('coursemoduleid'=>13,'userid'=>314159))
->will($this->returnValue($sillyrecord));
$result = $c->get_data($cm);
$this->assertEquals($sillyrecord, $result);
$this->assertEquals($sillyrecord, $SESSION->completioncache[42][13]);
// When checking time(), allow for second overlaps
$this->assertTrue(time()-$SESSION->completioncache[42]['updated']<2);
// 4. Current user, 'whole course', but from cache
$result = $c->get_data($cm, true);
$this->assertEquals($sillyrecord, $result);
// 5. Current user, single record, cache expired
$SESSION->completioncache[42]['updated']=37; // Quite a long time ago
$now = time();
$SESSION->completioncache[17]['updated']=$now;
$SESSION->completioncache[39]['updated']=72; // Also a long time ago
$DB->expects($this->at(0))
->method('get_record')
->with('course_modules_completion', array('coursemoduleid'=>13,'userid'=>314159))
->will($this->returnValue($sillyrecord));
$result = $c->get_data($cm, false);
$this->assertEquals($sillyrecord, $result);
// Check that updated value is right, then fudge it to make next compare
// work
$this->assertTrue(time()-$SESSION->completioncache[42]['updated']<2);
$SESSION->completioncache[42]['updated']=$now;
// Check things got expired from cache
$this->assertEquals(array(42=>array(13=>$sillyrecord, 'updated'=>$now), 17=>array('updated'=>$now)), $SESSION->completioncache);
// 6. Current user, 'whole course' and record not in cache
unset($SESSION->completioncache);
// Scenario: Completion data exists for one CMid
$basicrecord = (object)array('coursemoduleid'=>13);
$DB->expects($this->at(0))
->method('get_records_sql')
->will($this->returnValue(array('1'=>$basicrecord)));
/*
$DB->expectAt(0,'get_records_sql',array(new IgnoreWhitespaceExpectation("
SELECT
cmc.*
FROM
{course_modules} cm
INNER JOIN {course_modules_completion} cmc ON cmc.coursemoduleid=cm.id
WHERE
cm.course=? AND cmc.userid=?"),array(42,314159)));
*/
// There are two CMids in total, the one we had data for and another one
$modinfo = new stdClass();
$modinfo->cms = array((object)array('id'=>13), (object)array('id'=>14));
$result = $c->get_data($cm, true, 0, $modinfo);
// Check result
$this->assertEquals($basicrecord, $result);
// Check the cache contents
$this->assertTrue(time()-$SESSION->completioncache[42]['updated']<2);
$SESSION->completioncache[42]['updated'] = $now;
$this->assertEquals(array(42=>array(13=>$basicrecord, 14=>(object)array(
'id'=>'0', 'coursemoduleid'=>14, 'userid'=>314159, 'completionstate'=>0,
'viewed'=>0, 'timemodified'=>0), 'updated'=>$now)), $SESSION->completioncache);
}
function test_internal_set_data() {
global $DB, $SESSION;
$cm = (object)array('course' => 42,'id' => 13);
$c = new completion_info((object)array('id' => 42));
// 1) Test with new data
$data = (object)array('id'=>0, 'userid' => 314159, 'coursemoduleid' => 99);
$DB->expects($this->at(0))
->method('start_delegated_transaction')
->will($this->returnValue($this->getMock('moodle_transaction', array(), array($DB))));
$DB->expects($this->at(1))
->method('get_field')
->with('course_modules_completion', 'id', array('coursemoduleid'=>99, 'userid'=>314159))
->will($this->returnValue(false));
$DB->expects($this->at(2))
->method('insert_record')
->will($this->returnValue(4));
$c->internal_set_data($cm, $data);
$this->assertEquals(4, $data->id);
$this->assertEquals(array(42 => array(13 => $data)), $SESSION->completioncache);
// 2) Test with existing data and for different user (not cached)
unset($SESSION->completioncache);
$d2 = (object)array('id' => 7, 'userid' => 17, 'coursemoduleid' => 66);
$DB->expects($this->at(0))
->method('start_delegated_transaction')
->will($this->returnValue($this->getMock('moodle_transaction', array(), array($DB))));
$DB->expects($this->at(1))
->method('update_record')
->with('course_modules_completion', $d2);
$c->internal_set_data($cm, $d2);
$this->assertFalse(isset($SESSION->completioncache));
// 3) Test where it THINKS the data is new (from cache) but actually
// in the database it has been set since
// 1) Test with new data
$data = (object)array('id'=>0, 'userid' => 314159, 'coursemoduleid' => 99);
$d3 = (object)array('id' => 13, 'userid' => 314159, 'coursemoduleid' => 99);
$DB->expects($this->at(0))
->method('start_delegated_transaction')
->will($this->returnValue($this->getMock('moodle_transaction', array(), array($DB))));
$DB->expects($this->at(1))
->method('get_field')
->with('course_modules_completion', 'id', array('coursemoduleid' => 99, 'userid' => 314159))
->will($this->returnValue(13));
$DB->expects($this->at(2))
->method('update_record')
->with('course_modules_completion', $d3);
$c->internal_set_data($cm, $data);
}
function test_get_activities() {
global $DB;
$c = new completion_info((object)array('id'=>42));
// Try with no activities
$DB->expects($this->at(0))
->method('get_records_select')
->with('course_modules', 'course=42 AND completion<>'.COMPLETION_TRACKING_NONE)
->will($this->returnValue(array()));
$result = $c->get_activities();
$this->assertEquals(array(), $result);
// Try with an activity (need to fake up modinfo for it as well)
$DB->expects($this->at(0))
->method('get_records_select')
->with('course_modules', 'course=42 AND completion<>'.COMPLETION_TRACKING_NONE)
->will($this->returnValue(array(13=>(object)array('id'=>13))));
$modinfo = new stdClass;
$modinfo->sections = array(array(1, 2, 3), array(12, 13, 14));
$modinfo->cms[13] = (object)array('modname'=>'frog', 'name'=>'kermit');
$result = $c->get_activities($modinfo);
$this->assertEquals(array(13=>(object)array('id'=>13, 'modname'=>'frog', 'name'=>'kermit')), $result);
}
// get_tracked_users() cannot easily be tested because it uses
// get_role_users, so skipping that
function test_get_progress_all() {
global $DB;
$c = $this->getMock('completion_info',
array('delete_all_state', 'get_tracked_users', 'update_state', 'internal_get_grade_state', 'is_enabled', 'get_data', 'internal_get_state', 'internal_set_data'),
array((object)array('id'=>42)));
// 1) Basic usage
$c->expects($this->at(0))
->method('get_tracked_users')
->with(false, array(), 0, '', '', '', null)
->will($this->returnValue(array(
(object)array('id'=>100, 'firstname'=>'Woot', 'lastname'=>'Plugh'),
(object)array('id'=>201, 'firstname'=>'Vroom', 'lastname'=>'Xyzzy'))));
$DB->expects($this->at(0))
->method('get_in_or_equal')
->with(array(100, 201))
->will($this->returnValue(array(' IN (100, 201)', array())));
$progress1 = (object)array('userid'=>100, 'coursemoduleid'=>13);
$progress2 = (object)array('userid'=>201, 'coursemoduleid'=>14);
$DB->expects($this->at(1))
->method('get_recordset_sql')
->will($this->returnValue(new completion_test_fake_recordset(array($progress1, $progress2))));
/*
$DB->expectAt(0, 'get_recordset_sql', array(new IgnoreWhitespaceExpectation("
SELECT
cmc.*
FROM
{course_modules} cm
INNER JOIN {course_modules_completion} cmc ON cm.id = cmc.coursemoduleid
WHERE
cm.course = ? AND cmc.userid IN (100, 201)"), array(42)));
*/
$this->assertEquals(array(
100 => (object)array('id'=>100, 'firstname'=>'Woot', 'lastname'=>'Plugh',
'progress'=>array(13=>$progress1)),
201 => (object)array('id'=>201, 'firstname'=>'Vroom', 'lastname'=>'Xyzzy',
'progress'=>array(14=>$progress2)),
), $c->get_progress_all(false));
// 2) With more than 1, 000 results
$tracked = array();
$ids = array();
$progress = array();
for($i = 100;$i<2000;$i++) {
$tracked[] = (object)array('id'=>$i, 'firstname'=>'frog', 'lastname'=>$i);
$ids[] = $i;
$progress[] = (object)array('userid'=>$i, 'coursemoduleid'=>13);
$progress[] = (object)array('userid'=>$i, 'coursemoduleid'=>14);
}
$c->expects($this->at(0))
->method('get_tracked_users')
->with(true, 3, 0, '', '', '', null)
->will($this->returnValue($tracked));
$DB->expects($this->at(0))
->method('get_in_or_equal')
->with(array_slice($ids, 0, 1000))
->will($this->returnValue(array(' IN whatever', array())));
$DB->expects($this->at(1))
->method('get_recordset_sql')
->will($this->returnValue(new completion_test_fake_recordset(array_slice($progress, 0, 1000))));
/*
$DB->expectAt(1, 'get_recordset_sql', array(new IgnoreWhitespaceExpectation("
SELECT
cmc.*
FROM
{course_modules} cm
INNER JOIN {course_modules_completion} cmc ON cm.id = cmc.coursemoduleid
WHERE
cm.course = ? AND cmc.userid IN whatever"), array(42)));
*/
$DB->expects($this->at(2))
->method('get_in_or_equal')
->with(array_slice($ids, 1000))
->will($this->returnValue(array(' IN whatever2', array())));
$DB->expects($this->at(3))
->method('get_recordset_sql')
->will($this->returnValue(new completion_test_fake_recordset(array_slice($progress, 1000))));
$result = $c->get_progress_all(true, 3);
$resultok = true;
$resultok = $resultok && ($ids == array_keys($result));
foreach($result as $userid => $data) {
$resultok = $resultok && $data->firstname == 'frog';
$resultok = $resultok && $data->lastname == $userid;
$resultok = $resultok && $data->id == $userid;
$cms = $data->progress;
$resultok = $resultok && (array(13, 14) == array_keys($cms));
$resultok = $resultok && ((object)array('userid'=>$userid, 'coursemoduleid'=>13) == $cms[13]);
$resultok = $resultok && ((object)array('userid'=>$userid, 'coursemoduleid'=>14) == $cms[14]);
}
$this->assertTrue($resultok);
}
function test_inform_grade_changed() {
$c = $this->getMock('completion_info',
array('delete_all_state', 'get_tracked_users', 'update_state', 'internal_get_grade_state', 'is_enabled', 'get_data', 'internal_get_state', 'internal_set_data'),
array((object)array('id'=>42)));
$cm = (object)array('course'=>42, 'id'=>13, 'completion'=>0, 'completiongradeitemnumber'=>null);
$item = (object)array('itemnumber'=>3, 'gradepass'=>1, 'hidden'=>0);
$grade = (object)array('userid'=>31337, 'finalgrade'=>0, 'rawgrade'=>0);
// Not enabled (should do nothing)
$c->expects($this->at(0))
->method('is_enabled')
->with($cm)
->will($this->returnValue(false));
$c->inform_grade_changed($cm, $item, $grade, false);
// Enabled but still no grade completion required, should still do nothing
$c->expects($this->at(0))
->method('is_enabled')
->with($cm)
->will($this->returnValue(true));
$c->inform_grade_changed($cm, $item, $grade, false);
// Enabled and completion required but item number is wrong, does nothing
$cm = (object)array('course'=>42, 'id'=>13, 'completion'=>0, 'completiongradeitemnumber'=>7);
$c->expects($this->at(0))
->method('is_enabled')
->with($cm)
->will($this->returnValue(true));
$c->inform_grade_changed($cm, $item, $grade, false);
// Enabled and completion required and item number right. It is supposed
// to call update_state with the new potential state being obtained from
// internal_get_grade_state.
$cm = (object)array('course'=>42, 'id'=>13, 'completion'=>0, 'completiongradeitemnumber'=>3);
$grade = (object)array('userid'=>31337, 'finalgrade'=>1, 'rawgrade'=>0);
$c->expects($this->at(0))
->method('is_enabled')
->with($cm)
->will($this->returnValue(true));
$c->expects($this->at(1))
->method('update_state')
->with($cm, COMPLETION_COMPLETE_PASS, 31337)
->will($this->returnValue(true));
$c->inform_grade_changed($cm, $item, $grade, false);
// Same as above but marked deleted. It is supposed to call update_state
// with new potential state being COMPLETION_INCOMPLETE
$cm = (object)array('course'=>42, 'id'=>13, 'completion'=>0, 'completiongradeitemnumber'=>3);
$grade = (object)array('userid'=>31337, 'finalgrade'=>1, 'rawgrade'=>0);
$c->expects($this->at(0))
->method('is_enabled')
->with($cm)
->will($this->returnValue(true));
$c->expects($this->at(1))
->method('update_state')
->with($cm, COMPLETION_INCOMPLETE, 31337)
->will($this->returnValue(true));
$c->inform_grade_changed($cm, $item, $grade, true);
}
function test_internal_get_grade_state() {
$item = new stdClass;
$grade = new stdClass;
$item->gradepass = 4;
$item->hidden = 0;
$grade->rawgrade = 4.0;
$grade->finalgrade = null;
// Grade has pass mark and is not hidden, user passes
$this->assertEquals(
COMPLETION_COMPLETE_PASS,
completion_info::internal_get_grade_state($item, $grade));
// Same but user fails
$grade->rawgrade = 3.9;
$this->assertEquals(
COMPLETION_COMPLETE_FAIL,
completion_info::internal_get_grade_state($item, $grade));
// User fails on raw grade but passes on final
$grade->finalgrade = 4.0;
$this->assertEquals(
COMPLETION_COMPLETE_PASS,
completion_info::internal_get_grade_state($item, $grade));
// Item is hidden
$item->hidden = 1;
$this->assertEquals(
COMPLETION_COMPLETE,
completion_info::internal_get_grade_state($item, $grade));
// Item isn't hidden but has no pass mark
$item->hidden = 0;
$item->gradepass = 0;
$this->assertEquals(
COMPLETION_COMPLETE,
completion_info::internal_get_grade_state($item, $grade));
}
}
class completion_test_fake_recordset implements Iterator {
var $closed;
var $values, $index;
function __construct($values) {
$this->values = $values;
$this->index = 0;
}
function current() {
return $this->values[$this->index];
}
function key() {
return $this->values[$this->index];
}
function next() {
$this->index++;
}
function rewind() {
$this->index = 0;
}
function valid() {
return count($this->values) > $this->index;
}
function close() {
$this->closed = true;
}
function was_closed() {
return $this->closed;
}
}

View File

@ -2708,8 +2708,12 @@ function assignment_update_instance($assignment){
* Adds an assignment instance
*
* This is done by calling the add_instance() method of the assignment type class
*
* @param stdClass $assignment
* @param mod_assignment_mod_form $mform
* @return int intance id
*/
function assignment_add_instance($assignment) {
function assignment_add_instance($assignment, $mform = null) {
global $CFG;
$assignment->assignmenttype = clean_param($assignment->assignmenttype, PARAM_PLUGIN);

View File

@ -39,11 +39,11 @@ class mod_assignment_generator extends phpunit_module_generator {
/**
* Create new assignment module instance
* @param array|stdClass $record
* @param array $options
* @param array $options (mostly course_module properties)
* @return stdClass activity record with extra cmid field
*/
public function create_instance($record = null, array $options = null) {
global $DB, $CFG;
global $CFG;
require_once("$CFG->dirroot/mod/assignment/locallib.php");
$this->instancecount++;
@ -52,6 +52,9 @@ class mod_assignment_generator extends phpunit_module_generator {
$record = (object)(array)$record;
$options = (array)$options;
if (empty($record->course)) {
throw new coding_exception('module generator requires $record->course');
}
if (!isset($record->name)) {
$record->name = get_string('pluginname', 'assignment').' '.$i;
}
@ -67,15 +70,17 @@ class mod_assignment_generator extends phpunit_module_generator {
if (!isset($record->grade)) {
$record->grade = 100;
}
$record->timemodified = time();
if (!isset($record->timedue)) {
$record->timedue = 0;
}
if (isset($options['idnumber'])) {
$record->cmidnumber = $options['idnumber'];
} else {
$record->cmidnumber = '';
}
$id = $DB->insert_record('assignment', $record);
$instance = $DB->get_record('assignment', array('id'=>$id), '*', MUST_EXIST);
$cm = $this->create_course_module($instance, $options);
$instance->cmid = $cm->id;
return $instance;
$record->coursemodule = $this->precreate_course_module($record->course, $options);
$id = assignment_add_instance($record, null);
return $this->post_add_instance($id, $record->coursemodule);
}
}

View File

@ -36,28 +36,42 @@ defined('MOODLE_INTERNAL') || die();
*/
class mod_assignment_generator_testcase extends advanced_testcase {
public function test_generator() {
global $DB, $SITE;
global $DB;
$this->resetAfterTest(true);
$this->assertEquals(0, $DB->count_records('assignment'));
$course = $this->getDataGenerator()->create_course();
/** @var mod_assignment_generator $generator */
$generator = $this->getDataGenerator()->get_plugin_generator('mod_assignment');
$this->assertInstanceOf('mod_assignment_generator', $generator);
$this->assertEquals('assignment', $generator->get_modulename());
$generator->create_instance(array('course'=>$SITE->id));
$generator->create_instance(array('course'=>$SITE->id));
$assignment = $generator->create_instance(array('course'=>$SITE->id));
$generator->create_instance(array('course'=>$course->id, 'grade'=>0));
$generator->create_instance(array('course'=>$course->id, 'grade'=>0));
$assignment = $generator->create_instance(array('course'=>$course->id, 'grade'=>100));
$this->assertEquals(3, $DB->count_records('assignment'));
$cm = get_coursemodule_from_instance('assignment', $assignment->id);
$this->assertEquals($assignment->id, $cm->instance);
$this->assertEquals('assignment', $cm->modname);
$this->assertEquals($SITE->id, $cm->course);
$this->assertEquals($course->id, $cm->course);
$context = context_module::instance($cm->id);
$this->assertEquals($assignment->cmid, $context->instanceid);
// test gradebook integration using low level DB access - DO NOT USE IN PLUGIN CODE!
$gitem = $DB->get_record('grade_items', array('courseid'=>$course->id, 'itemtype'=>'mod', 'itemmodule'=>'assignment', 'iteminstance'=>$assignment->id));
$this->assertNotEmpty($gitem);
$this->assertEquals(100, $gitem->grademax);
$this->assertEquals(0, $gitem->grademin);
$this->assertEquals(GRADE_TYPE_VALUE, $gitem->gradetype);
// test eventslib integration
$this->setUser(2); // admin
$generator->create_instance(array('course'=>$course->id, 'timedue'=>(time()+60*60+24)));
$this->setUser(0);
}
}

View File

@ -829,11 +829,11 @@ function data_tags_check($dataid, $template) {
/**
* Adds an instance of a data
*
* @global object
* @param object $data
* @return $int
* @param stdClass $data
* @param mod_data_mod_form $mform
* @return int intance id
*/
function data_add_instance($data) {
function data_add_instance($data, $mform = null) {
global $DB;
if (empty($data->assessed)) {

View File

@ -39,11 +39,12 @@ class mod_data_generator extends phpunit_module_generator {
/**
* Create new data module instance
* @param array|stdClass $record
* @param array $options
* @param array $options (mostly course_module properties)
* @return stdClass activity record with extra cmid field
*/
public function create_instance($record = null, array $options = null) {
global $DB;
global $CFG;
require_once("$CFG->dirroot/mod/data/locallib.php");
$this->instancecount++;
$i = $this->instancecount;
@ -51,6 +52,9 @@ class mod_data_generator extends phpunit_module_generator {
$record = (object)(array)$record;
$options = (array)$options;
if (empty($record->course)) {
throw new coding_exception('module generator requires $record->course');
}
if (!isset($record->name)) {
$record->name = get_string('pluginname', 'data').' '.$i;
}
@ -60,15 +64,20 @@ class mod_data_generator extends phpunit_module_generator {
if (!isset($record->introformat)) {
$record->introformat = FORMAT_MOODLE;
}
$record->timemodified = time();
if (!isset($record->assessed)) {
$record->assessed = 0;
}
if (!isset($record->scale)) {
$record->scale = 0;
}
if (isset($options['idnumber'])) {
$record->cmidnumber = $options['idnumber'];
} else {
$record->cmidnumber = '';
}
$id = $DB->insert_record('data', $record);
$instance = $DB->get_record('data', array('id'=>$id), '*', MUST_EXIST);
$cm = $this->create_course_module($instance, $options);
$instance->cmid = $cm->id;
return $instance;
$record->coursemodule = $this->precreate_course_module($record->course, $options);
$id = data_add_instance($record, null);
return $this->post_add_instance($id, $record->coursemodule);
}
}

View File

@ -36,28 +36,39 @@ defined('MOODLE_INTERNAL') || die();
*/
class mod_data_generator_testcase extends advanced_testcase {
public function test_generator() {
global $DB, $SITE;
global $DB;
$this->resetAfterTest(true);
$this->assertEquals(0, $DB->count_records('data'));
$course = $this->getDataGenerator()->create_course();
/** @var mod_data_generator $generator */
$generator = $this->getDataGenerator()->get_plugin_generator('mod_data');
$this->assertInstanceOf('mod_data_generator', $generator);
$this->assertEquals('data', $generator->get_modulename());
$generator->create_instance(array('course'=>$SITE->id));
$generator->create_instance(array('course'=>$SITE->id));
$data = $generator->create_instance(array('course'=>$SITE->id));
$generator->create_instance(array('course'=>$course->id));
$generator->create_instance(array('course'=>$course->id));
$data = $generator->create_instance(array('course'=>$course->id));
$this->assertEquals(3, $DB->count_records('data'));
$cm = get_coursemodule_from_instance('data', $data->id);
$this->assertEquals($data->id, $cm->instance);
$this->assertEquals('data', $cm->modname);
$this->assertEquals($SITE->id, $cm->course);
$this->assertEquals($course->id, $cm->course);
$context = context_module::instance($cm->id);
$this->assertEquals($data->cmid, $context->instanceid);
// test gradebook integration using low level DB access - DO NOT USE IN PLUGIN CODE!
$data = $generator->create_instance(array('course'=>$course->id, 'assessed'=>1, 'scale'=>100));
$gitem = $DB->get_record('grade_items', array('courseid'=>$course->id, 'itemtype'=>'mod', 'itemmodule'=>'data', 'iteminstance'=>$data->id));
$this->assertNotEmpty($gitem);
$this->assertEquals(100, $gitem->grademax);
$this->assertEquals(0, $gitem->grademin);
$this->assertEquals(GRADE_TYPE_VALUE, $gitem->gradetype);
}
}

View File

@ -52,12 +52,11 @@ define('FORUM_TRACKING_ON', 2);
* will create a new instance and return the id number
* of the new instance.
*
* @global object
* @global object
* @param object $forum add forum instance (with magic quotes)
* @param stdClass $forum add forum instance
* @param mod_forum_mod_form $mform
* @return int intance id
*/
function forum_add_instance($forum, $mform) {
function forum_add_instance($forum, $mform = null) {
global $CFG, $DB;
$forum->timemodified = time();

View File

@ -43,7 +43,7 @@ class mod_forum_generator extends phpunit_module_generator {
* @return stdClass activity record with extra cmid field
*/
public function create_instance($record = null, array $options = null) {
global $DB, $CFG;
global $CFG;
require_once("$CFG->dirroot/mod/forum/locallib.php");
$this->instancecount++;
@ -52,6 +52,9 @@ class mod_forum_generator extends phpunit_module_generator {
$record = (object)(array)$record;
$options = (array)$options;
if (empty($record->course)) {
throw new coding_exception('module generator requires $record->course');
}
if (!isset($record->name)) {
$record->name = get_string('pluginname', 'forum').' '.$i;
}
@ -61,15 +64,26 @@ class mod_forum_generator extends phpunit_module_generator {
if (!isset($record->introformat)) {
$record->introformat = FORMAT_MOODLE;
}
$record->timemodified = time();
if (!isset($record->type)) {
$record->type = 'general';
}
if (!isset($record->assessed)) {
$record->assessed = 0;
}
if (!isset($record->scale)) {
$record->scale = 0;
}
if (!isset($record->forcesubscribe)) {
$record->forcesubscribe = FORUM_CHOOSESUBSCRIBE;
}
if (isset($options['idnumber'])) {
$record->cmidnumber = $options['idnumber'];
} else {
$record->cmidnumber = '';
}
$id = $DB->insert_record('forum', $record);
$instance = $DB->get_record('forum', array('id'=>$id), '*', MUST_EXIST);
$cm = $this->create_course_module($instance, $options);
$instance->cmid = $cm->id;
return $instance;
$record->coursemodule = $this->precreate_course_module($record->course, $options);
$id = forum_add_instance($record, null);
return $this->post_add_instance($id, $record->coursemodule);
}
}

View File

@ -36,28 +36,38 @@ defined('MOODLE_INTERNAL') || die();
*/
class mod_forum_generator_testcase extends advanced_testcase {
public function test_generator() {
global $DB, $SITE;
global $DB;
$this->resetAfterTest(true);
$this->assertEquals(0, $DB->count_records('forum'));
$course = $this->getDataGenerator()->create_course();
/** @var mod_forum_generator $generator */
$generator = $this->getDataGenerator()->get_plugin_generator('mod_forum');
$this->assertInstanceOf('mod_forum_generator', $generator);
$this->assertEquals('forum', $generator->get_modulename());
$generator->create_instance(array('course'=>$SITE->id));
$generator->create_instance(array('course'=>$SITE->id));
$forum = $generator->create_instance(array('course'=>$SITE->id));
$generator->create_instance(array('course'=>$course->id));
$generator->create_instance(array('course'=>$course->id));
$forum = $generator->create_instance(array('course'=>$course->id));
$this->assertEquals(3, $DB->count_records('forum'));
$cm = get_coursemodule_from_instance('forum', $forum->id);
$this->assertEquals($forum->id, $cm->instance);
$this->assertEquals('forum', $cm->modname);
$this->assertEquals($SITE->id, $cm->course);
$this->assertEquals($course->id, $cm->course);
$context = context_module::instance($cm->id);
$this->assertEquals($forum->cmid, $context->instanceid);
// test gradebook integration using low level DB access - DO NOT USE IN PLUGIN CODE!
$forum = $generator->create_instance(array('course'=>$course->id, 'assessed'=>1, 'scale'=>100));
$gitem = $DB->get_record('grade_items', array('courseid'=>$course->id, 'itemtype'=>'mod', 'itemmodule'=>'forum', 'iteminstance'=>$forum->id));
$this->assertNotEmpty($gitem);
$this->assertEquals(100, $gitem->grademax);
$this->assertEquals(0, $gitem->grademin);
$this->assertEquals(GRADE_TYPE_VALUE, $gitem->gradetype);
}
}

View File

@ -81,16 +81,15 @@ function page_get_post_actions() {
/**
* Add page instance.
* @param object $data
* @param object $mform
* @param stdClass $data
* @param mod_page_mod_form $mform
* @return int new page instance id
*/
function page_add_instance($data, $mform) {
function page_add_instance($data, $mform = null) {
global $CFG, $DB;
require_once("$CFG->libdir/resourcelib.php");
$cmid = $data->coursemodule;
$draftitemid = $data->page['itemid'];
$cmid = $data->coursemodule;
$data->timemodified = time();
$displayoptions = array();
@ -102,8 +101,10 @@ function page_add_instance($data, $mform) {
$displayoptions['printintro'] = $data->printintro;
$data->displayoptions = serialize($displayoptions);
$data->content = $data->page['text'];
$data->contentformat = $data->page['format'];
if ($mform) {
$data->content = $data->page['text'];
$data->contentformat = $data->page['format'];
}
$data->id = $DB->insert_record('page', $data);
@ -111,7 +112,8 @@ function page_add_instance($data, $mform) {
$DB->set_field('course_modules', 'instance', $data->id, array('id'=>$cmid));
$context = get_context_instance(CONTEXT_MODULE, $cmid);
if ($draftitemid) {
if ($mform and !empty($data->page['itemid'])) {
$draftitemid = $data->page['itemid'];
$data->content = file_save_draft_area_files($draftitemid, $context->id, 'mod_page', 'content', 0, page_get_editor_options($context), $data->content);
$DB->update_record('page', $data);
}

View File

@ -43,7 +43,7 @@ class mod_page_generator extends phpunit_module_generator {
* @return stdClass activity record with extra cmid field
*/
public function create_instance($record = null, array $options = null) {
global $DB, $CFG;
global $CFG;
require_once("$CFG->dirroot/mod/page/locallib.php");
$this->instancecount++;
@ -52,6 +52,9 @@ class mod_page_generator extends phpunit_module_generator {
$record = (object)(array)$record;
$options = (array)$options;
if (empty($record->course)) {
throw new coding_exception('module generator requires $record->course');
}
if (!isset($record->name)) {
$record->name = get_string('pluginname', 'page').' '.$i;
}
@ -70,15 +73,20 @@ class mod_page_generator extends phpunit_module_generator {
if (!isset($record->display)) {
$record->display = RESOURCELIB_DISPLAY_AUTO;
}
$record->timemodified = time();
if (isset($options['idnumber'])) {
$record->cmidnumber = $options['idnumber'];
} else {
$record->cmidnumber = '';
}
if (!isset($record->printheading)) {
$record->printheading = 1;
}
if (!isset($record->printintro)) {
$record->printintro = 0;
}
$id = $DB->insert_record('page', $record);
$instance = $DB->get_record('page', array('id'=>$id), '*', MUST_EXIST);
$cm = $this->create_course_module($instance, $options);
$instance->cmid = $cm->id;
return $instance;
$record->coursemodule = $this->precreate_course_module($record->course, $options);
$id = page_add_instance($record, null);
return $this->post_add_instance($id, $record->coursemodule);
}
}