dunkelfrosch/phpcoverfish

View on GitHub
src/PHPCoverFish/Common/CoverFishOutput.php

Summary

Maintainability
A
2 hrs
Test Coverage
<?php

namespace DF\PHPCoverFish\Common;

use DF\PHPCoverFish\CoverFishScanner;
use DF\PHPCoverFish\Common\Base\BaseCoverFishOutput;
use DF\PHPCoverFish\Common\CoverFishColor as Color;
use Symfony\Component\Console\Output\OutputInterface;

/**
 * Class CoverFishOutput
 *
 * @package    DF\PHPCoverFish
 * @author     Patrick Paechnatz <patrick.paechnatz@gmail.com>
 * @copyright  2015 Patrick Paechnatz <patrick.paechnatz@gmail.com>
 * @license    http://www.opensource.org/licenses/MIT
 * @link       http://github.com/dunkelfrosch/phpcoverfish/tree
 * @since      class available since Release 0.9.0
 * @version    1.0.0
 */
class CoverFishOutput extends BaseCoverFishOutput
{
    /**
     * @param array            $outputOptions
     * @param OutputInterface  $output
     * @param CoverFishScanner $scanner
     *
     * @codeCoverageIgnore
     */
    public function __construct(array $outputOptions, OutputInterface $output, CoverFishScanner $scanner)
    {
        $this->output = $output;
        $this->scanner = $scanner;
        $this->coverFishHelper = new CoverFishHelper();

        $this->initOutputConfig($outputOptions);
    }

    /**
     * @param array $outputOptions
     *
     * @codeCoverageIgnore
     */
    private function initOutputConfig(array $outputOptions)
    {
        $this->scanFailure = false;
        $this->outputFormat = $outputOptions['out_format'];
        $this->outputLevel = $outputOptions['out_level'];
        $this->preventAnsiColors = $outputOptions['out_no_ansi'];
        $this->preventEcho = $outputOptions['out_no_echo'];

        if ($this->outputFormat === 'json') {
            $this->outputFormatText = false;
            $this->outputFormatJson = true;
            $this->preventAnsiColors = true;
        }

        if ($this->outputLevel > 0) {
            $this->writeResultHeadlines();
        }
    }

    /**
     * @return bool
     */
    private function writeResultHeadlines()
    {
        if ($this->outputFormat === 'json') {
            return false;
        }

        $initLine = 'switch in raw scan mode, using commandline parameters:';
        if ($this->coverFishHelper->checkParamNotEmpty($this->scanner->getPhpUnitXMLFile())) {
            $initLine = sprintf('switch in phpunit-config scan mode, using phpunit-config file "%s"', $this->scanner->getPhpUnitXMLFile());
        }

        $this->output->writeln($initLine);
        $this->output->write(PHP_EOL);
        $this->output->writeln(sprintf('- autoload file: %s', $this->scanner->getTestAutoloadPath()));
        $this->output->writeln(sprintf('- test source path for scan: %s', $this->scanner->getTestSourcePath()));
        $this->output->writeln(sprintf('- exclude test source path: %s', $this->scanner->getTestExcludePath()));
        $this->output->write(PHP_EOL);

        return true;
    }

    /**
     * @param CoverFishPHPUnitFile $coverFishUnitFile
     * @param CoverFishResult      $coverFishResult
     */
    private function writeSingleTestResult(
        CoverFishPHPUnitFile $coverFishUnitFile,
        CoverFishResult $coverFishResult
    ) {

        $this->writeFileName($coverFishUnitFile);
        $this->writeJsonResult($coverFishUnitFile);

        $coverFishResult->addFileCount();

        /** @var CoverFishPHPUnitTest $coverFishTest */
        foreach ($coverFishUnitFile->getTests() as $coverFishTest) {
            // ignore output for className test result as function
            if ($coverFishUnitFile->getClassName() === $coverFishTest->getSignature()) {
                continue;
            }

            if (null === $coverFishTest->getVisibility()) {
                $coverFishTest->setVisibility('public');
            }

            // higher output level required? print out complete information!
            if ($this->outputLevel > 1) {
                $this->write(sprintf('%s-> %s %s : ',
                    PHP_EOL,
                    (false === $this->preventAnsiColors)
                        ? Color::tplDarkGrayColor($coverFishTest->getVisibility())
                        : $coverFishTest->getVisibility()
                    ,
                    $coverFishTest->getSignature()));
            }

            $this->writeSingleMappingResult($coverFishTest, $coverFishResult);

            // stop on failure defined? break ... do not check further unit tests!
            if ($this->scanFailure && $this->scanner->isStopOnFailure()) {
                break;
            }
        }
    }

