com phpruntests: Redirect tests implementaion: phpt-tests/redir ected/tests/sample_test.phpt phpt-tests/sample_redirect .phpt
phpt-tests/sample_skipif_ini.phpt src/rtUtil .php src/taskScheduler/rtTaskScheduler.php src/taskSche duler/rtTaskTestGroup.php
src/testcase/rtPhpTest.php sr c/testcase/rtTestConfiguration.php src/testcase/rtTestOutpu tWriter.php src/testcase/rtTestResults.php src/testcase
/sections/executablesections/rtSkipIfSection.php src/testc
| From: | Zoe Slattery | Date: | Wed, 03 Oct 2012 20:32:07 +0000 |
| Subject: | com phpruntests: Redirect tests implementaion: phpt-tests/redir ected/tests/sample_test.phpt phpt-tests/sample_redirect .phpt phpt-tests/sample_skipif_ini.phpt src/rtUtil .php src/taskScheduler/rtTaskScheduler.php src/taskSche duler/rtTaskTestGroup.php src/testcase/rtPhpTest.php sr c/testcase/rtTestConfiguration.php src/testcase/rtTestOutpu tWriter.php src/testcase/rtTestResults.php src/testcase /sections/executablesections/rtSkipIfSection.php src/testc |
||
| Groups: | php.qa | ||
| Request: | Send a blank email to php-qa+get-66738@lists.php.net to get a copy of this message | ||
Commit: 2a000e34b920604b6633d5a96950d30fdd9285d0
Author: zoe slattery <zoe@php.net> Wed, 3 Oct 2012 21:32:07 +0100
Parents: ca7438d6799e51f60e8c56ec93c5a39562f11f71
Branches: master
Link: http://git.php.net/?p=phpruntests.git;a=commitdiff;h=2a000e34b920604b6633d5a96950d30fdd9285d0
Log:
Redirect tests implementaion
Changed paths:
A phpt-tests/redirected/tests/sample_test.phpt
M phpt-tests/sample_redirect.phpt
A phpt-tests/sample_skipif_ini.phpt
M src/rtUtil.php
M src/taskScheduler/rtTaskScheduler.php
M src/taskScheduler/rtTaskTestGroup.php
M src/testcase/rtPhpTest.php
M src/testcase/rtTestConfiguration.php
M src/testcase/rtTestOutputWriter.php
M src/testcase/rtTestResults.php
M src/testcase/sections/executablesections/rtSkipIfSection.php
M src/testcase/sections/informationsections/rtTestHeaderSection.php
M src/testgroup/rtGroupConfiguration.php
M src/testgroup/rtPhpTestGroup.php
M src/testrun/rtPhpTestRun.php
A tests/rtGroupConfigurationTest.php
M tests/rtPhpTestGroupTest.php
diff --git a/phpt-tests/redirected/tests/sample_test.phpt b/phpt-tests/redirected/tests/sample_test.phpt new file mode 100644 index 0000000..06501aa --- /dev/null +++ b/phpt-tests/redirected/tests/sample_test.phpt @@ -0,0 +1,8 @@ +--TEST-- +Try a sample test +--FILE-- +<?php + echo "Hello world\n"; +?> +--EXPECT-- +Hello world diff --git a/phpt-tests/sample_redirect.phpt b/phpt-tests/sample_redirect.phpt index 778d42b..073620c 100644 --- a/phpt-tests/sample_redirect.phpt +++ b/phpt-tests/sample_redirect.phpt @@ -8,6 +8,6 @@ return array( 'ENV' => array( 'PDOTEST_DSN' => 'sqlite::memory:' ), - 'TESTS' => 'ext/pdo/tests' + 'TESTS' => 'phpt-tests/redirected/tests' ); diff --git a/phpt-tests/sample_skipif_ini.phpt b/phpt-tests/sample_skipif_ini.phpt new file mode 100644 index 0000000..a42606e --- /dev/null +++ b/phpt-tests/sample_skipif_ini.phpt @@ -0,0 +1,41 @@ +--TEST-- +Test to check that the warnings generated by running the INI section don't stop SKIPIF fprocessing +--INI-- +session.upload_progress.freq=200% +error_log= +--SKIPIF-- +<?php +if (!extension_loaded("session") && ini_get("enable_dl")) { + $dlext = (substr(PHP_OS, 0, 3) == "WIN") ? ".dll" : ".so"; + @dl("session$dlext"); +} +if (!extension_loaded("session")) { + die("skip Session module not loaded"); +} +$save_path = ini_get("session.save_path"); +if ($save_path) { + if (!file_exists($save_path)) { + die("skip Session save_path doesn't exist"); + } + + if ($save_path && !@is_writable($save_path)) { + if (($p = strpos($save_path, ';')) !== false) { + $save_path = substr($save_path, ++$p); + } + if (!@is_writable($save_path)) { + die("skip session.save_path $save_path is not writable\n"); + } + } +} +if(substr(PHP_OS, 0, 3) != "WIN") + die("skip windows only test"); +?> +--FILE-- +<?php +var_dump(ini_get("session.upload_progress.freq")); +?> +--EXPECTF-- +Warning: PHP Startup: session.upload_progress.freq cannot be over 100% in %s +string(2) "1%" +PHP Warning: PHP Startup: session.upload_progress.freq cannot be over 100% in %s + diff --git a/src/rtUtil.php b/src/rtUtil.php index 66cc9c1..f7573d5 100644 --- a/src/rtUtil.php +++ b/src/rtUtil.php @@ -60,8 +60,8 @@ class rtUtil { $list = array(); $found = false; - foreach (scandir($path) as $file) { + if (substr($file, 0, 1) != '.' && $file != 'CVS') { diff --git a/src/taskScheduler/rtTaskScheduler.php b/src/taskScheduler/rtTaskScheduler.php index 56bc3a8..3a6377c 100644 --- a/src/taskScheduler/rtTaskScheduler.php +++ b/src/taskScheduler/rtTaskScheduler.php @@ -18,7 +18,7 @@ class rtTaskScheduler protected $taskList = array(); // the list of the tasks to be executed protected $resultList = array(); // list of results protected $processCount = 0; // the number of processes - protected $reportStatus = 0; // reprort-status + protected $reportStatus = 0; // the level of repoerting as tests atr run (0, 1, 2) protected $redirectedTestCases = array(); diff --git a/src/taskScheduler/rtTaskTestGroup.php b/src/taskScheduler/rtTaskTestGroup.php index f1c225e..8509caa 100644 --- a/src/taskScheduler/rtTaskTestGroup.php +++ b/src/taskScheduler/rtTaskTestGroup.php @@ -16,12 +16,14 @@ class rtTaskTestGroup extends rtTask implements rtTaskInterface protected $runConfiguration; protected $subDirectory; protected $redirectedTestCases = array(); + protected $groupConfig; - public function __construct($runConfiguration, $subDirectory) + public function __construct($runConfiguration, $subDirectory, $groupConfig) { $this->runConfiguration = $runConfiguration; $this->subDirectory = $subDirectory; + $this->groupConfig = $groupConfig; } @@ -31,7 +33,7 @@ class rtTaskTestGroup extends rtTask implements rtTaskInterface */ public function run() { - $testGroup = new rtPhpTestGroup($this->runConfiguration, $this->subDirectory); + $testGroup = new rtPhpTestGroup($this->runConfiguration, $this->subDirectory, $this->groupConfig); $testGroup->run(); $this->result = $testGroup->getResults(); return true; diff --git a/src/testcase/rtPhpTest.php b/src/testcase/rtPhpTest.php index 4e572e0..9febda7 100644 --- a/src/testcase/rtPhpTest.php +++ b/src/testcase/rtPhpTest.php @@ -24,11 +24,13 @@ class rtPhpTest protected $fileSection; protected $expectSection; protected $sectionHeadings; + protected $redirectFromID = null; - public function __construct(array $contents, $testName, $sectionHeadings, $runConfiguration, $testStatus) + public function __construct(array $contents, $testName, $sectionHeadings, $runConfiguration, $testStatus, $redirectFromID=null) { $this->contents = $contents; $this->testName = $testName; + $this->redirectFromID = $redirectFromID; $this->sectionHeadings = $sectionHeadings; $this->testStatus = $testStatus; $this->parse(); @@ -79,11 +81,16 @@ class rtPhpTest //Identify the file and expect section types $this->fileSection = $this->setFileSection(); $this->expectSection = $this->setExpectSection(); + + //If the test is being run as a result of being redirected from somewhere else, update the TEST section with an identifier + if($this->redirectFromID != null) { + $this->sections['TEST']->addString(' Redirected ' . $this->redirectFromID . ' test'); + } } /** - * Initialises the configuration for this test. Uses the configuration sections from teh test case + * Initialises the configuration for this test. Uses the configuration sections from the test case * * @param rtRunTEstsConfiuration $runConfiguration * @@ -106,12 +113,12 @@ class rtPhpTest $this->testStatus = $this->sections['SKIPIF']->run($this, $runConfiguration); $this->testStatus->setExecutedPhpCommand($this->sections['SKIPIF']->getPhpCommand()); } - //TODO: D'oh - what is this meant to be? - if (!$this->testStatus->getValue('skip') && !$this->testStatus->getValue('skip')) { + + if (!$this->testStatus->getValue('skip')) { $this->testStatus = $this->fileSection->run($this, $runConfiguration); $this->testStatus->setExecutedPhpCommand($this->fileSection->getPhpCommand()); - //The test can be skipped by file sections if the CGI executable is not available + //The test status can also be set to 'skip' if the PHP CGI is missing, so we need to check it again here. if(!$this->testStatus->getValue('skip')) { $this->output = $this->fileSection->getOutput(); $this->compareOutput(); diff --git a/src/testcase/rtTestConfiguration.php b/src/testcase/rtTestConfiguration.php index b369d2b..0c540ec 100644 --- a/src/testcase/rtTestConfiguration.php +++ b/src/testcase/rtTestConfiguration.php @@ -92,8 +92,9 @@ class rtTestConfiguration protected function setPhpCommandLineArguments(rtRuntestsConfiguration $runConfiguration, $sections, $fileSection) { //Use different arguments for running SKIPIF and so on. The INI settings may be designed to - //produce an error whaich will inetrfere with SKIPIF processing. - $this->phpNonFileSectionCommandLineArguments = $runConfiguration->getSetting('PhpCommandLineArguments'); + //produce an error which will interfere with SKIPIF processing. + //TODO: But in some cases the INI section is required for SKIPIF so we need another way to deal with this. + //$this->phpNonFileSectionCommandLineArguments = $runConfiguration->getSetting('PhpCommandLineArguments'); $this->phpCommandLineArguments = $runConfiguration->getSetting('PhpCommandLineArguments'); if (array_key_exists('INI', $sections)) { $sections['INI']->substitutePWD($fileSection->getFileName()); @@ -101,6 +102,8 @@ class rtTestConfiguration $args = new rtIniAsCommandLineArgs(); $this->phpCommandLineArguments = $args->settingsToArguments($additionalArguments, $this->phpCommandLineArguments); } + //For SKIPIF sections - supress the error reporting which interferes with skip and warn processing + $this->phpNonFileSectionCommandLineArguments = preg_replace('/error_reporting=32767/', 'error_reporting=0', $this->phpCommandLineArguments); } protected function setTestCommandLineArguments($sections) diff --git a/src/testcase/rtTestOutputWriter.php b/src/testcase/rtTestOutputWriter.php index b582fe6..8cb23ac 100644 --- a/src/testcase/rtTestOutputWriter.php +++ b/src/testcase/rtTestOutputWriter.php @@ -98,21 +98,27 @@ abstract class rtTestOutputWriter * @param integer $processCount * @return string */ - public function getOverview($groups=NULL, $processCount=NULL) + public function getOverview($parallelGroups = 0, $serialGroups= 0, $processCount= 1) { // if the overview was already created retun it if (!is_null($this->overview)) { return $this->overview; } + /* + * Most of the code uses processCount = 0 to indicate serial runs. Clearly there must always be one process, + * so it is set to 1 here to make the output make sense. + */ + if($processCount == 0) { + $processCount ++; + } + // collect data $state = array(); $count = 0; - foreach ($this->resultList as $testGroupResults) { - - foreach ($testGroupResults as $testResult) { - + foreach ($this->resultList as $groupResult) { + foreach($groupResult as $testResult) { $s = $testResult->getStatus()->__toString(); if (!isset($state[$s])) { @@ -121,40 +127,48 @@ abstract class rtTestOutputWriter $state[$s]++; $count++; - } + } } // create the output-string + $secondColPosition = 30; $str = ''; $str .= "\n\n----------------------------------------\n"; - $str .= "Tests:\t\t$count\n"; + $str .= "Tests:"; + $blanks = 30 - strlen("Tests:") - strlen($count); + $str = $this->writeBlanks($str, $blanks); + $str .= "$count\n"; - if (is_numeric($groups)) { + if (is_numeric($parallelGroups)) { - $str .= "Groups:\t\t"; - $blanks = strlen($count)-strlen($groups); - for ($b=0; $b<$blanks; $b++) { - $str .= ' '; - } - $str .= $groups."\n"; + $str .= "Parallel Groups:"; + $blanks = 30 - strlen("Parallel Groups:") - strlen($parallelGroups); + $str = $this->writeBlanks($str, $blanks); + $str .= $parallelGroups."\n"; + } + + if (is_numeric($serialGroups)) { + + $str .= "Serial Groups:"; + $blanks = 30 - strlen("Serial Groups:") - strlen($serialGroups); + $str = $this->writeBlanks($str, $blanks); + $str .= $serialGroups."\n"; } if (is_numeric($processCount)) { - $str .= "Processes:\t"; - $blanks = strlen($count)-strlen($processCount); - for ($b=0; $b<$blanks; $b++) { - $str .= ' '; - } + $str .= "Processes:"; + $blanks = 30 -strlen("Processes:") - strlen($processCount); + $str = $this->writeBlanks($str, $blanks); $str .= $processCount."\n"; } $str .= "----------------------------------------\n"; - + foreach ($state as $k => $v) { - $str .= strtoupper($k).":\t"; + $str .= substr(strtoupper($k), 0, 5).":\t"; $blanks = strlen($count)-strlen($v); for ($b=0; $b<$blanks; $b++) { @@ -289,6 +303,12 @@ abstract class rtTestOutputWriter flush(); } + public function writeBlanks($str, $n) { + for ($b=0; $b<$n; $b++) { + $str .= ' '; + } + return $str; + } } ?> \ No newline at end of file diff --git a/src/testcase/rtTestResults.php b/src/testcase/rtTestResults.php index 93da986..b8f0e8e 100644 --- a/src/testcase/rtTestResults.php +++ b/src/testcase/rtTestResults.php @@ -34,7 +34,7 @@ class rtTestResults public function init(rtPhpTest $testCase = null, rtTestStatus $testStatus = null) { if ($testCase != null) { - $this->title = implode('',$testCase->getSection('TEST')->getContents()); + $this->title = $testCase->getSection('TEST')->getHeader(); $this->testStatus = $testCase->getStatus(); $this->testName = $testCase->getName(); if($testStatus == 'redirected') { @@ -109,7 +109,7 @@ class rtTestResults if (!$runConfiguration->hasCommandLineOption('keep-all') && !$runConfiguration->hasCommandLineOption('keep-clean')) { $testCase->getSection('CLEAN')->deleteFile(); } else { - $this->savedFileNames['clean'] = $this->testName. ".clean"; + $this->savedFileNames['clean'] = $this->testName. ".clean.php"; } } @@ -148,11 +148,11 @@ class rtTestResults //Note: if there are clean and skipif files they will not be deleted if the test fails if ($testCase->hasSection('CLEAN')) { - $this->savedFileNames['clean'] = $this->testName. 'clean.php'; + $this->savedFileNames['clean'] = $this->testName. '.clean.php'; } if ($testCase->hasSection('SKIPIF')) { - $this->savedFileNames['skipif'] = $this->testName. 'skipif.php'; + $this->savedFileNames['skipif'] = $this->testName. '.skipif.php'; } if($testCase->getStatus()->getValue('leak') == true) { @@ -163,19 +163,10 @@ class rtTestResults protected function onSkip(rtPhpTest $testCase, rtRuntestsConfiguration $runConfiguration) { if ($runConfiguration->hasCommandLineOption('keep-all') || $runConfiguration->hasCommandLineOption('keep-skip')) { - $this->savedFileNames['skipif'] = $this->testName. 'skipif.php'; + $this->savedFileNames['skipif'] = $this->testName. '.skipif.php'; } else if($testCase->hasSection('SKIPIF')) { $testCase->getSection('SKIPIF')->deleteFile(); } - - //TODO I think this can go? Have since updated code to BORK on finding blank sections - //It may seem odd to check for an XFAIL if we are skipping the test, on the other hand I found - //a few windows tests with blank XFAIL sections and wanted to know about those. - - if ($testCase->hasSection('XFAIL')) { - $this->testStatus->setTrue('xfail'); - $this->testStatus->setMessage('xfail',$testCase->getSection('XFAIL')->getReason()); - } } diff --git a/src/testcase/sections/executablesections/rtSkipIfSection.php b/src/testcase/sections/executablesections/rtSkipIfSection.php index 4b20834..f5c828d 100644 --- a/src/testcase/sections/executablesections/rtSkipIfSection.php +++ b/src/testcase/sections/executablesections/rtSkipIfSection.php @@ -40,7 +40,7 @@ class rtSkipIfSection extends rtExecutableSection $phpCommand .= ' -f '.$this->fileName; $this->phpCommand = $phpCommand; - + $PhpRunner = new rtPhpRunner($phpCommand, $runConfiguration->getEnvironmentVariables(), $runConfiguration->getSetting('WorkingDirectory') diff --git a/src/testcase/sections/informationsections/rtTestHeaderSection.php b/src/testcase/sections/informationsections/rtTestHeaderSection.php index 241f219..1aa14f8 100644 --- a/src/testcase/sections/informationsections/rtTestHeaderSection.php +++ b/src/testcase/sections/informationsections/rtTestHeaderSection.php @@ -26,5 +26,9 @@ class rtTestHeaderSection extends rtInformationSection { return $this->testHeader; } + + public function addString($string) { + $this->testHeader .= $string; + } } ?> diff --git a/src/testgroup/rtGroupConfiguration.php b/src/testgroup/rtGroupConfiguration.php index 00548e4..4339ee2 100644 --- a/src/testgroup/rtGroupConfiguration.php +++ b/src/testgroup/rtGroupConfiguration.php @@ -17,15 +17,105 @@ class rtGroupConfiguration { protected $testDirectory; + protected $environmentVariables = array(); + protected $isRedirect=false; + protected $serialGroup=false; + protected $redirectFromID = null; - public function __construct(rtRuntestsConfiguration $runConfiguration, $directory) - { + public function __construct($directory) + { $this->testDirectory = $directory; } public function parseRedirect(rtPHPTest $redirectedTest) { - var_dump($redirectedTest->getSection('REDIRECTTEST')); + //Going to assume that we have already parsed the SKIPIF (if it exists) + //var_dump($redirectedTest->getSection('REDIRECTTEST')->getContents()); + $name = $redirectedTest->getName(); + + $code = implode($redirectedTest->getSection('REDIRECTTEST')->getContents(), "\n"); + + //Strip tabs which interfere with eval + $code = preg_replace('|\\t|', ' ', $code); + + $setup = eval($code); + + $this->environmentVariables = $setup['ENV']; + + + //Remove trailing white space and add a slash to teh directory name + //Will need modification if we wanted to be able to give it a list of test directories. + + $dir = trim($setup['TESTS']); + if (substr($dir, -1) !== "/") {$dir .= "/";} + + /* + * Some of the PDO tests have __DIR__ in front of them, means you have to run then from the same place + * as run-tests.php. This is really annoying because in this case it just picks up the location of this class + * which is useless for anything. + */ + + + $stripchars = strlen(__DIR__) + 1; + + if ( substr($dir, 0, strlen(__DIR__)) === __DIR__) { + $dir = substr($dir, $stripchars); + } + + /* + * However... we do need some way of finding the absolute path of the tests. I think it is reasonable to assume + * that they are related to the the path of the test that is redirecting to them. + * Take the first three characters of the directory and try to find them in the name of the redirected test. + * The best way to do this is really for the test case to specify a path relative to the test tgey are redirected from + * but this would mean changes to run-tests.php and to the tests. + */ + + $key = substr($dir, 0, 3); + + //Find the key in the full name of the test contains the redirect + $position = strpos($name, $key); + + //Take the root strng from before the key + $root=substr($name, 0, $position); + + $title = $redirectedTest->getSection('TEST')->getContents(); + $this->redirectFromID = $title[0]; + + //And add it on to form a full directory path. + $dir = $root . $dir; + + $this->testDirectory = $dir; + + $this->isRedirect = true; + + //Finaly add the directory name to the env variables - checked by SKIPIF + $this->environmentVariables['REDIR_TEST_DIR'] = $dir; + + + } + + public function parse() { + //Here insert code to read a config file from the test directory that determines whether the set of tests shoudl be run + //in parallel or not? + $this->serialGroup = false; + return; + + } + + public function getEnvironmentVariables() { + return $this->environmentVariables; + } + public function getTestDirectory() { + return $this->testDirectory; + } + public function isRedirect() { + return $this->isRedirect; } + public function isSerial() { + return $this->serialGroup; + } + public function getRedirectFromID() { + return $this->redirectFromID; + } } diff --git a/src/testgroup/rtPhpTestGroup.php b/src/testgroup/rtPhpTestGroup.php index b052284..b49c0b6 100644 --- a/src/testgroup/rtPhpTestGroup.php +++ b/src/testgroup/rtPhpTestGroup.php @@ -16,20 +16,32 @@ class rtPhpTestGroup extends rtTask implements rtTaskInterface { protected $testDirectory; protected $testCases; - protected $result = null; + protected $result = array(); protected $runConfiguration; + protected $groupConfiguration; - public function __construct(rtRuntestsConfiguration $runConfiguration, $directory) + public function __construct(rtRuntestsConfiguration $runConfiguration, $directory, $groupConfiguration) { $this->runConfiguration = $runConfiguration; $this->testDirectory = $directory; + $this->groupConfiguration = $groupConfiguration; $this->init(); } public function init() - { + { + if($this->groupConfiguration->isRedirect()) { + //merge in environmental variables (this is for REDEIRRECT). + foreach($this->groupConfiguration->getEnvironmentVariables() as $key=>$value) { + $this->runConfiguration->setEnvironmentVariable($key, $value); + } + } + + $this->testFiles = rtUtil::getTestList($this->testDirectory); - + + $redirectFromID = $this->groupConfiguration->getRedirectFromID(); + foreach ($this->testFiles as $testFileName) { //testFiles is a list of file names relative to the current working directory @@ -49,7 +61,7 @@ class rtPhpTestGroup extends rtTask implements rtTaskInterface $testStatus = new rtTestStatus($testFile->getTestName()); if ($testFile->arePreconditionsMet() ) { // Create a new test case - $this->testCases[] = new rtPhpTest($testFile->getContents(), $testFile->getTestName(), $testFile->getSectionHeadings(), $this->runConfiguration, $testStatus); + $this->testCases[] = new rtPhpTest($testFile->getContents(), $testFile->getTestName(), $testFile->getSectionHeadings(), $this->runConfiguration, $testStatus, $redirectFromID); } elseif (in_array("REDIRECTTEST",$testFile->getSectionHeadings())){ //Redirect handler, save the test case for processing after the main groups have finished. //Check to make sure that it shouldn't be skipped, if skipped don't save it @@ -58,7 +70,7 @@ class rtPhpTestGroup extends rtTask implements rtTaskInterface $redirectedTest->runSkipif($this->runConfiguration); if($redirectedTest->getStatus()->getValue('skip')) { $testStatus->setTrue('skip'); - $testStatus->setMessage('skip', $testFile->getExitMessage()); + $testStatus->setMessage('skip', $testFile->getExitMessage(). ' and the skip condition has failed'); } else { $testStatus->setTrue('redirected'); $testStatus->setMessage('redirected', $testFile->getExitMessage()); diff --git a/src/testrun/rtPhpTestRun.php b/src/testrun/rtPhpTestRun.php index cacac1b..8b4c358 100644 --- a/src/testrun/rtPhpTestRun.php +++ b/src/testrun/rtPhpTestRun.php @@ -18,6 +18,12 @@ class rtPhpTestRun protected $commandLineArguments; protected $runConfiguration; protected $redirectedTestCases = array(); + protected $resultList = array(); //An array of arrays of Group Results + protected $serialTasks = array(); + protected $parallelTasks = array(); + protected $reportStatus = 0; + protected $numberOfSerialGroups = 0; + protected $numberOfParallelGroups = 0; public function __construct($argv) { @@ -55,18 +61,24 @@ class rtPhpTestRun //test cases expect to be able to use it. $php = $this->runConfiguration->getSetting('PhpExecutable'); $this->runConfiguration->setEnvironmentVariable('TEST_PHP_EXECUTABLE', $php); + + //Set reporting option + $this->setReportStatus(); - // Main decision point. Either we start this with a directory (or set of directories, in which case tests are - // run as a group (and in parallel if required) or...... - // + /* + * Main decision point. Either we start this with a directory (or set of directories, in which case tests are + * run as a group (and in parallel if required) or...... + */ if ($this->runConfiguration->getSetting('TestDirectories') != null) { - - $this->run_group($this->runConfiguration->getSetting('TestDirectories')); - + + $this->doGroupRuns(); + } else { - //.. the input is a test file, or list of files and are just run as single tests - // and not in parallel - + + /* + *... the input is a test file, or list of files and are just run as single tests + * and not in parallel + */ if ($this->runConfiguration->getSetting('TestFiles') == null) { echo rtText::get('invalidTestFileName'); exit(); @@ -74,110 +86,109 @@ class rtPhpTestRun $this->run_tests($this->runConfiguration->getSetting('TestFiles')); } } - + + /* + * At this stage we have run all groups or tests in the initial input. Now we check if any of those have + * redirected test cases and if so, run those one group at a time. + * It might be possible to run these in parallel too? + */ + $this->buildRedirectsList($this->resultList); if(count($this->redirectedTestCases) > 0) { - foreach($this->redirectedTestCases as $testCase){ - echo $testCase->getName() . "\n"; - - - } - - //For each test case - construct a new group - //Call run_group() again with an array of groups - // - // The redirect section has PHP code in it but no tags. - // It is code the needs to be run as part of run-tests, not as a 'runnable' section - - // eek it's eval(). Is there any better way to do this? It's setting a 'group - // configuration' which we don't have at the moment - so maybe we need one? - // re-implementing a differnt way would be nice. Just reading the config and not eval()ing it - //which seems unnecessary. - // - // for now, rtRedirectedSecion is part of 'config', not part of 'executable'; + $this->doRedirectedRuns(); + } + + if(($this->numberOfSerialGroups != 0) || ($this->numberOfParallelGroups != 0)) { + $this->createRunOutput(); } - } + + public function doGroupRuns() { + + $subDirectories = $this->buildSubDirectoryList($this->runConfiguration->getSetting('TestDirectories')); + $groupConfigurations = $this->buildGroupConfigurations($subDirectories); - public function run_group($testDirectories) { - // make a list of subdirectories which contain tests, includes the top level directory - - $subDirectories = array(); - foreach ($testDirectories as $testDirectory) { - $subDirectories = array_merge($subDirectories, rtUtil::parseDir($testDirectory)); - } - - // check for the cmd-line-option 'z' which defines parellel-execution - $processCount = 0; - if ($this->runConfiguration->hasCommandLineOption('z')) { - - $processCount = $this->runConfiguration->getCommandLineOption('z'); - - if (!is_numeric($processCount) || $processCount < 0) { - $processCount = 2; - } - } + //If there is only one subdirectory, run seqential + if(count($subDirectories) === 1) { + $this->run_serial_groups($subDirectories, $groupConfigurations); + $this->numberOfSerialGroups = 1; + + } else { + + //check to see if this is set to be a parallel run, if not, run the subdirectory groups in sequence. + if($this->requestedProcessorCount() <= 1) { + $this->run_serial_groups($subDirectories, $groupConfigurations); + $this->numberOfSerialGroups = count($subDirectories); + } else { + //At least part of this run can be in parallel, check group configurations to make sure that none are set to be serial. + //This builds parallel and serial task lists. + foreach($groupConfigurations as $key=>$gc) { + if($gc->isSerial()) { + $serialGroups[] = $key; + } else { + $parallelGroups[] = $key; + } + } + + if(isset($serialGroups)) {$this->numberOfSerialGroups = count($serialGroups);} + + $this->numberOfParallelGroups = count($parallelGroups); + + $this->run_parallel_groups($parallelGroups, $groupConfigurations, $this->requestedProcessorCount()); + if($this->numberOfSerialGroups > 0) { + $this->run_serial_groups($serialGroups, $groupConfigurations); + } + } + } + + } + + public function doRedirectedRuns() { + foreach($this->redirectedTestCases as $testCase){ + + $groupConfig = new rtGroupConfiguration(null); + $groupConfig->parseRedirect($testCase); + + $group = $groupConfig->getTestDirectory(); + + $this->run_serial_groups(array($group), array($group=>$groupConfig)); + + $this->numberOfSerialGroups++; + + } + } - // check for the cmd-line-option 'v' which defines the report-status - $reportStatus = 0; - if ($this->runConfiguration->hasCommandLineOption('v')) { - $reportStatus = 1; - } else if ($this->runConfiguration->hasCommandLineOption('vv')) { - $reportStatus = 2; - } else if ($this->runConfiguration->hasCommandLineOption('vvv')) { - $reportStatus = 3; - } + public function run_parallel_groups($testDirectories, $groupConfigurations, $processCount) { // create the task-list $taskList = array(); - - - foreach ($subDirectories as $subDirectory) { - $taskList[] = new rtTaskTestGroup($this->runConfiguration, $subDirectory); + foreach($testDirectories as $testGroup) { + $taskList[] = new rtTaskTestGroup($this->runConfiguration, $testGroup, $groupConfigurations[$testGroup]); } - // run the task-scheduler $scheduler = rtTaskScheduler::getInstance(); $scheduler->setTaskList($taskList); $scheduler->setProcessCount($processCount); - $scheduler->setReportStatus($reportStatus); + $scheduler->setReportStatus($this->reportStatus); $scheduler->run(); - - $resultList = $scheduler->getResultList(); - - //locate any redirected tests in teh group results files. - foreach ($resultList as $testGroupResults) { - - foreach ($testGroupResults as $testResult) { - - if($testResult->getStatus() == 'redirected') { - $this->redirectedTestCases[] = $testResult->getRedirectedTestCase(); - } - } - } - - - // create output - $type = null; - if ($this->runConfiguration->hasCommandLineOption('o')) { - $type = $this->runConfiguration->getCommandLineOption('o'); - } - - $outputWriter = rtTestOutputWriter::getInstance($type); - $outputWriter->setResultList($resultList); - $outputWriter->printOverview(sizeof($taskList), $scheduler->getProcessCount()); - $filename = null; - if ($this->runConfiguration->hasCommandLineOption('s')) { - $filename = $this->runConfiguration->getCommandLineOption('s'); - } - - if ($type || $filename) { - $outputWriter->write($filename); - } - - + foreach($scheduler->getResultList() as $groupResult) { + $this->resultList[] = $groupResult; + } + } + + public function run_serial_groups($testDirectories, $groupConfigurations) { + + foreach($testDirectories as $subDirectory) { + $testGroup = new rtPhpTestGroup($this->runConfiguration, $subDirectory, $groupConfigurations[$subDirectory]); + $testGroup->run(); + rtTestOutputWriter::flushResult($testGroup->getResults(), $this->reportStatus); + $this->resultList[] = $testGroup->getResults(); + + } } + public function run_tests($testNames) { //This section deals with running single test cases, or lists of test cases. @@ -188,7 +199,9 @@ class rtPhpTestRun echo rtText::get('invalidTestFileName', array($testName)); exit(); } - + + $allResults = array(); + //Read the test file $testFile = new rtPhpTestFile(); $testFile->doRead($testName); @@ -211,7 +224,6 @@ class rtPhpTestRun //Redirect handler //Build a list of redirected test cases - //TODO: need to run the skipif section here.. $this->redirectedTestCases[] = new rtPhpTest($testFile->getContents(), $testFile->getTestName(), $testFile->getSectionHeadings(), $this->runConfiguration, $testStatus); $testStatus->setTrue('redirected'); @@ -222,10 +234,84 @@ class rtPhpTestRun $testStatus->setMessage('bork', $testFile->getExitMessage()); $results = new rtTestResults(null, $testStatus); } - rtTestOutputWriter::flushResult(array($results), 3); + + } + } + + public function buildSubDirectoryList($testDirectories){ + $subDirectories = array(); + foreach ($testDirectories as $testDirectory) { + $subDirectories = array_merge($subDirectories, rtUtil::parseDir($testDirectory)); + } + return $subDirectories; + } + + public function requestedProcessorCount() { + // check for the cmd-line-option 'z' which defines parellel-execution + $processCount = 0; + if ($this->runConfiguration->hasCommandLineOption('z')) { + + $processCount = $this->runConfiguration->getCommandLineOption('z'); + + if (!is_numeric($processCount) || $processCount < 0) { + $processCount = 2; + } + } + return $processCount; + } + + public function buildGroupConfigurations($subDirectories) { + $groupConfigurations = array(); + foreach($subDirectories as $subDir) { + $groupConfig = new rtGroupConfiguration($subDir); + $groupConfig->parse(); + $groupConfigurations[$subDir] = $groupConfig; + } + return $groupConfigurations; + } + + public function buildRedirectsList($results) { + foreach ($results as $groupResult) { + foreach($groupResult as $testResult) { + if($testResult->getStatus() == 'redirected') { + $this->redirectedTestCases[] = $testResult->getRedirectedTestCase(); + } + } + } + } + + public function createRunOutput() { + $type = null; + if ($this->runConfiguration->hasCommandLineOption('o')) { + $type = $this->runConfiguration->getCommandLineOption('o'); + } + + $outputWriter = rtTestOutputWriter::getInstance($type); + $outputWriter->setResultList($this->resultList); + + $outputWriter->printOverview($this->numberOfParallelGroups, $this->numberOfSerialGroups, $this->requestedProcessorCount()); + + $filename = null; + if ($this->runConfiguration->hasCommandLineOption('s')) { + $filename = $this->runConfiguration->getCommandLineOption('s'); + } + + if ($type || $filename) { + $outputWriter->write($filename); + } + } + + public function setReportStatus() { + // check for the cmd-line-option 'v' which defines the report-status + if ($this->runConfiguration->hasCommandLineOption('v')) { + $this->reportStatus = 1; + } else if ($this->runConfiguration->hasCommandLineOption('vv')) { + $this->reportStatus = 2; + } else if ($this->runConfiguration->hasCommandLineOption('vvv')) { + $this->reportStatus = 3; } - } + } ?> diff --git a/tests/rtGroupConfigurationTest.php b/tests/rtGroupConfigurationTest.php new file mode 100644 index 0000000..a08e5fb --- /dev/null +++ b/tests/rtGroupConfigurationTest.php @@ -0,0 +1,14 @@ +<?php +require_once dirname(__FILE__) . '/../src/rtAutoload.php'; +require_once dirname(__FILE__) . '/rtTestBootstrap.php'; + +class rtGroupConfigurationTest extends PHPUnit_Framework_TestCase +{ + public function testCreateInstance() + { + //$directory = realpath(dirname(__FILE__) . '/../phpt-tests'); + $config = rtRuntestsConfiguration::getInstance(array('run-tests.php', '-p', RT_PHP_PATH, 'testgroup')); + + $config->configure(); + } +} \ No newline at end of file diff --git a/tests/rtPhpTestGroupTest.php b/tests/rtPhpTestGroupTest.php index 5557b74..0a02131 100644 --- a/tests/rtPhpTestGroupTest.php +++ b/tests/rtPhpTestGroupTest.php @@ -13,9 +13,11 @@ class rtPHpTestGroupTest extends PHPUnit_Framework_TestCase $directory = realpath(dirname(__FILE__) . '/../phpt-tests'); $config = rtRuntestsConfiguration::getInstance(array('run-tests.php', '-p', RT_PHP_PATH, $directory)); - $config->configure(); + $config->configure(); + + $gConf = new rtGroupConfiguration('wibble'); - $phpTestGroup = new rtPhpTestGroup($config, $directory); + $phpTestGroup = new rtPhpTestGroup($config, $directory, $gConf); $validTestCaseCount = count($phpTestGroup->getTestCases()); @@ -36,9 +38,10 @@ class rtPHpTestGroupTest extends PHPUnit_Framework_TestCase $directory = realpath(dirname(__FILE__) . '/../phpt-tests'); $config = rtRuntestsConfiguration::getInstance(array('run-tests.php', '-p', RT_PHP_PATH, $directory)); - $config->configure(); + $config->configure(); + $gConf = new rtGroupConfiguration('wibble'); - $phpTestGroup = new rtPhpTestGroup($config, $directory); + $phpTestGroup = new rtPhpTestGroup($config, $directory, $gConf);
diff --git a/phpt-tests/redirected/tests/sample_test.phpt b/phpt-tests/redirected/tests/sample_test.phpt new file mode 100644 index 0000000..06501aa --- /dev/null +++ b/phpt-tests/redirected/tests/sample_test.phpt @@ -0,0 +1,8 @@ +--TEST-- +Try a sample test +--FILE-- +<?php + echo "Hello world\n"; +?> +--EXPECT-- +Hello world diff --git a/phpt-tests/sample_redirect.phpt b/phpt-tests/sample_redirect.phpt index 778d42b..073620c 100644 --- a/phpt-tests/sample_redirect.phpt +++ b/phpt-tests/sample_redirect.phpt @@ -8,6 +8,6 @@ return array( 'ENV' => array( 'PDOTEST_DSN' => 'sqlite::memory:' ), - 'TESTS' => 'ext/pdo/tests' + 'TESTS' => 'phpt-tests/redirected/tests' ); diff --git a/phpt-tests/sample_skipif_ini.phpt b/phpt-tests/sample_skipif_ini.phpt new file mode 100644 index 0000000..a42606e --- /dev/null +++ b/phpt-tests/sample_skipif_ini.phpt @@ -0,0 +1,41 @@ +--TEST-- +Test to check that the warnings generated by running the INI section don't stop SKIPIF fprocessing +--INI-- +session.upload_progress.freq=200% +error_log= +--SKIPIF-- +<?php +if (!extension_loaded("session") && ini_get("enable_dl")) { + $dlext = (substr(PHP_OS, 0, 3) == "WIN") ? ".dll" : ".so"; + @dl("session$dlext"); +} +if (!extension_loaded("session")) { + die("skip Session module not loaded"); +} +$save_path = ini_get("session.save_path"); +if ($save_path) { + if (!file_exists($save_path)) { + die("skip Session save_path doesn't exist"); + } + + if ($save_path && !@is_writable($save_path)) { + if (($p = strpos($save_path, ';')) !== false) { + $save_path = substr($save_path, ++$p); + } + if (!@is_writable($save_path)) { + die("skip session.save_path $save_path is not writable\n"); + } + } +} +if(substr(PHP_OS, 0, 3) != "WIN") + die("skip windows only test"); +?> +--FILE-- +<?php +var_dump(ini_get("session.upload_progress.freq")); +?> +--EXPECTF-- +Warning: PHP Startup: session.upload_progress.freq cannot be over 100% in %s +string(2) "1%" +PHP Warning: PHP Startup: session.upload_progress.freq cannot be over 100% in %s + diff --git a/src/rtUtil.php b/src/rtUtil.php index 66cc9c1..f7573d5 100644 --- a/src/rtUtil.php +++ b/src/rtUtil.php @@ -60,8 +60,8 @@ class rtUtil { $list = array(); $found = false; - foreach (scandir($path) as $file) { + if (substr($file, 0, 1) != '.' && $file != 'CVS') { diff --git a/src/taskScheduler/rtTaskScheduler.php b/src/taskScheduler/rtTaskScheduler.php index 56bc3a8..3a6377c 100644 --- a/src/taskScheduler/rtTaskScheduler.php +++ b/src/taskScheduler/rtTaskScheduler.php @@ -18,7 +18,7 @@ class rtTaskScheduler protected $taskList = array(); // the list of the tasks to be executed protected $resultList = array(); // list of results protected $processCount = 0; // the number of processes - protected $reportStatus = 0; // reprort-status + protected $reportStatus = 0; // the level of repoerting as tests atr run (0, 1, 2) protected $redirectedTestCases = array(); diff --git a/src/taskScheduler/rtTaskTestGroup.php b/src/taskScheduler/rtTaskTestGroup.php index f1c225e..8509caa 100644 --- a/src/taskScheduler/rtTaskTestGroup.php +++ b/src/taskScheduler/rtTaskTestGroup.php @@ -16,12 +16,14 @@ class rtTaskTestGroup extends rtTask implements rtTaskInterface protected $runConfiguration; protected $subDirectory; protected $redirectedTestCases = array(); + protected $groupConfig; - public function __construct($runConfiguration, $subDirectory) + public function __construct($runConfiguration, $subDirectory, $groupConfig) { $this->runConfiguration = $runConfiguration; $this->subDirectory = $subDirectory; + $this->groupConfig = $groupConfig; } @@ -31,7 +33,7 @@ class rtTaskTestGroup extends rtTask implements rtTaskInterface */ public function run() { - $testGroup = new rtPhpTestGroup($this->runConfiguration, $this->subDirectory); + $testGroup = new rtPhpTestGroup($this->runConfiguration, $this->subDirectory, $this->groupConfig); $testGroup->run(); $this->result = $testGroup->getResults(); return true; diff --git a/src/testcase/rtPhpTest.php b/src/testcase/rtPhpTest.php index 4e572e0..9febda7 100644 --- a/src/testcase/rtPhpTest.php +++ b/src/testcase/rtPhpTest.php @@ -24,11 +24,13 @@ class rtPhpTest protected $fileSection; protected $expectSection; protected $sectionHeadings; + protected $redirectFromID = null; - public function __construct(array $contents, $testName, $sectionHeadings, $runConfiguration, $testStatus) + public function __construct(array $contents, $testName, $sectionHeadings, $runConfiguration, $testStatus, $redirectFromID=null) { $this->contents = $contents; $this->testName = $testName; + $this->redirectFromID = $redirectFromID; $this->sectionHeadings = $sectionHeadings; $this->testStatus = $testStatus; $this->parse(); @@ -79,11 +81,16 @@ class rtPhpTest //Identify the file and expect section types $this->fileSection = $this->setFileSection(); $this->expectSection = $this->setExpectSection(); + + //If the test is being run as a result of being redirected from somewhere else, update the TEST section with an identifier + if($this->redirectFromID != null) { + $this->sections['TEST']->addString(' Redirected ' . $this->redirectFromID . ' test'); + } } /** - * Initialises the configuration for this test. Uses the configuration sections from teh test case + * Initialises the configuration for this test. Uses the configuration sections from the test case * * @param rtRunTEstsConfiuration $runConfiguration * @@ -106,12 +113,12 @@ class rtPhpTest $this->testStatus = $this->sections['SKIPIF']->run($this, $runConfiguration); $this->testStatus->setExecutedPhpCommand($this->sections['SKIPIF']->getPhpCommand()); } - //TODO: D'oh - what is this meant to be? - if (!$this->testStatus->getValue('skip') && !$this->testStatus->getValue('skip')) { + + if (!$this->testStatus->getValue('skip')) { $this->testStatus = $this->fileSection->run($this, $runConfiguration); $this->testStatus->setExecutedPhpCommand($this->fileSection->getPhpCommand()); - //The test can be skipped by file sections if the CGI executable is not available + //The test status can also be set to 'skip' if the PHP CGI is missing, so we need to check it again here. if(!$this->testStatus->getValue('skip')) { $this->output = $this->fileSection->getOutput(); $this->compareOutput(); diff --git a/src/testcase/rtTestConfiguration.php b/src/testcase/rtTestConfiguration.php index b369d2b..0c540ec 100644 --- a/src/testcase/rtTestConfiguration.php +++ b/src/testcase/rtTestConfiguration.php @@ -92,8 +92,9 @@ class rtTestConfiguration protected function setPhpCommandLineArguments(rtRuntestsConfiguration $runConfiguration, $sections, $fileSection) { //Use different arguments for running SKIPIF and so on. The INI settings may be designed to - //produce an error whaich will inetrfere with SKIPIF processing. - $this->phpNonFileSectionCommandLineArguments = $runConfiguration->getSetting('PhpCommandLineArguments'); + //produce an error which will interfere with SKIPIF processing. + //TODO: But in some cases the INI section is required for SKIPIF so we need another way to deal with this. + //$this->phpNonFileSectionCommandLineArguments = $runConfiguration->getSetting('PhpCommandLineArguments'); $this->phpCommandLineArguments = $runConfiguration->getSetting('PhpCommandLineArguments'); if (array_key_exists('INI', $sections)) { $sections['INI']->substitutePWD($fileSection->getFileName()); @@ -101,6 +102,8 @@ class rtTestConfiguration $args = new rtIniAsCommandLineArgs(); $this->phpCommandLineArguments = $args->settingsToArguments($additionalArguments, $this->phpCommandLineArguments); } + //For SKIPIF sections - supress the error reporting which interferes with skip and warn processing + $this->phpNonFileSectionCommandLineArguments = preg_replace('/error_reporting=32767/', 'error_reporting=0', $this->phpCommandLineArguments); } protected function setTestCommandLineArguments($sections) diff --git a/src/testcase/rtTestOutputWriter.php b/src/testcase/rtTestOutputWriter.php index b582fe6..8cb23ac 100644 --- a/src/testcase/rtTestOutputWriter.php +++ b/src/testcase/rtTestOutputWriter.php @@ -98,21 +98,27 @@ abstract class rtTestOutputWriter * @param integer $processCount * @return string */ - public function getOverview($groups=NULL, $processCount=NULL) + public function getOverview($parallelGroups = 0, $serialGroups= 0, $processCount= 1) { // if the overview was already created retun it if (!is_null($this->overview)) { return $this->overview; } + /* + * Most of the code uses processCount = 0 to indicate serial runs. Clearly there must always be one process, + * so it is set to 1 here to make the output make sense. + */ + if($processCount == 0) { + $processCount ++; + } + // collect data $state = array(); $count = 0; - foreach ($this->resultList as $testGroupResults) { - - foreach ($testGroupResults as $testResult) { - + foreach ($this->resultList as $groupResult) { + foreach($groupResult as $testResult) { $s = $testResult->getStatus()->__toString(); if (!isset($state[$s])) { @@ -121,40 +127,48 @@ abstract class rtTestOutputWriter $state[$s]++; $count++; - } + } } // create the output-string + $secondColPosition = 30; $str = ''; $str .= "\n\n----------------------------------------\n"; - $str .= "Tests:\t\t$count\n"; + $str .= "Tests:"; + $blanks = 30 - strlen("Tests:") - strlen($count); + $str = $this->writeBlanks($str, $blanks); + $str .= "$count\n"; - if (is_numeric($groups)) { + if (is_numeric($parallelGroups)) { - $str .= "Groups:\t\t"; - $blanks = strlen($count)-strlen($groups); - for ($b=0; $b<$blanks; $b++) { - $str .= ' '; - } - $str .= $groups."\n"; + $str .= "Parallel Groups:"; + $blanks = 30 - strlen("Parallel Groups:") - strlen($parallelGroups); + $str = $this->writeBlanks($str, $blanks); + $str .= $parallelGroups."\n"; + } + + if (is_numeric($serialGroups)) { + + $str .= "Serial Groups:"; + $blanks = 30 - strlen("Serial Groups:") - strlen($serialGroups); + $str = $this->writeBlanks($str, $blanks); + $str .= $serialGroups."\n"; } if (is_numeric($processCount)) { - $str .= "Processes:\t"; - $blanks = strlen($count)-strlen($processCount); - for ($b=0; $b<$blanks; $b++) { - $str .= ' '; - } + $str .= "Processes:"; + $blanks = 30 -strlen("Processes:") - strlen($processCount); + $str = $this->writeBlanks($str, $blanks); $str .= $processCount."\n"; } $str .= "----------------------------------------\n"; - + foreach ($state as $k => $v) { - $str .= strtoupper($k).":\t"; + $str .= substr(strtoupper($k), 0, 5).":\t"; $blanks = strlen($count)-strlen($v); for ($b=0; $b<$blanks; $b++) { @@ -289,6 +303,12 @@ abstract class rtTestOutputWriter flush(); } + public function writeBlanks($str, $n) { + for ($b=0; $b<$n; $b++) { + $str .= ' '; + } + return $str; + } } ?> \ No newline at end of file diff --git a/src/testcase/rtTestResults.php b/src/testcase/rtTestResults.php index 93da986..b8f0e8e 100644 --- a/src/testcase/rtTestResults.php +++ b/src/testcase/rtTestResults.php @@ -34,7 +34,7 @@ class rtTestResults public function init(rtPhpTest $testCase = null, rtTestStatus $testStatus = null) { if ($testCase != null) { - $this->title = implode('',$testCase->getSection('TEST')->getContents()); + $this->title = $testCase->getSection('TEST')->getHeader(); $this->testStatus = $testCase->getStatus(); $this->testName = $testCase->getName(); if($testStatus == 'redirected') { @@ -109,7 +109,7 @@ class rtTestResults if (!$runConfiguration->hasCommandLineOption('keep-all') && !$runConfiguration->hasCommandLineOption('keep-clean')) { $testCase->getSection('CLEAN')->deleteFile(); } else { - $this->savedFileNames['clean'] = $this->testName. ".clean"; + $this->savedFileNames['clean'] = $this->testName. ".clean.php"; } } @@ -148,11 +148,11 @@ class rtTestResults //Note: if there are clean and skipif files they will not be deleted if the test fails if ($testCase->hasSection('CLEAN')) { - $this->savedFileNames['clean'] = $this->testName. 'clean.php'; + $this->savedFileNames['clean'] = $this->testName. '.clean.php'; } if ($testCase->hasSection('SKIPIF')) { - $this->savedFileNames['skipif'] = $this->testName. 'skipif.php'; + $this->savedFileNames['skipif'] = $this->testName. '.skipif.php'; } if($testCase->getStatus()->getValue('leak') == true) { @@ -163,19 +163,10 @@ class rtTestResults protected function onSkip(rtPhpTest $testCase, rtRuntestsConfiguration $runConfiguration) { if ($runConfiguration->hasCommandLineOption('keep-all') || $runConfiguration->hasCommandLineOption('keep-skip')) { - $this->savedFileNames['skipif'] = $this->testName. 'skipif.php'; + $this->savedFileNames['skipif'] = $this->testName. '.skipif.php'; } else if($testCase->hasSection('SKIPIF')) { $testCase->getSection('SKIPIF')->deleteFile(); } - - //TODO I think this can go? Have since updated code to BORK on finding blank sections - //It may seem odd to check for an XFAIL if we are skipping the test, on the other hand I found - //a few windows tests with blank XFAIL sections and wanted to know about those. - - if ($testCase->hasSection('XFAIL')) { - $this->testStatus->setTrue('xfail'); - $this->testStatus->setMessage('xfail',$testCase->getSection('XFAIL')->getReason()); - } } diff --git a/src/testcase/sections/executablesections/rtSkipIfSection.php b/src/testcase/sections/executablesections/rtSkipIfSection.php index 4b20834..f5c828d 100644 --- a/src/testcase/sections/executablesections/rtSkipIfSection.php +++ b/src/testcase/sections/executablesections/rtSkipIfSection.php @@ -40,7 +40,7 @@ class rtSkipIfSection extends rtExecutableSection $phpCommand .= ' -f '.$this->fileName; $this->phpCommand = $phpCommand; - + $PhpRunner = new rtPhpRunner($phpCommand, $runConfiguration->getEnvironmentVariables(), $runConfiguration->getSetting('WorkingDirectory') diff --git a/src/testcase/sections/informationsections/rtTestHeaderSection.php b/src/testcase/sections/informationsections/rtTestHeaderSection.php index 241f219..1aa14f8 100644 --- a/src/testcase/sections/informationsections/rtTestHeaderSection.php +++ b/src/testcase/sections/informationsections/rtTestHeaderSection.php @@ -26,5 +26,9 @@ class rtTestHeaderSection extends rtInformationSection { return $this->testHeader; } + + public function addString($string) { + $this->testHeader .= $string; + } } ?> diff --git a/src/testgroup/rtGroupConfiguration.php b/src/testgroup/rtGroupConfiguration.php index 00548e4..4339ee2 100644 --- a/src/testgroup/rtGroupConfiguration.php +++ b/src/testgroup/rtGroupConfiguration.php @@ -17,15 +17,105 @@ class rtGroupConfiguration { protected $testDirectory; + protected $environmentVariables = array(); + protected $isRedirect=false; + protected $serialGroup=false; + protected $redirectFromID = null; - public function __construct(rtRuntestsConfiguration $runConfiguration, $directory) - { + public function __construct($directory) + { $this->testDirectory = $directory; } public function parseRedirect(rtPHPTest $redirectedTest) { - var_dump($redirectedTest->getSection('REDIRECTTEST')); + //Going to assume that we have already parsed the SKIPIF (if it exists) + //var_dump($redirectedTest->getSection('REDIRECTTEST')->getContents()); + $name = $redirectedTest->getName(); + + $code = implode($redirectedTest->getSection('REDIRECTTEST')->getContents(), "\n"); + + //Strip tabs which interfere with eval + $code = preg_replace('|\\t|', ' ', $code); + + $setup = eval($code); + + $this->environmentVariables = $setup['ENV']; + + + //Remove trailing white space and add a slash to teh directory name + //Will need modification if we wanted to be able to give it a list of test directories. + + $dir = trim($setup['TESTS']); + if (substr($dir, -1) !== "/") {$dir .= "/";} + + /* + * Some of the PDO tests have __DIR__ in front of them, means you have to run then from the same place + * as run-tests.php. This is really annoying because in this case it just picks up the location of this class + * which is useless for anything. + */ + + + $stripchars = strlen(__DIR__) + 1; + + if ( substr($dir, 0, strlen(__DIR__)) === __DIR__) { + $dir = substr($dir, $stripchars); + } + + /* + * However... we do need some way of finding the absolute path of the tests. I think it is reasonable to assume + * that they are related to the the path of the test that is redirecting to them. + * Take the first three characters of the directory and try to find them in the name of the redirected test. + * The best way to do this is really for the test case to specify a path relative to the test tgey are redirected from + * but this would mean changes to run-tests.php and to the tests. + */ + + $key = substr($dir, 0, 3); + + //Find the key in the full name of the test contains the redirect + $position = strpos($name, $key); + + //Take the root strng from before the key + $root=substr($name, 0, $position); + + $title = $redirectedTest->getSection('TEST')->getContents(); + $this->redirectFromID = $title[0]; + + //And add it on to form a full directory path. + $dir = $root . $dir; + + $this->testDirectory = $dir; + + $this->isRedirect = true; + + //Finaly add the directory name to the env variables - checked by SKIPIF + $this->environmentVariables['REDIR_TEST_DIR'] = $dir; + + + } + + public function parse() { + //Here insert code to read a config file from the test directory that determines whether the set of tests shoudl be run + //in parallel or not? + $this->serialGroup = false; + return; + + } + + public function getEnvironmentVariables() { + return $this->environmentVariables; + } + public function getTestDirectory() { + return $this->testDirectory; + } + public function isRedirect() { + return $this->isRedirect; } + public function isSerial() { + return $this->serialGroup; + } + public function getRedirectFromID() { + return $this->redirectFromID; + } } diff --git a/src/testgroup/rtPhpTestGroup.php b/src/testgroup/rtPhpTestGroup.php index b052284..b49c0b6 100644 --- a/src/testgroup/rtPhpTestGroup.php +++ b/src/testgroup/rtPhpTestGroup.php @@ -16,20 +16,32 @@ class rtPhpTestGroup extends rtTask implements rtTaskInterface { protected $testDirectory; protected $testCases; - protected $result = null; + protected $result = array(); protected $runConfiguration; + protected $groupConfiguration; - public function __construct(rtRuntestsConfiguration $runConfiguration, $directory) + public function __construct(rtRuntestsConfiguration $runConfiguration, $directory, $groupConfiguration) { $this->runConfiguration = $runConfiguration; $this->testDirectory = $directory; + $this->groupConfiguration = $groupConfiguration; $this->init(); } public function init() - { + { + if($this->groupConfiguration->isRedirect()) { + //merge in environmental variables (this is for REDEIRRECT). + foreach($this->groupConfiguration->getEnvironmentVariables() as $key=>$value) { + $this->runConfiguration->setEnvironmentVariable($key, $value); + } + } + + $this->testFiles = rtUtil::getTestList($this->testDirectory); - + + $redirectFromID = $this->groupConfiguration->getRedirectFromID(); + foreach ($this->testFiles as $testFileName) { //testFiles is a list of file names relative to the current working directory @@ -49,7 +61,7 @@ class rtPhpTestGroup extends rtTask implements rtTaskInterface $testStatus = new rtTestStatus($testFile->getTestName()); if ($testFile->arePreconditionsMet() ) { // Create a new test case - $this->testCases[] = new rtPhpTest($testFile->getContents(), $testFile->getTestName(), $testFile->getSectionHeadings(), $this->runConfiguration, $testStatus); + $this->testCases[] = new rtPhpTest($testFile->getContents(), $testFile->getTestName(), $testFile->getSectionHeadings(), $this->runConfiguration, $testStatus, $redirectFromID); } elseif (in_array("REDIRECTTEST",$testFile->getSectionHeadings())){ //Redirect handler, save the test case for processing after the main groups have finished. //Check to make sure that it shouldn't be skipped, if skipped don't save it @@ -58,7 +70,7 @@ class rtPhpTestGroup extends rtTask implements rtTaskInterface $redirectedTest->runSkipif($this->runConfiguration); if($redirectedTest->getStatus()->getValue('skip')) { $testStatus->setTrue('skip'); - $testStatus->setMessage('skip', $testFile->getExitMessage()); + $testStatus->setMessage('skip', $testFile->getExitMessage(). ' and the skip condition has failed'); } else { $testStatus->setTrue('redirected'); $testStatus->setMessage('redirected', $testFile->getExitMessage()); diff --git a/src/testrun/rtPhpTestRun.php b/src/testrun/rtPhpTestRun.php index cacac1b..8b4c358 100644 --- a/src/testrun/rtPhpTestRun.php +++ b/src/testrun/rtPhpTestRun.php @@ -18,6 +18,12 @@ class rtPhpTestRun protected $commandLineArguments; protected $runConfiguration; protected $redirectedTestCases = array(); + protected $resultList = array(); //An array of arrays of Group Results + protected $serialTasks = array(); + protected $parallelTasks = array(); + protected $reportStatus = 0; + protected $numberOfSerialGroups = 0; + protected $numberOfParallelGroups = 0; public function __construct($argv) { @@ -55,18 +61,24 @@ class rtPhpTestRun //test cases expect to be able to use it. $php = $this->runConfiguration->getSetting('PhpExecutable'); $this->runConfiguration->setEnvironmentVariable('TEST_PHP_EXECUTABLE', $php); + + //Set reporting option + $this->setReportStatus(); - // Main decision point. Either we start this with a directory (or set of directories, in which case tests are - // run as a group (and in parallel if required) or...... - // + /* + * Main decision point. Either we start this with a directory (or set of directories, in which case tests are + * run as a group (and in parallel if required) or...... + */ if ($this->runConfiguration->getSetting('TestDirectories') != null) { - - $this->run_group($this->runConfiguration->getSetting('TestDirectories')); - + + $this->doGroupRuns(); + } else { - //.. the input is a test file, or list of files and are just run as single tests - // and not in parallel - + + /* + *... the input is a test file, or list of files and are just run as single tests + * and not in parallel + */ if ($this->runConfiguration->getSetting('TestFiles') == null) { echo rtText::get('invalidTestFileName'); exit(); @@ -74,110 +86,109 @@ class rtPhpTestRun $this->run_tests($this->runConfiguration->getSetting('TestFiles')); } } - + + /* + * At this stage we have run all groups or tests in the initial input. Now we check if any of those have + * redirected test cases and if so, run those one group at a time. + * It might be possible to run these in parallel too? + */ + $this->buildRedirectsList($this->resultList); if(count($this->redirectedTestCases) > 0) { - foreach($this->redirectedTestCases as $testCase){ - echo $testCase->getName() . "\n"; - - - } - - //For each test case - construct a new group - //Call run_group() again with an array of groups - // - // The redirect section has PHP code in it but no tags. - // It is code the needs to be run as part of run-tests, not as a 'runnable' section - - // eek it's eval(). Is there any better way to do this? It's setting a 'group - // configuration' which we don't have at the moment - so maybe we need one? - // re-implementing a differnt way would be nice. Just reading the config and not eval()ing it - //which seems unnecessary. - // - // for now, rtRedirectedSecion is part of 'config', not part of 'executable'; + $this->doRedirectedRuns(); + } + + if(($this->numberOfSerialGroups != 0) || ($this->numberOfParallelGroups != 0)) { + $this->createRunOutput(); } - } + + public function doGroupRuns() { + + $subDirectories = $this->buildSubDirectoryList($this->runConfiguration->getSetting('TestDirectories')); + $groupConfigurations = $this->buildGroupConfigurations($subDirectories); - public function run_group($testDirectories) { - // make a list of subdirectories which contain tests, includes the top level directory - - $subDirectories = array(); - foreach ($testDirectories as $testDirectory) { - $subDirectories = array_merge($subDirectories, rtUtil::parseDir($testDirectory)); - } - - // check for the cmd-line-option 'z' which defines parellel-execution - $processCount = 0; - if ($this->runConfiguration->hasCommandLineOption('z')) { - - $processCount = $this->runConfiguration->getCommandLineOption('z'); - - if (!is_numeric($processCount) || $processCount < 0) { - $processCount = 2; - } - } + //If there is only one subdirectory, run seqential + if(count($subDirectories) === 1) { + $this->run_serial_groups($subDirectories, $groupConfigurations); + $this->numberOfSerialGroups = 1; + + } else { + + //check to see if this is set to be a parallel run, if not, run the subdirectory groups in sequence. + if($this->requestedProcessorCount() <= 1) { + $this->run_serial_groups($subDirectories, $groupConfigurations); + $this->numberOfSerialGroups = count($subDirectories); + } else { + //At least part of this run can be in parallel, check group configurations to make sure that none are set to be serial. + //This builds parallel and serial task lists. + foreach($groupConfigurations as $key=>$gc) { + if($gc->isSerial()) { + $serialGroups[] = $key; + } else { + $parallelGroups[] = $key; + } + } + + if(isset($serialGroups)) {$this->numberOfSerialGroups = count($serialGroups);} + + $this->numberOfParallelGroups = count($parallelGroups); + + $this->run_parallel_groups($parallelGroups, $groupConfigurations, $this->requestedProcessorCount()); + if($this->numberOfSerialGroups > 0) { + $this->run_serial_groups($serialGroups, $groupConfigurations); + } + } + } + + } + + public function doRedirectedRuns() { + foreach($this->redirectedTestCases as $testCase){ + + $groupConfig = new rtGroupConfiguration(null); + $groupConfig->parseRedirect($testCase); + + $group = $groupConfig->getTestDirectory(); + + $this->run_serial_groups(array($group), array($group=>$groupConfig)); + + $this->numberOfSerialGroups++; + + } + } - // check for the cmd-line-option 'v' which defines the report-status - $reportStatus = 0; - if ($this->runConfiguration->hasCommandLineOption('v')) { - $reportStatus = 1; - } else if ($this->runConfiguration->hasCommandLineOption('vv')) { - $reportStatus = 2; - } else if ($this->runConfiguration->hasCommandLineOption('vvv')) { - $reportStatus = 3; - } + public function run_parallel_groups($testDirectories, $groupConfigurations, $processCount) { // create the task-list $taskList = array(); - - - foreach ($subDirectories as $subDirectory) { - $taskList[] = new rtTaskTestGroup($this->runConfiguration, $subDirectory); + foreach($testDirectories as $testGroup) { + $taskList[] = new rtTaskTestGroup($this->runConfiguration, $testGroup, $groupConfigurations[$testGroup]); } - // run the task-scheduler $scheduler = rtTaskScheduler::getInstance(); $scheduler->setTaskList($taskList); $scheduler->setProcessCount($processCount); - $scheduler->setReportStatus($reportStatus); + $scheduler->setReportStatus($this->reportStatus); $scheduler->run(); - - $resultList = $scheduler->getResultList(); - - //locate any redirected tests in teh group results files. - foreach ($resultList as $testGroupResults) { - - foreach ($testGroupResults as $testResult) { - - if($testResult->getStatus() == 'redirected') { - $this->redirectedTestCases[] = $testResult->getRedirectedTestCase(); - } - } - } - - - // create output - $type = null; - if ($this->runConfiguration->hasCommandLineOption('o')) { - $type = $this->runConfiguration->getCommandLineOption('o'); - } - - $outputWriter = rtTestOutputWriter::getInstance($type); - $outputWriter->setResultList($resultList); - $outputWriter->printOverview(sizeof($taskList), $scheduler->getProcessCount()); - $filename = null; - if ($this->runConfiguration->hasCommandLineOption('s')) { - $filename = $this->runConfiguration->getCommandLineOption('s'); - } - - if ($type || $filename) { - $outputWriter->write($filename); - } - - + foreach($scheduler->getResultList() as $groupResult) { + $this->resultList[] = $groupResult; + } + } + + public function run_serial_groups($testDirectories, $groupConfigurations) { + + foreach($testDirectories as $subDirectory) { + $testGroup = new rtPhpTestGroup($this->runConfiguration, $subDirectory, $groupConfigurations[$subDirectory]); + $testGroup->run(); + rtTestOutputWriter::flushResult($testGroup->getResults(), $this->reportStatus); + $this->resultList[] = $testGroup->getResults(); + + } } + public function run_tests($testNames) { //This section deals with running single test cases, or lists of test cases. @@ -188,7 +199,9 @@ class rtPhpTestRun echo rtText::get('invalidTestFileName', array($testName)); exit(); } - + + $allResults = array(); + //Read the test file $testFile = new rtPhpTestFile(); $testFile->doRead($testName); @@ -211,7 +224,6 @@ class rtPhpTestRun //Redirect handler //Build a list of redirected test cases - //TODO: need to run the skipif section here.. $this->redirectedTestCases[] = new rtPhpTest($testFile->getContents(), $testFile->getTestName(), $testFile->getSectionHeadings(), $this->runConfiguration, $testStatus); $testStatus->setTrue('redirected'); @@ -222,10 +234,84 @@ class rtPhpTestRun $testStatus->setMessage('bork', $testFile->getExitMessage()); $results = new rtTestResults(null, $testStatus); } - rtTestOutputWriter::flushResult(array($results), 3); + + } + } + + public function buildSubDirectoryList($testDirectories){ + $subDirectories = array(); + foreach ($testDirectories as $testDirectory) { + $subDirectories = array_merge($subDirectories, rtUtil::parseDir($testDirectory)); + } + return $subDirectories; + } + + public function requestedProcessorCount() { + // check for the cmd-line-option 'z' which defines parellel-execution + $processCount = 0; + if ($this->runConfiguration->hasCommandLineOption('z')) { + + $processCount = $this->runConfiguration->getCommandLineOption('z'); + + if (!is_numeric($processCount) || $processCount < 0) { + $processCount = 2; + } + } + return $processCount; + } + + public function buildGroupConfigurations($subDirectories) { + $groupConfigurations = array(); + foreach($subDirectories as $subDir) { + $groupConfig = new rtGroupConfiguration($subDir); + $groupConfig->parse(); + $groupConfigurations[$subDir] = $groupConfig; + } + return $groupConfigurations; + } + + public function buildRedirectsList($results) { + foreach ($results as $groupResult) { + foreach($groupResult as $testResult) { + if($testResult->getStatus() == 'redirected') { + $this->redirectedTestCases[] = $testResult->getRedirectedTestCase(); + } + } + } + } + + public function createRunOutput() { + $type = null; + if ($this->runConfiguration->hasCommandLineOption('o')) { + $type = $this->runConfiguration->getCommandLineOption('o'); + } + + $outputWriter = rtTestOutputWriter::getInstance($type); + $outputWriter->setResultList($this->resultList); + + $outputWriter->printOverview($this->numberOfParallelGroups, $this->numberOfSerialGroups, $this->requestedProcessorCount()); + + $filename = null; + if ($this->runConfiguration->hasCommandLineOption('s')) { + $filename = $this->runConfiguration->getCommandLineOption('s'); + } + + if ($type || $filename) { + $outputWriter->write($filename); + } + } + + public function setReportStatus() { + // check for the cmd-line-option 'v' which defines the report-status + if ($this->runConfiguration->hasCommandLineOption('v')) { + $this->reportStatus = 1; + } else if ($this->runConfiguration->hasCommandLineOption('vv')) { + $this->reportStatus = 2; + } else if ($this->runConfiguration->hasCommandLineOption('vvv')) { + $this->reportStatus = 3; } - } + } ?> diff --git a/tests/rtGroupConfigurationTest.php b/tests/rtGroupConfigurationTest.php new file mode 100644 index 0000000..a08e5fb --- /dev/null +++ b/tests/rtGroupConfigurationTest.php @@ -0,0 +1,14 @@ +<?php +require_once dirname(__FILE__) . '/../src/rtAutoload.php'; +require_once dirname(__FILE__) . '/rtTestBootstrap.php'; + +class rtGroupConfigurationTest extends PHPUnit_Framework_TestCase +{ + public function testCreateInstance() + { + //$directory = realpath(dirname(__FILE__) . '/../phpt-tests'); + $config = rtRuntestsConfiguration::getInstance(array('run-tests.php', '-p', RT_PHP_PATH, 'testgroup')); + + $config->configure(); + } +} \ No newline at end of file diff --git a/tests/rtPhpTestGroupTest.php b/tests/rtPhpTestGroupTest.php index 5557b74..0a02131 100644 --- a/tests/rtPhpTestGroupTest.php +++ b/tests/rtPhpTestGroupTest.php @@ -13,9 +13,11 @@ class rtPHpTestGroupTest extends PHPUnit_Framework_TestCase $directory = realpath(dirname(__FILE__) . '/../phpt-tests'); $config = rtRuntestsConfiguration::getInstance(array('run-tests.php', '-p', RT_PHP_PATH, $directory)); - $config->configure(); + $config->configure(); + + $gConf = new rtGroupConfiguration('wibble'); - $phpTestGroup = new rtPhpTestGroup($config, $directory); + $phpTestGroup = new rtPhpTestGroup($config, $directory, $gConf); $validTestCaseCount = count($phpTestGroup->getTestCases()); @@ -36,9 +38,10 @@ class rtPHpTestGroupTest extends PHPUnit_Framework_TestCase $directory = realpath(dirname(__FILE__) . '/../phpt-tests'); $config = rtRuntestsConfiguration::getInstance(array('run-tests.php', '-p', RT_PHP_PATH, $directory)); - $config->configure(); + $config->configure(); + $gConf = new rtGroupConfiguration('wibble'); - $phpTestGroup = new rtPhpTestGroup($config, $directory); + $phpTestGroup = new rtPhpTestGroup($config, $directory, $gConf);