com phpruntests: phpruntests - integrated taskScheduler into "rtPhpTestRun" - added argument for cmd-line-option 'z' (number of processes) - added
rtExcpetion and a simple exception-hanlder: src/configuration/exceptions/rtEnvironmentException.php src/configuration/exceptions/rtException.php
src/configuration/exceptions/rtMissingArgumentException.php src/configuration/exceptions/rtUnknownIniSettingException.php
src/configuration/exceptions/rtUnkno

From: Date: Fri, 26 Jun 2009 00:07:24 +0000
Subject: com phpruntests: phpruntests - integrated taskScheduler into "rtPhpTestRun" - added argument for cmd-line-option 'z' (number of processes) - added
rtExcpetion and a simple exception-hanlder: src/configuration/exceptions/rtEnvironmentException.php src/configuration/exceptions/rtException.php
src/configuration/exceptions/rtMissingArgumentException.php src/configuration/exceptions/rtUnknownIniSettingException.php
src/configuration/exceptions/rtUnkno
Groups: php.qa 
Request: Send a blank email to php-qa+get-66397@lists.php.net to get a copy of this message
Commit: 7198476ba72ed738444d7eed96d8c3e14126d4c7 Author: Georg Gradwohl <g2@php.net> Fri, 26 Jun 2009 00:07:24 +0000 Parents: 9cfa93ee82941cd7050141cc9db9618623d15c35 Branches: master Link: http://git.php.net/?p=phpruntests.git;a=commitdiff;h=7198476ba72ed738444d7eed96d8c3e14126d4c7 Log: phpruntests - integrated taskScheduler into "rtPhpTestRun" - added argument for cmd-line-option 'z' (number of processes) - added rtExcpetion and a simple exception-hanlder Changed paths: M src/configuration/exceptions/rtEnvironmentException.php A src/configuration/exceptions/rtException.php M src/configuration/exceptions/rtMissingArgumentException.php M src/configuration/exceptions/rtUnknownIniSettingException.php M src/configuration/exceptions/rtUnknownOptionException.php M src/configuration/rtCommandLineOptions.php M src/rtClassMap.php M src/run-tests.php A src/taskScheduler/rtTask.php A src/taskScheduler/rtTaskInterface.php A src/taskScheduler/rtTaskScheduler.php A src/taskScheduler/rtTaskSchedulerFile.php A src/taskScheduler/rtTaskSchedulerMsgQ.php A src/taskScheduler/rtTaskTestGroup.php M src/testrun/rtPhpTestRun.php A tests/rtTaskSchedulerTest.php

