Merge branch 'w12_MDL-31857_m23_phpunit' of git://github.com/skodak/moodle
This commit is contained in:
@@ -25,3 +25,4 @@ CVS
|
||||
/.project
|
||||
/.buildpath
|
||||
/.cache
|
||||
/phpunit.xml
|
||||
@@ -0,0 +1,93 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* PHPUnit related utilities.
|
||||
*
|
||||
* Exit codes:
|
||||
* 0 - success
|
||||
* 1 - general error
|
||||
* 130 - coding error
|
||||
* 131 - configuration problem
|
||||
* 133 - drop existing data before installing
|
||||
*
|
||||
* @package tool_phpunit
|
||||
* @copyright 2012 Petr Skoda {@link http://skodak.org}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
define('PHPUNIT_CLI_UTIL', true);
|
||||
|
||||
require(__DIR__ . '/../../../../lib/phpunit/bootstrap.php');
|
||||
require_once($CFG->libdir.'/phpunit/lib.php');
|
||||
require_once($CFG->libdir.'/adminlib.php');
|
||||
require_once($CFG->libdir.'/upgradelib.php');
|
||||
require_once($CFG->libdir.'/clilib.php');
|
||||
require_once($CFG->libdir.'/pluginlib.php');
|
||||
require_once($CFG->libdir.'/installlib.php');
|
||||
|
||||
// now get cli options
|
||||
list($options, $unrecognized) = cli_get_params(
|
||||
array(
|
||||
'drop' => false,
|
||||
'install' => false,
|
||||
'buildconfig' => false,
|
||||
'help' => false,
|
||||
),
|
||||
array(
|
||||
'h' => 'help'
|
||||
)
|
||||
);
|
||||
|
||||
if ($unrecognized) {
|
||||
$unrecognized = implode("\n ", $unrecognized);
|
||||
cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
|
||||
}
|
||||
|
||||
$drop = $options['drop'];
|
||||
$install = $options['install'];
|
||||
$buildconfig = $options['buildconfig'];
|
||||
|
||||
if ($options['help'] or (!$drop and !$install and !$buildconfig)) {
|
||||
$help = "Various PHPUnit utility functions
|
||||
|
||||
Options:
|
||||
--drop Drop database and dataroot
|
||||
--install Install database
|
||||
--buildconfig Build /phpunit.xml from /phpunit.xml.dist that includes suites for all plugins and core
|
||||
|
||||
-h, --help Print out this help
|
||||
|
||||
Example:
|
||||
\$/usr/bin/php lib/phpunit/tool.php
|
||||
";
|
||||
echo $help;
|
||||
die;
|
||||
}
|
||||
|
||||
if ($buildconfig) {
|
||||
phpunit_util::build_config_file();
|
||||
exit(0);
|
||||
|
||||
} else if ($drop) {
|
||||
phpunit_util::drop_site();
|
||||
// note: we must stop here because $CFG is messed up and we can not reinstall, sorry
|
||||
exit(0);
|
||||
|
||||
} else if ($install) {
|
||||
phpunit_util::install_site();
|
||||
exit(0);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* PHPUnit info
|
||||
*
|
||||
* @package tool_phpunit
|
||||
* @copyright 2012 Petr Skoda {@link http://skodak.org}
|
||||
*/
|
||||
|
||||
define('NO_OUTPUT_BUFFERING', true);
|
||||
|
||||
require(dirname(__FILE__) . '/../../../config.php');
|
||||
require_once($CFG->libdir.'/adminlib.php');
|
||||
|
||||
admin_externalpage_setup('toolphpunit');
|
||||
|
||||
echo $OUTPUT->header();
|
||||
echo $OUTPUT->heading(get_string('pluginname', 'tool_phpunit'));
|
||||
echo $OUTPUT->box_start();
|
||||
|
||||
$info = file_get_contents("$CFG->libdir/phpunit/readme.md");
|
||||
echo markdown_to_html($info);
|
||||
|
||||
echo $OUTPUT->box_end();
|
||||
echo $OUTPUT->footer();
|
||||
@@ -0,0 +1,25 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Strings for component 'tool_phpunit'
|
||||
*
|
||||
* @package tool_phpunit
|
||||
* @copyright 2012 Petr Skoda {@link http://skodak.org}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
$string['pluginname'] = 'PHPUnit tests';
|
||||
@@ -0,0 +1,28 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* PHPunit integration
|
||||
*
|
||||
* @package tool_phpunit
|
||||
* @copyright 2012 Petr Skoda {@link http://skodak.org}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
|
||||
defined('MOODLE_INTERNAL') || die;
|
||||
|
||||
$ADMIN->add('development', new admin_externalpage('toolphpunit', get_string('pluginname', 'tool_phpunit'), "$CFG->wwwroot/$CFG->admin/tool/phpunit/index.php"));
|
||||
@@ -0,0 +1,30 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Plugin version info
|
||||
*
|
||||
* @package tool_phpunit
|
||||
* @copyright 2012 Petr Skoda {@link http://skodak.org}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2012030800; // The current plugin version (Date: YYYYMMDDXX)
|
||||
$plugin->requires = 2012030100; // Requires this Moodle version
|
||||
$plugin->component = 'tool_phpunit'; // Full name of the plugin (used for diagnostics)
|
||||
|
||||
+7
-1
@@ -479,7 +479,13 @@ $CFG->admin = 'admin';
|
||||
// Example:
|
||||
// $CFG->forced_plugin_settings = array('pluginname' => array('settingname' => 'value', 'secondsetting' => 'othervalue'),
|
||||
// 'otherplugin' => array('mysetting' => 'myvalue', 'thesetting' => 'thevalue'));
|
||||
|
||||
//
|
||||
//=========================================================================
|
||||
// 9. PHPUNIT SUPPORT
|
||||
//=========================================================================
|
||||
// $CFG->phpunit_prefix = 'phpu_';
|
||||
// $CFG->phpunit_dataroot = '/home/example/phpu_moodledata';
|
||||
// $CFG->phpunit_directorypermissions = 02777; // optional
|
||||
|
||||
//=========================================================================
|
||||
// ALL DONE! To continue installation, visit your main page with a browser
|
||||
|
||||
+1
-1
@@ -217,7 +217,7 @@ $ACCESSLIB_PRIVATE->capabilities = null; // detailed information about th
|
||||
*/
|
||||
function accesslib_clear_all_caches_for_unit_testing() {
|
||||
global $UNITTEST, $USER;
|
||||
if (empty($UNITTEST->running)) {
|
||||
if (empty($UNITTEST->running) and !PHPUNITTEST) {
|
||||
throw new coding_exception('You must not call clear_all_caches outside of unit tests.');
|
||||
}
|
||||
|
||||
|
||||
@@ -112,18 +112,15 @@ class database_manager {
|
||||
|
||||
/**
|
||||
* Reset a sequence to the id field of a table.
|
||||
* @param string $table Name of table.
|
||||
* @throws ddl_exception|ddl_table_missing_exception Exception thrown upon reset errors.
|
||||
* @param string|xmldb_table $table Name of table.
|
||||
* @throws ddl_exception thrown upon reset errors.
|
||||
*/
|
||||
public function reset_sequence($table) {
|
||||
if (!is_string($table) and !($table instanceof xmldb_table)) {
|
||||
throw new ddl_exception('ddlunknownerror', NULL, 'incorrect table parameter!');
|
||||
}
|
||||
|
||||
/// Check the table exists
|
||||
if (!$this->table_exists($table)) {
|
||||
throw new ddl_table_missing_exception($table);
|
||||
}
|
||||
// Do not test if table exists because it is slow
|
||||
|
||||
if (!$sqlarr = $this->generator->getResetSequenceSQL($table)) {
|
||||
throw new ddl_exception('ddlunknownerror', null, 'table reset sequence sql not generated');
|
||||
|
||||
@@ -8,6 +8,8 @@ if (!defined('MOODLE_INTERNAL')) {
|
||||
die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page
|
||||
}
|
||||
|
||||
global $CFG;
|
||||
|
||||
require_once($CFG->libdir . '/adminlib.php');
|
||||
|
||||
class ddl_test extends UnitTestCase {
|
||||
|
||||
+1
-1
@@ -7775,7 +7775,7 @@ function get_plugin_types($fullpaths=true) {
|
||||
function get_plugin_list($plugintype) {
|
||||
global $CFG;
|
||||
|
||||
$ignored = array('CVS', '_vti_cnf', 'simpletest', 'db', 'yui', 'phpunit');
|
||||
$ignored = array('CVS', '_vti_cnf', 'simpletest', 'db', 'yui', 'tests');
|
||||
if ($plugintype == 'auth') {
|
||||
// Historically we have had an auth plugin called 'db', so allow a special case.
|
||||
$key = array_search('db', $ignored);
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Prepares PHPUnit environment, it is called automatically.
|
||||
*
|
||||
* Exit codes:
|
||||
* 0 - success
|
||||
* 1 - general error
|
||||
* 130 - coding error
|
||||
* 131 - configuration problem
|
||||
* 132 - drop data, then install new test database
|
||||
*
|
||||
* @package core_core
|
||||
* @category phpunit
|
||||
* @copyright 2012 Petr Skoda {@link http://skodak.org}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
// we want to know about all problems
|
||||
error_reporting(E_ALL | E_STRICT);
|
||||
ini_set('display_errors', '1');
|
||||
ini_set('log_errors', '1');
|
||||
|
||||
if (isset($_SERVER['REMOTE_ADDR'])) {
|
||||
phpunit_bootstrap_error('Unit tests can be executed only from commandline!', 1);
|
||||
}
|
||||
|
||||
if (defined('PHPUNITTEST')) {
|
||||
phpunit_bootstrap_error("PHPUNITTEST constant must not be manually defined anywhere!", 130);
|
||||
}
|
||||
define('PHPUNITTEST', true);
|
||||
|
||||
if (defined('CLI_SCRIPT')) {
|
||||
phpunit_bootstrap_error('CLI_SCRIPT must not be manually defined in any PHPUnit test scripts', 130);
|
||||
}
|
||||
define('CLI_SCRIPT', true);
|
||||
|
||||
define('NO_OUTPUT_BUFFERING', true);
|
||||
|
||||
// only load CFG from config.php
|
||||
define('ABORT_AFTER_CONFIG', true);
|
||||
require(__DIR__ . '/../../config.php');
|
||||
|
||||
// remove error handling overrides done in config.php
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', '1');
|
||||
ini_set('log_errors', '1');
|
||||
|
||||
// prepare dataroot
|
||||
umask(0);
|
||||
if (isset($CFG->phpunit_directorypermissions)) {
|
||||
$CFG->directorypermissions = $CFG->phpunit_directorypermissions;
|
||||
} else {
|
||||
$CFG->directorypermissions = 02777;
|
||||
}
|
||||
$CFG->filepermissions = ($CFG->directorypermissions & 0666);
|
||||
if (!isset($CFG->phpunit_dataroot)) {
|
||||
phpunit_bootstrap_error('Missing $CFG->phpunit_dataroot in config.php, can not run tests!', 131);
|
||||
}
|
||||
if (isset($CFG->dataroot) and $CFG->phpunit_dataroot === $CFG->dataroot) {
|
||||
phpunit_bootstrap_error('$CFG->dataroot and $CFG->phpunit_dataroot must not be identical, can not run tests!', 131);
|
||||
}
|
||||
if (!file_exists($CFG->phpunit_dataroot)) {
|
||||
mkdir($CFG->phpunit_dataroot, $CFG->directorypermissions);
|
||||
}
|
||||
if (!is_dir($CFG->phpunit_dataroot)) {
|
||||
phpunit_bootstrap_error('$CFG->phpunit_dataroot directory can not be created, can not run tests!', 131);
|
||||
}
|
||||
if (!is_writable($CFG->phpunit_dataroot)) {
|
||||
// try to fix premissions if possible
|
||||
if (function_exists('posix_getuid')) {
|
||||
$chmod = fileperms($CFG->phpunit_dataroot);
|
||||
if (fileowner($dir) == posix_getuid()) {
|
||||
$chmod = $chmod | 0700;
|
||||
chmod($CFG->phpunit_dataroot, $chmod);
|
||||
}
|
||||
}
|
||||
if (!is_writable($CFG->phpunit_dataroot)) {
|
||||
phpunit_bootstrap_error('$CFG->phpunit_dataroot directory is not writable, can not run tests!', 131);
|
||||
}
|
||||
}
|
||||
if (!file_exists("$CFG->phpunit_dataroot/phpunittestdir.txt")) {
|
||||
if ($dh = opendir($CFG->phpunit_dataroot)) {
|
||||
while (($file = readdir($dh)) !== false) {
|
||||
if ($file === 'phpunit' or $file === '.' or $file === '..' or $file === '.DS_store') {
|
||||
continue;
|
||||
}
|
||||
phpunit_bootstrap_error('$CFG->phpunit_dataroot directory is not empty, can not run tests! Is it used for anything else?', 131);
|
||||
}
|
||||
closedir($dh);
|
||||
unset($dh);
|
||||
unset($file);
|
||||
}
|
||||
|
||||
// now we are 100% sure this dir is used only for phpunit tests
|
||||
phpunit_bootstrap_initdataroot($CFG->phpunit_dataroot);
|
||||
}
|
||||
|
||||
|
||||
// verify db prefix
|
||||
if (!isset($CFG->phpunit_prefix)) {
|
||||
phpunit_bootstrap_error('Missing $CFG->phpunit_dataroot in config.php, can not run tests!', 131);
|
||||
}
|
||||
if (isset($CFG->prefix) and $CFG->prefix === $CFG->phpunit_prefix) {
|
||||
phpunit_bootstrap_error('$CFG->prefix and $CFG->phpunit_prefix must not be identical, can not run tests!', 131);
|
||||
}
|
||||
|
||||
// throw away standard CFG settings
|
||||
|
||||
$CFG->dataroot = $CFG->phpunit_dataroot;
|
||||
$CFG->prefix = $CFG->phpunit_prefix;
|
||||
|
||||
$allowed = array('wwwroot', 'dataroot', 'dirroot', 'admin', 'directorypermissions', 'filepermissions',
|
||||
'dbtype', 'dblibrary', 'dbhost', 'dbname', 'dbuser', 'dbpass', 'prefix', 'dboptions');
|
||||
$productioncfg = (array)$CFG;
|
||||
$CFG = new stdClass();
|
||||
foreach ($productioncfg as $key=>$value) {
|
||||
if (!in_array($key, $allowed) and strpos($key, 'phpunit_') !== 0) {
|
||||
// ignore
|
||||
continue;
|
||||
}
|
||||
$CFG->{$key} = $value;
|
||||
}
|
||||
unset($key);
|
||||
unset($value);
|
||||
unset($allowed);
|
||||
unset($productioncfg);
|
||||
|
||||
// force the same CFG settings in all sites
|
||||
$CFG->debug = (E_ALL | E_STRICT | 38911); // can not use DEBUG_DEVELOPER here
|
||||
$CFG->debugdisplay = 1;
|
||||
error_reporting($CFG->debug);
|
||||
ini_set('display_errors', '1');
|
||||
ini_set('log_errors', '0');
|
||||
|
||||
$CFG->noemailever = true; // better not mail anybody from tests, override temporarily if necessary
|
||||
$CFG->cachetext = 0; // disable this very nasty setting
|
||||
|
||||
// some ugly hacks
|
||||
$CFG->themerev = 1;
|
||||
$CFG->jsrev = 1;
|
||||
|
||||
// load test case stub classes and other stuff
|
||||
require_once("$CFG->dirroot/lib/phpunit/lib.php");
|
||||
|
||||
// finish moodle init
|
||||
define('ABORT_AFTER_CONFIG_CANCEL', true);
|
||||
require("$CFG->dirroot/lib/setup.php");
|
||||
|
||||
raise_memory_limit(MEMORY_EXTRA);
|
||||
|
||||
if (defined('PHPUNIT_CLI_UTIL')) {
|
||||
// all other tests are done in the CLI scripts...
|
||||
return;
|
||||
}
|
||||
|
||||
if (!phpunit_util::is_testing_ready()) {
|
||||
phpunit_bootstrap_error('Database is not initialised to run unit tests, please use "php admin/tool/phpunit/cli/util.php --install"', 132);
|
||||
}
|
||||
|
||||
// refresh data in all tables, clear caches, etc.
|
||||
phpunit_util::reset_all_data();
|
||||
|
||||
// store fresh globals
|
||||
phpunit_util::init_globals();
|
||||
|
||||
|
||||
//=========================================================
|
||||
|
||||
/**
|
||||
* Print error and stop execution
|
||||
* @param $text
|
||||
* @param int $errorcode
|
||||
* @return void - stops code execution with error code
|
||||
*/
|
||||
function phpunit_bootstrap_error($text, $errorcode = 1) {
|
||||
fwrite(STDERR, $text."\n");
|
||||
exit($errorcode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark empty dataroot to be used for testing.
|
||||
* @param $dataroot
|
||||
* @return void
|
||||
*/
|
||||
function phpunit_bootstrap_initdataroot($dataroot) {
|
||||
global $CFG;
|
||||
|
||||
file_put_contents("$dataroot/phpunittestdir.txt", 'Contents of this directory are used during tests only, do not delete this file!');
|
||||
chmod("$dataroot/phpunittestdir.txt", $CFG->filepermissions);
|
||||
if (!file_exists("$CFG->phpunit_dataroot/phpunit")) {
|
||||
mkdir("$CFG->phpunit_dataroot/phpunit", $CFG->directorypermissions);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,602 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Various PHPUnit classes and functions
|
||||
*
|
||||
* @package core_core
|
||||
* @category phpunit
|
||||
* @copyright 2012 Petr Skoda {@link http://skodak.org}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
require_once 'PHPUnit/Autoload.php'; // necessary when loaded from cli/util.php script
|
||||
|
||||
|
||||
/**
|
||||
* Collection of utility methods.
|
||||
*
|
||||
* @package core_phpunit
|
||||
* @copyright 2012 Petr Skoda {@link http://skodak.org}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class phpunit_util {
|
||||
/**
|
||||
* @var array original content of all database tables
|
||||
*/
|
||||
protected static $tabledata = null;
|
||||
|
||||
protected static $globals = array();
|
||||
|
||||
/**
|
||||
* Returns contents of all tables right after installation.
|
||||
* @static
|
||||
* @return array $table=>$records
|
||||
*/
|
||||
protected static function get_tabledata() {
|
||||
global $CFG;
|
||||
|
||||
if (!isset(self::$tabledata)) {
|
||||
$data = file_get_contents("$CFG->dataroot/phpunit/tabledata.ser");
|
||||
self::$tabledata = unserialize($data);
|
||||
}
|
||||
|
||||
if (!is_array(self::$tabledata)) {
|
||||
phpunit_bootstrap_error('Can not read dataroot/phpunit/tabledata.ser or invalid format!');
|
||||
}
|
||||
|
||||
return self::$tabledata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise CFG using data from fresh new install.
|
||||
* @static
|
||||
*/
|
||||
public static function initialise_cfg() {
|
||||
global $CFG, $DB;
|
||||
|
||||
if (!file_exists("$CFG->dataroot/phpunit/tabledata.ser")) {
|
||||
// most probably PHPUnit CLI installer
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$DB->get_manager()->table_exists('config') or !$DB->count_records('config')) {
|
||||
@unlink("$CFG->dataroot/phpunit/tabledata.ser");
|
||||
@unlink("$CFG->dataroot/phpunit/versionshash.txt");
|
||||
self::$tabledata = null;
|
||||
return;
|
||||
}
|
||||
|
||||
$data = self::get_tabledata();
|
||||
|
||||
foreach($data['config'] as $record) {
|
||||
$name = $record->name;
|
||||
$value = $record->value;
|
||||
if (property_exists($CFG, $name)) {
|
||||
// config.php settings always take precedence
|
||||
continue;
|
||||
}
|
||||
$CFG->{$name} = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset contents of all database tables to initial values, reset caches, etc.
|
||||
*
|
||||
* Note: this is relatively slow (cca 2 seconds for pg and 7 for mysql) - please use with care!
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
public static function reset_all_data() {
|
||||
global $DB, $CFG;
|
||||
|
||||
$data = self::get_tabledata();
|
||||
|
||||
$trans = $DB->start_delegated_transaction(); // faster and safer
|
||||
foreach ($data as $table=>$records) {
|
||||
$DB->delete_records($table, array());
|
||||
$resetseq = null;
|
||||
foreach ($records as $record) {
|
||||
if (is_null($resetseq)) {
|
||||
$resetseq = property_exists($record, 'id');
|
||||
}
|
||||
$DB->import_record($table, $record, false, true);
|
||||
}
|
||||
if ($resetseq === true) {
|
||||
$DB->get_manager()->reset_sequence($table, true);
|
||||
}
|
||||
}
|
||||
$trans->allow_commit();
|
||||
|
||||
purge_all_caches();
|
||||
|
||||
$user = new stdClass();
|
||||
$user->id = 0;
|
||||
$user->mnet = 0;
|
||||
$user->mnethostid = $CFG->mnet_localhost_id;
|
||||
session_set_user($user);
|
||||
accesslib_clear_all_caches_for_unit_testing();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called during bootstrap only!
|
||||
* @static
|
||||
* @return void
|
||||
*/
|
||||
public static function init_globals() {
|
||||
global $CFG;
|
||||
|
||||
self::$globals['CFG'] = clone($CFG);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns original state of global variable.
|
||||
* @static
|
||||
* @param string $name
|
||||
* @return mixed
|
||||
*/
|
||||
public static function get_global_backup($name) {
|
||||
if (isset(self::$globals[$name])) {
|
||||
if (is_object(self::$globals[$name])) {
|
||||
$return = clone(self::$globals[$name]);
|
||||
return $return;
|
||||
} else {
|
||||
return self::$globals[$name];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this site (db and dataroot) appear to be used for production?
|
||||
* We try very hard to prevent accidental damage done to production servers!!
|
||||
*
|
||||
* @static
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_test_site() {
|
||||
global $DB, $CFG;
|
||||
|
||||
if (!file_exists("$CFG->dataroot/phpunittestdir.txt")) {
|
||||
// this is already tested in bootstrap script,
|
||||
// but anway presence of this file means the dataroot is for testing
|
||||
return false;
|
||||
}
|
||||
|
||||
$tables = $DB->get_tables(false);
|
||||
if ($tables) {
|
||||
if (!$DB->get_manager()->table_exists('config')) {
|
||||
return false;
|
||||
}
|
||||
if (!get_config('core', 'phpunittest')) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this site initialised to run unit tests?
|
||||
*
|
||||
* @static
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_testing_ready() {
|
||||
global $DB, $CFG;
|
||||
|
||||
if (!self::is_test_site()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$tables = $DB->get_tables(true);
|
||||
|
||||
if (!$tables) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!get_config('core', 'phpunittest')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!file_exists("$CFG->dataroot/phpunit/tabledata.ser")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!file_exists("$CFG->dataroot/phpunit/versionshash.txt")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$hash = phpunit_util::get_version_hash();
|
||||
$oldhash = file_get_contents("$CFG->dataroot/phpunit/versionshash.txt");
|
||||
|
||||
if ($hash !== $oldhash) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop all test site data.
|
||||
*
|
||||
* Note: To be used from CLI scripts only.
|
||||
*
|
||||
* @static
|
||||
* @return void, may terminate execution with exit code
|
||||
*/
|
||||
public static function drop_site() {
|
||||
global $DB, $CFG;
|
||||
|
||||
if (!self::is_test_site()) {
|
||||
cli_error('Can not drop non-test sites!!', 131);
|
||||
}
|
||||
|
||||
// drop dataroot
|
||||
remove_dir($CFG->dataroot, true);
|
||||
phpunit_bootstrap_initdataroot($CFG->dataroot);
|
||||
|
||||
// drop all tables
|
||||
$trans = $DB->start_delegated_transaction();
|
||||
$tables = $DB->get_tables(false);
|
||||
foreach ($tables as $tablename) {
|
||||
$DB->delete_records($tablename, array());
|
||||
}
|
||||
$trans->allow_commit();
|
||||
|
||||
// now drop them
|
||||
foreach ($tables as $tablename) {
|
||||
$table = new xmldb_table($tablename);
|
||||
$DB->get_manager()->drop_table($table);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a fresh test site installation
|
||||
*
|
||||
* Note: To be used from CLI scripts only.
|
||||
*
|
||||
* @static
|
||||
* @return void, may terminate execution with exit code
|
||||
*/
|
||||
public static function install_site() {
|
||||
global $DB, $CFG;
|
||||
|
||||
if (!self::is_test_site()) {
|
||||
cli_error('Can not install non-test sites!!', 131);
|
||||
}
|
||||
|
||||
if ($DB->get_tables()) {
|
||||
cli_error('Database tables already installed, drop the site first.', 133);
|
||||
}
|
||||
|
||||
$options = array();
|
||||
$options['adminpass'] = 'admin'; // removed later
|
||||
$options['shortname'] = 'phpunit';
|
||||
$options['fullname'] = 'PHPUnit test site';
|
||||
|
||||
install_cli_database($options, false);
|
||||
|
||||
// just in case remove admin password so that normal login is not possible
|
||||
$DB->set_field('user', 'password', 'not cached', array('username' => 'admin'));
|
||||
|
||||
// add test db flag
|
||||
set_config('phpunittest', 'phpunittest');
|
||||
|
||||
// store data for all tables
|
||||
$data = array();
|
||||
$tables = $DB->get_tables();
|
||||
foreach ($tables as $table) {
|
||||
$data[$table] = $DB->get_records($table, array());
|
||||
}
|
||||
$data = serialize($data);
|
||||
@unlink("$CFG->dataroot/phpunit/tabledata.ser");
|
||||
file_put_contents("$CFG->dataroot/phpunit/tabledata.ser", $data);
|
||||
|
||||
// hash all plugin versions - helps with very fast detection of db structure changes
|
||||
$hash = phpunit_util::get_version_hash();
|
||||
@unlink("$CFG->dataroot/phpunit/versionshash.txt");
|
||||
file_put_contents("$CFG->dataroot/phpunit/versionshash.txt", $hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Culculate unique version hash for all available plugins and core.
|
||||
* @static
|
||||
* @return string sha1 hash
|
||||
*/
|
||||
public static function get_version_hash() {
|
||||
global $CFG;
|
||||
|
||||
$versions = array();
|
||||
|
||||
// main version first
|
||||
$version = null;
|
||||
include($CFG->dirroot.'/version.php');
|
||||
$versions['core'] = $version;
|
||||
|
||||
// modules
|
||||
$mods = get_plugin_list('mod');
|
||||
ksort($mods);
|
||||
foreach ($mods as $mod => $fullmod) {
|
||||
$module = new stdClass();
|
||||
$module->version = null;
|
||||
include($fullmod.'/version.php');
|
||||
$versions[$mod] = $module->version;
|
||||
}
|
||||
|
||||
// now the rest of plugins
|
||||
$plugintypes = get_plugin_types();
|
||||
unset($plugintypes['mod']);
|
||||
ksort($plugintypes);
|
||||
foreach ($plugintypes as $type=>$unused) {
|
||||
$plugs = get_plugin_list($type);
|
||||
ksort($plugs);
|
||||
foreach ($plugs as $plug=>$fullplug) {
|
||||
$plugin = new stdClass();
|
||||
$plugin->version = null;
|
||||
@include($fullplug.'/version.php');
|
||||
$versions[$plug] = $plugin->version;
|
||||
}
|
||||
}
|
||||
|
||||
$hash = sha1(serialize($versions));
|
||||
|
||||
return $hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds /phpunit.xml file using defaults from /phpunit.xml.dist
|
||||
* @static
|
||||
* @return void
|
||||
*/
|
||||
public static function build_config_file() {
|
||||
global $CFG;
|
||||
|
||||
$template = '
|
||||
<testsuites>
|
||||
<testsuite name="@component@">
|
||||
<directory suffix="_test.php">@dir@</directory>
|
||||
</testsuite>
|
||||
</testsuites>';
|
||||
$data = file_get_contents("$CFG->dirroot/phpunit.xml.dist");
|
||||
|
||||
$suites = '';
|
||||
|
||||
$plugintypes = get_plugin_types();
|
||||
ksort($plugintypes);
|
||||
foreach ($plugintypes as $type=>$unused) {
|
||||
$plugs = get_plugin_list($type);
|
||||
ksort($plugs);
|
||||
foreach ($plugs as $plug=>$fullplug) {
|
||||
if (!file_exists("$fullplug/tests/")) {
|
||||
continue;
|
||||
}
|
||||
$dir = preg_replace("|$CFG->dirroot/|", '', $fullplug, 1);
|
||||
$dir .= '/tests';
|
||||
$component = $type.'_'.$plug;
|
||||
|
||||
$suite = str_replace('@component@', $component, $template);
|
||||
$suite = str_replace('@dir@', $dir, $suite);
|
||||
|
||||
$suites .= $suite;
|
||||
}
|
||||
}
|
||||
|
||||
$data = preg_replace('|<!--@plugin_suits_start@-->.*<!--@plugin_suits_end@-->|s', $suites, $data, 1);
|
||||
|
||||
@unlink("$CFG->dirroot/phpunit.xml");
|
||||
file_put_contents("$CFG->dirroot/phpunit.xml", $data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Simplified emulation test case for legacy SimpleTest.
|
||||
*
|
||||
* Note: this is supposed to work for very simple tests only.
|
||||
*
|
||||
* @deprecated
|
||||
* @package core_phpunit
|
||||
* @author Petr Skoda
|
||||
* @copyright 2012 Petr Skoda {@link http://skodak.org}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class UnitTestCase extends PHPUnit_Framework_TestCase {
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @param bool $expected
|
||||
* @param string $message
|
||||
* @return void
|
||||
*/
|
||||
public function expectException($expected, $message = '') {
|
||||
// use phpdocs: @expectedException ExceptionClassName
|
||||
if (!$expected) {
|
||||
return;
|
||||
}
|
||||
$this->setExpectedException('moodle_exception', $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @param bool $expected
|
||||
* @param string $message
|
||||
* @return void
|
||||
*/
|
||||
public static function expectError($expected = false, $message = '') {
|
||||
// not available in PHPUnit
|
||||
if (!$expected) {
|
||||
return;
|
||||
}
|
||||
self::skipIf(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @static
|
||||
* @param mixed $actual
|
||||
* @param string $messages
|
||||
* @return void
|
||||
*/
|
||||
public static function assertTrue($actual, $messages = '') {
|
||||
parent::assertTrue((bool)$actual, $messages);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @static
|
||||
* @param mixed $actual
|
||||
* @param string $messages
|
||||
* @return void
|
||||
*/
|
||||
public static function assertFalse($actual, $messages = '') {
|
||||
parent::assertFalse((bool)$actual, $messages);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @static
|
||||
* @param mixed $expected
|
||||
* @param mixed $actual
|
||||
* @param string $message
|
||||
* @return void
|
||||
*/
|
||||
public static function assertEqual($expected, $actual, $message = '') {
|
||||
parent::assertEquals($expected, $actual, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @static
|
||||
* @param mixed $expected
|
||||
* @param mixed $actual
|
||||
* @param string $message
|
||||
* @return void
|
||||
*/
|
||||
public static function assertNotEqual($expected, $actual, $message = '') {
|
||||
parent::assertNotEquals($expected, $actual, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @static
|
||||
* @param mixed $expected
|
||||
* @param mixed $actual
|
||||
* @param string $message
|
||||
* @return void
|
||||
*/
|
||||
public static function assertIdentical($expected, $actual, $message = '') {
|
||||
parent::assertSame($expected, $actual, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @static
|
||||
* @param mixed $expected
|
||||
* @param mixed $actual
|
||||
* @param string $message
|
||||
* @return void
|
||||
*/
|
||||
public static function assertNotIdentical($expected, $actual, $message = '') {
|
||||
parent::assertNotSame($expected, $actual, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @static
|
||||
* @param mixed $actual
|
||||
* @param mixed $expected
|
||||
* @param string $message
|
||||
* @return void
|
||||
*/
|
||||
public static function assertIsA($actual, $expected, $message = '') {
|
||||
parent::assertInstanceOf($expected, $actual, $message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The simplest PHPUnit test case customised for Moodle,
|
||||
* do not modify database or any globals.
|
||||
*
|
||||
* @package core_phpunit
|
||||
* @copyright 2012 Petr Skoda {@link http://skodak.org}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class basic_testcase extends PHPUnit_Framework_TestCase {
|
||||
|
||||
/**
|
||||
* Constructs a test case with the given name.
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $data
|
||||
* @param string $dataName
|
||||
*/
|
||||
public function __construct($name = NULL, array $data = array(), $dataName = '') {
|
||||
parent::__construct($name, $data, $dataName);
|
||||
|
||||
$this->setBackupGlobals(false);
|
||||
$this->setBackupStaticAttributes(false);
|
||||
$this->setRunTestInSeparateProcess(false);
|
||||
$this->setInIsolation(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the bare test sequence.
|
||||
* @return void
|
||||
*/
|
||||
public function runBare() {
|
||||
global $CFG, $USER, $DB;
|
||||
|
||||
$dbwrites = $DB->perf_get_writes();
|
||||
|
||||
parent::runBare();
|
||||
|
||||
$oldcfg = phpunit_util::get_global_backup('CFG');
|
||||
foreach($CFG as $k=>$v) {
|
||||
if (!property_exists($oldcfg, $k)) {
|
||||
unset($CFG->$k);
|
||||
error_log('warning: unexpected new $CFG->'.$k.' value in testcase: '.get_class($this).'->'.$this->getName(true));
|
||||
} else if ($oldcfg->$k !== $CFG->$k) {
|
||||
$CFG->$k = $oldcfg->$k;
|
||||
error_log('warning: unexpected change of $CFG->'.$k.' value in testcase: '.get_class($this).'->'.$this->getName(true));
|
||||
}
|
||||
unset($oldcfg->$k);
|
||||
|
||||
}
|
||||
if ($oldcfg) {
|
||||
foreach($oldcfg as $k=>$v) {
|
||||
$CFG->$k = $v;
|
||||
error_log('warning: unexpected removal of $CFG->'.$k.' in testcase: '.get_class($this).'->'.$this->getName(true));
|
||||
}
|
||||
}
|
||||
|
||||
if ($USER->id != 0) {
|
||||
error_log('warning: unexpected change of $USER in testcase: '.get_class($this).'->'.$this->getName(true));
|
||||
$USER = new stdClass();
|
||||
$USER->id = 0;
|
||||
}
|
||||
|
||||
if ($dbwrites != $DB->perf_get_writes()) {
|
||||
//TODO: find out what was changed exactly
|
||||
error_log('warning: unexpected database modification, resetting DB state in testcase: '.get_class($this).'->'.$this->getName(true));
|
||||
phpunit_util::reset_all_data();
|
||||
}
|
||||
|
||||
//TODO: somehow find out if there are changes in dataroot
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
PHPUnit testing support in Moodle
|
||||
==================================
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
1. install PHPUnit PEAR extension - see [PHPUnit docs](http://www.phpunit.de/manual/current/en/installation.html) for more details
|
||||
2. edit main config.php - add $CFG->phpunit_prefix and $CFG->phpunit_dataroot - see config-dist.php for more details
|
||||
3. execute `php admin/tool/phpunit/cli/util.php --install` to initialise test database
|
||||
4. it is necessary to reinitialise the test database manually after every upgrade or installation of new plugins
|
||||
|
||||
|
||||
Test execution
|
||||
--------------
|
||||
* optionally generate phpunit.xml by executing `php admin/tool/phpunit/cli/util.php --buildconfig` - it collects test cases from all plugins
|
||||
* execute `phpunit` shell command from dirroot directory
|
||||
* you can also execute a single test `phpunit core_phpunit_basic_testcase lib/tests/phpunit_test.php`
|
||||
* or all tests in one directory `phpunit --configuration phpunit.xml lib/tests/*_test.php`
|
||||
* it is possible to create custom configuration files in xml format and use `phpunit -c myconfig.xml`
|
||||
|
||||
|
||||
How to add more tests
|
||||
---------------------
|
||||
1. create `tests` directory in any plugin
|
||||
2. add `*_test.php` files with custom class that extends `basic_testcase`
|
||||
3. manually add all core unit test locations to `phpunit.xml.dist`
|
||||
|
||||
|
||||
How to convert existing tests
|
||||
-----------------------------
|
||||
1. create new test file in `xxx/tests/yyy_test.php`
|
||||
2. copy contents of the old test file
|
||||
3. replace `extends UnitTestCase` with `extends basic_testcase`
|
||||
4. fix setUp, tearDown, asserts, etc.
|
||||
5. some old SimpleTest tests can be executed directly - mocking, database operations, assert(), etc. does not work, you may need to add `global $CFG;` before includes
|
||||
|
||||
|
||||
FAQs
|
||||
----
|
||||
* Why is it necessary to execute the tests from commandline? PHPUnit is designed to be executed from shell, existing Moodle globals and constants would interfere with it.
|
||||
* Why `tests` subdirectory? It should not collide with any plugin name because plugin names use singular form.
|
||||
* Why is it necessary to include core and plugin suites in configuration files? PHPUnit does not seem to allow dynamic loading of tests from our dir structure.
|
||||
|
||||
|
||||
TODO
|
||||
----
|
||||
* stage 2 - implement advaced_testcase - support for database modifications, object generators, automatic rollback of db, blobals and dataroot
|
||||
* stage 3 - mocking and other advanced features, add support for execution of functional DB tests for different engines together (new options in phpunit.xml)
|
||||
* other - support for execution of tests and cli/util.php from web UI (to be implemented via shell execution), shell script that prepares everything for the first execution
|
||||
+16
-13
@@ -45,7 +45,7 @@
|
||||
global $CFG; // this should be done much earlier in config.php before creating new $CFG instance
|
||||
|
||||
if (!isset($CFG)) {
|
||||
if (defined('PHPUNIT_SCRIPT') and PHPUNIT_SCRIPT) {
|
||||
if (defined('PHPUNITTEST') and PHPUNITTEST) {
|
||||
echo('There is a missing "global $CFG;" at the beginning of the config.php file.'."\n");
|
||||
exit(1);
|
||||
} else {
|
||||
@@ -112,7 +112,10 @@ if (!isset($CFG->cachedir)) {
|
||||
// directory of the script when run from the command line. The require_once()
|
||||
// would fail, so we'll have to chdir()
|
||||
if (!isset($_SERVER['REMOTE_ADDR']) && isset($_SERVER['argv'][0])) {
|
||||
chdir(dirname($_SERVER['argv'][0]));
|
||||
// do it only once - skip the second time when continuing after prevous abort
|
||||
if (!defined('ABORT_AFTER_CONFIG') and !defined('ABORT_AFTER_CONFIG_CANCEL')) {
|
||||
chdir(dirname($_SERVER['argv'][0]));
|
||||
}
|
||||
}
|
||||
|
||||
// sometimes default PHP settings are borked on shared hosting servers, I wonder why they have to do that??
|
||||
@@ -130,6 +133,11 @@ if (!defined('NO_OUTPUT_BUFFERING')) {
|
||||
define('NO_OUTPUT_BUFFERING', false);
|
||||
}
|
||||
|
||||
// PHPUnit tests need custom init
|
||||
if (!defined('PHPUNITTEST')) {
|
||||
define('PHPUNITTEST', false);
|
||||
}
|
||||
|
||||
// Servers should define a default timezone in php.ini, but if they don't then make sure something is defined.
|
||||
// This is a quick hack. Ideally we should ask the admin for a value. See MDL-22625 for more on this.
|
||||
if (function_exists('date_default_timezone_set') and function_exists('date_default_timezone_get')) {
|
||||
@@ -139,15 +147,6 @@ if (function_exists('date_default_timezone_set') and function_exists('date_defau
|
||||
unset($olddebug);
|
||||
}
|
||||
|
||||
// PHPUnit scripts are a special case, for now we treat them as normal CLI scripts,
|
||||
// please note you must install PHPUnit library separately via PEAR
|
||||
if (!defined('PHPUNIT_SCRIPT')) {
|
||||
define('PHPUNIT_SCRIPT', false);
|
||||
}
|
||||
if (PHPUNIT_SCRIPT) {
|
||||
define('CLI_SCRIPT', true);
|
||||
}
|
||||
|
||||
// Detect CLI scripts - CLI scripts are executed from command line, do not have session and we do not want HTML in output
|
||||
// In your new CLI scripts just add "define('CLI_SCRIPT', true);" before requiring config.php.
|
||||
// Please note that one script can not be accessed from both CLI and web interface.
|
||||
@@ -471,7 +470,11 @@ if (isset($CFG->debug)) {
|
||||
}
|
||||
|
||||
// Load up any configuration from the config table
|
||||
initialise_cfg();
|
||||
if (PHPUNITTEST) {
|
||||
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)) {
|
||||
@@ -820,7 +823,7 @@ if (!empty($CFG->customscripts)) {
|
||||
}
|
||||
}
|
||||
|
||||
if (CLI_SCRIPT and !defined('WEB_CRON_EMULATED_CLI') and !PHPUNIT_SCRIPT) {
|
||||
if ((CLI_SCRIPT and !defined('WEB_CRON_EMULATED_CLI')) or PHPUNITTEST) {
|
||||
// no ip blocking
|
||||
} else if (!empty($CFG->allowbeforeblock)) { // allowed list processed before blocked list?
|
||||
// in this case, ip in allowed list will be performed first
|
||||
|
||||
@@ -26,6 +26,9 @@
|
||||
if (!defined('MOODLE_INTERNAL')) {
|
||||
die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page
|
||||
}
|
||||
|
||||
global $CFG;
|
||||
|
||||
require_once($CFG->libdir.'/componentlib.class.php');
|
||||
|
||||
class componentlib_test extends UnitTestCase {
|
||||
|
||||
@@ -26,6 +26,9 @@
|
||||
if (!defined('MOODLE_INTERNAL')) {
|
||||
die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page
|
||||
}
|
||||
|
||||
global $CFG;
|
||||
|
||||
require_once($CFG->libdir . '/csslib.php');
|
||||
|
||||
|
||||
|
||||
@@ -27,6 +27,9 @@
|
||||
if (!defined('MOODLE_INTERNAL')) {
|
||||
die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page
|
||||
}
|
||||
|
||||
global $CFG;
|
||||
|
||||
require_once($CFG->libdir . '/externallib.php');
|
||||
|
||||
class externallib_test extends UnitTestCase {
|
||||
|
||||
@@ -27,6 +27,9 @@
|
||||
if (!defined('MOODLE_INTERNAL')) {
|
||||
die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page
|
||||
}
|
||||
|
||||
global $CFG;
|
||||
|
||||
require_once($CFG->libdir . '/filelib.php');
|
||||
|
||||
class filelib_test extends UnitTestCase {
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
if (!defined('MOODLE_INTERNAL')) {
|
||||
die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page
|
||||
}
|
||||
|
||||
require_once($CFG->libdir . '/formslib.php');
|
||||
require_once($CFG->libdir . '/form/radio.php');
|
||||
require_once($CFG->libdir . '/form/select.php');
|
||||
|
||||
@@ -27,6 +27,9 @@
|
||||
if (!defined('MOODLE_INTERNAL')) {
|
||||
die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page
|
||||
}
|
||||
|
||||
global $CFG;
|
||||
|
||||
require_once($CFG->libdir . '/outputcomponents.php');
|
||||
|
||||
|
||||
|
||||
@@ -26,6 +26,9 @@
|
||||
if (!defined('MOODLE_INTERNAL')) {
|
||||
die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page
|
||||
}
|
||||
|
||||
global $CFG;
|
||||
|
||||
require_once($CFG->libdir . '/navigationlib.php');
|
||||
|
||||
class navigation_node_test extends UnitTestCase {
|
||||
@@ -52,7 +55,7 @@ class navigation_node_test extends UnitTestCase {
|
||||
|
||||
$this->activeurl = $PAGE->url;
|
||||
navigation_node::override_active_url($this->activeurl);
|
||||
|
||||
|
||||
$this->inactiveurl = new moodle_url('http://www.moodle.com/');
|
||||
$this->fakeproperties['action'] = $this->inactiveurl;
|
||||
|
||||
@@ -165,11 +168,11 @@ class navigation_node_test extends UnitTestCase {
|
||||
public function test_find_active_node() {
|
||||
$activenode1 = $this->node->find_active_node();
|
||||
$activenode2 = $this->node->get('demo1')->find_active_node();
|
||||
|
||||
|
||||
if ($this->assertIsA($activenode1, 'navigation_node')) {
|
||||
$this->assertReference($activenode1, $this->node->get('demo3')->get('demo5')->get('activity1'));
|
||||
}
|
||||
|
||||
|
||||
$this->assertNotA($activenode2, 'navigation_node');
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,9 @@
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
|
||||
require_once($CFG->libdir . '/outputcomponents.php');
|
||||
|
||||
/**
|
||||
@@ -142,7 +145,7 @@ EOF;
|
||||
$this->assertTrue($menu instanceof custom_menu);
|
||||
$this->assertTrue($menu->has_children());
|
||||
$firstlevel = $menu->get_children();
|
||||
$this->assertIsA($firstlevel, 'array');
|
||||
$this->assertTrue(is_array($firstlevel));
|
||||
$this->assertEqual(2, count($firstlevel));
|
||||
|
||||
$item = array_shift($firstlevel);
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
|
||||
require_once($CFG->libdir . '/questionlib.php');
|
||||
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@ if (!defined('MOODLE_INTERNAL')) {
|
||||
* If networking/proxy configuration is wrong these tests will fail..
|
||||
*/
|
||||
|
||||
global $CFG;
|
||||
|
||||
require_once($CFG->libdir.'/simplepie/moodle_simplepie.php');
|
||||
|
||||
class moodlesimplepie_test extends UnitTestCase {
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Unit tests for the HTMLPurifier integration
|
||||
*
|
||||
* @package core_core
|
||||
* @category phpunit
|
||||
* @copyright 2012 Petr Skoda {@link http://skodak.org}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
|
||||
class core_htmlpurifier_testcase extends basic_testcase {
|
||||
|
||||
/**
|
||||
* Verify _blank target is allowed
|
||||
* @return void
|
||||
*/
|
||||
public function test_allow_blank_target() {
|
||||
$text = '<a href="http://moodle.org" target="_blank">Some link</a>';
|
||||
$result = format_text($text, FORMAT_HTML);
|
||||
$this->assertSame($text, $result);
|
||||
|
||||
$result = format_text('<a href="http://moodle.org" target="some">Some link</a>', FORMAT_HTML);
|
||||
$this->assertSame('<a href="http://moodle.org">Some link</a>', $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify our nolink tag accepted
|
||||
* @return void
|
||||
*/
|
||||
public function test_nolink() {
|
||||
// we can not use format text because nolink changes result
|
||||
$text = '<nolink><div>no filters</div></nolink>';
|
||||
$result = purify_html($text, array());
|
||||
$this->assertSame($text, $result);
|
||||
|
||||
$text = '<nolink>xxx<em>xx</em><div>xxx</div></nolink>';
|
||||
$result = purify_html($text, array());
|
||||
$this->assertSame($text, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify our tex tag accepted
|
||||
* @return void
|
||||
*/
|
||||
public function test_tex() {
|
||||
$text = '<tex>a+b=c</tex>';
|
||||
$result = purify_html($text, array());
|
||||
$this->assertSame($text, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify our algebra tag accepted
|
||||
* @return void
|
||||
*/
|
||||
public function test_algebra() {
|
||||
$text = '<algebra>a+b=c</algebra>';
|
||||
$result = purify_html($text, array());
|
||||
$this->assertSame($text, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify our hacky multilang works
|
||||
* @return void
|
||||
*/
|
||||
public function test_multilang() {
|
||||
$text = '<lang lang="en">hmmm</lang><lang lang="anything">hm</lang>';
|
||||
$result = purify_html($text, array());
|
||||
$this->assertSame($text, $result);
|
||||
|
||||
$text = '<span lang="en" class="multilang">hmmm</span><span lang="anything" class="multilang">hm</span>';
|
||||
$result = purify_html($text, array());
|
||||
$this->assertSame($text, $result);
|
||||
|
||||
$text = '<span lang="en">hmmm</span>';
|
||||
$result = purify_html($text, array());
|
||||
$this->assertNotSame($text, $result);
|
||||
|
||||
// keep standard lang tags
|
||||
|
||||
$text = '<span lang="de_DU" class="multilang">asas</span>';
|
||||
$result = purify_html($text, array());
|
||||
$this->assertSame($text, $result);
|
||||
|
||||
$text = '<lang lang="de_DU">xxxxxx</lang>';
|
||||
$result = purify_html($text, array());
|
||||
$this->assertSame($text, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the 'allowid' option for format_text.
|
||||
* @return void
|
||||
*/
|
||||
public function test_format_text_allowid() {
|
||||
// Start off by not allowing ids (default)
|
||||
$options = array(
|
||||
'nocache' => true
|
||||
);
|
||||
$result = format_text('<div id="example">Frog</div>', FORMAT_HTML, $options);
|
||||
$this->assertSame('<div>Frog</div>', $result);
|
||||
|
||||
// Now allow ids
|
||||
$options['allowid'] = true;
|
||||
$result = format_text('<div id="example">Frog</div>', FORMAT_HTML, $options);
|
||||
$this->assertSame('<div id="example">Frog</div>', $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if linebreaks kept unchanged.
|
||||
* @return void
|
||||
*/
|
||||
function test_line_breaking() {
|
||||
$text = "\n\raa\rsss\nsss\r";
|
||||
$this->assertSame($text, purify_html($text));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test fixing of strict problems.
|
||||
* @return void
|
||||
*/
|
||||
function test_tidy() {
|
||||
$text = "<p>xx";
|
||||
$this->assertSame('<p>xx</p>', purify_html($text));
|
||||
|
||||
$text = "<P>xx</P>";
|
||||
$this->assertSame('<p>xx</p>', purify_html($text));
|
||||
|
||||
$text = "xx<br>";
|
||||
$this->assertSame('xx<br />', purify_html($text));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test nesting - this used to cause problems in earlier versions
|
||||
* @return void
|
||||
*/
|
||||
function test_nested_lists() {
|
||||
$text = "<ul><li>One<ul><li>Two</li></ul></li><li>Three</li></ul>";
|
||||
$this->assertSame($text, purify_html($text));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that XSS protection works, complete smoke tests are in htmlpurifier itself.
|
||||
* @return void
|
||||
*/
|
||||
function test_cleaning_nastiness() {
|
||||
$text = "x<SCRIPT>alert('XSS')</SCRIPT>x";
|
||||
$this->assertSame('xx', purify_html($text));
|
||||
|
||||
$text = '<DIV STYLE="background-image:url(javascript:alert(\'XSS\'))">xx</DIV>';
|
||||
$this->assertSame('<div>xx</div>', purify_html($text));
|
||||
|
||||
$text = '<DIV STYLE="width:expression(alert(\'XSS\'));">xx</DIV>';
|
||||
$this->assertSame('<div>xx</div>', purify_html($text));
|
||||
|
||||
$text = 'x<IFRAME SRC="javascript:alert(\'XSS\');"></IFRAME>x';
|
||||
$this->assertSame('xx', purify_html($text));
|
||||
|
||||
$text = 'x<OBJECT TYPE="text/x-scriptlet" DATA="http://ha.ckers.org/scriptlet.html"></OBJECT>x';
|
||||
$this->assertSame('xx', purify_html($text));
|
||||
|
||||
$text = 'x<EMBED SRC="http://ha.ckers.org/xss.swf" AllowScriptAccess="always"></EMBED>x';
|
||||
$this->assertSame('xx', purify_html($text));
|
||||
|
||||
$text = 'x<form></form>x';
|
||||
$this->assertSame('xx', purify_html($text));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
<?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/>.
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Test integration of PHPUnit and custom Moodle hacks.
|
||||
*
|
||||
* @package core_core
|
||||
* @category phpunit
|
||||
* @copyright 2012 Petr Skoda {@link http://skodak.org}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class core_phpunit_basic_testcase extends basic_testcase {
|
||||
|
||||
public function test_bootstrap() {
|
||||
global $CFG;
|
||||
$this->assertTrue(isset($CFG->httpswwwroot));
|
||||
$this->assertEquals($CFG->httpswwwroot, $CFG->wwwroot);
|
||||
$this->assertEquals($CFG->prefix, $CFG->phpunit_prefix);
|
||||
}
|
||||
|
||||
/*
|
||||
public function test_borked_tests() {
|
||||
global $DB;
|
||||
|
||||
$DB->set_field('user', 'confirmed', 1, array('id'=>-1));
|
||||
$CFG->xx = 'yy';
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
@@ -0,0 +1,420 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* textlib unit teststest
|
||||
*
|
||||
* @package core_core
|
||||
* @category phpunit
|
||||
* @copyright 2012 Petr Skoda {@link http://skodak.org}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
|
||||
/**
|
||||
* Unit tests for our utf-8 aware text processing
|
||||
*
|
||||
* @package core
|
||||
* @subpackage lib
|
||||
* @copyright 2010 Petr Skoda (http://skodak.org)
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class core_textlib_testcase extends basic_testcase {
|
||||
|
||||
public function test_parse_charset() {
|
||||
$this->assertSame(textlib::parse_charset('Cp1250'), 'windows-1250');
|
||||
// does typo3 work? some encoding moodle does not use
|
||||
$this->assertSame(textlib::parse_charset('ms-ansi'), 'windows-1252');
|
||||
}
|
||||
|
||||
public function test_convert() {
|
||||
$utf8 = "Žluťoučký koníček";
|
||||
$iso2 = pack("H*", "ae6c75bb6f75e86bfd206b6f6eede8656b");
|
||||
$win = pack("H*", "8e6c759d6f75e86bfd206b6f6eede8656b");
|
||||
$this->assertSame(textlib::convert($utf8, 'utf-8', 'iso-8859-2'), $iso2);
|
||||
$this->assertSame(textlib::convert($iso2, 'iso-8859-2', 'utf-8'), $utf8);
|
||||
$this->assertSame(textlib::convert($utf8, 'utf-8', 'win-1250'), $win);
|
||||
$this->assertSame(textlib::convert($win, 'win-1250', 'utf-8'), $utf8);
|
||||
$this->assertSame(textlib::convert($win, 'win-1250', 'iso-8859-2'), $iso2);
|
||||
$this->assertSame(textlib::convert($iso2, 'iso-8859-2', 'win-1250'), $win);
|
||||
$this->assertSame(textlib::convert($iso2, 'iso-8859-2', 'iso-8859-2'), $iso2);
|
||||
$this->assertSame(textlib::convert($win, 'win-1250', 'cp1250'), $win);
|
||||
|
||||
|
||||
$utf8 = '言語設定';
|
||||
$str = pack("H*", "b8c0b8ecc0dfc4ea"); //EUC-JP
|
||||
$this->assertSame(textlib::convert($utf8, 'utf-8', 'EUC-JP'), $str);
|
||||
$this->assertSame(textlib::convert($str, 'EUC-JP', 'utf-8'), $utf8);
|
||||
|
||||
$str = pack("H*", "1b24423840386c405f446a1b2842"); //ISO-2022-JP
|
||||
$this->assertSame(textlib::convert($utf8, 'utf-8', 'ISO-2022-JP'), $str);
|
||||
$this->assertSame(textlib::convert($str, 'ISO-2022-JP', 'utf-8'), $utf8);
|
||||
|
||||
$str = pack("H*", "8cbe8cea90dd92e8"); //SHIFT-JIS
|
||||
$this->assertSame(textlib::convert($utf8, 'utf-8', 'SHIFT-JIS'), $str);
|
||||
$this->assertSame(textlib::convert($str, 'SHIFT-JIS', 'utf-8'), $utf8);
|
||||
|
||||
$utf8 = '简体中文';
|
||||
$str = pack("H*", "bcf2cce5d6d0cec4"); //GB2312
|
||||
$this->assertSame(textlib::convert($utf8, 'utf-8', 'GB2312'), $str);
|
||||
$this->assertSame(textlib::convert($str, 'GB2312', 'utf-8'), $utf8);
|
||||
|
||||
$str = pack("H*", "bcf2cce5d6d0cec4"); //GB18030
|
||||
$this->assertSame(textlib::convert($utf8, 'utf-8', 'GB18030'), $str);
|
||||
$this->assertSame(textlib::convert($str, 'GB18030', 'utf-8'), $utf8);
|
||||
}
|
||||
|
||||
public function test_substr() {
|
||||
$str = "Žluťoučký koníček";
|
||||
$this->assertSame(textlib::substr($str, 0), $str);
|
||||
$this->assertSame(textlib::substr($str, 1), 'luťoučký koníček');
|
||||
$this->assertSame(textlib::substr($str, 1, 3), 'luť');
|
||||
$this->assertSame(textlib::substr($str, 0, 100), $str);
|
||||
$this->assertSame(textlib::substr($str, -3, 2), 'če');
|
||||
|
||||
$iso2 = pack("H*", "ae6c75bb6f75e86bfd206b6f6eede8656b");
|
||||
$this->assertSame(textlib::substr($iso2, 1, 3, 'iso-8859-2'), textlib::convert('luť', 'utf-8', 'iso-8859-2'));
|
||||
$this->assertSame(textlib::substr($iso2, 0, 100, 'iso-8859-2'), textlib::convert($str, 'utf-8', 'iso-8859-2'));
|
||||
$this->assertSame(textlib::substr($iso2, -3, 2, 'iso-8859-2'), textlib::convert('če', 'utf-8', 'iso-8859-2'));
|
||||
|
||||
$win = pack("H*", "8e6c759d6f75e86bfd206b6f6eede8656b");
|
||||
$this->assertSame(textlib::substr($win, 1, 3, 'cp1250'), textlib::convert('luť', 'utf-8', 'cp1250'));
|
||||
$this->assertSame(textlib::substr($win, 0, 100, 'cp1250'), textlib::convert($str, 'utf-8', 'cp1250'));
|
||||
$this->assertSame(textlib::substr($win, -3, 2, 'cp1250'), textlib::convert('če', 'utf-8', 'cp1250'));
|
||||
|
||||
|
||||
$str = pack("H*", "b8c0b8ecc0dfc4ea"); //EUC-JP
|
||||
$s = pack("H*", "b8ec"); //EUC-JP
|
||||
$this->assertSame(textlib::substr($str, 1, 1, 'EUC-JP'), $s);
|
||||
|
||||
$str = pack("H*", "1b24423840386c405f446a1b2842"); //ISO-2022-JP
|
||||
$s = pack("H*", "1b2442386c1b2842"); //ISO-2022-JP
|
||||
$this->assertSame(textlib::substr($str, 1, 1, 'ISO-2022-JP'), $s);
|
||||
|
||||
$str = pack("H*", "8cbe8cea90dd92e8"); //SHIFT-JIS
|
||||
$s = pack("H*", "8cea"); //SHIFT-JIS
|
||||
$this->assertSame(textlib::substr($str, 1, 1, 'SHIFT-JIS'), $s);
|
||||
|
||||
$str = pack("H*", "bcf2cce5d6d0cec4"); //GB2312
|
||||
$s = pack("H*", "cce5"); //GB2312
|
||||
$this->assertSame(textlib::substr($str, 1, 1, 'GB2312'), $s);
|
||||
|
||||
$str = pack("H*", "bcf2cce5d6d0cec4"); //GB18030
|
||||
$s = pack("H*", "cce5"); //GB18030
|
||||
$this->assertSame(textlib::substr($str, 1, 1, 'GB18030'), $s);
|
||||
}
|
||||
|
||||
public function test_strlen() {
|
||||
$str = "Žluťoučký koníček";
|
||||
$this->assertSame(textlib::strlen($str), 17);
|
||||
|
||||
$iso2 = pack("H*", "ae6c75bb6f75e86bfd206b6f6eede8656b");
|
||||
$this->assertSame(textlib::strlen($iso2, 'iso-8859-2'), 17);
|
||||
|
||||
$win = pack("H*", "8e6c759d6f75e86bfd206b6f6eede8656b");
|
||||
$this->assertSame(textlib::strlen($win, 'cp1250'), 17);
|
||||
|
||||
|
||||
$str = pack("H*", "b8ec"); //EUC-JP
|
||||
$this->assertSame(textlib::strlen($str, 'EUC-JP'), 1);
|
||||
$str = pack("H*", "b8c0b8ecc0dfc4ea"); //EUC-JP
|
||||
$this->assertSame(textlib::strlen($str, 'EUC-JP'), 4);
|
||||
|
||||
$str = pack("H*", "1b2442386c1b2842"); //ISO-2022-JP
|
||||
$this->assertSame(textlib::strlen($str, 'ISO-2022-JP'), 1);
|
||||
$str = pack("H*", "1b24423840386c405f446a1b2842"); //ISO-2022-JP
|
||||
$this->assertSame(textlib::strlen($str, 'ISO-2022-JP'), 4);
|
||||
|
||||
$str = pack("H*", "8cea"); //SHIFT-JIS
|
||||
$this->assertSame(textlib::strlen($str, 'SHIFT-JIS'), 1);
|
||||
$str = pack("H*", "8cbe8cea90dd92e8"); //SHIFT-JIS
|
||||
$this->assertSame(textlib::strlen($str, 'SHIFT-JIS'), 4);
|
||||
|
||||
$str = pack("H*", "cce5"); //GB2312
|
||||
$this->assertSame(textlib::strlen($str, 'GB2312'), 1);
|
||||
$str = pack("H*", "bcf2cce5d6d0cec4"); //GB2312
|
||||
$this->assertSame(textlib::strlen($str, 'GB2312'), 4);
|
||||
|
||||
$str = pack("H*", "cce5"); //GB18030
|
||||
$this->assertSame(textlib::strlen($str, 'GB18030'), 1);
|
||||
$str = pack("H*", "bcf2cce5d6d0cec4"); //GB18030
|
||||
$this->assertSame(textlib::strlen($str, 'GB18030'), 4);
|
||||
}
|
||||
|
||||
public function test_strtolower() {
|
||||
$str = "Žluťoučký koníček";
|
||||
$low = 'žluťoučký koníček';
|
||||
$this->assertSame(textlib::strtolower($str), $low);
|
||||
|
||||
$iso2 = pack("H*", "ae6c75bb6f75e86bfd206b6f6eede8656b");
|
||||
$this->assertSame(textlib::strtolower($iso2, 'iso-8859-2'), textlib::convert($low, 'utf-8', 'iso-8859-2'));
|
||||
|
||||
$win = pack("H*", "8e6c759d6f75e86bfd206b6f6eede8656b");
|
||||
$this->assertSame(textlib::strtolower($win, 'cp1250'), textlib::convert($low, 'utf-8', 'cp1250'));
|
||||
|
||||
|
||||
$str = '言語設定';
|
||||
$this->assertSame(textlib::strtolower($str), $str);
|
||||
|
||||
$str = '简体中文';
|
||||
$this->assertSame(textlib::strtolower($str), $str);
|
||||
|
||||
$str = pack("H*", "1b24423840386c405f446a1b2842"); //ISO-2022-JP
|
||||
$this->assertSame(textlib::strtolower($str, 'ISO-2022-JP'), $str);
|
||||
|
||||
$str = pack("H*", "8cbe8cea90dd92e8"); //SHIFT-JIS
|
||||
$this->assertSame(textlib::strtolower($str, 'SHIFT-JIS'), $str);
|
||||
|
||||
$str = pack("H*", "bcf2cce5d6d0cec4"); //GB2312
|
||||
$this->assertSame(textlib::strtolower($str, 'GB2312'), $str);
|
||||
|
||||
$str = pack("H*", "bcf2cce5d6d0cec4"); //GB18030
|
||||
$this->assertSame(textlib::strtolower($str, 'GB18030'), $str);
|
||||
}
|
||||
|
||||
public function test_strtoupper() {
|
||||
$str = "Žluťoučký koníček";
|
||||
$up = 'ŽLUŤOUČKÝ KONÍČEK';
|
||||
$this->assertSame(textlib::strtoupper($str), $up);
|
||||
|
||||
$iso2 = pack("H*", "ae6c75bb6f75e86bfd206b6f6eede8656b");
|
||||
$this->assertSame(textlib::strtoupper($iso2, 'iso-8859-2'), textlib::convert($up, 'utf-8', 'iso-8859-2'));
|
||||
|
||||
$win = pack("H*", "8e6c759d6f75e86bfd206b6f6eede8656b");
|
||||
$this->assertSame(textlib::strtoupper($win, 'cp1250'), textlib::convert($up, 'utf-8', 'cp1250'));
|
||||
|
||||
|
||||
$str = '言語設定';
|
||||
$this->assertSame(textlib::strtoupper($str), $str);
|
||||
|
||||
$str = '简体中文';
|
||||
$this->assertSame(textlib::strtoupper($str), $str);
|
||||
|
||||
$str = pack("H*", "1b24423840386c405f446a1b2842"); //ISO-2022-JP
|
||||
$this->assertSame(textlib::strtoupper($str, 'ISO-2022-JP'), $str);
|
||||
|
||||
$str = pack("H*", "8cbe8cea90dd92e8"); //SHIFT-JIS
|
||||
$this->assertSame(textlib::strtoupper($str, 'SHIFT-JIS'), $str);
|
||||
|
||||
$str = pack("H*", "bcf2cce5d6d0cec4"); //GB2312
|
||||
$this->assertSame(textlib::strtoupper($str, 'GB2312'), $str);
|
||||
|
||||
$str = pack("H*", "bcf2cce5d6d0cec4"); //GB18030
|
||||
$this->assertSame(textlib::strtoupper($str, 'GB18030'), $str);
|
||||
}
|
||||
|
||||
public function test_strpos() {
|
||||
$str = "Žluťoučký koníček";
|
||||
$this->assertSame(textlib::strpos($str, 'koníč'), 10);
|
||||
}
|
||||
|
||||
public function test_strrpos() {
|
||||
$str = "Žluťoučký koníček";
|
||||
$this->assertSame(textlib::strrpos($str, 'o'), 11);
|
||||
}
|
||||
|
||||
public function test_specialtoascii() {
|
||||
$str = "Žluťoučký koníček";
|
||||
$this->assertSame(textlib::specialtoascii($str), 'Zlutoucky konicek');
|
||||
}
|
||||
|
||||
public function test_encode_mimeheader() {
|
||||
$str = "Žluťoučký koníček";
|
||||
$this->assertSame(textlib::encode_mimeheader($str), '=?utf-8?B?xb1sdcWlb3XEjWvDvSBrb27DrcSNZWs=?=');
|
||||
}
|
||||
|
||||
public function test_entities_to_utf8() {
|
||||
$str = "Žluťoučký koníček";
|
||||
$this->assertSame(textlib::entities_to_utf8($str), "Žluťoučký koníček");
|
||||
}
|
||||
|
||||
public function test_utf8_to_entities() {
|
||||
$str = "Žluťoučký koníček";
|
||||
$this->assertSame(textlib::utf8_to_entities($str), "Žluťoučký koníček");
|
||||
$this->assertSame(textlib::utf8_to_entities($str, true), "Žluťoučký koníček");
|
||||
|
||||
}
|
||||
|
||||
public function test_trim_utf8_bom() {
|
||||
$bom = "\xef\xbb\xbf";
|
||||
$str = "Žluťoučký koníček";
|
||||
$this->assertSame(textlib::trim_utf8_bom($bom.$str.$bom), $str.$bom);
|
||||
}
|
||||
|
||||
public function test_get_encodings() {
|
||||
$encodings = textlib::get_encodings();
|
||||
$this->assertTrue(is_array($encodings));
|
||||
$this->assertTrue(count($encodings) > 1);
|
||||
$this->assertTrue(isset($encodings['UTF-8']));
|
||||
}
|
||||
|
||||
public function test_code2utf8() {
|
||||
$this->assertSame(textlib::code2utf8(381), 'Ž');
|
||||
}
|
||||
|
||||
public function test_strtotitle() {
|
||||
$str = "žluťoučký koníček";
|
||||
$this->assertSame(textlib::strtotitle($str), "Žluťoučký Koníček");
|
||||
}
|
||||
|
||||
public function test_deprecated_textlib_get_instance() {
|
||||
ob_start();
|
||||
$textlib = textlib_get_instance();
|
||||
$output = ob_get_contents();
|
||||
$this->assertNotEmpty($output);
|
||||
ob_end_clean();
|
||||
$this->assertSame($textlib->substr('abc', 1, 1), 'b');
|
||||
$this->assertSame($textlib->strlen('abc'), 3);
|
||||
$this->assertSame($textlib->strtoupper('Abc'), 'ABC');
|
||||
$this->assertSame($textlib->strtolower('Abc'), 'abc');
|
||||
$this->assertSame($textlib->strpos('abc', 'a'), 0);
|
||||
$this->assertSame($textlib->strpos('abc', 'd'), false);
|
||||
$this->assertSame($textlib->strrpos('abcabc', 'a'), 3);
|
||||
$this->assertSame($textlib->specialtoascii('ábc'), 'abc');
|
||||
$this->assertSame($textlib->strtotitle('abc ABC'), 'Abc Abc');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Unit tests for our utf-8 aware collator.
|
||||
*
|
||||
* Used for sorting.
|
||||
*
|
||||
* @package core
|
||||
* @subpackage lib
|
||||
* @copyright 2011 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class collatorlib_testcase extends basic_testcase {
|
||||
|
||||
protected $initiallang = null;
|
||||
protected $error = null;
|
||||
|
||||
public function setUp() {
|
||||
global $SESSION;
|
||||
if (isset($SESSION->lang)) {
|
||||
$this->initiallang = $SESSION->lang;
|
||||
}
|
||||
$SESSION->lang = 'en'; // make sure we test en language to get consistent results, hopefully all systems have this locale
|
||||
if (extension_loaded('intl')) {
|
||||
$this->error = 'Collation aware sorting not supported';
|
||||
} else {
|
||||
$this->error = 'Collation aware sorting not supported, PHP extension "intl" is not available.';
|
||||
}
|
||||
parent::setUp();
|
||||
}
|
||||
public function tearDown() {
|
||||
global $SESSION;
|
||||
parent::tearDown();
|
||||
if ($this->initiallang !== null) {
|
||||
$SESSION->lang = $this->initiallang;
|
||||
$this->initiallang = null;
|
||||
} else {
|
||||
unset($SESSION->lang);
|
||||
}
|
||||
}
|
||||
function test_asort() {
|
||||
$arr = array('b' => 'ab', 1 => 'aa', 0 => 'cc');
|
||||
collatorlib::asort($arr);
|
||||
$this->assertSame(array_keys($arr), array(1, 'b', 0));
|
||||
$this->assertSame(array_values($arr), array('aa', 'ab', 'cc'));
|
||||
|
||||
$arr = array('a' => 'áb', 'b' => 'ab', 1 => 'aa', 0=>'cc');
|
||||
collatorlib::asort($arr);
|
||||
$this->assertSame(array_keys($arr), array(1, 'b', 'a', 0), $this->error);
|
||||
$this->assertSame(array_values($arr), array('aa', 'ab', 'áb', 'cc'), $this->error);
|
||||
}
|
||||
function test_asort_objects_by_method() {
|
||||
$objects = array(
|
||||
'b' => new string_test_class('ab'),
|
||||
1 => new string_test_class('aa'),
|
||||
0 => new string_test_class('cc')
|
||||
);
|
||||
collatorlib::asort_objects_by_method($objects, 'get_protected_name');
|
||||
$this->assertSame(array_keys($objects), array(1, 'b', 0));
|
||||
$this->assertSame($this->get_ordered_names($objects, 'get_protected_name'), array('aa', 'ab', 'cc'));
|
||||
|
||||
$objects = array(
|
||||
'a' => new string_test_class('áb'),
|
||||
'b' => new string_test_class('ab'),
|
||||
1 => new string_test_class('aa'),
|
||||
0 => new string_test_class('cc')
|
||||
);
|
||||
collatorlib::asort_objects_by_method($objects, 'get_private_name');
|
||||
$this->assertSame(array_keys($objects), array(1, 'b', 'a', 0), $this->error);
|
||||
$this->assertSame($this->get_ordered_names($objects, 'get_private_name'), array('aa', 'ab', 'áb', 'cc'), $this->error);
|
||||
}
|
||||
function test_asort_objects_by_property() {
|
||||
$objects = array(
|
||||
'b' => new string_test_class('ab'),
|
||||
1 => new string_test_class('aa'),
|
||||
0 => new string_test_class('cc')
|
||||
);
|
||||
collatorlib::asort_objects_by_property($objects, 'publicname');
|
||||
$this->assertSame(array_keys($objects), array(1, 'b', 0));
|
||||
$this->assertSame($this->get_ordered_names($objects, 'publicname'), array('aa', 'ab', 'cc'));
|
||||
|
||||
$objects = array(
|
||||
'a' => new string_test_class('áb'),
|
||||
'b' => new string_test_class('ab'),
|
||||
1 => new string_test_class('aa'),
|
||||
0 => new string_test_class('cc')
|
||||
);
|
||||
collatorlib::asort_objects_by_property($objects, 'publicname');
|
||||
$this->assertSame(array_keys($objects), array(1, 'b', 'a', 0), $this->error);
|
||||
$this->assertSame($this->get_ordered_names($objects, 'publicname'), array('aa', 'ab', 'áb', 'cc'), $this->error);
|
||||
}
|
||||
protected function get_ordered_names($objects, $methodproperty = 'get_protected_name') {
|
||||
$return = array();
|
||||
foreach ($objects as $object) {
|
||||
if ($methodproperty == 'publicname') {
|
||||
$return[] = $object->publicname;
|
||||
} else {
|
||||
$return[] = $object->$methodproperty();
|
||||
}
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Simple class used to work with the unit test.
|
||||
*
|
||||
* @package core
|
||||
* @subpackage lib
|
||||
* @copyright 2011 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class string_test_class extends stdClass {
|
||||
public $publicname;
|
||||
protected $protectedname;
|
||||
private $privatename;
|
||||
public function __construct($name) {
|
||||
$this->publicname = $name;
|
||||
$this->protectedname = $name;
|
||||
$this->privatename = $name;
|
||||
}
|
||||
public function get_protected_name() {
|
||||
return $this->protectedname;
|
||||
}
|
||||
public function get_private_name() {
|
||||
return $this->publicname;
|
||||
}
|
||||
}
|
||||
+4
-1
@@ -2742,7 +2742,10 @@ function debugging($message = '', $level = DEBUG_NORMAL, $backtrace = null) {
|
||||
$backtrace = debug_backtrace();
|
||||
}
|
||||
$from = format_backtrace($backtrace, CLI_SCRIPT);
|
||||
if (!empty($UNITTEST->running)) {
|
||||
if (PHPUNITTEST) {
|
||||
echo 'Debugging: ' . $message . "\n" . $from;
|
||||
|
||||
} else if (!empty($UNITTEST->running)) {
|
||||
// When the unit tests are running, any call to trigger_error
|
||||
// is intercepted by the test framework and reported as an exception.
|
||||
// Therefore, we cannot use trigger_error during unit tests.
|
||||
|
||||
@@ -50,6 +50,8 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die;
|
||||
|
||||
global $CFG;
|
||||
|
||||
require_once($CFG->dirroot . '/mod/lti/locallib.php');
|
||||
require_once($CFG->dirroot . '/mod/lti/servicelib.php');
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@ if (!defined('MOODLE_INTERNAL')) {
|
||||
|
||||
// Unit tests for scorm_formatduration function from locallib.php
|
||||
|
||||
global $CFG;
|
||||
|
||||
// Make sure the code being tested is accessible.
|
||||
require_once($CFG->dirroot . '/mod/scorm/locallib.php'); // Include the code to test
|
||||
|
||||
|
||||
@@ -40,9 +40,9 @@ require_once("$CFG->dirroot/mod/url/lib.php");
|
||||
function url_appears_valid_url($url) {
|
||||
if (preg_match('/^(\/|https?:|ftp:)/i', $url)) {
|
||||
// note: this is not exact validation, we look for severely malformed URLs only
|
||||
return preg_match('/^[a-z]+:\/\/([^:@\s]+:[^@\s]+@)?[a-z0-9_\.\-]+(:[0-9]+)?(\/[^#]*)?(#.*)?$/i', $url);
|
||||
return (bool)preg_match('/^[a-z]+:\/\/([^:@\s]+:[^@\s]+@)?[a-z0-9_\.\-]+(:[0-9]+)?(\/[^#]*)?(#.*)?$/i', $url);
|
||||
} else {
|
||||
return preg_match('/^[a-z]+:\/\/...*$/i', $url);
|
||||
return (bool)preg_match('/^[a-z]+:\/\/...*$/i', $url);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
|
||||
require_once($CFG->dirroot . '/mod/url/locallib.php');
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Unit tests for some mod URL lib stuff.
|
||||
*
|
||||
* @package mod_url
|
||||
* @category phpunit
|
||||
* @copyright 2012 Petr Skoda {@link http://skodak.org}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
|
||||
/**
|
||||
* mod_url tests
|
||||
*
|
||||
* @package mod_url
|
||||
* @category phpunit
|
||||
* @copyright 2011 Petr Skoda {@link http://skodak.org}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class mod_url_lib_testcase extends basic_testcase {
|
||||
|
||||
public static function setUpBeforeClass() {
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/mod/url/locallib.php');
|
||||
}
|
||||
|
||||
public function test_url_appears_valid_url() {
|
||||
$this->assertTrue(url_appears_valid_url('http://example'));
|
||||
$this->assertTrue(url_appears_valid_url('http://www.example.com'));
|
||||
$this->assertTrue(url_appears_valid_url('http://www.exa-mple2.com'));
|
||||
$this->assertTrue(url_appears_valid_url('http://www.example.com/~nobody/index.html'));
|
||||
$this->assertTrue(url_appears_valid_url('http://www.example.com#hmm'));
|
||||
$this->assertTrue(url_appears_valid_url('http://www.example.com/#hmm'));
|
||||
$this->assertTrue(url_appears_valid_url('http://www.example.com/žlutý koníček/lala.txt'));
|
||||
$this->assertTrue(url_appears_valid_url('http://www.example.com/žlutý koníček/lala.txt#hmmmm'));
|
||||
$this->assertTrue(url_appears_valid_url('http://www.example.com/index.php?xx=yy&zz=aa'));
|
||||
$this->assertTrue(url_appears_valid_url('https://user:[email protected]/žlutý koníček/lala.txt'));
|
||||
$this->assertTrue(url_appears_valid_url('ftp://user:[email protected]/žlutý koníček/lala.txt'));
|
||||
|
||||
$this->assertFalse(url_appears_valid_url('http:example.com'));
|
||||
$this->assertFalse(url_appears_valid_url('http:/example.com'));
|
||||
$this->assertFalse(url_appears_valid_url('http://'));
|
||||
$this->assertFalse(url_appears_valid_url('http://www.exa mple.com'));
|
||||
$this->assertFalse(url_appears_valid_url('http://www.examplé.com'));
|
||||
$this->assertFalse(url_appears_valid_url('http://@www.example.com'));
|
||||
$this->assertFalse(url_appears_valid_url('http://user:@www.example.com'));
|
||||
|
||||
$this->assertTrue(url_appears_valid_url('lalala://@:@/'));
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,8 @@ if (!defined('MOODLE_INTERNAL')) {
|
||||
die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page
|
||||
}
|
||||
|
||||
global $CFG;
|
||||
|
||||
require_once($CFG->dirroot . '/mod/wiki/parser/parser.php');
|
||||
|
||||
class wikiparser_test extends UnitTestCase {
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
<phpunit
|
||||
bootstrap="lib/phpunit/bootstrap.php"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
stopOnError="false"
|
||||
stopOnFailure="false"
|
||||
stopOnIncomplete="false"
|
||||
stopOnSkipped="false"
|
||||
strict="false"
|
||||
verbose="false"
|
||||
>
|
||||
|
||||
<!--All core suites need to be added manually here-->
|
||||
|
||||
<testsuites>
|
||||
<testsuite name="core_lib">
|
||||
<directory suffix="_test.php">lib/tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<!--Plugin suites: use admin/tool/phpunit/cli/util.php to build phpunit.xml from phpunit.xml.dist with up-to-date list of plugins in current install-->
|
||||
<!--@plugin_suits_start@-->
|
||||
<!--@plugin_suits_end@-->
|
||||
|
||||
</phpunit>
|
||||
Reference in New Issue
Block a user