require_once( dirname( dirname( __FILE__ ) )."/Maintenance.php" );
require_once( 'PHPUnit/Framework.php' );
require_once( 'PHPUnit/Extensions/SeleniumTestCase.php' );
+require_once( dirname( __FILE__ ) . "/selenium/SeleniumServerManager.php" );
class SeleniumTester extends Maintenance {
protected $selenium;
+ protected $serverManager;
+ protected $seleniumServerExecPath;
public function __construct() {
parent::__construct();
$this->mDescription = "Selenium Test Runner. For documentation, visit http://www.mediawiki.org/wiki/SeleniumFramework";
- $this->addOption( 'port', 'Port used by selenium server. Default: 4444' );
- $this->addOption( 'host', 'Host selenium server. Default: $wgServer . $wgScriptPath' );
- $this->addOption( 'testBrowser', 'The browser he used during testing. Default: firefox' );
- $this->addOption( 'wikiUrl', 'The Mediawiki installation to point to. Default: http://localhost' );
- $this->addOption( 'username', 'The login username for sunning tests. Default: empty' );
- $this->addOption( 'userPassword', 'The login password for running tests. Default: empty' );
- $this->addOption( 'seleniumConfig', 'Location of the selenium config file. Default: empty' );
+ $this->addOption( 'port', 'Port used by selenium server. Default: 4444', false, true );
+ $this->addOption( 'host', 'Host selenium server. Default: $wgServer . $wgScriptPath', false, true );
+ $this->addOption( 'testBrowser', 'The browser used during testing. Default: firefox', false, true );
+ $this->addOption( 'wikiUrl', 'The Mediawiki installation to point to. Default: http://localhost', false, true );
+ $this->addOption( 'username', 'The login username for sunning tests. Default: empty', false, true );
+ $this->addOption( 'userPassword', 'The login password for running tests. Default: empty', false, true );
+ $this->addOption( 'seleniumConfig', 'Location of the selenium config file. Default: empty', false, true );
$this->addOption( 'list-browsers', 'List the available browsers.' );
$this->addOption( 'verbose', 'Be noisier.' );
-
+ $this->addOption( 'startserver', 'Start Selenium Server (on localhost) before the run.' );
+ $this->addOption( 'stopserver', 'Stop Selenium Server (on localhost) after the run.' );
$this->deleteOption( 'dbpass' );
$this->deleteOption( 'dbuser' );
$this->deleteOption( 'globals' );
echo $desc;
}
+ protected function startServer() {
+ if ( $this->seleniumServerExecPath == '' ) {
+ die ( "The selenium server exec path is not set in " .
+ "selenium_settings.ini. Cannot start server \n" .
+ "as requested - terminating RunSeleniumTests\n" );
+ }
+ $this->serverManager = new SeleniumServerManager( 'true',
+ $this->selenium->getPort(),
+ $this->seleniumServerExecPath );
+ switch ( $this->serverManager->start() ) {
+ case 'started':
+ break;
+ case 'failed':
+ die ( "Unable to start the Selenium Server - " .
+ "terminating RunSeleniumTests\n" );
+ case 'running':
+ echo ( "Warning: The Selenium Server is " .
+ "already running\n" );
+ break;
+ }
+
+ return;
+ }
+
+ protected function stopServer() {
+ if ( !isset ( $this->serverManager ) ) {
+ echo ( "Warning: Request to stop Selenium Server, but it was " .
+ "not stared by RunSeleniumTests\n" .
+ "RunSeleniumTests cannot stop a Selenium Server it " .
+ "did not start\n" );
+ } else {
+ switch ( $this->serverManager->stop() ) {
+ case 'stopped':
+ break;
+ case 'failed':
+ echo ( "unable to stop the Selenium Server\n" );
+ }
+ }
+ return;
+ }
+
protected function runTests( $seleniumTestSuites = array() ) {
$result = new PHPUnit_Framework_TestResult;
$result->addListener( new SeleniumTestListener( $this->selenium->getLogger() ) );
$seleniumTestSuites ) );
}
+ // State for starting/stopping the Selenium server has nothing to do with the Selenium
+ // class. Keep this state local to SeleniumTester class. Using getOption() is clumsy, but
+ // the Maintenance class does not have a setOption()
+ if ( isset( $seleniumSettings['startserver'] ) ) $this->getOption( 'startserver', true );
+ if ( isset( $seleniumSettings['stopserver'] ) ) $this->getOption( 'stopserver', true );
+ if ( !isset( $seleniumSettings['seleniumserverexecpath'] ) ) $seleniumSettings['seleniumserverexecpath'] = '';
+ $this->seleniumServerExecPath = $seleniumSettings['seleniumserverexecpath'];
//set reasonable defaults if we did not find the settings
if ( !isset( $seleniumBrowsers ) ) $seleniumBrowsers = array ('firefox' => '*firefox');
if ( !isset( $seleniumSettings['username'] ) ) $seleniumSettings['username'] = '';
if ( !isset( $seleniumSettings['userPassword'] ) ) $seleniumSettings['userPassword'] = '';
if ( !isset( $seleniumSettings['testBrowser'] ) ) $seleniumSettings['testBrowser'] = 'firefox';
-
+
+ // Setup Selenium class
$this->selenium = new Selenium( );
$this->selenium->setAvailableBrowsers( $seleniumBrowsers );
$this->selenium->setUrl( $this->getOption( 'wikiUrl', $seleniumSettings['wikiUrl'] ) );
$this->listBrowsers();
exit(0);
}
+ if ( $this->hasOption( 'startserver' ) ) {
+ $this->startServer();
+ }
$logger = new SeleniumTestConsoleLogger;
$this->selenium->setLogger( $logger );
$this->runTests( $seleniumTestSuites );
+
+ if ( $this->hasOption( 'stopserver' ) ) {
+ $this->stopServer();
+ }
}
}
--- /dev/null
+<?php\r
+/**\r
+ * Selenium server manager\r
+ *\r
+ * @file\r
+ * @ingroup Maintenance\r
+ * Copyright (C) 2010 Dan Nessett <dnessett@yahoo.com>\r
+ * http://citizendium.org/\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\r
+ * http://www.gnu.org/copyleft/gpl.html\r
+ *\r
+ * @addtogroup Maintenance\r
+ */\r
+\r
+class SeleniumServerManager {\r
+ private $SeleniumStartServer = false;\r
+ private $OS = '';\r
+ private $SeleniumServerPid = 'NaN';\r
+ private $SeleniumServerPort = 4444;\r
+ private $SeleniumServerStartTimeout = 10; // 10 secs.\r
+ private $SeleniumServerExecPath;\r
+\r
+ public function __construct( $startServer,\r
+ $serverPort,\r
+ $serverExecPath ) {\r
+ $this->OS = (string) PHP_OS;\r
+ if ( isset( $startServer ) )\r
+ $this->SeleniumStartServer = $startServer;\r
+ if ( isset( $serverPort ) )\r
+ $this->SeleniumServerPort = $serverPort;\r
+ if ( isset( $serverExecPath ) )\r
+ $this->SeleniumServerExecPath = $serverExecPath;\r
+ return;\r
+ }\r
+\r
+ // Getters for certain private attributes. No setters, since they\r
+ // should not change after the manager object is created.\r
+\r
+ public function getSeleniumStartServer() {\r
+ return $this->SeleniumStartServer;\r
+ }\r
+\r
+ public function getSeleniumServerPort() {\r
+ return $this->SeleniumServerPort;\r
+ }\r
+\r
+ public function getSeleniumServerPid() {\r
+ return $this->SeleniumServerPid;\r
+ }\r
+\r
+ // Changing value of SeleniumStartServer allows starting server after\r
+ // creation of the class instance. Only allow setting SeleniumStartServer\r
+ // to true, since after server is started, it is shut down by stop().\r
+\r
+ public function setSeleniumStartServer( $startServer ) {\r
+ if ( $startServer == true ) $this->SeleniumStartServer = true;\r
+ }\r
+\r
+ // return values are: 1) started - server started, 2) failed -\r
+ // server not started, 3) running - instructed to start server, but\r
+ // server already running\r
+\r
+ public function start() {\r
+\r
+ if ( !$this->SeleniumStartServer ) return 'failed';\r
+\r
+ // commented out cases are untested\r
+\r
+ switch ( $this->OS ) {\r
+ case "Linux":\r
+# case' CYGWIN_NT-5.1':\r
+# case 'Darwin':\r
+# case 'FreeBSD':\r
+# case 'HP-UX':\r
+# case 'IRIX64':\r
+# case 'NetBSD':\r
+# case 'OpenBSD':\r
+# case 'SunOS':\r
+# case 'Unix':\r
+ // *nix based OS\r
+ return $this->startServerOnUnix();\r
+ break;\r
+ case "Windows":\r
+ case "WIN32":\r
+ case "WINNT":\r
+ // Windows\r
+ return $this->startServerOnWindows();\r
+ break;\r
+ default:\r
+ // An untested OS\r
+ return 'failed';\r
+ break;\r
+ }\r
+ }\r
+\r
+ public function stop() {\r
+\r
+ // commented out cases are untested\r
+\r
+ switch ( $this->OS ) {\r
+ case "Linux":\r
+# case' CYGWIN_NT-5.1':\r
+# case 'Darwin':\r
+# case 'FreeBSD':\r
+# case 'HP-UX':\r
+# case 'IRIX64':\r
+# case 'NetBSD':\r
+# case 'OpenBSD':\r
+# case 'SunOS':\r
+# case 'Unix':\r
+ // *nix based OS\r
+ return $this->stopServerOnUnix();\r
+ break;\r
+ case "Windows":\r
+ case "WIN32":\r
+ case "WINNT":\r
+ // Windows\r
+ return $this->stopServerOnWindows();\r
+ break;\r
+ default:\r
+ // An untested OS\r
+ return 'failed';\r
+ break;\r
+ }\r
+ }\r
+\r
+ private function startServerOnUnix() {\r
+\r
+ $output = array();\r
+ $user = $_ENV['USER'];\r
+ exec("ps -U " . $user . " w | grep -i selenium-server", $output);\r
+\r
+ // Start server. If there is already a server running,\r
+ // return running.\r
+\r
+ if ( isset( $this->SeleniumServerExecPath ) ) {\r
+ $found = 0;\r
+ foreach ( $output as $string ) {\r
+ $found += preg_match(\r
+ '~^(.*)java(.+)-jar(.+)selenium-server~',\r
+ $string );\r
+ }\r
+ if ( $found == 0 ) {\r
+\r
+ // Didn't find the selenium server. Start it up.\r
+ // First set up comamand line suffix.\r
+ // NB: $! is pid of last job run in background\r
+ // The echo guarentees it is put into $op when\r
+ // the exec command is run.\r
+\r
+ $commandSuffix = ' > /dev/null 2>&1'. ' & echo $!';\r
+ $portText = ' -port ' . $this->SeleniumServerPort;\r
+ $command = "java -jar " . \r
+ $this->SeleniumServerExecPath .\r
+ $portText . $commandSuffix;\r
+ exec($command ,$op); \r
+ $pid = (int)$op[0]; \r
+ if ( $pid != "" )\r
+ $this->SeleniumServerPid = $pid;\r
+ else {\r
+ $this->SeleniumServerPid = 'NaN';\r
+ // Server start failed.\r
+ return 'failed';\r
+ }\r
+ // Wait for the server to startup and listen\r
+ // on its port. Note: this solution kinda\r
+ // stinks, since it uses a wait loop - dnessett\r
+\r
+ for ( $cnt = 1;\r
+ $cnt <= $this->SeleniumServerStartTimeout;\r
+ $cnt++ ) {\r
+ $fp = @fsockopen ( 'localhost',\r
+ $this->SeleniumServerPort,\r
+ &$errno, &$errstr, 0 );\r
+ if ( !$fp ) {\r
+ sleep( 1 );\r
+ continue;\r
+ // Server start succeeded.\r
+ } else {\r
+ fclose ( $fp );\r
+ return 'started';\r
+ }\r
+ }\r
+ echo ( "Starting Selenium server timed out.\n" );\r
+ return 'failed';\r
+ }\r
+ // server already running.\r
+ else return 'running';\r
+\r
+ }\r
+ // No Server execution path defined.\r
+ return 'failed';\r
+ }\r
+\r
+ private function startServerOnWindows() {\r
+ // Unimplemented. \r
+ return 'failed';\r
+ }\r
+\r
+ private function stopServerOnUnix() {\r
+\r
+ if ( !empty( $this->SeleniumServerPid ) &&\r
+ $this->SeleniumServerPid != 'NaN' ) {\r
+ exec( "kill -9 " . $this->SeleniumServerPid );\r
+ return 'stopped';\r
+ }\r
+ else return 'failed';\r
+ }\r
+\r
+ private function stopServerOnWindows() {\r
+ // Unimplemented. \r
+ return 'failed';\r
+\r
+ }\r
+}\r