Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Pull changes from D7 branch into original master #2

Open
wants to merge 16 commits into from

1 participant

@benbuckman

Fixed verbose() and other bugs, added file dump mechanism, added HTTP auth mechanism to phpunit.xml, other changes.
Split drupal_test_case.php into 2 files, upal.php and DrupalTestCase.class.php.
Does not fully copy the changes in my master branch, because they are a) backported to D6 and b) meant for a ready-made sandbox site.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Dec 1, 2011
  1. @benbuckman
  2. @benbuckman
  3. @benbuckman
  4. @benbuckman

    manual [cleaner] replication of 67beb30: move class to its own file, …

    benbuckman authored
    …rename drupal_test_case to upal
  5. @benbuckman
  6. @benbuckman
  7. @benbuckman
  8. @benbuckman
  9. @benbuckman
  10. @benbuckman
  11. @benbuckman
  12. @benbuckman

    include URL in output on content dump

    benbuckman authored
    (cherry picked from commit a47275c)
  13. @benbuckman

    fix assertFieldById to work without value

    benbuckman authored
    (cherry picked from commit 15264c0)
  14. @benbuckman
  15. @benbuckman

    description param for dumpContentToFile()

    benbuckman authored
    (cherry picked from commit 5d92aa9)
  16. @benbuckman

    method to set and restore variables on tearDown

    benbuckman authored
    (cherry picked from commit 9cf5764)
