MDL-14905 Started on the DDL functional tests. Added an ugly but temporary hack into error() and print_error(), so that they throw an exception instead of dying when UNITTEST is defined.

This commit is contained in:
nicolasconnault 2008-05-21 14:59:33 +00:00
parent ab7ac6ac55
commit 73f7ad715e
6 changed files with 250 additions and 32 deletions

View File

@ -14,15 +14,14 @@ require_once($CFG->libdir . '/ddllib.php');
class ddllib_test extends UnitTestCase {
private $tables = array();
private $db;
private $dbmanager;
public function setUp() {
global $CFG;
$this->db = new mysqli_adodb_moodle_database();
$this->db->connect($CFG->dbhost, $CFG->dbuser, $CFG->dbpass, $CFG->dbname, $CFG->dbpersist, $CFG->prefix);
$this->dbmanager = $this->db->get_manager();
$db = new mysqli_adodb_moodle_database();
$db->connect($CFG->dbhost, $CFG->dbuser, $CFG->dbpass, $CFG->dbname, $CFG->dbpersist, $CFG->prefix);
$this->dbmanager = $db->get_manager();
$table = new xmldb_table("testtable");
$table->addFieldInfo('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null, null, null);

View File

@ -7,7 +7,7 @@
// Moodle - Modular Object-Oriented Dynamic Learning Environment //
// http://moodle.org //
// //
// Copyright (C) 1999 onwards Martin Dougiamas, Moodle http://moodle.com //
// Copyright (C) 1999 onwards Martin Dougiamas, Moodle http://moodle.com//
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
@ -506,8 +506,8 @@ function get_group_students($groupids, $sort='ul.timeaccess DESC') {
}
/**
* Determines if the HTML editor is enabled. This function has
* been deprecated, but needs to remain available because it is
* Determines if the HTML editor is enabled. This function has
* been deprecated, but needs to remain available because it is
* used in language packs for Moodle 1.6 to 1.9.
*
* @deprecated Use {@link can_use_html_editor()} instead.
@ -819,6 +819,15 @@ function error ($message, $link='') {
debugging('error() is a deprecated function, please call print_error() instead of error()', DEBUG_DEVELOPER);
$message = clean_text($message); // In case nasties are in here
/**
* TODO VERY DIRTY HACK USED FOR UNIT TESTING UNTIL PROPER EXCEPTION HANDLING IS IMPLEMENTED
*/
if (defined('UNITTEST')) {
// Errors in unit test become exceptions, so you can unit test
// code that might call error().
throw new Exception('error() call: '. $message.($link!=='' ? ' ['.$link.']' : ''));
}
if (defined('FULLME') && FULLME == 'cron') {
// Errors in cron should be mtrace'd.
mtrace($message);

View File

@ -181,8 +181,9 @@ abstract class moodle_database {
// convert table names
$sql = preg_replace('/\{([a-z][a-z0-9_]*)\}/', $this->prefix.'$1', $sql);
$named_count = preg_match_all('/(?!:):[a-z][a-z0-9_]*/', $sql, $named_matches); // :: used in pgsql casts
$dolar_count = preg_match_all('/\$[1-9][0-9]*/', $sql, $dolar_matches);
// NICOLAS C: Fixed regexp for negative backwards lookahead of double colons. Thanks for Sam Marshall's help
$named_count = preg_match_all('/(?<!:):[a-z][a-z0-9_]*/', $sql, $named_matches); // :: used in pgsql casts
$dollar_count = preg_match_all('/\$[1-9][0-9]*/', $sql, $dollar_matches);
$q_count = substr_count($sql, '?');
$count = 0;
@ -192,12 +193,12 @@ abstract class moodle_database {
$count = $named_count;
}
if ($dolar_count) {
if ($dollar_count) {
if ($count) {
error('ERROR: Mixed types of sql query parameters!!');
}
$type = SQL_PARAMS_DOLAR;
$count = $dolar_count;
$type = SQL_PARAMS_DOLLAR;
$count = $dollar_count;
}
if ($q_count) {
@ -216,12 +217,12 @@ abstract class moodle_database {
} else if ($allowed_types & SQL_PARAMS_QM) {
return array($sql, array(), SQL_PARAMS_QM);
} else {
return array($sql, array(), SQL_PARAMS_DOLAR);
return array($sql, array(), SQL_PARAMS_DOLLAR);
}
}
if ($count > count($params)) {
error('ERROR: Incorrect number of query parameters!! '.s($sql));
error('ERROR: Incorrect number of query parameters!!');
}
if ($type & $allowed_types) { // bitwise AND
@ -229,7 +230,7 @@ abstract class moodle_database {
if ($type == SQL_PARAMS_QM) {
return array($sql, array_values($params), SQL_PARAMS_QM); // 0-based array required
} else {
//better do the validation of names bellow
//better do the validation of names below
}
}
// needs some fixing or validation - there might be more params than needed
@ -253,15 +254,15 @@ abstract class moodle_database {
}
if ($target_type & SQL_PARAMS_QM) {
$sql = preg_replace('/(?!:):[a-z][a-z0-9_]*/', '?', $sql);
$sql = preg_replace('/(?<!:):[a-z][a-z0-9_]*/', '?', $sql);
return array($sql, array_values($finalparams), SQL_PARAMS_QM); // 0-based required
} else if ($target_type & SQL_PARAMS_NAMED) {
return array($sql, $finalparams, SQL_PARAMS_NAMED);
} else { // $type & SQL_PARAMS_DOLAR
} else { // $type & SQL_PARAMS_DOLLAR
error('Pg $1, $2 bound syntax not supported yet :-(');
}
} else if ($type == SQL_PARAMS_DOLAR) {
} else if ($type == SQL_PARAMS_DOLLAR) {
error('Pg $1, $2 bound syntax not supported yet :-(');
} else { // $type == SQL_PARAMS_QM
@ -283,7 +284,7 @@ abstract class moodle_database {
$finalparams[$pname] = $param;
}
return array($sql, $finalparams, SQL_PARAMS_NAMED);
} else { // $type & SQL_PARAMS_DOLAR
} else { // $type & SQL_PARAMS_DOLLAR
error('Pg $1, $2 bound syntax not supported yet :-(');
}
}

199
lib/dml/simpletest/testdmllib.php Executable file
View File

@ -0,0 +1,199 @@
<?php
/**
* Unit tests for dmllib
* @package dmllib
*/
if (!defined('MOODLE_INTERNAL')) {
die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page
}
require_once($CFG->libdir . '/simpletestlib/web_tester.php');
require_once($CFG->libdir . '/dmllib.php');
require_once($CFG->libdir . '/dml/mysql_adodb_moodle_database.php');
class dmllib_test extends UnitTestCase {
private $tables = array();
private $dbmanager;
private $db;
function setUp() {
global $CFG;
$this->db = new mysqli_adodb_moodle_database();
$this->db->connect($CFG->dbhost, $CFG->dbuser, $CFG->dbpass, $CFG->dbname, $CFG->dbpersist, $CFG->prefix);
$this->dbmanager = $this->db->get_manager();
$table = new xmldb_table("testtable");
$table->addFieldInfo('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null, null, null);
$table->addFieldInfo('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0');
$table->addFieldInfo('type', XMLDB_TYPE_CHAR, '20', null, XMLDB_NOTNULL, null, XMLDB_ENUM,
array('single', 'news', 'general', 'social', 'eachuser', 'teacher', 'qanda'), 'general');
$table->addFieldInfo('name', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null, null);
$table->addFieldInfo('intro', XMLDB_TYPE_TEXT, 'small', null, XMLDB_NOTNULL, null, null, null, null);
$table->addFieldInfo('logo', XMLDB_TYPE_BINARY, 'big', null, XMLDB_NOTNULL, null, null, null);
$table->addFieldInfo('assessed', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0');
$table->addFieldInfo('assesstimestart', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0');
$table->addFieldInfo('assesstimefinish', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0');
$table->addFieldInfo('scale', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null, null, '0');
$table->addFieldInfo('maxbytes', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0');
$table->addFieldInfo('forcesubscribe', XMLDB_TYPE_INTEGER, '1', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0');
$table->addFieldInfo('trackingtype', XMLDB_TYPE_INTEGER, '2', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '1');
$table->addFieldInfo('rsstype', XMLDB_TYPE_INTEGER, '2', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0');
$table->addFieldInfo('rssarticles', XMLDB_TYPE_INTEGER, '2', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0');
$table->addFieldInfo('timemodified', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0');
$table->addFieldInfo('grade', XMLDB_TYPE_NUMBER, '20,0', XMLDB_UNSIGNED, null, null, null, null, null);
$table->addFieldInfo('percent', XMLDB_TYPE_NUMBER, '5,2', null, null, null, null, null, null);
$table->addFieldInfo('warnafter', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0');
$table->addFieldInfo('blockafter', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0');
$table->addFieldInfo('blockperiod', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0');
$table->addKeyInfo('primary', XMLDB_KEY_PRIMARY, array('id'));
$table->addIndexInfo('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
$table->setComment("This is a test'n drop table. You can drop it safely");
$this->dbmanager->create_table($table);
$this->tables[] = $table;
// insert records
$datafile = $CFG->libdir . '/dml/simpletest/fixtures/testdata.xml';
$xml = simplexml_load_file($datafile);
foreach ($xml->record as $record) {
$this->db->insert_record('testtable', $record);
}
}
function tearDown() {
foreach ($this->tables as $key => $table) {
if ($this->dbmanager->table_exists($table)) {
$this->dbmanager->drop_table($table, true, false);
}
}
unset($this->tables);
setup_DB();
}
function test_insert_record() {
}
function test_get_record_select() {
$record = $this->db->get_record_select('testtable', 'id = 1');
}
function test_fix_sql_params() {
// Malformed table placeholder
$sql = "SELECT * FROM [testtable]";
$sqlarray = $this->db->fix_sql_params($sql);
$this->assertEqual($sql, $sqlarray[0]);
// Correct table placeholder substitution
$sql = "SELECT * FROM {testtable}";
$sqlarray = $this->db->fix_sql_params($sql);
$this->assertEqual("SELECT * FROM {$this->db->get_prefix()}testtable", $sqlarray[0]);
// Malformed param placeholders
$sql = "SELECT * FROM {testtable} WHERE name = ?param1";
$params = array('param1' => 'first record');
$sqlarray = $this->db->fix_sql_params($sql, $params);
$this->assertEqual("SELECT * FROM {$this->db->get_prefix()}testtable WHERE name = ?param1", $sqlarray[0]);
// Mixed param types (colon and dollar)
$sql = "SELECT * FROM {testtable} WHERE name = :param1, rsstype = \$1";
$params = array('param1' => 'first record', 'param2' => 1);
try {
$sqlarray = $this->db->fix_sql_params($sql, $params);
} catch (Exception $e) {
$this->assertEqual('error() call: ERROR: Mixed types of sql query parameters!!', $e->getMessage());
}
// Mixed param types (question and dollar)
$sql = "SELECT * FROM {testtable} WHERE name = ?, rsstype = \$1";
$params = array('param1' => 'first record', 'param2' => 1);
$exception_caught = false;
try {
$sqlarray = $this->db->fix_sql_params($sql, $params);
} catch (Exception $e) {
$exception_caught = true;
}
$this->assertTrue($exception_caught);
// Too many params in sql
$sql = "SELECT * FROM {testtable} WHERE name = ?, rsstype = ?, course = ?";
$params = array('first record', 1);
$exception_caught = false;
try {
$sqlarray = $this->db->fix_sql_params($sql, $params);
} catch (Exception $e) {
$exception_caught = true;
}
$this->assertTrue($exception_caught);
// Too many params in array: no error
$params[] = 1;
$params[] = time();
$exception_caught = false;
$sqlarray = null;
try {
$sqlarray = $this->db->fix_sql_params($sql, $params);
} catch (Exception $e) {
$exception_caught = true;
}
$this->assertFalse($exception_caught);
$this->assertTrue($sqlarray[0]);
// Named params missing from array
$sql = "SELECT * FROM {testtable} WHERE name = :name, rsstype = :rsstype";
$params = array('wrongname' => 'first record', 'rsstype' => 1);
$exception_caught = false;
try {
$sqlarray = $this->db->fix_sql_params($sql, $params);
} catch (Exception $e) {
$exception_caught = true;
}
$this->assertTrue($exception_caught);
// Duplicate named param in query
$sql = "SELECT * FROM {testtable} WHERE name = :name, rsstype = :name";
$params = array('name' => 'first record', 'rsstype' => 1);
$exception_caught = false;
try {
$sqlarray = $this->db->fix_sql_params($sql, $params);
} catch (Exception $e) {
$exception_caught = true;
}
$this->assertTrue($exception_caught);
// Unsupported Bound params
$sql = "SELECT * FROM {testtable} WHERE name = $1, rsstype = $2";
$params = array('first record', 1);
$exception_caught = false;
try {
$sqlarray = $this->db->fix_sql_params($sql, $params);
} catch (Exception $e) {
$exception_caught = true;
}
$this->assertTrue($exception_caught);
// Correct named param placeholders
$sql = "SELECT * FROM {testtable} WHERE name = :name, rsstype = :rsstype";
$params = array('name' => 'first record', 'rsstype' => 1);
$sqlarray = $this->db->fix_sql_params($sql, $params);
$this->assertEqual("SELECT * FROM {$this->db->get_prefix()}testtable WHERE name = ?, rsstype = ?", $sqlarray[0]);
$this->assertEqual(2, count($sqlarray[1]));
// Correct ? params
$sql = "SELECT * FROM {testtable} WHERE name = ?, rsstype = ?";
$params = array('first record', 1);
$sqlarray = $this->db->fix_sql_params($sql, $params);
$this->assertEqual("SELECT * FROM {$this->db->get_prefix()}testtable WHERE name = ?, rsstype = ?", $sqlarray[0]);
$this->assertEqual(2, count($sqlarray[1]));
}
}
?>

View File

@ -52,7 +52,7 @@ define('SQL_PARAMS_QM', 2);
/**
* Bitmask, indicates only $1, $2.. type parameters are supported by db backend.
*/
define('SQL_PARAMS_DOLAR', 4);
define('SQL_PARAMS_DOLLAR', 4);
/**

View File

@ -622,7 +622,7 @@ function break_up_long_words($string, $maxsize=20, $cutchar=' ') {
*
* $url must be relative to home page eg /mod/survey/stuff.php
* @param string $url Web link relative to home page
* @param string $name Name to be assigned to the popup window (this is used by
* @param string $name Name to be assigned to the popup window (this is used by
* client-side scripts to "talk" to the popup window)
* @param string $linkname Text to be displayed as web link
* @param int $height Height to assign to popup window
@ -673,7 +673,7 @@ function element_to_popup_window ($type=null, $url=null, $name=null, $linkname=n
} else {
$name = 'popup';
}
// get some default string, using the localized version of legacy defaults
if (is_null($linkname) || $linkname === '') {
$linkname = get_string('clickhere');
@ -859,7 +859,7 @@ function choose_from_menu ($options, $name, $selected='', $nothing='choose', $sc
* Choose value 0 or 1 from a menu with options 'No' and 'Yes'.
* Other options like choose_from_menu.
* @param string $name
* @param string $selected
* @param string $selected
* @param string $string (defaults to '')
* @param boolean $return whether this function should return a string or output it (defaults to false)
* @param boolean $disabled (defaults to false)
@ -1110,16 +1110,16 @@ $targetwindow='self', $selectlabel='', $optionsextra=NULL) {
$selectlabel = '<label for="'.$formid.'_jump">'.$selectlabel.'</label>';
}
//IE and Opera fire the onchange when ever you move into a dropdwown list with the keyboard.
//IE and Opera fire the onchange when ever you move into a dropdwown list with the keyboard.
//onfocus will call a function inside dropdown.js. It fixes this IE/Opera behavior.
if (check_browser_version('MSIE') || check_browser_version('Opera')) {
$output .= '<div>'.$selectlabel.$button.'<select id="'.$formid.'_jump" onfocus="initSelect(\''.$formid.'\','.$targetwindow.')" name="jump">'."\n";
}
//Other browser
else {
$output .= '<div>'.$selectlabel.$button.'<select id="'.$formid.'_jump" name="jump" onchange="'.$targetwindow.'.location=document.getElementById(\''.$formid.'\').jump.options[document.getElementById(\''.$formid.'\').jump.selectedIndex].value;">'."\n";
$output .= '<div>'.$selectlabel.$button.'<select id="'.$formid.'_jump" name="jump" onchange="'.$targetwindow.'.location=document.getElementById(\''.$formid.'\').jump.options[document.getElementById(\''.$formid.'\').jump.selectedIndex].value;">'."\n";
}
if ($nothing != '') {
$output .= " <option value=\"javascript:void(0)\">$nothing</option>\n";
}
@ -5638,6 +5638,16 @@ function print_error ($errorcode, $module='', $link='', $a=NULL) {
$message = get_string($errorcode, $module, $a);
/**
* TODO VERY DIRTY HACK USED FOR UNIT TESTING UNTIL PROPER EXCEPTION HANDLING IS IMPLEMENTED
*/
if (defined('UNITTEST')) {
// Errors in unit test become exceptions, so you can unit test
// code that might call error().
throw new Exception('error() call: '. $message.($link!=='' ? ' ['.$link.']' : ''));
}
if (!isset($CFG->theme)) {
// error found before setup.php finished
print_early_error($message);
@ -5746,11 +5756,11 @@ function print_early_error($message) {
* Default errorcode is 1.
*
* Very useful for perl-like error-handling:
*
*
* do_somethting() or mdie("Something went wrong");
*
* @param string $msg Error message
* @param integer $errorcode Error code to emit
* @param integer $errorcode Error code to emit
*/
function mdie($msg='', $errorcode=1) {
trigger_error($msg);
@ -6442,8 +6452,8 @@ function print_side_block_start($heading='', $attributes = array()) {
// page block including the h2 for accessibility
if(strpos($heading,'</div>')===false) {
$heading='<div class="title"><h2>'.$heading.'</h2></div>';
}
}
echo '<div class="header">';
if (!empty($THEME->customcorners)) {
echo '<div class="bt"><div>&nbsp;</div></div>';
@ -6801,7 +6811,7 @@ function page_doc_link($text='', $iconpath='') {
*/
function doc_link($path='', $text='', $iconpath='') {
global $CFG;
if (empty($CFG->docroot)) {
return '';
}
@ -6819,7 +6829,7 @@ function doc_link($path='', $text='', $iconpath='') {
$str = "<a href=\"$url\"$target>";
if (empty($iconpath)) {
if (empty($iconpath)) {
$iconpath = $CFG->httpswwwroot . '/pix/docs.gif';
}