From 73f7ad715e9df2e09f344dfe021600f1085d9f5c Mon Sep 17 00:00:00 2001 From: nicolasconnault Date: Wed, 21 May 2008 14:59:33 +0000 Subject: [PATCH] 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. --- lib/ddl/simpletest/testddllib.php | 7 +- lib/deprecatedlib.php | 15 ++- lib/dml/moodle_database.php | 25 ++-- lib/dml/simpletest/testdmllib.php | 199 ++++++++++++++++++++++++++++++ lib/dmllib.php | 2 +- lib/weblib.php | 34 +++-- 6 files changed, 250 insertions(+), 32 deletions(-) create mode 100755 lib/dml/simpletest/testdmllib.php diff --git a/lib/ddl/simpletest/testddllib.php b/lib/ddl/simpletest/testddllib.php index ee1732dde8..7545d1608b 100755 --- a/lib/ddl/simpletest/testddllib.php +++ b/lib/ddl/simpletest/testddllib.php @@ -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); diff --git a/lib/deprecatedlib.php b/lib/deprecatedlib.php index f8997abc33..02f72e450c 100644 --- a/lib/deprecatedlib.php +++ b/lib/deprecatedlib.php @@ -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); diff --git a/lib/dml/moodle_database.php b/lib/dml/moodle_database.php index 21c0104350..503b501b79 100644 --- a/lib/dml/moodle_database.php +++ b/lib/dml/moodle_database.php @@ -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('/(? 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('/(?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])); + + } + +} + +?> diff --git a/lib/dmllib.php b/lib/dmllib.php index 1b09e3691d..a62cd5ff97 100644 --- a/lib/dmllib.php +++ b/lib/dmllib.php @@ -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); /** diff --git a/lib/weblib.php b/lib/weblib.php index a87df9058d..e9d7819c90 100644 --- a/lib/weblib.php +++ b/lib/weblib.php @@ -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 = ''; } - //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 .= '
'.$selectlabel.$button.''."\n"; + $output .= '
'.$selectlabel.$button.'