* start / stop selenium server on Linux
authorMarkus Glaser <mglaser@users.mediawiki.org>
Thu, 14 Oct 2010 00:06:24 +0000 (00:06 +0000)
committerMarkus Glaser <mglaser@users.mediawiki.org>
Thu, 14 Oct 2010 00:06:24 +0000 (00:06 +0000)
* credits to Dan Nessett

maintenance/tests/RunSeleniumTests.php
maintenance/tests/selenium/Selenium.php
maintenance/tests/selenium/SeleniumConfig.php
maintenance/tests/selenium/SeleniumServerManager.php [new file with mode: 0644]
maintenance/tests/selenium/selenium_settings.ini.php52.sample
maintenance/tests/selenium/selenium_settings.ini.sample

index 2208ff8..4472751 100644 (file)
@@ -29,24 +29,28 @@ define( 'SELENIUMTEST', true );
 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' );
@@ -63,6 +67,47 @@ class SeleniumTester extends Maintenance {
                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() ) );
@@ -108,6 +153,13 @@ class SeleniumTester extends Maintenance {
                                $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');
@@ -117,7 +169,8 @@ class SeleniumTester extends Maintenance {
                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'] ) );
@@ -132,11 +185,18 @@ class SeleniumTester extends Maintenance {
                        $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();
+               }
        }
 }
 
index 925d335..e41e673 100644 (file)
@@ -112,6 +112,10 @@ class Selenium {
                $this->port = $port;
        }
 
+       public function getPort() {
+               return $this->port;
+       }
+
        public function setUser( $user ) {
                $this->user = $user;
        }
index 8847cde..757bc09 100644 (file)
@@ -44,7 +44,11 @@ class SeleniumConfig {
                        $seleniumSettings['wikiUrl'] = $configArray['SeleniumSettings']['wikiUrl'];
                        $seleniumSettings['username'] = $configArray['SeleniumSettings']['username'];
                        $seleniumSettings['userPassword'] = $configArray['SeleniumSettings']['userPassword'];
-                       $seleniumSettings['testBrowser'] = $configArray['SeleniumSettings']['testBrowser'];     
+                       $seleniumSettings['testBrowser'] = $configArray['SeleniumSettings']['testBrowser'];
+                       $seleniumSettings['startserver'] = $configArray['SeleniumSettings']['startserver'];
+                       $seleniumSettings['stopserver'] = $configArray['SeleniumSettings']['stopserver'];
+                       $seleniumSettings['seleniumserverexecpath'] = $configArray['SeleniumSettings']['seleniumserverexecpath'];
+
                        wfRestoreWarnings();
                }
                if ( array_key_exists( 'SeleniumTests', $configArray)  ) {
diff --git a/maintenance/tests/selenium/SeleniumServerManager.php b/maintenance/tests/selenium/SeleniumServerManager.php
new file mode 100644 (file)
index 0000000..e3daaff
--- /dev/null
@@ -0,0 +1,228 @@
+<?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
index aa09240..7b617ea 100644 (file)
@@ -12,6 +12,8 @@ wikiUrl                       = "http://localhost/mediawiki/latest_trunk/trunk/phase3"
 username               = "Wikiadmin"
 userPassword   = "Wikiadminpw"
 testBrowser            = "firefox"
+startserver            =
+stopserver             =
 
 [testSuite]
 
index c27f53c..3d743d8 100644 (file)
@@ -9,6 +9,8 @@ wikiUrl                         = "http://localhost/deployment"
 username                       = "wikiuser"
 userPassword           = "wikipass"
 testBrowser            = "firefox"
+startserver                    =
+stopserver                     =
 
 [SeleniumTests]