diff --git a/.travis.yml b/.travis.yml
index 83c3c4d55..d57935b1e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -12,5 +12,7 @@ matrix:
before_script:
- composer install
- cp src/config.php.dist src/config.php
+ - git remote set-branches --add origin master
+ - git fetch
script:
-- ./build.sh
+ - composer check
diff --git a/build.sh b/build.sh
deleted file mode 100755
index 0a7560724..000000000
--- a/build.sh
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/bin/bash
-set -e
-
-vendor/bin/parallel-lint --exclude vendor .
-git diff origin/master... > diff.txt
-
-vendor/bin/phpcs \
- --standard=tools/codesniffer/JoindInPSR2/ruleset.xml \
- --ignore=**/config.php,**/database.php,vendor,tools,tests/bootstrap.php \
- --extensions=php \
- --runtime-set ignore_warnings_on_exit true \
- -p \
- .
-
-
-php vendor/bin/security-checker security:check composer.lock
-
-vendor/bin/phpunit
-vendor/bin/diffFilter --phpunit diff.txt build/logs/clover.xml 80
diff --git a/build.xml b/build.xml
index 096ddb7a5..1b92dd925 100644
--- a/build.xml
+++ b/build.xml
@@ -4,6 +4,13 @@
+
+
+
+
+
+
+
@@ -18,12 +25,12 @@
-
+
-
+
@@ -39,16 +46,7 @@
-
-
-
-
-
-
-
-
-
-
+
@@ -77,17 +75,11 @@
-
-
-
-
-
-
-
-
+
+
-
+
diff --git a/composer.json b/composer.json
index 535ef0b8d..dd5aa6832 100644
--- a/composer.json
+++ b/composer.json
@@ -49,6 +49,25 @@
"tests/models",
"tests/routers",
"tests/views"
+ ],
+ "files": ["tests/compatibility/File.php"]
+ },
+ "scripts": {
+ "test": "phpunit -c . tests/",
+ "lint": "parallel-lint --exclude vendor .",
+ "sniff": "phpcs --standard=tools/codesniffer/JoindInPSR2/ruleset.xml --ignore=**/config.php,**/database.php,vendor,tools,tests/bootstrap.php --extensions=php --report-checkstyle=build/logs/checkstyle.xml --runtime-set ignore_warnings_on_exit true -p .",
+ "security": "security-checker security:check composer.lock",
+ "coverage": [
+ "git diff origin/master... -- > diff.txt",
+ "diffFilter --phpunit diff.txt build/logs/clover.xml 80"
+ ],
+ "check": [
+ "mkdir -p build/logs",
+ "@lint",
+ "@sniff",
+ "@security",
+ "@test",
+ "@coverage"
]
},
"config" : {
diff --git a/tests/compatibility/File.php b/tests/compatibility/File.php
new file mode 100644
index 000000000..2aa752aaf
--- /dev/null
+++ b/tests/compatibility/File.php
@@ -0,0 +1,569 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\CodeCoverage\Report\Html;
+
+use SebastianBergmann\CodeCoverage\Node\File as FileNode;
+use SebastianBergmann\CodeCoverage\Util;
+
+/**
+ * This is a temporary fix for phpunit 5 coverage running in php 7
+ * This will throw an exception requiring removal when phpunit is upgraded
+ * past 6.1.1 that has the fix.
+ */
+
+if (PHP_MAJOR_VERSION >= 7) {
+ if (class_exists('\PHPUnit\Runner\Version')) {
+ $version = new \PHPUnit\Runner\Version();
+ $version = explode(".", $version->id());
+ if ($version[0] >= 6 && $version[1] > 1) {
+ throw new \Exception(
+ "This file is no longer needed with version of php and phpunit, please remove from composer.json"
+ );
+ }
+ }
+}
+
+/**
+ * Renders a file node.
+ */
+class File extends Renderer
+{
+ /**
+ * @var int
+ */
+ private $htmlspecialcharsFlags;
+
+ /**
+ * Constructor.
+ *
+ * @param string $templatePath
+ * @param string $generator
+ * @param string $date
+ * @param int $lowUpperBound
+ * @param int $highLowerBound
+ */
+ public function __construct($templatePath, $generator, $date, $lowUpperBound, $highLowerBound)
+ {
+ parent::__construct(
+ $templatePath,
+ $generator,
+ $date,
+ $lowUpperBound,
+ $highLowerBound
+ );
+
+ $this->htmlspecialcharsFlags = ENT_COMPAT;
+
+ $this->htmlspecialcharsFlags = $this->htmlspecialcharsFlags | ENT_HTML401 | ENT_SUBSTITUTE;
+ }
+
+ /**
+ * @param FileNode $node
+ * @param string $file
+ */
+ public function render(FileNode $node, $file)
+ {
+ $template = new \Text_Template($this->templatePath . 'file.html', '{{', '}}');
+
+ $template->setVar(
+ [
+ 'items' => $this->renderItems($node),
+ 'lines' => $this->renderSource($node)
+ ]
+ );
+
+ $this->setCommonTemplateVariables($template, $node);
+
+ $template->renderTo($file);
+ }
+
+ /**
+ * @param FileNode $node
+ *
+ * @return string
+ */
+ protected function renderItems(FileNode $node)
+ {
+ $template = new \Text_Template($this->templatePath . 'file_item.html', '{{', '}}');
+
+ $methodItemTemplate = new \Text_Template(
+ $this->templatePath . 'method_item.html',
+ '{{',
+ '}}'
+ );
+
+ $items = $this->renderItemTemplate(
+ $template,
+ [
+ 'name' => 'Total',
+ 'numClasses' => $node->getNumClassesAndTraits(),
+ 'numTestedClasses' => $node->getNumTestedClassesAndTraits(),
+ 'numMethods' => $node->getNumMethods(),
+ 'numTestedMethods' => $node->getNumTestedMethods(),
+ 'linesExecutedPercent' => $node->getLineExecutedPercent(false),
+ 'linesExecutedPercentAsString' => $node->getLineExecutedPercent(),
+ 'numExecutedLines' => $node->getNumExecutedLines(),
+ 'numExecutableLines' => $node->getNumExecutableLines(),
+ 'testedMethodsPercent' => $node->getTestedMethodsPercent(false),
+ 'testedMethodsPercentAsString' => $node->getTestedMethodsPercent(),
+ 'testedClassesPercent' => $node->getTestedClassesAndTraitsPercent(false),
+ 'testedClassesPercentAsString' => $node->getTestedClassesAndTraitsPercent(),
+ 'crap' => 'CRAP'
+ ]
+ );
+
+ $items .= $this->renderFunctionItems(
+ $node->getFunctions(),
+ $methodItemTemplate
+ );
+
+ $items .= $this->renderTraitOrClassItems(
+ $node->getTraits(),
+ $template,
+ $methodItemTemplate
+ );
+
+ $items .= $this->renderTraitOrClassItems(
+ $node->getClasses(),
+ $template,
+ $methodItemTemplate
+ );
+
+ return $items;
+ }
+
+ /**
+ * @param array $items
+ * @param \Text_Template $template
+ * @param \Text_Template $methodItemTemplate
+ *
+ * @return string
+ */
+ protected function renderTraitOrClassItems(array $items, \Text_Template $template, \Text_Template $methodItemTemplate)
+ {
+ if (empty($items)) {
+ return '';
+ }
+
+ $buffer = '';
+
+ foreach ($items as $name => $item) {
+ $numMethods = count($item['methods']);
+ $numTestedMethods = 0;
+
+ foreach ($item['methods'] as $method) {
+ if ($method['executedLines'] == $method['executableLines']) {
+ $numTestedMethods++;
+ }
+ }
+
+ if ($item['executableLines'] > 0) {
+ $numClasses = 1;
+ $numTestedClasses = $numTestedMethods == $numMethods ? 1 : 0;
+ $linesExecutedPercentAsString = Util::percent(
+ $item['executedLines'],
+ $item['executableLines'],
+ true
+ );
+ } else {
+ $numClasses = 'n/a';
+ $numTestedClasses = 'n/a';
+ $linesExecutedPercentAsString = 'n/a';
+ }
+
+ $buffer .= $this->renderItemTemplate(
+ $template,
+ [
+ 'name' => $name,
+ 'numClasses' => $numClasses,
+ 'numTestedClasses' => $numTestedClasses,
+ 'numMethods' => $numMethods,
+ 'numTestedMethods' => $numTestedMethods,
+ 'linesExecutedPercent' => Util::percent(
+ $item['executedLines'],
+ $item['executableLines'],
+ false
+ ),
+ 'linesExecutedPercentAsString' => $linesExecutedPercentAsString,
+ 'numExecutedLines' => $item['executedLines'],
+ 'numExecutableLines' => $item['executableLines'],
+ 'testedMethodsPercent' => Util::percent(
+ $numTestedMethods,
+ $numMethods,
+ false
+ ),
+ 'testedMethodsPercentAsString' => Util::percent(
+ $numTestedMethods,
+ $numMethods,
+ true
+ ),
+ 'testedClassesPercent' => Util::percent(
+ $numTestedMethods == $numMethods ? 1 : 0,
+ 1,
+ false
+ ),
+ 'testedClassesPercentAsString' => Util::percent(
+ $numTestedMethods == $numMethods ? 1 : 0,
+ 1,
+ true
+ ),
+ 'crap' => $item['crap']
+ ]
+ );
+
+ foreach ($item['methods'] as $method) {
+ $buffer .= $this->renderFunctionOrMethodItem(
+ $methodItemTemplate,
+ $method,
+ ' '
+ );
+ }
+ }
+
+ return $buffer;
+ }
+
+ /**
+ * @param array $functions
+ * @param \Text_Template $template
+ *
+ * @return string
+ */
+ protected function renderFunctionItems(array $functions, \Text_Template $template)
+ {
+ if (empty($functions)) {
+ return '';
+ }
+
+ $buffer = '';
+
+ foreach ($functions as $function) {
+ $buffer .= $this->renderFunctionOrMethodItem(
+ $template,
+ $function
+ );
+ }
+
+ return $buffer;
+ }
+
+ /**
+ * @param \Text_Template $template
+ *
+ * @return string
+ */
+ protected function renderFunctionOrMethodItem(\Text_Template $template, array $item, $indent = '')
+ {
+ $numTestedItems = $item['executedLines'] == $item['executableLines'] ? 1 : 0;
+
+ return $this->renderItemTemplate(
+ $template,
+ [
+ 'name' => sprintf(
+ '%s%s',
+ $indent,
+ $item['startLine'],
+ htmlspecialchars($item['signature']),
+ isset($item['functionName']) ? $item['functionName'] : $item['methodName']
+ ),
+ 'numMethods' => 1,
+ 'numTestedMethods' => $numTestedItems,
+ 'linesExecutedPercent' => Util::percent(
+ $item['executedLines'],
+ $item['executableLines'],
+ false
+ ),
+ 'linesExecutedPercentAsString' => Util::percent(
+ $item['executedLines'],
+ $item['executableLines'],
+ true
+ ),
+ 'numExecutedLines' => $item['executedLines'],
+ 'numExecutableLines' => $item['executableLines'],
+ 'testedMethodsPercent' => Util::percent(
+ $numTestedItems,
+ 1,
+ false
+ ),
+ 'testedMethodsPercentAsString' => Util::percent(
+ $numTestedItems,
+ 1,
+ true
+ ),
+ 'crap' => $item['crap']
+ ]
+ );
+ }
+
+ /**
+ * @param FileNode $node
+ *
+ * @return string
+ */
+ protected function renderSource(FileNode $node)
+ {
+ $coverageData = $node->getCoverageData();
+ $testData = $node->getTestData();
+ $codeLines = $this->loadFile($node->getPath());
+ $lines = '';
+ $i = 1;
+
+ foreach ($codeLines as $line) {
+ $trClass = '';
+ $popoverContent = '';
+ $popoverTitle = '';
+
+ if (array_key_exists($i, $coverageData)) {
+ $numTests = ($coverageData[$i] ? count($coverageData[$i]) : 0);
+
+ if ($coverageData[$i] === null) {
+ $trClass = ' class="warning"';
+ } elseif ($numTests == 0) {
+ $trClass = ' class="danger"';
+ } else {
+ $lineCss = 'covered-by-large-tests';
+ $popoverContent = '';
+
+ if ($numTests > 1) {
+ $popoverTitle = $numTests . ' tests cover line ' . $i;
+ } else {
+ $popoverTitle = '1 test covers line ' . $i;
+ }
+
+ foreach ($coverageData[$i] as $test) {
+ if ($lineCss == 'covered-by-large-tests' && $testData[$test]['size'] == 'medium') {
+ $lineCss = 'covered-by-medium-tests';
+ } elseif ($testData[$test]['size'] == 'small') {
+ $lineCss = 'covered-by-small-tests';
+ }
+
+ switch ($testData[$test]['status']) {
+ case 0:
+ switch ($testData[$test]['size']) {
+ case 'small':
+ $testCSS = ' class="covered-by-small-tests"';
+ break;
+
+ case 'medium':
+ $testCSS = ' class="covered-by-medium-tests"';
+ break;
+
+ default:
+ $testCSS = ' class="covered-by-large-tests"';
+ break;
+ }
+ break;
+
+ case 1:
+ case 2:
+ $testCSS = ' class="warning"';
+ break;
+
+ case 3:
+ $testCSS = ' class="danger"';
+ break;
+
+ case 4:
+ $testCSS = ' class="danger"';
+ break;
+
+ default:
+ $testCSS = '';
+ }
+
+ $popoverContent .= sprintf(
+ '- %s
',
+ $testCSS,
+ htmlspecialchars($test)
+ );
+ }
+
+ $popoverContent .= '
';
+ $trClass = ' class="' . $lineCss . ' popin"';
+ }
+ }
+
+ if (!empty($popoverTitle)) {
+ $popover = sprintf(
+ ' data-title="%s" data-content="%s" data-placement="bottom" data-html="true"',
+ $popoverTitle,
+ htmlspecialchars($popoverContent)
+ );
+ } else {
+ $popover = '';
+ }
+
+ $lines .= sprintf(
+ ' | %s |
' . "\n",
+ $trClass,
+ $popover,
+ $i,
+ $i,
+ $i,
+ $line
+ );
+
+ $i++;
+ }
+
+ return $lines;
+ }
+
+ /**
+ * @param string $file
+ *
+ * @return array
+ */
+ protected function loadFile($file)
+ {
+ $buffer = file_get_contents($file);
+ $tokens = token_get_all($buffer);
+ $result = [''];
+ $i = 0;
+ $stringFlag = false;
+ $fileEndsWithNewLine = substr($buffer, -1) == "\n";
+
+ unset($buffer);
+
+ foreach ($tokens as $j => $token) {
+ if (is_string($token)) {
+ if ($token === '"' && $tokens[$j - 1] !== '\\') {
+ $result[$i] .= sprintf(
+ '%s',
+ htmlspecialchars($token)
+ );
+
+ $stringFlag = !$stringFlag;
+ } else {
+ $result[$i] .= sprintf(
+ '%s',
+ htmlspecialchars($token)
+ );
+ }
+
+ continue;
+ }
+
+ list($token, $value) = $token;
+
+ $value = str_replace(
+ ["\t", ' '],
+ [' ', ' '],
+ htmlspecialchars($value, $this->htmlspecialcharsFlags)
+ );
+
+ if ($value === "\n") {
+ $result[++$i] = '';
+ } else {
+ $lines = explode("\n", $value);
+
+ foreach ($lines as $jj => $line) {
+ $line = trim($line);
+
+ if ($line !== '') {
+ if ($stringFlag) {
+ $colour = 'string';
+ } else {
+ switch ($token) {
+ case T_INLINE_HTML:
+ $colour = 'html';
+ break;
+
+ case T_COMMENT:
+ case T_DOC_COMMENT:
+ $colour = 'comment';
+ break;
+
+ case T_ABSTRACT:
+ case T_ARRAY:
+ case T_AS:
+ case T_BREAK:
+ case T_CALLABLE:
+ case T_CASE:
+ case T_CATCH:
+ case T_CLASS:
+ case T_CLONE:
+ case T_CONTINUE:
+ case T_DEFAULT:
+ case T_ECHO:
+ case T_ELSE:
+ case T_ELSEIF:
+ case T_EMPTY:
+ case T_ENDDECLARE:
+ case T_ENDFOR:
+ case T_ENDFOREACH:
+ case T_ENDIF:
+ case T_ENDSWITCH:
+ case T_ENDWHILE:
+ case T_EXIT:
+ case T_EXTENDS:
+ case T_FINAL:
+ case T_FINALLY:
+ case T_FOREACH:
+ case T_FUNCTION:
+ case T_GLOBAL:
+ case T_IF:
+ case T_IMPLEMENTS:
+ case T_INCLUDE:
+ case T_INCLUDE_ONCE:
+ case T_INSTANCEOF:
+ case T_INSTEADOF:
+ case T_INTERFACE:
+ case T_ISSET:
+ case T_LOGICAL_AND:
+ case T_LOGICAL_OR:
+ case T_LOGICAL_XOR:
+ case T_NAMESPACE:
+ case T_NEW:
+ case T_PRIVATE:
+ case T_PROTECTED:
+ case T_PUBLIC:
+ case T_REQUIRE:
+ case T_REQUIRE_ONCE:
+ case T_RETURN:
+ case T_STATIC:
+ case T_THROW:
+ case T_TRAIT:
+ case T_TRY:
+ case T_UNSET:
+ case T_USE:
+ case T_VAR:
+ case T_WHILE:
+ case T_YIELD:
+ $colour = 'keyword';
+ break;
+
+ default:
+ $colour = 'default';
+ }
+ }
+
+ $result[$i] .= sprintf(
+ '%s',
+ $colour,
+ $line
+ );
+ }
+
+ if (isset($lines[$jj + 1])) {
+ $result[++$i] = '';
+ }
+ }
+ }
+ }
+
+ if ($fileEndsWithNewLine) {
+ unset($result[count($result)-1]);
+ }
+
+ return $result;
+ }
+}