<?php

namespace atoum\atoum\tests\units\reports\asynchronous;

use atoum\atoum;
use atoum\atoum\mock;
use atoum\atoum\reports\asynchronous\coveralls as testedClass;

require_once __DIR__ . '/../../../runner.php';

class coveralls extends atoum\test
{
    public function beforeTestMethod($method)
    {
        $this->extension('json')->isLoaded();
    }

    public function testClass()
    {
        $this->testedClass->extends(atoum\reports\asynchronous::class);
    }

    public function testClassConstants()
    {
        $this
            ->string(testedClass::defaultServiceName)->isEqualTo('atoum')
            ->string(testedClass::defaultEvent)->isEqualTo('manual')
            ->string(testedClass::defaultCoverallsApiUrl)->isEqualTo('https://coveralls.io/api/v1/jobs')
            ->string(testedClass::defaultCoverallsApiMethod)->isEqualTo('POST')
            ->string(testedClass::defaultCoverallsApiParameter)->isEqualTo('json')
        ;
    }

    public function test__construct()
    {
        $this
            ->if($report = new testedClass($sourceDir = uniqid(), $token = uniqid()))
            ->then
                ->array($report->getFields(atoum\runner::runStart))->isEmpty()
                ->object($report->getLocale())->isInstanceOf(atoum\locale::class)
                ->object($report->getAdapter())->isInstanceOf(atoum\adapter::class)
                ->array($report->getFields())->isEmpty()
                ->object($report->getSourceDir())->isInstanceOf(atoum\fs\path::class)
                ->castToString($report->getSourceDir())->isEqualTo($sourceDir)
                ->object($report->getBranchFinder())->isInstanceOf(\closure::class)
                ->string($report->getServiceName())->isEqualTo('atoum')
                ->variable($report->getServiceJobId())->isNull()
            ->if($report = new testedClass($sourceDir, $token, $adapter = new atoum\test\adapter()))
            ->then
                ->adapter($report->getAdapter())->call('extension_loaded')->withArguments('json')->once()
            ->if($adapter->extension_loaded = false)
            ->then
                ->exception(function () use ($adapter) {
                    new testedClass(uniqid(), uniqid(), $adapter);
                })
                    ->isInstanceOf(atoum\exceptions\runtime::class)
                    ->hasMessage('JSON PHP extension is mandatory for coveralls report')
        ;
    }

    public function testGetSetBranchFinder()
    {
        $this
            ->if($report = new testedClass(uniqid(), uniqid()))
            ->then
                ->object($report->getBranchFinder())->isInstanceOf(\closure::class)
            ->if($finder = function () {
            })
            ->then
                ->object($report->setBranchFinder($finder))->isIdenticalTo($report)
                ->object($report->getBranchFinder())->isIdenticalTo($finder)
        ;
    }

    public function testGetSetServiceName()
    {
        $this
            ->if($report = new testedClass(uniqid(), uniqid()))
            ->then
                ->string($report->getServiceName())->isEqualTo('atoum')
            ->if($service = uniqid())
            ->then
                ->object($report->setServiceName($service))->isIdenticalTo($report)
                ->string($report->getServiceName())->isEqualTo($service)
        ;
    }

    public function testGetSetServiceJobId()
    {
        $this
            ->if($report = new testedClass(uniqid(), uniqid()))
            ->then
                ->variable($report->getServiceJobId())->isNull()
            ->if($service = uniqid())
            ->then
                ->object($report->setServiceJobId($service))->isIdenticalTo($report)
                ->string($report->getServiceJobId())->isEqualTo($service)
        ;
    }

