Selenium.class.php

From Organic Design wiki
Legacy.svg Legacy: This article describes a concept that has been superseded in the course of ongoing development on the Organic Design wiki. Please do not develop this any further or base work on this concept, this is only useful for a historic record of work done. You may find a link to the currently used concept or function in this article, if not you can contact the author to find out what has taken the place of this legacy item.
<?php
/**
 * Selenium extension
 *
 * See http://www.mediawiki.org/Extension:Selenium for installation and usage details
 * See http://www.organicdesign.co.nz/Extension_talk:Selenium.php for development notes and disucssion
 * 
 * @package MediaWiki
 * @subpackage Extensions
 * @author Marcus Davy [http://www.organicdesign.co.nz/User:Sven User:Sven]
 * @copyright © 2007 Marcus Davy
 * @licence GNU General Public Licence 2.0 or later
 */
if (!defined('MEDIAWIKI')) die('Not an entry point.');

class Selenium {

	var $testCount = array(); # keep track of number of tests in each suite processed

	function __construct() {
		global $wgParser, $egSeleniumTag;

		# Add the tag hook
		$wgParser->setHook($egSeleniumTag, array($this, 'tagSelenium'));

		# Check the schedule and see if any test-runs should be spawned
		$this->checkSchedule();
	}

	/**
	 * Process a selenium tag with test syntax in it
	 * - this tag also handles the categorisation of the article into Category:Selenium
	 */
	public function tagSelenium($text, &$argv, &$parser) {
		global $wgOut, $egSeleniumSuiteCat, $egSeleniumTestCat;

		# The suite being processed is the article title text, keep track of number of tests in it
		$suite = $parser->mTitle->getPrefixedText();
		$this->testCount[$suite] = array_key_exists($suite, $this->testCount) ? $this->testCount[$suite]+1 : 1;
		
		# Get name of test from title tag
		$name = preg_match("|<title>(.+?)</title>|i", $text, $match) ? $match[1] : 'untitled';

		# Get rid of unwanted tags
		$text = preg_replace("#\\s*<head>.+</head>\\s*#is", "", $text);
		$text = preg_replace("#\\s*</?t?(html|head|body)>\\s*#i", "", $text);

		# Categorise the article into Selenium tests category if more than one test in this suite
		# - todo: and also remove from the cat for individual tests
		if ($this->testCount[$suite] > 1) $text .= "[[Category:$egSeleniumSuiteCat]]";

		# Parse the content normally and return wrapped in a div of class selenium and id of test name
		$out = $parser->parse($text, $parser->mTitle, $parser->mOptions, false, false);
		$text = $out->getText();
		return "<div class=\"selenium\" id=\"$name\">$text</div><!-- selenium-end -->";
	}

	/**
	 * Extract the individual tests from the suite indexed by name in selenese HTML format
	 * - also add the selenese HTML for the whole suite under index of suite name
	 */
	public static function selenese($suite, $path = false) {
		global $wgParser;

		# Get the content of the suite article and parse it
		# NOTE: parsing the content results in the head, body, thead, tbody etc being removed,
		#       but it seems that the TestRunner doesn't care and can work with the basic table structure
		$suiteTitle = Title::newFromText($suite);
		$suiteArticle = new Article($suiteTitle);
		$text = $wgParser->parse($suiteArticle->getContent(), $suiteTitle, new ParserOptions(), true, true)->getText();

		# extract the name and content of the individual tests in the suite
		$numTests = preg_match_all(
			"|<div class=\"selenium\" id=\"(.+?)\">\\s*(.+?)\\s*</div><!-- selenium-end -->|is",
			$text,
			$matches,
			PREG_PATTERN_ORDER
		);

		# Re-organise the matched test content into a hash indexed by test name
		$tests = array();
		for ($i = 0; $i < $numTests; $i++) $tests[$matches[1][$i]] = $matches[2][$i];
		
		# Create the HTML for the entire suite under index of suite name
		$html = "<html><head><meta content=\"text/html; charset=ISO-8859-1\" http-equiv=\"content-type\">
			<title>Selenium</title></head><body><table cellpadding=\"1\" cellspacing=\"1\" border=\"1\">
			<tbody><tr><td><b>$suite</b></td></tr>\n";
		$title = Title::makeTitle(NS_SPECIAL, 'Selenium');
		foreach (array_keys($tests) as $test) {
			$url = $path ? "$path/wiki-".urlencode($test).".html" : $title->getLocalURL("suite=$suite&test=$test");
			$html .= "<tr><td><a href=\"$url\">$test</a></td></tr>\n";
		}
		$tests[$suite] = "$html</tbody></table></body></html>";
		
		return $tests;
	}

	/**
	 * Check whether any tests should run
	 */
	public function checkSchedule() {
		global $egSeleniumSchdeule;
		if (isset($_REQUEST['action']) && $_REQUEST['action'] == 'runtests') $this->runTests();
		$ts = date('H:i');
		if (in_array($ts, $egSeleniumSchdeule)) {
			$ts = date('d M Y').", $ts";

			# Get the last log entry

			# If tests for this time and day haven't run yet, run them now
			if ("Last log entry time stamp not equal to current") $this->runTests();
		}
	}

	/**
	 * Run all the tests in the schedule in Selenium Remote Control (SRC)
	 */
	public function runTests() {
		global $IP, $egSeleniumSchedule, $egSeleniumPath, $egSeleniumDir;
		
		# Update the log with tests start message
		
		# Define paths for the java server, the HTML test files and the results file
		$jar   = "$egSeleniumDir/selenium-server/selenium-server.jar";
		$tests = "$egSeleniumDir/selenium-core/tests";
		$out   = "$egSeleniumDir/selenium-core/tests/results.txt";

		# We need to flip the operation to work by domain on the outer loop
		# - because it needs to build a set of selenese suite/test files for each site
		#   since the parser-functions used in the tests must be resolved my the wiki being tested

		foreach ($egSeleniumSchedule as $suite => $domains) if (!is_numeric($suite)) {
			$es = urlencode($suite);

			# Copy the suite and its tests as selenese HTML into the place SRC expects to find them
			foreach (Selenium::selenese($suite, "/selenium-server/tests") as $k => $v) {
				$ek = urlencode($k);
				file_put_contents("$tests/wiki-$ek.html", $v);
			}

			# Run the suite for each domain
			foreach ($domains as $domain) {
				shell_exec("java -jar $jar -htmlSuite \"*custom /usr/bin/firefox\" \"$domain\" \"$tests/wiki-$es.html\" \"$out\"");
				$results = file_get_contents($out);
			}
		}
		
		# Update the log with test results
	}

	/**
	 * Needed in some versions to prevent Special:Version from breaking
	 */
	private function __toString() {
		return __CLASS__;
	}
}