This page is out of date. Refresh to see the latest.
View
270 drupal_test_case.php → DrupalTestCase.class.php
@@ -1,27 +1,6 @@
<?php
/*
- * @file
- * Test Framework for Drupal based on PHPUnit.
- *
- * @todo
- * - Simpletest's assertFalse casts to boolean whereas PHPUnit requires boolean. See testSiteWideContact().
- * - hard coded TRUE at end of drupalLogin() because PHPUnit doesn't return
- * anything from an assertion (unlike simpletest). Even if we fix drupalLogin(),
- * we have to fix this to get 100% compatibility with simpletest.
- * - setUp() only resets DB for mysql. Probably should use Drush and thus
- * support postgres and sqlite easily. That buys us auto creation of upal DB
- * as well.
- * - Unlikely: Instead of DB restore, clone as per http://drupal.org/node/666956.
- * - error() could log $caller info.
- * - Fix verbose().
- * - Fix random test failures.
- * - Split into separate class files and add autoloader for upal.
- * - Compare speed versus simpletest.
- * - move upal_init() to a class thats called early in the suite.
- */
-
-/*
* @todo: Perhaps move these annotations down to the instance classes and tests.
*
* @runTestsInSeparateProcess
@@ -150,6 +129,19 @@
* The number of redirects followed during the handling of a request.
*/
protected $redirect_count;
+
+
+ /**
+ * stuff to clean up on tearDown()
+ */
+ protected $cleanup = array();
+
+
+ /**
+ * for verbose output
+ */
+ protected static $printer;
+
public function run(PHPUnit_Framework_TestResult $result = NULL) {
$this->setPreserveGlobalState(FALSE);
@@ -206,14 +198,27 @@ protected function error($message = '', $group = 'Other', array $caller = NULL)
return $this->fail('exception: ' . $message, $group);
}
- function assertEqual($expected, $actual, $msg = NULL) {
- return $this->assertEquals($expected, $actual);
+ // legacy supports
+
+ public static function assertTrue($condition, $message = '') {
+ parent::assertTrue($condition, $message);
+ return TRUE; // needed for simpletest back-comp (e.g. in drupalLogin), but dumb / always true.
+ }
+
+ function assertEqual($expected, $actual, $message = '') {
+ return $this->assertEquals($expected, $actual, $message);
+ }
+
+ function assertNotEqual($expected, $actual, $message = '') {
+ return $this->assertNotEquals($expected, $actual, $message);
}
function assertIdentical($first, $second, $message = '', $group = 'Other') {
return $this->assertSame($first, $second, $message);
}
+
+
/**
* Pass if the internal browser's URL matches the given path.
*
@@ -517,7 +522,7 @@ protected function assertFieldByXPath($xpath, $value = NULL, $message = '', $gro
// If value specified then check array for match.
$found = TRUE;
- if (isset($value)) {
+ if (!empty($value)) {
$found = FALSE;
if ($fields) {
foreach ($fields as $field) {
@@ -653,7 +658,7 @@ protected function assertNoFieldByName($name, $value = '', $message = '') {
* @return
* TRUE on pass, FALSE on fail.
*/
- protected function assertFieldById($id, $value = '', $message = '') {
+ protected function assertFieldById($id, $value = NULL, $message = '') {
return $this->assertFieldByXPath($this->constructFieldXpath('id', $id), $value, $message ? $message : t('Found field by id @id', array('@id' => $id)), t('Browser'));
}
@@ -1096,18 +1101,42 @@ public static function generatePermutations($parameters) {
return $all_permutations;
}
- function verbose($message) {
- if (strlen($message) < 500) {
- // $this->log($message, 'verbose');
+
+ public static function verbose($message, $cutoff = 500) {
+
+ // init printer on first time
+ if (! self::$printer instanceof PHPUnit_TextUI_ResultPrinter) {
+ self::$printer = new PHPUnit_TextUI_ResultPrinter('php://stdout', TRUE, TRUE, TRUE); // can change to stderr
+ //echo "SET UP PRINTER!!!\n"; // (this works too, but not as good...?)
}
+
+ // default limit to arbitrary length
+ if (is_int($cutoff) && strlen($message) > $cutoff) {
+ $message = truncate_utf8($message, $cutoff, FALSE, TRUE);
+ }
+
+ //$this->log($message, 'verbose'); // this doesn't do anything
+ //echo "[verbose] " . $message . "\n"; // this works but is crude
+
+ self::$printer->write("\n" . $message . "\n"); // seems to be a more native approach
+ }
+
+ /**
+ * output objects
+ */
+ public function debug($obj, $heading = '') {
+ self::verbose( (empty($heading) ? '' : "* {$heading}:\n") . print_r($obj,TRUE), NULL );
}
+
+
/**
* Create a user with a given set of permissions. The permissions correspond to the
* names given on the privileges page.
*
* @param $permissions
* Array of permission names to assign to user.
+ * @param $cleanup if TRUE, gets deleted in tearDown()
* @return
* A fully loaded user object with pass_raw property, or FALSE if account
* creation fails.
@@ -1132,6 +1161,11 @@ protected function drupalCreateUser($permissions = array('access comments', 'acc
if (empty($account->uid)) {
return FALSE;
}
+
+ // mark for cleanup on tearDown()
+ if ($cleanup) {
+ $this->cleanup['users'][] = $account->uid;
+ }
// Add the raw password so that we can log in as this user.
$account->pass_raw = $edit['pass'];
@@ -1211,7 +1245,7 @@ protected function drupalGet($path, array $options = array(), array $headers = a
/**
* Initializes the cURL connection.
*
- * If the simpletest_httpauth_credentials variable is set, this function will
+ * If the UPAL_HTTP_USER and UPAL_HTTP_PASS config vars are set, this function will
* add HTTP authentication headers. This is necessary for testing sites that
* are protected by login credentials from public access.
* See the description of $curl_options for other options.
@@ -1231,10 +1265,16 @@ protected function curlInitialize() {
CURLOPT_HEADERFUNCTION => array(&$this, 'curlHeaderCallback'),
CURLOPT_USERAGENT => 'Upal',
);
- if (isset($this->httpauth_credentials)) {
+
+ // http credentials optionally in phpunit.xml
+ if (isset($GLOBALS['UPAL_HTTP_USER']) && isset($GLOBALS['UPAL_HTTP_PASS']) && !empty($GLOBALS['UPAL_HTTP_USER']) && !empty($GLOBALS['UPAL_HTTP_PASS'])) {
+ $this->httpauth_credentials = $GLOBALS['UPAL_HTTP_USER'] . ':' . $GLOBALS['UPAL_HTTP_PASS'];
+
$curl_options[CURLOPT_HTTPAUTH] = $this->httpauth_method;
$curl_options[CURLOPT_USERPWD] = $this->httpauth_credentials;
}
+
+
curl_setopt_array($this->curlHandle, $this->additionalCurlOptions + $curl_options);
// By default, the child session name should be the same as the parent.
@@ -1651,6 +1691,7 @@ protected function drupalLogout() {
$pass = $this->assertField('name', t('Username field found.'), t('Logout'));
$pass = $pass && $this->assertField('pass', t('Password field found.'), t('Logout'));
+ // [bb] $pass here is forced to TRUE, but really phpunit doesn't return TRUE on success, so this runs either way:
if ($pass) {
$this->loggedInUser = FALSE;
}
@@ -1662,10 +1703,11 @@ protected function drupalLogout() {
* @param $settings
* An associative array of settings to change from the defaults, keys are
* node properties, for example 'title' => 'Hello, world!'.
+ * @param $cleanup if TRUE, gets deleted in tearDown()
* @return
* Created node object.
*/
- protected function drupalCreateNode($settings = array()) {
+ protected function drupalCreateNode($settings = array(), $cleanup = TRUE) {
// Populate defaults array.
$settings += array(
'body' => array(LANGUAGE_NONE => array(array())),
@@ -1709,6 +1751,11 @@ protected function drupalCreateNode($settings = array()) {
$node = (object) $settings;
node_save($node);
+
+ // mark for cleanup on tearDown()
+ if ($cleanup) {
+ $this->cleanup['nodes'][] = $node->nid;
+ }
// Small hack to link revisions to our test user.
db_update('node_revision')
@@ -1717,6 +1764,19 @@ protected function drupalCreateNode($settings = array()) {
->execute();
return $node;
}
+
+
+ /**
+ * [added] wrapper around variable_set, saves original value for restore on tearDown.
+ */
+ function drupalVariableSet($key, $value, $cleanup = TRUE) {
+ if ($cleanup) {
+ $this->cleanup['variables'][$key] = variable_get($key, 0);
+ }
+
+ variable_set($key, $value);
+ }
+
/**
* Creates a custom content type based on default settings.
@@ -2315,10 +2375,102 @@ protected function drupalSetSettings($settings) {
$this->drupalSettings = $settings;
}
- }
+
+
+ /**
+ * crude mechanism to dump current CURL'd html to file system
+ * dir set in phpunit.xml as DUMP_DIR.
+ * [tried and removed: create subdir for this test run and file for each dump]
+ *
+ * @param $description optional description of file dump for the logs
+ */
+ public function dumpContentToFile($description = '') {
+ $dump_dir = isset($GLOBALS['DUMP_DIR']) ? $GLOBALS['DUMP_DIR'] : NULL;
+ if (empty($dump_dir) || !file_exists($dump_dir)) {
+ $this->error("Missing or invalid DUMP_DIR in " . __FUNCTION__);
+ return;
+ }
+
+
+ // tried per-run subdir, but dropped; should be handled separately in jenkins/bash
+ //static $run_ts = NULL;
+ // if (! $run_ts) $run_ts = date('Y-m-d_H:m:s');
+
+ //$dump_dir .= '/' . $run_ts;
+ //if (! file_exists($dump_dir)) {
+ // $made = mkdir($dump_dir);
+ // if (! $made) {
+ // $this->error("Unable to create dump subdir $dump_dir");
+ // return;
+ // }
+ //}
+
+ $filename = $this->getUrl();
+ $filename = str_replace('/', '-', $filename);
+ $filename = str_replace(':', '', $filename);
+ $filename = str_replace('.', '_', $filename);
+
+ // add counter to identify order and prevent dups/overrides of same URL
+ static $count = 0;
+ $filename = (++$count) . '-' . $filename . '.html';
+
+ $filepath = realpath($dump_dir) . '/' . $filename;
+
+ //$this->verbose(sprintf("Dumping content to %s", $filepath));
+
+ $put = file_put_contents($filepath, $this->drupalGetContent());
+ if ($put === FALSE) $this->error("Unable to dump content to $filepath.");
+ else $this->verbose("Dumped content of " . $this->getUrl() . " to $filepath." . (empty($description) ? '' : " ($description)") );
+ }
+
+
+ /**
+ * tear down after tests: added for cleanup.
+ */
+ function tearDown() {
+
+ // nodes
+ if (is_array($this->cleanup['nodes'])) {
+ foreach($this->cleanup['nodes'] as $nid) {
+ if (is_numeric($nid)) {
+ $this->verbose("Cleanup: deleting node [{$nid}]");
+ node_delete($nid);
+ }
+ }
+ }
+
+ // users
+ if (is_array($this->cleanup['users'])) {
+ foreach($this->cleanup['users'] as $uid) {
+ if (is_numeric($uid)) {
+ $this->verbose("Cleanup: deleting user [{$uid}]");
+ user_delete(array(), $uid);
+ }
+ }
+ }
+
+ // [restore] variables
+ if (is_array($this->cleanup['variables'])) {
+ foreach($this->cleanup['variables'] as $key => $value) {
+ $this->verbose("Cleanup: restoring variable [{$key}]");
+ variable_set($key, $value);
+ }
+ }
+
+ // @todo apply this $cleanup logic to other created stuff.
+
+ parent::tearDown();
+ }
+
+
+
+} // abstract class DrupalTestCase
+
class DrupalUnitTestCase extends DrupalTestCase {
function setUp() {
+ $this->verbose("Setting up " . get_class($this));
+
parent::setUp();
if (!defined('DRUPAL_ROOT')) {
@@ -2331,6 +2483,8 @@ function setUp() {
class DrupalWebTestCase extends DrupalTestCase {
public function setUp() {
+ $this->verbose("Setting up " . get_class($this));
+
parent::setUp();
if (!defined('DRUPAL_ROOT')) {
@@ -2409,55 +2563,3 @@ public function setUp() {
}
}
-
-/*
- * Initialize our environment at the start of each run (i.e. suite).
- */
-function upal_init() {
- // UNISH_DRUSH value can come from phpunit.xml or `which drush`.
- if (!defined('UNISH_DRUSH')) {
- // Let the UNISH_DRUSH environment variable override if set.
- $unish_drush = isset($_SERVER['UNISH_DRUSH']) ? $_SERVER['UNISH_DRUSH'] : NULL;
- $unish_drush = isset($GLOBALS['UNISH_DRUSH']) ? $GLOBALS['UNISH_DRUSH'] : $unish_drush;
- if (empty($unish_drush)) {
- // $unish_drush = Drush_TestCase::is_windows() ? exec('for %i in (drush) do @echo. %~$PATH:i') : trim(`which drush`);
- $unish_drush = trim(`which drush`);
- }
- define('UNISH_DRUSH', $unish_drush);
- }
-
- // We read from globals here because env can be empty and ini did not work in quick test.
- define('UPAL_DB_URL', getenv('UPAL_DB_URL') ? getenv('UPAL_DB_URL') : (!empty($GLOBALS['UPAL_DB_URL']) ? $GLOBALS['UPAL_DB_URL'] : 'mysql://root:@127.0.0.1/upal'));
-
- // Make sure we use the right Drupal codebase.
- define('UPAL_ROOT', getenv('UPAL_ROOT') ? getenv('UPAL_ROOT') : (isset($GLOBALS['UPAL_ROOT']) ? $GLOBALS['UPAL_ROOT'] : realpath('.')));
- chdir(UPAL_ROOT);
-
- // The URL that browser based tests (ewwwww) should use.
- define('UPAL_WEB_URL', getenv('UPAL_WEB_URL') ? getenv('UPAL_WEB_URL') : (isset($GLOBALS['UPAL_WEB_URL']) ? $GLOBALS['UPAL_WEB_URL'] : 'http://upal'));
-
-
- // Set the env vars that Derupal expects. Largely copied from drush.
- $url = parse_url(UPAL_WEB_URL);
-
- if (array_key_exists('path', $url)) {
- $_SERVER['PHP_SELF'] = $url['path'] . '/index.php';
- }
- else {
- $_SERVER['PHP_SELF'] = '/index.php';
- }
-
- $_SERVER['REQUEST_URI'] = $_SERVER['SCRIPT_NAME'] = $_SERVER['PHP_SELF'];
- $_SERVER['REMOTE_ADDR'] = '127.0.0.1';
- $_SERVER['REQUEST_METHOD'] = NULL;
-
- $_SERVER['SERVER_SOFTWARE'] = NULL;
- $_SERVER['HTTP_USER_AGENT'] = NULL;
-
- $_SERVER['HTTP_HOST'] = $url['host'];
- $_SERVER['SERVER_PORT'] = array_key_exists('port', $url) ? $url['port'] : NULL;
-}
-
- // This code is in global scope.
- // TODO: I would rather this code at top of file, but I get Fatal error: Class 'Drush_TestCase' not found
- upal_init();
View
15 phpunit.xml.dist
@@ -2,7 +2,7 @@
<phpunit backupGlobals="false"
backupStaticAttributes="false"
syntaxCheck="false"
- bootstrap="drupal_test_case.php">
+ bootstrap="upal.php">
<php>
<!-- These variables may alternatively be set as bash environment variables. -->
@@ -15,11 +15,20 @@
<!-- <var name="UPAL_WEB_URL" value="http://example.com/upal"/> -->
<!--Hard code a fileystem path to the to-be-tested Drupal. Defaults to cwd.-->
- <!--<var name="UPAL_ROOT" value="/Users/mw/htd/drupal"/>-->
+ <!--<var name="UPAL_ROOT" value="/Users/example/drupal"/>-->
<!--Uncomment the line below if your path to drush differs from `which drush`. Use absolute path.-->
<!--You currently need 'master' branch of drush after 2011.07.21. Drush 4.6 will be OK - http://drupal.org/node/1105514-->
- <!--<var name="UNISH_DRUSH" value="/Users/mw/bin/drush"/>-->
+ <!--<var name="UNISH_DRUSH" value="/Users/example/bin/drush"/>-->
+
+
+ <!-- optional HTTP authentication to sandbox -->
+ <var name="UPAL_HTTP_USER" value="user"/>
+ <var name="UPAL_HTTP_PASS" value="pass"/>
+
+ <!-- optional for html/content dumps -->
+ <var name="DUMP_DIR" value="/Users/example/tests-dump" />
+
<includePath>.</includePath>
</php>
View
79 upal.php
@@ -0,0 +1,79 @@
+<?php
+
+/*
+ * @file
+ * Test Framework for Drupal based on PHPUnit.
+ *
+ * @todo
+ * - Simpletest's assertFalse casts to boolean whereas PHPUnit requires boolean. See testSiteWideContact().
+ * - hard coded TRUE at end of drupalLogin() because PHPUnit doesn't return
+ * anything from an assertion (unlike simpletest). Even if we fix drupalLogin(),
+ * we have to fix this to get 100% compatibility with simpletest.
+ * - setUp() only resets DB for mysql. Probably should use Drush and thus
+ * support postgres and sqlite easily. That buys us auto creation of upal DB
+ * as well.
+ * - Unlikely: Instead of DB restore, clone as per http://drupal.org/node/666956.
+ * - error() could log $caller info.
+ * - Fix random test failures.
+ * - Split into separate class files and add autoloader for upal.
+ * - Compare speed versus simpletest.
+ * - move upal_init() to a class thats called early in the suite.
+ */
+
+// class moved to its own file
+include_once 'DrupalTestCase.class.php';
+
+upal_init();
+
+
+/*
+ * Initialize our environment at the start of each run (i.e. suite).
+ */
+function upal_init() {
+ // UNISH_DRUSH value can come from phpunit.xml or `which drush`.
+ if (!defined('UNISH_DRUSH')) {
+ // Let the UNISH_DRUSH environment variable override if set.
+ $unish_drush = isset($_SERVER['UNISH_DRUSH']) ? $_SERVER['UNISH_DRUSH'] : NULL;
+ $unish_drush = isset($GLOBALS['UNISH_DRUSH']) ? $GLOBALS['UNISH_DRUSH'] : $unish_drush;
+ if (empty($unish_drush)) {
+ // $unish_drush = Drush_TestCase::is_windows() ? exec('for %i in (drush) do @echo. %~$PATH:i') : trim(`which drush`);
+ $unish_drush = trim(`which drush`);
+ }
+ define('UNISH_DRUSH', $unish_drush);
+ }
+
+ // We read from globals here because env can be empty and ini did not work in quick test.
+ define('UPAL_DB_URL', getenv('UPAL_DB_URL') ? getenv('UPAL_DB_URL') : (!empty($GLOBALS['UPAL_DB_URL']) ? $GLOBALS['UPAL_DB_URL'] : 'mysql://root:@127.0.0.1/upal'));
+
+ // Make sure we use the right Drupal codebase.
+ define('UPAL_ROOT', getenv('UPAL_ROOT') ? getenv('UPAL_ROOT') : (isset($GLOBALS['UPAL_ROOT']) ? $GLOBALS['UPAL_ROOT'] : realpath('.')));
+ chdir(UPAL_ROOT);
+
+ // The URL that browser based tests (ewwwww) should use.
+ define('UPAL_WEB_URL', getenv('UPAL_WEB_URL') ? getenv('UPAL_WEB_URL') : (isset($GLOBALS['UPAL_WEB_URL']) ? $GLOBALS['UPAL_WEB_URL'] : 'http://upal'));
+
+
+ // http credentials (optional)
+ $GLOBALS['UPAL_HTTP_USER'] = getenv('UPAL_HTTP_USER') ? getenv('UPAL_HTTP_USER') : (isset($GLOBALS['UPAL_HTTP_USER']) ? $GLOBALS['UPAL_HTTP_USER'] : NULL);
+ $GLOBALS['UPAL_HTTP_PASS'] = getenv('UPAL_HTTP_PASS') ? getenv('UPAL_HTTP_PASS') : (isset($GLOBALS['UPAL_HTTP_PASS']) ? $GLOBALS['UPAL_HTTP_PASS'] : NULL);
+
+ // Set the env vars that Drupal expects. Largely copied from drush.
+ $url = parse_url(UPAL_WEB_URL);
+
+ if (array_key_exists('path', $url)) {
+ $_SERVER['PHP_SELF'] = $url['path'] . '/index.php';
+ }
+ else {
+ $_SERVER['PHP_SELF'] = '/index.php';
+ }
+
+ $_SERVER['REQUEST_URI'] = $_SERVER['SCRIPT_NAME'] = $_SERVER['PHP_SELF'];
+ $_SERVER['REMOTE_ADDR'] = '127.0.0.1';
+ $_SERVER['REQUEST_METHOD'] = NULL;
+
+ $_SERVER['SERVER_SOFTWARE'] = NULL;
+ $_SERVER['HTTP_USER_AGENT'] = NULL;
+
+ $_SERVER['HTTP_HOST'] = $url['host'];
+ $_SERVER['SERVER_PORT'] = array_key_exists('port', $url) ? $url['port'] : NULL;
+}
Something went wrong with that request. Please try again.