    public function testHandleEvent()
    {
        $this
            ->if($adapter = new atoum\test\adapter())
            ->and($adapter->extension_loaded = true)
            ->and($adapter->exec = function ($command) {
                switch ($command) {
                    case 'git log -1 --pretty=format:\'{"id":"%H","author_name":"%aN","author_email":"%ae","committer_name":"%cN","committer_email":"%ce","message":"%s"}\'':
                        return '{"id":"7282ea7620b45fcba0f9d3bfd484ab146aba2bd0","author_name":"mageekguy","author_email":"atoum@atoum.org","comitter_name":"mageekguy","comitter_email":"atoum@atoum.org"}';

                    case 'git rev-parse --abbrev-ref HEAD':
                        return 'master';

                    default:
                        return null;
                }
            })
            ->and($report = new testedClass($sourceDir = uniqid(), $token = '51bb597d202b4', $adapter))
            ->and($score = new \mock\atoum\atoum\score())
            ->and($coverage = new \mock\atoum\atoum\score\coverage())
            ->and($writer = new \mock\atoum\atoum\writers\http())
            ->and($writer->getMockController()->writeAsynchronousReport = function () use ($writer) {
                return $writer;
            })
            ->then
                ->when(function () use ($report, $writer) {
                    $report->addWriter($writer)->handleEvent(atoum\runner::runStop, new \atoum\atoum\runner());
                })
                    ->mock($writer)->call('writeAsynchronousReport')->withArguments($report)->once()
            ->if($adapter->date = '2013-05-13 10:00:00 +0000')
            ->and($adapter->file_get_contents = '<?php')
            ->and($observable = new \mock\atoum\atoum\runner())
            ->and($observable->getMockController()->getScore = $score)
            ->and($score->getMockController()->getCoverage = $coverage)
            ->and($coverage->getMockController()->getClasses = [])
            ->and($filepath = implode(
                DIRECTORY_SEPARATOR,
                [
                    __DIR__,
                    'coveralls',
                    'resources',
                    '1.json'
                ]
            ))
            ->and($report = new testedClass($sourceDir, $token, $adapter))
            ->and($report->addWriter($writer))
            ->then
                ->object($report->handleEvent(atoum\runner::runStop, $observable))->isIdenticalTo($report)
                ->castToString($report)->isEqualToContentsOfFile($filepath)
                ->mock($writer)->call('writeAsynchronousReport')->withArguments($report)->once()
            ->if($coverage->getMockController()->getClasses = [])
            ->and($classController = new mock\controller())
            ->and($classController->disableMethodChecking())
            ->and($classController->__construct = function () {
            })
            ->and($classController->getName = $className = 'bar')
            ->and($classController->getFileName = $classFile = 'foo/bar.php')
            ->and($classController->getTraits = [])
            ->and($classController->getStartLine = 1)
            ->and($classController->getEndLine = 12)
            ->and($class = new \mock\reflectionClass(uniqid(), $classController))
            ->and($methodController = new mock\controller())
            ->and($methodController->__construct = function () {
            })
            ->and($methodController->getName = $methodName = 'baz')
            ->and($methodController->isAbstract = false)
            ->and($methodController->getFileName = $classFile)
            ->and($methodController->getDeclaringClass = $class)
            ->and($methodController->getStartLine = 4)
            ->and($methodController->getEndLine = 8)
            ->and($classController->getMethods = [new \mock\reflectionMethod($className, $methodName, $methodController)])
            ->and($coverage->getMockController()->getClasses = [
                $className => $classFile,
                'foo' => 'bar/foo.php'
            ])
            ->and($xdebugData = [
                $classFile =>
                [
                    3 => 1,
                    4 => 1,
                    5 => -2,
                    6 => 0,
                    7 => -1,
                    8 => 1,
                    9 => 1
                ]
            ])
            ->and($filepath = implode(
                DIRECTORY_SEPARATOR,
                [
                    __DIR__,
                    'coveralls',
                    'resources',
                    '2' . (PHP_OS_FAMILY === 'Windows' ? '-windows' : '') . '.json'
                ]
            ))
            ->and($coverage->setReflectionClassFactory(function () use ($class) {
                return $class;
            }))
            ->and($coverage->addXdebugDataForTest($this, $xdebugData))
            ->then
                ->object($report->handleEvent(atoum\runner::runStop, $observable))->isIdenticalTo($report)
                ->castToString($report)->isEqualToContentsOfFile($filepath)
                ->mock($writer)->call('writeAsynchronousReport')->withArguments($report)->twice()
            ->if($finder = function () use (& $branch) {
                return 'feature';
            })
            ->and($report->setBranchFinder($finder))
            ->and($filepath = implode(
                DIRECTORY_SEPARATOR,
                [
                    __DIR__,
                    'coveralls',
                    'resources',
                    '3' . (PHP_OS_FAMILY === 'Windows' ? '-windows' : '') . '.json'
                ]
            ))
            ->then
                ->object($report->handleEvent(atoum\runner::runStop, $observable))->isIdenticalTo($report)
                ->castToString($report)->isEqualToContentsOfFile($filepath)
                ->mock($writer)->call('writeAsynchronousReport')->withArguments($report)->thrice()
            ->if($report->setBranchFinder(function () use (&$branch) {
                return $branch = uniqid();
            }))
            ->and($report->handleEvent(atoum\runner::runStop, $observable))
            ->then
                ->castToString($report)->contains('"branch":"' . $branch . '"')
            ->if($report->setBranchFinder(function () {
            }))
            ->and($report->handleEvent(atoum\runner::runStop, $observable))
            ->then
                ->castToString($report)->notContains('"branch":')
            ->if($report->setBranchFinder(function () {
                return '';
            }))
            ->and($report->handleEvent(atoum\runner::runStop, $observable))
            ->then
                ->castToString($report)->notContains('"branch":')
        ;
    }

    public function testAddDefaultWriter()
    {
        $this
            ->if($adapter = new atoum\test\adapter())
            ->and($adapter->extension_loaded = true)
            ->and($adapter->file_get_contents = '')
            ->and($adapter->stream_context_create = $context = uniqid())
            ->and($report = new testedClass(uniqid(), uniqid(), $adapter))
            ->and($writer = new \mock\atoum\atoum\writers\http())
            ->then
                ->object($report->addDefaultWriter($writer))->isIdenticalTo($report)
                ->mock($writer)
                    ->call('setUrl')->withArguments(testedClass::defaultCoverallsApiUrl)->once()
                    ->call('setMethod')->withArguments(testedClass::defaultCoverallsApiMethod)->once()
                    ->call('setParameter')->withArguments(testedClass::defaultCoverallsApiParameter)->once()
                    ->call('addHeader')->withArguments('Content-Type', 'multipart/form-data')->once()
        ;
    }
}
