diff --git a/api/RestfulServer.php b/api/RestfulServer.php index 27e555c15..7714b8de5 100644 --- a/api/RestfulServer.php +++ b/api/RestfulServer.php @@ -162,12 +162,12 @@ class RestfulServer extends Controller { } } else { - $obj = $this->search($className, $this->request->getVars(), $sort, $limit); - // show empty serialized result when no records are present - if(!$obj) $obj = new DataObjectSet(); if(!singleton($className)->stat('api_access')) { return $this->permissionFailure(); } + $obj = $this->search($className, $this->request->getVars(), $sort, $limit); + // show empty serialized result when no records are present + if(!$obj) $obj = new DataObjectSet(); } if($obj instanceof DataObjectSet) return $formatter->convertDataObjectSet($obj); @@ -187,9 +187,13 @@ class RestfulServer extends Controller { * @return DataObjectSet */ protected function search($className, $params = null, $sort = null, $limit = null, $existingQuery = null) { - $searchContext = singleton($className)->getDefaultSearchContext(); + if(singleton($className)->hasMethod('getRestfulSearchContext')) { + $searchContext = singleton($className)->{'getRestfulSearchContext'}(); + } else { + $searchContext = singleton($className)->getDefaultSearchContext(); + } $query = $searchContext->getQuery($params, $sort, $limit, $existingQuery); - + return singleton($className)->buildDataObjectSet($query->execute()); } diff --git a/cli-script.php b/cli-script.php index 0e4dffa1a..920fda1b6 100755 --- a/cli-script.php +++ b/cli-script.php @@ -49,7 +49,10 @@ if( preg_match( '/(test\.totallydigital\.co\.nz|dev\.totallydigital\.co\.nz\/tes } else { echo "Error: could not determine server configuration {$_SERVER['SCRIPT_FILENAME']}\n"; exit(); -} +} + +// set request method (doesn't allow POST through cli) +$_SERVER['REQUEST_METHOD'] = "GET"; $baseURL = dirname( dirname( $_SERVER['SCRIPT_NAME'] ) ); diff --git a/core/model/DataObject.php b/core/model/DataObject.php index c6a58e887..a76075ee2 100644 --- a/core/model/DataObject.php +++ b/core/model/DataObject.php @@ -1081,8 +1081,9 @@ class DataObject extends ViewableData implements DataObjectInterface { if ($this->many_many()) { foreach($this->many_many() as $relationship => $component) { $relationshipFields = singleton($component)->summary_fields(); + $filterWhere = $this->getManyManyFilter($relationship, $component); $filterJoin = $this->getManyManyJoin($relationship, $component); - $tableField = new ComplexTableField($this, $relationship, $component, $relationshipFields, "getCMSFields", '', '', $filterJoin); + $tableField = new ComplexTableField($this, $relationship, $component, $relationshipFields, "getCMSFields", $filterWhere, '', $filterJoin); $tableField->popupClass = "ScaffoldingComplexTableField_Popup"; $fieldSet->addFieldToTab("Root.$relationship", $tableField); } @@ -1111,6 +1112,12 @@ class DataObject extends ViewableData implements DataObjectInterface { return "LEFT JOIN `$table` ON (`$componentField` = `$componentClass`.`ID` AND `$parentField` = '{$this->ID}')"; } } + + function getManyManyFilter($componentName, $baseTable) { + list($parentClass, $componentClass, $parentField, $componentField, $table) = $this->many_many($componentName); + + return "`$table`.`$parentField` = '{$this->ID}'"; + } /** * Return the class of a one-to-one component. If $component is null, return all of the one-to-one components and their classes. diff --git a/dev/Debug.php b/dev/Debug.php index 75f16f6ff..b7fde16aa 100644 --- a/dev/Debug.php +++ b/dev/Debug.php @@ -135,12 +135,17 @@ class Debug { if(!Director::isLive()) { $caller = Debug::caller(); $file = basename($caller['file']); - echo "
\n"; - if($showHeader) echo "Debug (line $caller[line] of $file):\n "; - echo Convert::raw2xml(trim($message)) . "
\n"; + if(Director::is_cli()) { + if($showHeader) echo "Debug (line $caller[line] of $file):\n "; + echo trim($message) . "\n"; + } else { + echo "\n"; + if($showHeader) echo "Debug (line $caller[line] of $file):\n "; + echo Convert::raw2xml(trim($message)) . "
\n"; + } } } - + /** * Load an error handler * @@ -208,20 +213,7 @@ class Debug { echo "Line $errline in $errfile
"; echo ''; echo ''; - $offset++; - foreach($lines as $line) { - $line = htmlentities($line); - if ($offset == $errline) { - echo "$offset $line"; - } else { - echo "$offset $line"; - } - $offset++; - } + Debug::showLines($errfile, $errline); echo '
'; + $offset++; + foreach($lines as $line) { + $line = htmlentities($line); + if ($offset == $errline) { + echo "$offset $line"; + } else { + echo "$offset $line"; + } + $offset++; + } + echo ''; + } static function emailError($emailAddress, $errno, $errstr, $errfile, $errline, $errcontext, $errorType = "Error") { if(strtolower($errorType) == 'warning') { diff --git a/dev/SapphireTestReporter.php b/dev/SapphireTestReporter.php new file mode 100644 index 000000000..4aeee0067 --- /dev/null +++ b/dev/SapphireTestReporter.php @@ -0,0 +1,286 @@ +timer = new Benchmark_Timer(); + $this->hasTimer = true; + } else { + $this->hasTimer = false; + } + + $this->suiteResults = array( + 'suites' => array(), // array of suites run + 'hasTimer' => $this->hasTimer, // availability of PEAR Benchmark_Timer + 'totalTests' => 0 // total number of tests run + ); + } + + /** + * Returns the suite results + * + * @access public + * @return array Suite results + */ + public function getSuiteResults() { + return $this->suiteResults; + } + + /** + * Sets up the container for result details of the current test suite when + * each suite is first run + * + * @access public + * @param obj PHPUnit2_Framework_TestSuite, the suite that is been run + * @return void + */ + public function startTestSuite( PHPUnit_Framework_TestSuite $suite) { + if(strlen($suite->getName())) { + $this->currentSuite = array( + 'suite' => $suite, // the test suite + 'tests' => array(), // the tests in the suite + 'errors' => 0, // number of tests with errors + 'failures' => 0, // number of tests which failed + 'incomplete' => 0); // number of tests that were not completed correctly + } + } + + /** + * Sets up the container for result details of the current test when each + * test is first run + * + * @access public + * @param obj PHPUnit_Framework_Test, the test that is being run + * @return void + */ + public function startTest(PHPUnit_Framework_Test $test) { + if($test instanceof PHPUnit_Framework_TestCase) { + $this->currentTest = array( + 'name' => preg_replace('(\(.*\))', '', $test->toString()), // the name of the test (without the suite name) + 'timeElapsed' => 0, // execution time of the test + 'status' => TEST_SUCCESS, // status of the test execution + 'message' => '', // user message of test result + 'exception' => NULL, // original caught exception thrown by test upon failure/error + 'uid' => md5(microtime()) // a unique ID for this test (used for identification purposes in results) + ); + if($this->hasTimer) $this->timer->start(); + } + } + + /** + * Adds the failure detail to the current test and increases the failure + * count for the current suite + * + * @access public + * @param obj PHPUnit_Framework_Test, current test that is being run + * @param obj PHPUnit_Framework_AssertationFailedError, PHPUnit error + * @return void + */ + public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) { + $this->currentSuite['failures']++; + $this->currentTest['status'] = TEST_FAILURE; + $this->currentTest['message'] = $e->toString(); + $this->currentTest['exception'] = $this->getTestException($test, $e); + } + + /** + * Adds the error detail to the current test and increases the error + * count for the current suite + * + * @access public + * @param obj PHPUnit_Framework_Test, current test that is being run + * @param obj PHPUnit_Framework_AssertationFailedError, PHPUnit error + * @return void + */ + public function addError(PHPUnit_Framework_Test $test, Exception $e, $time) { + $this->currentSuite['errors']++; + $this->currentTest['status'] = TEST_ERROR; + $this->currentTest['message'] = $e->getMessage(); + $this->currentTest['exception'] = $this->getTestException($test, $e); + } + + /** + * Adds the test incomplete detail to the current test and increases the incomplete + * count for the current suite + * + * @access public + * @param obj PHPUnit_Framework_Test, current test that is being run + * @param obj PHPUnit_Framework_AssertationFailedError, PHPUnit error + * @return void + */ + public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time) { + $this->currentSuite['incomplete']++; + $this->currentTest['status'] = TEST_INCOMPLETE; + $this->currentTest['message'] = $e->toString(); + $this->currentTest['exception'] = $this->getTestException($test, $e); + } + + /** + * Not used + * + * @param PHPUnit_Framework_Test $test + * @param unknown_type $time + */ + public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time) { + // not implemented + } + + /** + * Upon completion of a test, records the execution time (if available) and adds the test to + * the tests performed in the current suite. + * + * @access public + * @param obj PHPUnit_Framework_Test, current test that is being run + * @return void + */ + public function endTest( PHPUnit_Framework_Test $test, $time) { + if($this->hasTimer) { + $this->timer->stop(); + $this->currentTest['timeElapsed'] = $this->timer->timeElapsed(); + } + array_push($this->currentSuite['tests'], $this->currentTest); + } + + /** + * Upon completion of a test suite adds the suite to the suties performed + * + * @acces public + * @param obj PHPUnit_Framework_TestSuite, current suite that is being run + * @return void + */ + public function endTestSuite( PHPUnit_Framework_TestSuite $suite) { + if(strlen($suite->getName())) { + array_push($this->suiteResults['suites'], $this->currentSuite); + } + } + + /** + * Trys to get the original exception thrown by the test on failure/error + * to enable us to give a bit more detail about the failure/error + * + * @access private + * @param obj PHPUnit_Framework_Test, current test that is being run + * @param obj PHPUnit_Framework_AssertationFailedError, PHPUnit error + * @return array + */ + private function getTestException(PHPUnit_Framework_Test $test, Exception $e) { + // get the name of the testFile from the test + $testName = ereg_replace('(.*)\((.*[^)])\)', '\\2', $test->toString()); + $trace = $e->getTrace(); + // loop through the exception trace to find the original exception + for($i = 0; $i < count($trace); $i++) { + + if(array_key_exists('file', $trace[$i])) { + if(stristr($trace[$i]['file'], $testName.'.php') != false) return $trace[$i]; + } + if(array_key_exists('file:protected', $trace[$i])) { + if(stristr($trace[$i]['file:protected'], $testName.'.php') != false) return $trace[$i]; + } + } + } + + /** + * Display error bar if it exists + */ + public function writeResults() { + $passCount = 0; + $failCount = 0; + $testCount = 0; + $errorCount = 0; + + foreach($this->suiteResults['suites'] as $suite) { + foreach($suite['tests'] as $test) { + $testCount++; + ($test['status'] == 1) ? $passCount++ : $failCount++; + if ($test['status'] == -1) { + echo "
".htmlentities($test['message'])."
In line {$test['exception']['line']} of {$test['exception']['file']}
Using the following subclasses of SapphireTest for testing: " . implode(", ", $classList) . "
"; + if (count($classList) > 1) { + echo "Running test cases: " . implode(", ", $classList) . "
"; + } else { + echo "Running test case:
"; + } echo ""; } else { echo "Sapphire PHPUnit Test Runner\n"; echo "Using the following subclasses of SapphireTest for testing: " . implode(", ", $classList) . "\n\n"; @@ -114,18 +128,23 @@ class TestRunner extends Controller { $suite->addTest(new PHPUnit_Framework_TestSuite($className)); } + $reporter = new SapphireTestReporter(); + $results = new PHPUnit_Framework_TestResult(); + $results->addListener($reporter); + /*, array("reportDirectory" => "/Users/sminnee/phpunit-report")*/ if($coverage) { - $testResult = PHPUnit_TextUI_TestRunner::run($suite, array("reportDirectory" => "../assets/coverage-report")); - } else { - $testResult = PHPUnit_TextUI_TestRunner::run($suite); - } - - if($coverage) { + $suite->run($results); + //$testResult = PHPUnit_TextUI_TestRunner::run($suite, array("reportDirectory" => "../assets/coverage-report")); $coverageURL = Director::absoluteURL('assets/coverage-report'); echo ""; + } else { + $suite->run($results); + //$testResult = PHPUnit_TextUI_TestRunner::run($suite); } + $reporter->writeResults(); + if(!Director::is_cli()) echo '