    /**
     * output mapping/scanning result of each scanned file
     *
     * @param CoverFishPHPUnitTest $coverFishTest
     * @param CoverFishResult      $coverFishResult
     */
    private function writeSingleMappingResult(
        CoverFishPHPUnitTest $coverFishTest,
        CoverFishResult $coverFishResult
    ) {
        /** @var CoverFishMapping $coverMappings */
        foreach ($coverFishTest->getCoverMappings() as $coverMappings) {
            $coverFishResult->addTestCount();
            if (false === $coverMappings->getValidatorResult()->isPass()) {
                $this->scanFailure = true;
                $this->writeFailureStream($coverFishResult, $coverFishTest, $coverMappings);
                $this->writeProgress(self::MACRO_FAILURE);
                // stop on failure defined? break ... do not check further methods, exit!
                if ($this->scanner->isStopOnFailure()) {
                    break;
                }

            } else {
                $coverFishResult->addPassCount();
                $this->writeProgress(self::MACRO_PASS);
            }
        }

        if (count($coverFishTest->getCoverMappings()) === 0) {
            $this->writeProgress(self::MACRO_SKIPPED);
        }
    }

    /**
     * handle single file/unit test result (process/failureStream and final check result)
     *
     * @param CoverFishResult $coverFishResult
     *
     * @return null|string
     */
    public function writeResult(CoverFishResult $coverFishResult)
    {
        /** @var CoverFishPHPUnitFile $coverFishUnitFile */
        foreach ($coverFishResult->getUnits() as $coverFishUnitFile) {
            $this->scanFailure = false;
            $coverFishResult->setFailureStream(null);

            if (false === $this->checkForEmptyUnitTestClass($coverFishUnitFile)) {
                continue;
            }

            $this->writeSingleTestResult($coverFishUnitFile, $coverFishResult);
            $this->writeFinalCheckResults($coverFishResult);
        }

        return $this->outputResult($coverFishResult);
    }

    /**
     * @codeCoverageIgnore
     * 
     * @param CoverFishPHPUnitFile $coverFishUnitFile
     *
     * @return bool
     */
    public function checkForEmptyUnitTestClass(CoverFishPHPUnitFile $coverFishUnitFile)
    {
        $testCount = 0;
        foreach ($coverFishUnitFile->getTests() as $coverFishTest) {
            // ignore output for className test result as function
            if ($coverFishUnitFile->getClassName() === $coverFishTest->getSignature()) {
                continue;
            }

            $testCount++;
        }

        return $testCount > 0;
    }

    /**
     * write single file check result
     *
     * @param CoverFishResult $coverFishResult
     */
    private function writeFinalCheckResults(CoverFishResult $coverFishResult)
    {
        if (false === $this->scanFailure) {
            $this->writeFileResult(self::FILE_PASS, null);
        } else {
            $this->writeFileResult(self::FILE_FAILURE, $coverFishResult->getFailureStream());
        }

        $this->jsonResults[] = $this->jsonResult;
    }

    /**
     * write single json error line
     *
     * @param CoverFishResult       $coverFishResult
     * @param CoverFishPHPUnitTest  $unitTest
     * @param CoverFishMessageError $mappingError
     * @param $coverLine
     */
    private function writeJsonFailureStream(
        CoverFishResult $coverFishResult,
        CoverFishPHPUnitTest $unitTest,
        CoverFishMessageError $mappingError,
        $coverLine
    ) {
        $this->jsonResult['errorCount'] = $coverFishResult->getFailureCount();
        $this->jsonResult['errorMessage'] = $mappingError->getMessageTitle();
        $this->jsonResult['errorCode'] = $mappingError->getMessageCode();
        $this->jsonResult['cover'] = $coverLine;
        $this->jsonResult['method'] = $unitTest->getSignature();
        $this->jsonResult['line'] = $unitTest->getLine();
        $this->jsonResult['file'] = $unitTest->getFile();
    }

    /**
     * @param CoverFishPHPUnitFile $coverFishUnitFile
     */
    private function writeJsonResult(CoverFishPHPUnitFile $coverFishUnitFile)
    {
        $this->jsonResult['pass'] = false;
        $this->jsonResult['file'] = $this->coverFishHelper->getFileNameFromPath($coverFishUnitFile->getFile());
        $this->jsonResult['fileFQN'] = $coverFishUnitFile->getFile();
    }