diff --git a/src/configuration/exceptions/rtEnvironmentException.php b/src/configuration/exceptions/rtEnvironmentException.php index c345081..fb6ef80 100644 --- a/src/configuration/exceptions/rtEnvironmentException.php +++ b/src/configuration/exceptions/rtEnvironmentException.php @@ -1,6 +1,6 @@ <?php -class rtEnvironmentException extends RunTimeException +class rtEnvironmentException extends rtException { } ?> diff --git a/src/configuration/exceptions/rtException.php b/src/configuration/exceptions/rtException.php new file mode 100755 index 0000000..26c34a5 --- /dev/null +++ b/src/configuration/exceptions/rtException.php @@ -0,0 +1,18 @@ +<?php + +class rtException extends RuntimeException +{ + public function __toString() { + + $r = "\n--------------------------------------------------------------------------------\n"; + $r .= "EXCEPTION\n"; + $r .= $this->getMessage()." (CODE ".$this->getCode().")\n"; + $r .= $this->getFile().":".$this->getLine()."\n\n"; + $r .= $this->getTraceAsString(); + $r .= "\n--------------------------------------------------------------------------------\n"; + + return $r; + } +} + +?> \ No newline at end of file diff --git a/src/configuration/exceptions/rtMissingArgumentException.php b/src/configuration/exceptions/rtMissingArgumentException.php index 5e90d36..2ff536f 100644 --- a/src/configuration/exceptions/rtMissingArgumentException.php +++ b/src/configuration/exceptions/rtMissingArgumentException.php @@ -1,6 +1,6 @@ <?php -class rtMissingArgumentException extends RuntimeException +class rtMissingArgumentException extends rtException { } ?> diff --git a/src/configuration/exceptions/rtUnknownIniSettingException.php b/src/configuration/exceptions/rtUnknownIniSettingException.php index f1ebe2c..4d9e6be 100644 --- a/src/configuration/exceptions/rtUnknownIniSettingException.php +++ b/src/configuration/exceptions/rtUnknownIniSettingException.php @@ -1,6 +1,6 @@ <?php -class rtUnknownIniSettingException extends RuntimeException +class rtUnknownIniSettingException extends rtException { } ?> diff --git a/src/configuration/exceptions/rtUnknownOptionException.php b/src/configuration/exceptions/rtUnknownOptionException.php index 4bb2c36..e6517fe 100644 --- a/src/configuration/exceptions/rtUnknownOptionException.php +++ b/src/configuration/exceptions/rtUnknownOptionException.php @@ -1,6 +1,6 @@ <?php -class rtUnknownOptionException extends RuntimeException +class rtUnknownOptionException extends rtException { } ?> diff --git a/src/configuration/rtCommandLineOptions.php b/src/configuration/rtCommandLineOptions.php index 00351aa..1be3f61 100644 --- a/src/configuration/rtCommandLineOptions.php +++ b/src/configuration/rtCommandLineOptions.php @@ -24,8 +24,7 @@ class rtCommandLineOptions 'q', 'x', 'v', - 'h', - 'z', //parallel - run out of obvious letters + 'h', ); /** @@ -40,6 +39,7 @@ class rtCommandLineOptions 'd', 'p', 's', + 'z', //parallel - run out of obvious letters ); /** diff --git a/src/rtClassMap.php b/src/rtClassMap.php index 07526b5..a88ab3a 100644 --- a/src/rtClassMap.php +++ b/src/rtClassMap.php @@ -8,6 +8,7 @@ */ $rtClassMap = array( 'rtEnvironmentException' => 'configuration/exceptions/rtEnvironmentException.php', + 'rtException' => 'configuration/exceptions/rtException.php', 'rtMissingArgumentException' => 'configuration/exceptions/rtMissingArgumentException.php', 'rtUnknownIniSettingException' => 'configuration/exceptions/rtUnknownIniSettingException.php', 'rtUnknownOptionException' => 'configuration/exceptions/rtUnknownOptionException.php', @@ -48,6 +49,12 @@ $rtClassMap = array( 'rtPhptFilterIterator' => 'rtPhptFilterIterator.php', 'rtText' => 'rtText.php', 'rtUtil' => 'rtUtil.php', + 'rtTask' => 'taskScheduler/rtTask.php', + 'rtTaskInterface' => 'taskScheduler/rtTaskInterface.php', + 'rtTaskScheduler' => 'taskScheduler/rtTaskScheduler.php', + 'rtTaskSchedulerFile' => 'taskScheduler/rtTaskSchedulerFile.php', + 'rtTaskSchedulerMsgQ' => 'taskScheduler/rtTaskSchedulerMsgQ.php', + 'rtTaskTestGroup' => 'taskScheduler/rtTaskTestGroup.php', 'rtPhpRunnerException' => 'testcase/exceptions/rtPhpRunnerException.php', 'rtTestOutputWriterList' => 'testcase/output/rtTestOutputWriterList.php', 'rtTestOutputWriterXML' => 'testcase/output/rtTestOutputWriterXML.php', diff --git a/src/run-tests.php b/src/run-tests.php index 963963f..ffefaa8 100644 --- a/src/run-tests.php +++ b/src/run-tests.php @@ -4,6 +4,22 @@ */ + +/** + * rtExceptionHandler + * + * @param Exception $e + * @return unknown_type + */ +function rtExceptionHandler(Exception $e) { + + print $e; +} + +set_exception_handler('rtExceptionHandler'); + + + /* * check the version of the running php-executable and * ensure that is 5.3 or higher @@ -14,7 +30,13 @@ if (version_compare(PHP_VERSION, '5.3.0RC1', '<')) { require_once dirname(__FILE__) . '/rtAutoload.php'; +$s = microtime(true); + $phpTestRun = new rtPhpTestRun($argv); $phpTestRun->run(); +$e = microtime(true); + +print "\n".($e-$s)." sec\n\n"; + ?> diff --git a/src/taskScheduler/rtTask.php b/src/taskScheduler/rtTask.php new file mode 100644 index 0000000..4af1902 --- /dev/null +++ b/src/taskScheduler/rtTask.php @@ -0,0 +1,48 @@ +<?php + +abstract class rtTask +{ + const NOEX = 0; + const PASS = 1; + const FAIL = -1; + + private $state = self::NOEX; + private $index = NULL; + private $message = NULL; + + + public function setState($state) + { + $this->state = $state; + } + + public function getState() + { + return $this->state; + } + + + public function setMessage($msg) + { + $this->message = $msg; + } + + public function getMessage() + { + return $this->message; + } + + + public function setIndex($index) + { + $this->index = $index; + } + + public function getIndex() + { + return $this->index; + } + +} + +?> \ No newline at end of file diff --git a/src/taskScheduler/rtTaskInterface.php b/src/taskScheduler/rtTaskInterface.php new file mode 100644 index 0000000..cbfe979 --- /dev/null +++ b/src/taskScheduler/rtTaskInterface.php @@ -0,0 +1,8 @@ +<?php + +interface rtTaskInterface +{ + public function run(); +} + +?> \ No newline at end of file diff --git a/src/taskScheduler/rtTaskScheduler.php b/src/taskScheduler/rtTaskScheduler.php new file mode 100644 index 0000000..ab6666f --- /dev/null +++ b/src/taskScheduler/rtTaskScheduler.php @@ -0,0 +1,233 @@ +<?php +/** + * rtTaskScheduler + * + * Main class of the TaskScheduler + * + * @category Testing + * @package RUNTESTS + * @author Zoe Slattery <zoe@php.net> + * @author Stefan Priebsch <spriebsch@php.net> + * @author Georg Gradwohl <g2@php.net> + * @copyright 2009 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * + */ +class rtTaskScheduler +{ + protected $taskList = array(); // the list of the tasks to be executed + protected $processCount = 0; // the number of processes + + protected $time = 0; // the needed time + protected $countPass = 0; // counts the passed tasks + protected $countFail = 0; // counts the failed tasks + + protected $memStore = array(); // stores the mem-usage after an incomming task + + + /** + * the constructor + * + * @param array $taskList (optional) + * @param int $processCount (optional) + */ + public function __construct(array $taskList=NULL, $processCount=NULL) + { + if (is_array($taskList)) { + $this->setTaskList($taskList); + } + + $this->setProcessCount($processCount); + } + + + public static function getInstance(array $taskList=NULL, $processCount=NUL, $useMsgQ=false) + { + // TODO: remove this statement and check this by runtime-config + if (extension_loaded('pcntl')) { + + if ($useMsgQ === true) { + + return new rtTaskSchedulerMsgQ($taskList, $processCount); + } + + return new rtTaskSchedulerFile($taskList, $processCount); + } + + return new rtTaskScheduler($taskList, $processCount); + } + + + /** + * sets the task-list which has to be an array of task-objects. + * + * @param array $taskList + */ + public function setTaskList(array $taskList) + { + $this->taskList = $taskList; + } + + + /** + * @return array $taskList + */ + public function getTaskList() + { + return $this->taskList; + } + + + /** + * sets the number of child-processes. + * in the case of using a multidimensional task-list this parameter is + * ignored and set to the number of task-groups. + * + * @param int $count + */ + public function setProcessCount($processCount) + { + if (is_numeric($processCount) && $processCount >= 0) { + $this->processCount = $processCount; + } + } + + + /** + * executes the tasks in a simple loop + * + * @return void + */ + public function run() + { + $s = microtime(true); + + for ($i=0; $i<sizeof($this->taskList); $i++) { + + $task = $this->taskList[$i]; + + if ($task->run() === true) { + $task->setState(task::PASS); + $this->countPass++; + } else { + $task->setState(task::FAIL); + $this->countFail++; + } + + $this->memStore[] = memory_get_usage(true); + + print "."; + flush(); + + $this->taskList[$i] = $task; + } + + $error = microtime(true); + + $this->time = round($error-$s,5); + + return; + } + + + /** + * prints the statistic + * + * @return void + */ + public function printStatistic() + { + print "\n----------------------------------------\n"; + + /* + if (is_array($this->taskList[0])) { + + $count = 0; + foreach ($this->taskList as $list) { + $count += sizeof($list); + } + + print "Groups:\t\t".sizeof($this->taskList)."\n"; + print "Tasks:\t\t".$count."\n"; + + } else { + + $count = sizeof($this->taskList); + print "Tasks:\t\t".$count."\n"; + } + + print "PASSED:\t\t".$this->countPass." (".@round($this->countPass/$count*100,2)."%)\n"; + print "FAILED:\t\t".$this->countFail." (".@round($this->countFail/$count*100,2)."%)\n"; + */ + + $count = sizeof($this->taskList); + print "Test-Groups:\t".$count."\n"; + print "Processes:\t".$this->processCount."\n"; + print "Seconds:\t".$this->time."\n"; + + if ($this->processCount > 0 && sizeof($this->memStore) > 0) { + print "AVG sec/task:\t".@round($this->time/$this->processCount,5)."\n"; + print "Memory-MAX:\t".number_format(@max($this->memStore))."\n"; + print "Memory-MIN:\t".number_format(@min($this->memStore))."\n"; + $avg = array_sum($this->memStore)/sizeof($this->memStore); + print "Memory-AVG:\t".number_format($avg)."\n"; + } + + print "----------------------------------------\n"; + flush(); + } + + + /** + * prints a overview of the faild tasks + * + * @return void + */ + public function printFailedTasks() + { + if ($this->countFail > 0) { + + print "FAILED TASKS"; + print "\n----------------------------------------\n"; + + for ($i=0; $i<sizeof($this->taskList); $i++) { + + $task = $this->taskList[$i]; + + if ($task->getState() == task::FAIL) { + print "Task $i: ".$task->getMessage()."\n"; + } + } + + print "----------------------------------------\n"; + flush(); + } + } + + + + public function printMemStatistic($int=10) + { + print "MEMORY-USAGE"; + print "\n----------------------------------------\n"; + + $int = ceil(sizeof($this->memStore)/$int); + + $title = "TASK:\t"; + $body = "kB:\t"; + + for ($i=0; $i<sizeof($this->memStore); $i+=$int) { + + $title .= "$i\t"; + $body .= round($this->memStore[$i]/1000)."\t"; + } + + print $title."\n".$body; + + print "\n----------------------------------------\n"; + flush(); + } + +} + +?> \ No newline at end of file diff --git a/src/taskScheduler/rtTaskSchedulerFile.php b/src/taskScheduler/rtTaskSchedulerFile.php new file mode 100644 index 0000000..05a790e --- /dev/null +++ b/src/taskScheduler/rtTaskSchedulerFile.php @@ -0,0 +1,261 @@ +<?php +/** + * rtTaskSchedulerFile + * + * extention of TaskScheduler, implements a ipc via temporary files + * + * @category Testing + * @package RUNTESTS + * @author Zoe Slattery <zoe@php.net> + * @author Stefan Priebsch <spriebsch@php.net> + * @author Georg Gradwohl <g2@php.net> + * @copyright 2009 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * + */ +class rtTaskSchedulerFile extends rtTaskScheduler +{ + const TMP_FILE = 'taskFile'; + + private $inputQueue = NULL; // the input-queue (only used by the sender) + private $pidStore = array(); // stores the pids of all child-processes + private $groupTasks = false; // are the tasks stored in groups? + + private $tmpTaskList = array(); + + /** + * the constructor + * + * @param array $taskList (optional) + * @param int $processCount (optional) + */ + public function __construct(array $taskList=NULL, $processCount=NULL) + { + if (is_array($taskList)) { + $this->setTaskList($taskList); + } + + $this->setProcessCount($processCount); + } + + + /** + * sets the task-list which has to be an array of task-objects. + * it's also possible to use a multidimensional array. in this case the + * tasks are distributed to the child-processes exactly in the way as they + * are grouped in the list. the first-level index strictly has to be + * numeric and continuous starting with zero. + * + * @param array $taskList + * @Overrides + */ + public function setTaskList(array $taskList) + { + if (is_array($taskList[0])) { + $this->groupTasks = true; + $this->processCount = sizeof($taskList); + } + + $this->taskList = $taskList; + } + + + + /** + * sets the number of child-processes. + * in the case of using a multidimensional task-list this parameter is + * ignored and set to the number of task-groups. + * + * @param int $count + * @Overrides + */ + public function setProcessCount($processCount) + { + if ($this->groupTasks !== true && is_numeric($processCount) && $processCount >= 0) { + $this->processCount = $processCount; + } + } + + + + /** + * starts the sender, the receiver and forks the defined + * number of child-processes. + * + * @return void + * @Overrides + */ + public function run() + { + if ($this->processCount == 0) { + return parent::run(); + } + + $startTime = microtime(true); + + // trim the processCount if nesecarry + if ($this->processCount > sizeof($this->taskList)) { + $this->processCount = sizeof($this->taskList); + } + + $this->createTaskFiles(); + + + // fork the child-processes + for ($i=0; $i<$this->processCount; $i++) { + + $this->pidStore[$i] = pcntl_fork(); + + switch ($this->pidStore[$i]) { + + case -1: // failure + die("could not fork"); + break; + + case 0: // child + $this->child($i); + break; + + default: // parent + break; + } + } + + // wait until all child-processes are terminated + for ($i=0; $i<$this->processCount; $i++) { + pcntl_waitpid($this->pidStore[$i], $status); + } + + // ensure that the tmp-files are completly written + sleep(1); + + // collecting the results + $this->receiver(); + + $endTime = microtime(true); + $this->time = round($endTime-$startTime,5); + + return; + } + + + + /** + * creates a temporary file for each child which stores the allocated + * array-indices. + * + */ + private function createTaskFiles() { + + $taskStr = array(); + + if ($this->groupTasks == true) { + + $c = 0; + + foreach ($this->taskList as $key => $list) { + + for ($i=0; $i<sizeof($list); $i++) { + + $taskStr[$key] .= $i.';'; + } + } + + } else { + + for ($i=0; $i<sizeof($this->taskList); $i++) { + + $taskStr[$i%$this->processCount] .= $i.';'; + } + } + + for ($i=0; $i<$this->processCount; $i++) { + + file_put_contents(self::TMP_FILE.$i, $taskStr[$i]); + } + } + + + + /** + * @return void + */ + private function receiver() + { + for ($cid=0; $cid<$this->processCount; $cid++) { + + $response = file_get_contents(self::TMP_FILE.$cid); + $response = explode("\n", $response); + array_pop($response); + + foreach ($response as $task) { + + $task = unserialize($task); + + if ($task->getState() == rtTask::PASS) { + $this->countPass++; + } else { + $this->countFail++; + } + + $index = $task->getIndex(); + + if ($this->groupTasks == true) { + + $this->taskList[$cid][$index] = $task; + + } else { + + $this->taskList[$index] = $task; + } + } + + unlink(self::TMP_FILE.$cid); + } + + return; + } + + + + /** + * @param int $cid the child-id + * @return void + */ + private function child($cid) + { + $indexList = file_get_contents(self::TMP_FILE.$cid); + $indexList = explode(';', $indexList); + array_pop($indexList); + + $response = ''; + + foreach ($indexList as $index) { + + if ($this->groupTasks == true) { + $task = $this->taskList[$cid][$index]; + } else { + $task = $this->taskList[$index]; + } + + if ($task->run() === true) { + $task->setState(rtTask::PASS); + + } else { + $task->setState(rtTask::FAIL); + } + + $task->setIndex($index); + + $response .= serialize($task)."\n"; + } + + file_put_contents(self::TMP_FILE.$cid, $response); + + exit(0); + } + + +} + +?> \ No newline at end of file diff --git a/src/taskScheduler/rtTaskSchedulerMsgQ.php b/src/taskScheduler/rtTaskSchedulerMsgQ.php new file mode 100644 index 0000000..14dd2b4 --- /dev/null +++ b/src/taskScheduler/rtTaskSchedulerMsgQ.php @@ -0,0 +1,348 @@ +<?php +/** + * rtTaskSchedulerMsgQ + * + * extention of TaskScheduler, implements a ipc via message-queues + * + * @category Testing + * @package RUNTESTS + * @author Zoe Slattery <zoe@php.net> + * @author Stefan Priebsch <spriebsch@php.net> + * @author Georg Gradwohl <g2@php.net> + * @copyright 2009 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * + */ +declare(ticks=true); + +class rtTaskSchedulerMsgQ extends rtTaskScheduler +{ + const MSG_QUEUE_KEY = 1234; // id of the message-queue + const MSG_QUEUE_SIZE = 1024; // max-size of a single message + const KILL_CHILD = 'killBill'; // kill-signal to terminate a child + + private $inputQueue = NULL; // the input-queue (only used by the sender) + private $pidStore = array(); // stores the pids of all child-processes + private $groupTasks = false; // are the tasks stored in groups? + + + + + + /** + * sets the task-list which has to be an array of task-objects. + * it's also possible to use a multidimensional array. in this case the + * tasks are distributed to the child-processes exactly in the way as they + * are grouped in the list. the first-level index strictly has to be + * numeric and continuous starting with zero. + * + * @param array $taskList + * @Overrides + */ + public function setTaskList(array $taskList) + { + if (is_array($taskList[0])) { + $this->groupTasks = true; + $this->processCount = sizeof($taskList); + } + + $this->taskList = $taskList; + } + + + + /** + * sets the number of child-processes. + * in the case of using a multidimensional task-list this parameter is + * ignored and set to the number of task-groups. + * + * @param int $count + * @Overrides + */ + public function setProcessCount($processCount) + { + if ($this->groupTasks !== true && is_numeric($processCount) && $processCount >= 0) { + $this->processCount = $processCount; + } + } + + + /** + * removes the used message-queues. + */ + private static function cleanUp() + { + @msg_remove_queue(msg_get_queue(self::MSG_QUEUE_KEY)); + @msg_remove_queue(msg_get_queue(self::MSG_QUEUE_KEY+1)); + } + + + /** + * the signal-handler is called by the interrupt- or quit-signal and calls + * the cleanUp-method. + * + * @param int $signal + */ + public static function signalHandler($signal) + { + switch($signal) { + + case SIGINT: + case SIGQUIT: + self::cleanUp(); + die("\n"); + break; + + default: + break; + } + } + + + /** + * starts the sender, the receiver and forks the defined + * number of child-processes. + * + * @return void + * @Overrides + */ + public function run() + { + if ($this->processCount == 0) { + return parent::run(); + } + + $startTime = microtime(true); + + // register signal-handler + pcntl_signal(SIGINT, "taskSchedulerMsgQ::signalHandler"); + pcntl_signal(SIGQUIT, "taskSchedulerMsgQ::signalHandler"); + + // trim the processCount if nesecarry + if ($this->processCount > sizeof($this->taskList)) { + $this->processCount = sizeof($this->taskList); + } + + // fork the child-processes + for ($i=0; $i<=$this->processCount; $i++) { + + $this->pidStore[$i] = pcntl_fork(); + + switch ($this->pidStore[$i]) { + + case -1: // failure + die("could not fork"); + break; + + case 0: // child + if ($i==0) { + $this->sender(); + } else { + $cid = ($this->groupTasks == true) ? $i : NULL; + $this->child($cid); + } + break; + + default: // parent + break; + } + } + + // start the receiver + $this->receiver(); + + // wait until all child-processes are terminated + for ($i=0; $i<=$this->processCount; $i++) { + pcntl_waitpid($this->pidStore[$i], $status); + } + + $endTime = microtime(true); + $this->time = round($endTime-$startTime,5); + + // remove the msg-queue + self::cleanUp(); + + return; + } + + + /** + * the receiver is listening to the result-queue and stores the incomming + * tasks back to the task-list. + * when finished it sends the kill-signal to all children and terminates + * itself. + * + * @return void + */ + private function receiver() + { + $resultQueue = msg_get_queue(self::MSG_QUEUE_KEY+1); + + $task = ''; + $type = 1; + + if ($this->groupTasks == true) { + $limit = 0; + foreach ($this->taskList as $list) { + $limit += sizeof($list); + } + } else { + $limit = sizeof($this->taskList); + } + + for ($i=0; $i<$limit; $i++) { + + $this->memStore[] = memory_get_usage(true); + + if (msg_receive($resultQueue, 0, $type, self::MSG_QUEUE_SIZE, $task, true, NULL, $error)) { + + // check state + if ($task->getState() == rtTask::PASS) { + $this->countPass++; + } else { + $this->countFail++; + } + + // store result + $index = $task->getIndex(); + + if ($this->groupTasks == true) { + $this->taskList[$type-2][$index] = $task; + } else { + $this->taskList[$index] = $task; + } + + } else { + + print "RECEIVER ERROR $error\n"; + } + } + + $inputQueue = msg_get_queue(self::MSG_QUEUE_KEY); + + for ($i=1; $i<=$this->processCount; $i++) { + + if (msg_send($inputQueue, $i, self::KILL_CHILD, true, true, $error)) { + + } else { + + print "RECEIVER ERROR $error\n"; + } + } + + return; + } + + + /** + * the sender is passes through the task-list and distributes the single + * tasks to the child-processes using the input-queue. + * when finished it terminates itself. + * + * @return void + */ + private function sender() + { + $this->inputQueue = msg_get_queue(self::MSG_QUEUE_KEY); + + for ($i=0; $i<sizeof($this->taskList); $i++) { + + if ($this->groupTasks == true) { + + for ($j=0; $j<sizeof($this->taskList[$i]); $j++) { + + $this->sendTask($this->taskList[$i][$j], $j, $i+1); + } + + } else { + + $this->sendTask($this->taskList[$i], $i); + } + } + + exit(0); + } + + + /** + * helper-class of sender. + * sends a task to a child-process using the input-queue. + * + * @param task $task the task to send + * @param int $index the task's index in the taskList + * @param int $type the message-type (default=1) + * @return void + */ + private function sendTask(task $task, $index, $type=1) + { + $task->setIndex($index); + + if (msg_send($this->inputQueue, $type, $task, true, true, $error)) { + + } else { + + print "SENDER ERROR $error\n"; + } + + return; + } + + + /** + * the child is listening to the input-queue and executes the incomming + * tasks. afterwards it setts the task-state and sends it back to the + * receiver via the result-queue. + * after receiving the kill-signal from the receiver it terminates itself. + * + * @param int $cid the child-id (default=NULL) + * @return void + */ + private function child($cid=NULL) + { + if (is_null($cid)) { + $cid = 0; + } + + $inputQueue = msg_get_queue(self::MSG_QUEUE_KEY); + $resultQueue = msg_get_queue(self::MSG_QUEUE_KEY+1); + + $type = 1; + + while (true) { + + if (msg_receive($inputQueue, $cid, $type, self::MSG_QUEUE_SIZE, $task, true, NULL, $error)) { + + if ($task == self::KILL_CHILD) + break; + + $index = $task->getIndex(); + + if ($task->run() === true) { + $task->setState(rtTask::PASS); + } else { + $task->setState(rtTask::FAIL); + } + + print "."; + flush(); + + if (msg_send($resultQueue, $cid+1, $task, true, true, $error)) { + + } else { + + print "CHILD ERROR $error\n"; + } + + } else { + + print "CHILD ERROR $error\n"; + } + } + + exit(0); + } + + +} + +?> \ No newline at end of file diff --git a/src/taskScheduler/rtTaskTestGroup.php b/src/taskScheduler/rtTaskTestGroup.php new file mode 100644 index 0000000..0952e2c --- /dev/null +++ b/src/taskScheduler/rtTaskTestGroup.php @@ -0,0 +1,42 @@ +<?php +/** + * rtTaskTestGroup + * + * @category Testing + * @package RUNTESTS + * @author Zoe Slattery <zoe@php.net> + * @author Stefan Priebsch <spriebsch@php.net> + * @author Georg Gradwohl <g2@php.net> + * @copyright 2009 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * + */ +class rtTaskTestGroup extends rtTask implements rtTaskInterface +{ + private $runConfiguration; + private $subDirectory; + + + public function __construct($runConfiguration, $subDirectory) + { + $this->runConfiguration = $runConfiguration; + $this->subDirectory = $subDirectory; + } + + public function run() + { + $testGroup = new rtPhpTestGroup($this->runConfiguration, $this->subDirectory); + $testGroup->runGroup($this->runConfiguration); + // $testGroup->writeGroup(); + return true; + } + + public function getDir() + { + return $this->subDirectory; + } + +} + + +?> \ No newline at end of file diff --git a/src/testrun/rtPhpTestRun.php b/src/testrun/rtPhpTestRun.php index 0937dd2..689d1f6 100644 --- a/src/testrun/rtPhpTestRun.php +++ b/src/testrun/rtPhpTestRun.php @@ -8,6 +8,7 @@ * @package RUNTESTS * @author Zoe Slattery <zoe@php.net> * @author Stefan Priebsch <spriebsch@php.net> + * @author Georg Gradwohl <g2@php.net> * @copyright 2009 The PHP Group * @license http://www.php.net/license/3_01.txt PHP License 3.01 * @@ -39,16 +40,43 @@ class rtPhpTestRun if ($runConfiguration->getSetting('TestDirectories') != null) { foreach ($runConfiguration->getSetting('TestDirectories') as $testDirectory) { - - //make list of subdirectories which contain tests, includes the top level directory - $subDirectories = rtUtil::getDirectoryList($testDirectory); - - //Run tests in each subdirectory in sequence - foreach ($subDirectories as $subDirectory) { - $testGroup = new rtPhpTestGroup($runConfiguration, $subDirectory); - $testGroup->runGroup($runConfiguration); - $testGroup->writeGroup(); - } + + // make list of subdirectories which contain tests, includes the top level directory + $subDirectories = rtUtil::parseDir($testDirectory); + + // check for the cmd-line-option 'z' which defines parellel-execution + if ($runConfiguration->hasCommandLineOption('z')) { + + $processCount = $runConfiguration->getCommandLineOption('z'); + + if (!is_numeric($processCount) || $processCount <= 0) { + $processCount = sizeof($subDirectories); + } + + // create the task-list + $taskList = array(); + foreach ($subDirectories as $subDirectory) { + $taskList[] = new rtTaskTestGroup(&$runConfiguration, &$subDirectory); + } + + + // start the task-scheduler for multi-processing + $scheduler = rtTaskScheduler::getInstance(); + $scheduler->setTaskList($taskList); + $scheduler->setProcessCount($processCount); + $scheduler->run(); + $scheduler->printStatistic(); + + } else { + + //Run tests in each subdirectory in sequence + foreach ($subDirectories as $subDirectory) { + $testGroup = new rtPhpTestGroup($runConfiguration, $subDirectory); + $testGroup->runGroup($runConfiguration); + // $testGroup->writeGroup(); + } + + } } //*have a directory or list of directories to test. diff --git a/tests/rtTaskSchedulerTest.php b/tests/rtTaskSchedulerTest.php new file mode 100644 index 0000000..cb0b47d --- /dev/null +++ b/tests/rtTaskSchedulerTest.php @@ -0,0 +1,65 @@ +<?php + +require_once 'PHPUnit/Framework.php'; +require_once dirname(__FILE__) . '/../src/rtAutoload.php'; + +class rtTaskSchedulerTest extends PHPUnit_Framework_TestCase +{ + public function testResult() + { + // create 10 tasks with random numbers + $taskList = array(); + $expected = array(); + + for ($i=0; $i<10; $i++) { + $n = rand(0,9); + $expected[$i] = $n+1; + $taskList[$i] = new rtTaskIncTest($n); + } + + // run the task-scheduler + $scheduler = rtTaskScheduler::getInstance(); + $scheduler->setTaskList($taskList); + $scheduler->setProcessCount(3); + $scheduler->run(); + + // get the results from the manupilated task-list + $results = array(); + + foreach ($scheduler->getTaskList() as $task) { + $results[] = $task->getNumber(); + } + + $this->assertEquals($expected, $results); + } +} + + +/** + * rtTaskIncTest + * + * nested helper-class for rtTaskSchedulerTest + */ +class rtTaskIncTest extends rtTask implements rtTaskInterface +{ + private $num = null; + + public function __construct($num) + { + $this->num = $num; + } + + public function run() + { + $this->num++; + return true; + } + + public function getNumber() + { + return $this->num; + } +} + + +?>
« previous php.qa (#66397) next »