public function testGetBackgroundColors() { $expectedKeys = array('black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'light_gray'); $this->assertEquals($expectedKeys, Utility::getBackgroundColors()); }
/** * Run Both Unit-test and Code-coverage analysist * * @param string Path to Test (Juriya idiomatic style directory structure is required) * @return stream */ public static function runTest($target = '') { $paths = explode(PATH_SEPARATOR, get_include_path()); $pwd = $_SERVER['PWD']; if (!$target) { self::out('--test command missed target folder'); } else { // Find configuration definition $configuration = ''; $configurationExists = FALSE; if (file_exists($pwd . DIRECTORY_SEPARATOR . 'phpunit.xml')) { $configuration = $pwd . DIRECTORY_SEPARATOR . 'phpunit.xml'; $configurationExists = TRUE; } elseif (($path = $pwd . DIRECTORY_SEPARATOR . $target) && is_dir($path)) { if (($testSrc = $path . DIRECTORY_SEPARATOR . 'Tests') && is_dir($testSrc)) { $configuration = $testSrc . DIRECTORY_SEPARATOR . 'phpunit.xml'; } } else { foreach ($paths as $path) { if (($testSrc = $path . DIRECTORY_SEPARATOR . $target . DIRECTORY_SEPARATOR . 'Tests') && is_dir($testSrc)) { $configuration = $testSrc . DIRECTORY_SEPARATOR . 'phpunit.xml'; break 1; } } } // Garbage collection $coveragePhp = '/tmp/coverage.php'; file_exists($coveragePhp) and File::delete($coveragePhp); // Try to read the configuration try { $configurationInstance = \PHPUnit_Util_Configuration::getInstance($configuration); } catch (\Exception $e) { print $e->getMessage() . "\n"; exit(\PHPUnit_TextUI_TestRunner::FAILURE_EXIT); } $configurationArray = $configurationInstance->getPHPUnitConfiguration(); // Validate test suites before start processing furthermore $testDirectory = new \DirectoryIterator(str_replace('phpunit.xml', '', $configuration)); $validTestSuite = FALSE; while ($testDirectory->valid()) { if ($testDirectory->isFile() && strpos($testDirectory->getFilename(), 'Test.php') !== FALSE) { $validTestSuite = TRUE; } $testDirectory->next(); } if (!$validTestSuite) { $errorText = 'ERROR: ' . I18n::translate('command_testsuite_not_found'); if ($configurationArray['colors'] === TRUE) { $errorText = Utility::getColoredString($errorText, 'black', 'red'); } self::out($errorText); exit(\PHPUnit_TextUI_TestRunner::FAILURE_EXIT); } // Reset server arguments $_SERVER['argv'] = array('--configuration', $configuration, '--coverage-php', $coveragePhp); // Preparing reports $strippedText = 'Generating code coverage report in PHP format ... done'; $reportTestTitle = 'PHPUnit Report : '; self::out($configurationArray['colors'] ? Utility::getColoredString($reportTestTitle, 'yellow') : $reportTestTitle); $reportCcTitle = 'CodeCoverage Report : '; $reportCcTitle = $configurationArray['colors'] ? Utility::getColoredString($reportCcTitle, 'yellow') : $reportCcTitle; // Get phpunit report ob_start(); $exitCode = \PHPUnit_TextUI_Command::main(FALSE); $out = ob_get_clean(); $report = substr($out, strpos($out, "\n\n") + 1); $unitTestReport = str_replace($strippedText, $reportCcTitle, $report); $codeCoverage = @unserialize(File::read($coveragePhp)); $codeCoverageData = $codeCoverage->getData(); $codeCoverageRoot = $codeCoverage->getReport(); $codeCoverageNodes = $codeCoverageRoot->getChildNodes(); // Generate code coverage report $classCoverage = array(); $overallCoverage = array('totalClasses' => 0, 'totalCoveredClasses' => 0, 'totalMethods' => 0, 'totalCoveredMethods' => 0, 'totalLines' => 0, 'totalCoveredLines' => 0); // Closure for calculate the level $calculate = function ($percentages) { $level = 'gray'; if ($percentages >= 75) { $level = 'green'; } elseif ($percentages < 75 && $percentages >= 50) { $level = 'yellow'; } elseif ($percentages < 50 && $percentages >= 25) { $level = 'magenta'; } else { $level = 'red'; } return $level; }; $collectFromNode = function ($node) { $classes = array_merge($node->getClasses(), $node->getTraits()); $coverage = $node->getCoverageData(); $lines = array(); $ignoredLines = $node->getIgnoredLines(); foreach ($classes as $className => $class) { $classStatements = 0; $coveredClassStatements = 0; $coveredMethods = 0; foreach ($class['methods'] as $methodName => $method) { $methodCount = 0; $methodLines = 0; $methodLinesCovered = 0; for ($z = $method['startLine']; $z <= $method['endLine']; $z++) { if (isset($ignoredLines[$z])) { continue; } $add = TRUE; $count = 0; if (isset($coverage[$z])) { if ($coverage[$z] !== NULL) { $classStatements++; $methodLines++; } else { $add = FALSE; } $count = count($coverage[$z]); if ($count > 0) { $coveredClassStatements++; $methodLinesCovered++; } } else { $add = FALSE; } $methodCount = max($methodCount, $count); if ($add) { $lines[$z] = array('count' => $count, 'type' => 'stmt'); } } if ($methodCount > 0) { $coveredMethods++; } } if (!empty($class['package']['namespace'])) { $namespace = '\\' . $class['package']['namespace'] . '\\'; } elseif (!empty($class['package']['fullPackage'])) { $namespace = '@' . $class['package']['fullPackage'] . '\\'; } else { $namespace = ''; } return array('namespace' => $namespace, 'className' => $className, 'methodsCovered' => $coveredMethods, 'methodCount' => count($class['methods']), 'statementsCovered' => $coveredClassStatements, 'statementCount' => $classStatements); } }; for ($i = 0, $max = count($codeCoverageNodes); $i < $max; $i++) { $item = $codeCoverageNodes[$i]; if ($item instanceof \PHP_CodeCoverage_Report_Node_File) { array_push($classCoverage, $collectFromNode($item)); } elseif ($item instanceof \PHP_CodeCoverage_Report_Node_Directory) { foreach ($item->getChildNodes() as $itemNode) { array_push($classCoverage, $collectFromNode($itemNode)); } } else { continue; } } ksort($classCoverage); $codeCoverageReport = ''; for ($x = 0, $max = count($classCoverage); $x < $max; $x++) { $classInfo = $classCoverage[$x]; $fullQualifiedPath = $classInfo['namespace'] . $classInfo['className']; if ($classInfo['statementsCovered'] != 0) { // Increase overallCoverage $overallCoverage['totalClasses']++; $overallCoverage['totalMethods'] = $overallCoverage['totalMethods'] + $classInfo['methodCount']; $overallCoverage['totalCoveredMethods'] = $overallCoverage['totalCoveredMethods'] + $classInfo['methodsCovered']; $overallCoverage['totalLines'] = $overallCoverage['totalLines'] + $classInfo['statementCount']; $overallCoverage['totalCoveredLines'] = $overallCoverage['totalCoveredLines'] + $classInfo['statementsCovered']; // Build item Code-coverage main report $ccPath = $fullQualifiedPath; $ccMethods = str_pad(' Methods(' . $classInfo['methodsCovered'] . '/' . $classInfo['methodCount'] . ')', 18, ' '); $ccLines = str_pad(' Lines(' . $classInfo['statementsCovered'] . '/' . $classInfo['statementCount'] . ')', 18, ' '); // Calculate the percentage $ccMethodsPercentages = round((int) $classInfo['methodsCovered'] / (int) $classInfo['methodCount'] * 100, 2); $ccLinesPercentages = round((int) $classInfo['statementsCovered'] / (int) $classInfo['statementCount'] * 100, 2); // Normalize the pad $ccMethodsPercentagesText = $ccMethods . ': ' . str_pad($ccMethodsPercentages . '% ', 8, ' ', STR_PAD_LEFT); $ccLinesPercentagesText = $ccLines . ': ' . str_pad($ccLinesPercentages . '% ', 8, ' ', STR_PAD_LEFT); // Increase classed covered for 100% classes if ($ccMethodsPercentages == 100 && $ccLinesPercentages == 100) { $overallCoverage['totalCoveredClasses']++; } // Colorize if necessary if ($configurationArray['colors'] === TRUE) { $ccPath = Utility::getColoredString($ccPath, 'light_cyan'); $ccMethodsLevel = $calculate($ccMethodsPercentages); $ccLinesLevel = $calculate($ccLinesPercentages); $ccMethodsPercentagesText = Utility::getColoredString($ccMethodsPercentagesText, 'black', $ccMethodsLevel); $ccLinesPercentagesText = Utility::getColoredString($ccLinesPercentagesText, 'black', $ccLinesLevel); } $codeCoverageReport .= PHP_EOL . $ccPath . PHP_EOL . $ccMethodsPercentagesText . PHP_EOL . $ccLinesPercentagesText . PHP_EOL; } } // Finalize Code-coverage report if ($overallCoverage['totalCoveredClasses'] > 0) { $overallPercentage = round((int) $overallCoverage['totalCoveredClasses'] / (int) $overallCoverage['totalClasses'] * 100, 2); } else { $overallPercentage = 0; } if ($overallPercentage >= 75) { $overallPercentageText = 'OK'; } elseif ($overallPercentage < 75 && $overallPercentage >= 50) { $overallPercentageText = 'MEDIUM'; } else { $overallPercentageText = 'LOW'; } $overallCoverageReport = $overallPercentageText . ' ('; $overallCoverageReport .= $overallPercentage; $overallCoverageReport .= '% classes, '; if ($overallCoverage['totalCoveredMethods'] > 0) { $overallCoverageReport .= round((int) $overallCoverage['totalCoveredMethods'] / (int) $overallCoverage['totalMethods'] * 100, 2); } else { $overallCoverageReport .= 0; } $overallCoverageReport .= '% methods, '; if ($overallCoverage['totalCoveredLines'] > 0) { $overallCoverageReport .= round((int) $overallCoverage['totalCoveredLines'] / (int) $overallCoverage['totalLines'] * 100, 2); } else { $overallCoverageReport .= 0; } $overallCoverageReport .= '% lines were covered)'; // Colorize if necessary if ($configurationArray['colors'] === TRUE) { $coverageStatus = $calculate($overallPercentage); $overallCoverageReport = Utility::getColoredString($overallCoverageReport, 'black', $coverageStatus); } $codeCoverageReport .= PHP_EOL . $overallCoverageReport . PHP_EOL; // Output all tests reports self::out($unitTestReport . $codeCoverageReport); exit($exitCode); } }
/** * Serve trace spec * * @param string Trace type * @param string Trace message * @param array Trace stack * @throws SomeException * @return mixed */ public static function trace($type = '', $message = '', $stack = array()) { // Resolve tunnel self::_resolveTunnel(); $tunnel = self::$tunnel; if ($tunnel == 'CLI') { $output = Utility::getColoredString(str_pad('TYPE ', 7, " ", STR_PAD_LEFT), 'yellow'); $output .= Utility::getColoredString($type, 'cyan') . "\n"; $output .= Utility::getColoredString(str_pad('MSG ', 7, " ", STR_PAD_LEFT), 'yellow'); $output .= Utility::getColoredString($message, 'cyan') . "\n"; } else { $output = '<strong>' . str_pad('TYPE ', 7, " ", STR_PAD_LEFT) . '</strong><span>' . $type . '</span>' . "\n"; $output .= '<strong>' . str_pad('MSG ', 7, " ", STR_PAD_LEFT) . '</strong><span>' . $message . '</span>' . "\n"; } if (!empty($stack)) { // Begin format the stack trace $formatedStack = array(); $paddedSpace = ' '; foreach ($stack as $index => $item) { $itemNode = ''; // Add new line for CLI if ($tunnel == 'CLI') { $itemNode .= "\n"; $itemNode .= Utility::getColoredString(str_pad('#' . ($index + 1), 10, ' ', STR_PAD_LEFT), 'green'); $itemNode .= "\n"; } foreach ($item as $head => $body) { // Parse the arguments if ($head == 'args') { $body = var_export($body, TRUE); $body = str_replace("\n", '', $body); } // Build the trace stack if ($tunnel == 'CLI') { $itemNode .= Utility::getColoredString(str_pad(strtoupper($head), 10, " ", STR_PAD_LEFT), 'light_green'); $itemNode .= " => " . Utility::getColoredString($body, 'cyan') . "\n"; } else { $itemNode .= $paddedSpace . '<strong>' . str_pad(strtoupper($head), 10, " ", STR_PAD_LEFT) . ' </strong>'; $itemNode .= '<span>' . $body . ' </span>' . "\n"; } } $formatedStack[] = $itemNode; } // Return the stack trace based by request's tunnel method if ($tunnel == 'CLI') { $output .= Utility::getColoredString(str_pad('STACK ', 7, " ", STR_PAD_LEFT), 'yellow') . Utility::getColoredString(count($formatedStack) . ' level(s)', 'cyan') . "\n" . "\n" . Utility::getColoredString('#START', 'green') . "\n" . implode('', $formatedStack) . "\n" . Utility::getColoredString('#END', 'green'); } else { $output .= '<strong>' . str_pad('STACK ', 7, " ", STR_PAD_LEFT) . '</strong>' . "\n" . implode("\n", $formatedStack); } } // Return the formatted dump if (self::$tunnel == 'CLI') { return "\n" . $output . "\n"; } else { return '<pre class="debug">' . $output . '</pre>'; } }