    /**
     * message block macro, line 01, message title
     *
     * @param int                  $failureCount
     * @param CoverFishPHPUnitTest $unitTest
     *
     * @return string
     */
    protected function getMacroLineInfo($failureCount, CoverFishPHPUnitTest $unitTest)
    {
        $lineInfoMacro = '%sError #%s in method "%s" (L:~%s)';
        if ($this->outputLevel > 1) {
            $lineInfoMacro = '%sError #%s in method "%s", Line ~%s';
        }

        return sprintf($lineInfoMacro,
            $this->setIndent(self::MACRO_CONFIG_DETAIL_LINE_INDENT),
            (false === $this->preventAnsiColors)
                ? Color::tplWhiteColor($failureCount)
                : $failureCount,
            (false === $this->preventAnsiColors)
                ? Color::tplWhiteColor($unitTest->getSignature())
                : $unitTest->getSignature(),
            (false === $this->preventAnsiColors)
                ? Color::tplWhiteColor($unitTest->getLine())
                : $unitTest->getLine(),
            PHP_EOL
        );
    }

    /**
     * message block macro, line 02, cover/annotation line
     *
     * @param CoverFishPHPUnitTest $unitTest
     *
     * @return string
     */
    protected function getMacroFileInfo(CoverFishPHPUnitTest $unitTest)
    {
        $fileInfoMacro = '%s%s%s: %s';
        return sprintf($fileInfoMacro,
            PHP_EOL,
            $this->setIndent(self::MACRO_CONFIG_DETAIL_LINE_INDENT),
            (false === $this->preventAnsiColors)
                ? Color::tplDarkGrayColor('File')
                : 'File'
            ,
            $unitTest->getFileAndPath()
        );
    }

    /**
     * message block macro, line 03, cover/annotation line
     *
     * @param string $coverLine
     * @return string
     */
    protected function getMacroCoverInfo($coverLine)
    {
        $lineCoverMacro = '%s%s%s: %s%s';
        return sprintf($lineCoverMacro,
            PHP_EOL,
            $this->setIndent(self::MACRO_CONFIG_DETAIL_LINE_INDENT),
            (false === $this->preventAnsiColors)
                ? Color::tplDarkGrayColor('Annotation')
                : 'Annotation'
            ,
            $coverLine,
            PHP_EOL
        );
    }

    /**
     * message block macro, line 04, error message
     *
     * @param CoverFishMessageError $mappingError
     *
     * @return string
     */
    protected function getMacroCoverErrorMessage(CoverFishMessageError $mappingError)
    {
        $lineMessageMacro = '%s%s: %s ';
        if ($this->outputLevel > 1) {
            $lineMessageMacro = '%s%s: %s (ErrorCode: %s)';
        }

        return sprintf($lineMessageMacro,
            $this->setIndent(self::MACRO_CONFIG_DETAIL_LINE_INDENT),
            (false === $this->preventAnsiColors)
                ? Color::tplDarkGrayColor('Message')
                : 'Message',

            $mappingError->getMessageTitle(),
            $mappingError->getMessageCode()
        );
    }

    /**
     * @param CoverFishResult      $coverFishResult
     * @param CoverFishPHPUnitTest $unitTest
     * @param CoverFishMapping     $coverMapping
     *
     * @return null
     */
    private function writeFailureStream(
        CoverFishResult $coverFishResult,
        CoverFishPHPUnitTest $unitTest,
        CoverFishMapping $coverMapping
    )
    {
        /** @var CoverFishMessageError $mappingError */
        foreach ($coverMapping->getValidatorResult()->getErrors() as $mappingError) {
            $coverFishResult->addFailureCount();
            $coverLine = $mappingError->getErrorStreamTemplate($coverMapping, $this->preventAnsiColors);
            $this->writeJsonFailureStream($coverFishResult, $unitTest, $mappingError, $coverLine);

            if (0 === $this->outputLevel) {
                continue;
            }

            $coverFishResult->addFailureToStream(PHP_EOL);

            $lineInfo = $this->getMacroLineInfo($coverFishResult->getFailureCount(), $unitTest);
            $fileInfo = $this->getMacroFileInfo($unitTest);
            $lineCover = $this->getMacroCoverInfo($coverLine);
            $lineMessage = $this->getMacroCoverErrorMessage($mappingError);

            $coverFishResult->addFailureToStream($lineInfo);

            if ($this->outputLevel > 1) {
                $coverFishResult->addFailureToStream($fileInfo);
            }

            $coverFishResult->addFailureToStream($lineCover);
            $coverFishResult->addFailureToStream($lineMessage);
            $coverFishResult->addFailureToStream(PHP_EOL);
        }
    }
}