<?php

namespace atoum\atoum\tests\units;

use atoum\atoum;
use atoum\atoum\runner as testedClass;
use atoum\atoum\test;

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

class runner extends atoum\test
{
    public function testClass()
    {
        $this
            ->testedClass
                ->hasInterface(atoum\observable::class)
            ->string(atoum\runner::atoumVersionConstant)->isEqualTo('atoum\atoum\version')
            ->string(atoum\runner::atoumDirectoryConstant)->isEqualTo('atoum\atoum\directory')
            ->string(atoum\runner::runStart)->isEqualTo('runnerStart')
            ->string(atoum\runner::runStop)->isEqualTo('runnerStop')
        ;
    }

    public function test__construct()
    {
        $this
            ->if($runner = new testedClass())
            ->then
                ->object($runner->getScore())->isInstanceOf(atoum\score::class)
                ->object($runner->getAdapter())->isInstanceOf(atoum\adapter::class)
                ->object($runner->getLocale())->isInstanceOf(atoum\locale::class)
                ->object($runner->getIncluder())->isInstanceOf(atoum\includer::class)
                ->variable($runner->getTestGenerator())->isNull()
                ->object($runner->getTestDirectoryIterator())->isInstanceOf(atoum\iterators\recursives\directory\factory::class)
                ->object($defaultGlobIteratorFactory = $runner->getGlobIteratorFactory())->isInstanceOf(\closure::class)
                ->object($defaultGlobIteratorFactory($pattern = uniqid()))->isEqualTo(new \globIterator($pattern))
                ->object($defaultReflectionClassFactory = $runner->getReflectionClassFactory())->isInstanceOf(\closure::class)
                ->object($defaultReflectionClassFactory($this))->isEqualTo(new \reflectionClass($this))
                ->object($defaultTestFactory = $runner->getTestFactory())->isInstanceOf(\closure::class)
                ->object($defaultTestFactory(__CLASS__))->isInstanceOf($this)
                ->variable($runner->getRunningDuration())->isNull()
                ->boolean($runner->codeCoverageIsEnabled())->isTrue()
                ->variable($runner->getDefaultReportTitle())->isNull()
                ->array($runner->getObservers())->isEmpty()
                ->array($runner->getTestPaths())->isEmpty()
                ->variable($runner->getXdebugConfig())->isNull()
        ;
    }

    public function testSetTestPaths()
    {
        $this
            ->if($runner = new testedClass())
            ->then
                ->object($runner->setTestPaths($paths = [uniqid(), uniqid(), uniqid()]))->isIdenticalTo($runner)
                ->array($runner->getTestPaths())->isEqualTo($paths)
        ;
    }

    public function testResetTestPaths()
    {
        $this
            ->if($runner = new testedClass())
            ->and($runner->setTestPaths([uniqid(), uniqid(), uniqid()]))
            ->then
                ->object($runner->resetTestPaths())->isIdenticalTo($runner)
                ->array($runner->getTestPaths())->isEmpty()
        ;
    }

    public function testSetPhp()
    {
        $this
            ->if($runner = new testedClass())
            ->then
                ->object($runner->setPhp($php = new atoum\php()))->isIdenticalTo($runner)
                ->object($runner->getPhp())->isIdenticalTo($php)
                ->object($runner->setPhp())->isIdenticalTo($runner)
                ->object($runner->getPhp())
                    ->isEqualTo(new atoum\php())
                    ->isNotIdenticalTo($php)
        ;
    }

    public function testSetAdapter()
    {
        $this
            ->if($runner = new testedClass())
            ->then
                ->object($runner->setAdapter($adapter = new atoum\test\adapter()))->isIdenticalTo($runner)
                ->object($runner->getAdapter())->isIdenticalTo($adapter)
        ;
    }

    public function testSetScore()
    {
        $this
            ->if($runner = new testedClass())
            ->then
                ->object($runner->setScore($score = new atoum\runner\score()))->isIdenticalTo($runner)
                ->object($runner->getScore())->isIdenticalTo($score);
        ;
    }

    public function testSetDefaultReportTtitle()
    {
        $this
            ->if($runner = new testedClass())
            ->then
                ->object($runner->setDefaultReportTitle($title = uniqid()))->isIdenticalTo($runner)
                ->string($runner->getDefaultReportTitle())->isEqualTo($title)
        ;
    }

    public function testGetPhpPath()
    {
        $this
            ->if($runner = new testedClass())
            ->then
                ->string($runner->getPhpPath())->isEqualTo($runner->getPhp()->getBinaryPath())
        ;
    }

    public function testSetPhpPath()
    {
        $this
            ->if($runner = new testedClass())
            ->then
                ->object($runner->setPhpPath($phpPath = uniqid()))->isIdenticalTo($runner)
                ->string($runner->getPhpPath())->isIdenticalTo($phpPath)
        ;
    }

    public function testSetTestGenerator()
    {
        $this
            ->if($runner = new testedClass())
            ->then
                ->object($runner->setTestGenerator($generator = new test\generator()))->isIdenticalTo($runner)
                ->object($runner->getTestGenerator())->isIdenticalTo($generator)
                ->object($runner->setTestGenerator())->isIdenticalTo($runner)
                ->object($runner->getTestGenerator())
                    ->isInstanceOf(atoum\test\generator::class)
                    ->isNotIdenticalTo($generator)
        ;
    }

    public function testEnableDebugMode()
    {
        $this
            ->if($runner = new testedClass())
            ->then
                ->object($runner->enableDebugMode())->isIdenticalTo($runner)
                ->boolean($runner->debugModeIsEnabled())->isTrue()
                ->object($runner->enableDebugMode())->isIdenticalTo($runner)
                ->boolean($runner->debugModeIsEnabled())->isTrue()
        ;
    }

    public function testDisableDebugMode()
    {
        $this
            ->if($runner = new testedClass())
            ->then
                ->object($runner->disableDebugMode())->isIdenticalTo($runner)
                ->boolean($runner->debugModeIsEnabled())->isFalse()
                ->object($runner->disableDebugMode())->isIdenticalTo($runner)
                ->boolean($runner->debugModeIsEnabled())->isFalse()
            ->if($runner->enableDebugMode())
            ->then
                ->object($runner->disableDebugMode())->isIdenticalTo($runner)
                ->boolean($runner->debugModeIsEnabled())->isFalse()
        ;
    }

    public function testDisallowUsageOfUndefinedMethodInMock()
    {
        $this
            ->if($runner = new testedClass())
            ->then
                ->object($runner->disallowUsageOfUndefinedMethodInMock())->isIdenticalTo($runner)
                ->boolean($runner->usageOfUndefinedMethodInMockAreAllowed())->isFalse()
                ->object($runner->disallowUsageOfUndefinedMethodInMock())->isIdenticalTo($runner)
                ->boolean($runner->debugModeIsEnabled())->isFalse()
        ;
    }

    public function testAllowUsageOfUndefinedMethodInMock()
    {
        $this
            ->if($runner = new testedClass())
            ->then
                ->object($runner->allowUsageOfUndefinedMethodInMock())->isIdenticalTo($runner)
                ->boolean($runner->usageOfUndefinedMethodInMockAreAllowed())->isTrue()
                ->object($runner->allowUsageOfUndefinedMethodInMock())->isIdenticalTo($runner)
                ->boolean($runner->usageOfUndefinedMethodInMockAreAllowed())->isTrue()
            ->if($runner->disallowUsageOfUndefinedMethodInMock())
            ->then
                ->object($runner->allowUsageOfUndefinedMethodInMock())->isIdenticalTo($runner)
                ->boolean($runner->usageOfUndefinedMethodInMockAreAllowed())->isTrue()
        ;
    }

    public function testSetXdebugConfig()
    {
        $this
            ->if($runner = new testedClass())
            ->then
                ->object($runner->setXdebugConfig($value = uniqid()))->isIdenticalTo($runner)
                ->string($runner->getXdebugConfig())->isEqualTo($value)
        ;
    }

    public function testAddObserver()
    {
        $this
            ->if($runner = new testedClass())
            ->then
                ->array($runner->getObservers())->isEmpty()
                ->object($runner->addObserver($observer = new \mock\atoum\atoum\observers\runner()))->isIdenticalTo($runner)
                ->array($runner->getObservers())->isEqualTo([$observer])
        ;
    }

    public function testRemoveObserver()
    {
        $this
            ->if($runner = new testedClass())
            ->then
                ->array($runner->getObservers())->isEmpty()
                ->object($runner->removeObserver(new \mock\atoum\atoum\observers\runner()))->isIdenticalTo($runner)
                ->array($runner->getObservers())->isEmpty()
            ->if($runner->addObserver($observer1 = new \mock\atoum\atoum\observers\runner()))
            ->and($runner->addObserver($observer2 = new \mock\atoum\atoum\observers\runner()))
            ->then
                ->array($runner->getObservers())->isEqualTo([$observer1, $observer2])
                ->object($runner->removeObserver(new \mock\atoum\atoum\observers\runner()))->isIdenticalTo($runner)
                ->array($runner->getObservers())->isEqualTo([$observer1, $observer2])
                ->object($runner->removeObserver($observer1))->isIdenticalTo($runner)
                ->array($runner->getObservers())->isEqualTo([$observer2])
                ->object($runner->removeObserver($observer2))->isIdenticalTo($runner)
                ->array($runner->getObservers())->isEmpty()
        ;
    }

    public function testCallObservers()
    {
        $this
            ->if($runner = new testedClass())
            ->then
                ->object($runner->callObservers(atoum\runner::runStart))->isIdenticalTo($runner)
            ->if($runner->addObserver($observer = new \mock\atoum\atoum\observers\runner()))
            ->then
                ->object($runner->callObservers(atoum\runner::runStart))->isIdenticalTo($runner)
                ->mock($observer)->call('handleEvent')->withArguments(atoum\runner::runStart, $runner)->once()
        ;
    }

    public function testGetRunningDuration()
    {
        $this
            ->if($adapter = new atoum\test\adapter())
            ->and($adapter->microtime = function () {
                static $call = 0;
                return (++$call * 100);
            })
            ->and($adapter->get_declared_classes = [])
            ->and($runner = new testedClass())
            ->and($runner->setAdapter($adapter))
            ->then
                ->variable($runner->getRunningDuration())->isNull()
            ->if($runner->run())
            ->then
                ->integer($runner->getRunningDuration())->isEqualTo(100)
            ->if(eval('namespace ' . __NAMESPACE__ . ' { class forTestGetRunningDuration extends \atoum\atoum\test { public function testSomething() {} public function run(array $runTestMethods = array(), array $tags = array()) { return $this; } } }'))
            ->and($adapter->get_declared_classes = [__NAMESPACE__ . '\forTestGetRunningDuration'])
            ->and($runner->run())
            ->then
                ->integer($runner->getRunningDuration())->isEqualTo(100)
        ;
    }

    public function testGetTestNumber()
    {
        $this
            ->if($adapter = new atoum\test\adapter())
            ->and($adapter->get_declared_classes = [])
            ->and($runner = new testedClass())
            ->and($runner->setAdapter($adapter))
            ->then
                ->integer($runner->getTestNumber())->isZero()
            ->if($runner->run())
            ->then
                ->integer($runner->getTestNumber())->isZero();
        ;
    }

    public function testGetTestMethodNumber()
    {
        $this
            ->if($adapter = new atoum\test\adapter())
            ->and($adapter->get_declared_classes = [])
            ->and($runner = new testedClass())
            ->and($runner->setAdapter($adapter))
            ->then
                ->integer($runner->getTestMethodNumber())->isZero()
            ->if($runner->run())
            ->then
                ->integer($runner->getTestMethodNumber())->isZero()
        ;
    }

    public function testGetBootstrapFile()
    {
        $this
            ->if($runner = new testedClass())
            ->and($includer = new \mock\atoum\atoum\includer())
            ->and($includer->getMockController()->includePath = function () {
            })
            ->and($runner->setIncluder($includer))
            ->then
                ->object($runner->setBootstrapFile($path = uniqid()))->isIdenticalTo($runner)
                ->string($runner->getBootstrapFile())->isEqualTo($path)
                ->mock($includer)->call('includePath')->withArguments($path)->once()
        ;
    }

    public function testGetAutoloaderFile()
    {
        $this
            ->if($runner = new testedClass())
            ->and($includer = new \mock\atoum\atoum\includer())
            ->and($includer->getMockController()->includePath = function () {
            })
            ->and($runner->setIncluder($includer))
            ->then
                ->object($runner->setAutoloaderFile($path = uniqid()))->isIdenticalTo($runner)
                ->string($runner->getAutoloaderFile())->isEqualTo($path)
                ->mock($includer)->call('includePath')->withArguments($path)->once()
        ;
    }

    public function testHasReports()
    {
        $this
            ->if($runner = new testedClass())
            ->then
                ->boolean($runner->hasReports())->isFalse()
            ->if($runner->addReport(new atoum\reports\realtime\cli()))
            ->then
                ->boolean($runner->hasReports())->isTrue()
        ;
    }

    public function testSetReport()
    {
        $this
            ->if($runner = new testedClass())
            ->then
                ->object($runner->setReport($report = new atoum\reports\realtime\cli()))->isIdenticalTo($runner)
                ->array($runner->getReports())->isEqualTo([$report])
                ->array($runner->getObservers())->contains($report)
                ->object($runner->addReport($otherReport = new atoum\reports\realtime\cli()))->isIdenticalTo($runner)
                ->array($runner->getReports())->isEqualTo([$report])
                ->array($runner->getObservers())->contains($report)
                ->object($runner->setReport($otherReport))->isIdenticalTo($runner)
                ->array($runner->getReports())->isEqualTo([$otherReport])
                ->array($runner->getObservers())->contains($otherReport)
                ->object($runner->addReport($report))->isIdenticalTo($runner)
                ->array($runner->getReports())->isEqualTo([$otherReport])
                ->array($runner->getObservers())->contains($otherReport)
        ;
    }

    public function testAddReport()
    {
        $this
            ->if($runner = new testedClass())
            ->then
                ->object($runner->addReport($report = new atoum\reports\realtime\cli()))->isIdenticalTo($runner)
                ->array($runner->getReports())->isEqualTo([$report])
                ->array($runner->getObservers())->contains($report)
            ->if($runner->setReport($otherReport = new atoum\reports\realtime\cli()))
            ->then
                ->object($runner->addReport($report = new atoum\reports\realtime\cli()))->isIdenticalTo($runner)
                ->array($runner->getReports())->isEqualTo([$otherReport])
                ->array($runner->getObservers())->contains($otherReport)
        ;
    }

    public function testRemoveReport()
    {
        $this
            ->if($runner = new testedClass())
            ->then
                ->array($runner->getReports())->isEmpty()
                ->array($runner->getObservers())->isEmpty()
                ->object($runner->removeReport(new atoum\reports\realtime\cli()))->isIdenticalTo($runner)
                ->array($runner->getReports())->isEmpty()
                ->array($runner->getObservers())->isEmpty()
            ->if($report1 = new \mock\atoum\atoum\report())
            ->and($report2 = new \mock\atoum\atoum\report())
            ->and($runner->addReport($report1)->addReport($report2))
            ->then
                ->array($runner->getReports())->isEqualTo([$report1, $report2])
                ->array($runner->getObservers())->isEqualTo([$report1, $report2])
                ->object($runner->removeReport(new atoum\reports\realtime\cli()))->isIdenticalTo($runner)
                ->array($runner->getReports())->isEqualTo([$report1, $report2])
                ->array($runner->getObservers())->isEqualTo([$report1, $report2])
                ->object($runner->removeReport($report1))->isIdenticalTo($runner)
                ->array($runner->getReports())->isEqualTo([$report2])
                ->array($runner->getObservers())->isEqualTo([$report2])
                ->object($runner->removeReport($report2))->isIdenticalTo($runner)
                ->array($runner->getReports())->isEmpty()
                ->array($runner->getObservers())->isEmpty()
            ->if($runner->setReport($otherReport = new atoum\reports\realtime\cli()))
            ->then
                ->array($runner->getReports())->isEqualTo([$otherReport])
                ->array($runner->getObservers())->isEqualTo([$otherReport])
                ->object($runner->removeReport($otherReport))->isIdenticalTo($runner)
                ->array($runner->getReports())->isEmpty()
                ->array($runner->getObservers())->isEmpty()
            ->if($runner->addReport($report1)->addReport($report2))
            ->then
                ->array($runner->getReports())->isEqualTo([$report1, $report2])
                ->array($runner->getObservers())->isEqualTo([$report1, $report2])
        ;
    }

    public function testRemoveReports()
    {
        $this
            ->if($runner = new testedClass())
            ->then
                ->array($runner->getReports())->isEmpty()
                ->array($runner->getObservers())->isEmpty()
                ->object($runner->removeReports())->isIdenticalTo($runner)
                ->array($runner->getReports())->isEmpty()
                ->array($runner->getObservers())->isEmpty()
            ->if($report1 = new \mock\atoum\atoum\report())
            ->and($report2 = new \mock\atoum\atoum\report())
            ->and($runner->addReport($report1)->addReport($report2))
            ->then
                ->array($runner->getReports())->isEqualTo([$report1, $report2])
                ->array($runner->getObservers())->isEqualTo([$report1, $report2])
                ->object($runner->removeReports())->isIdenticalTo($runner)
                ->array($runner->getReports())->isEmpty()
                ->array($runner->getObservers())->isEmpty()
            ->if($runner->setReport($otherReport = new atoum\reports\realtime\cli()))
            ->then
                ->array($runner->getReports())->isEqualTo([$otherReport])
                ->array($runner->getObservers())->isEqualTo([$otherReport])
                ->object($runner->removeReports())->isIdenticalTo($runner)
                ->array($runner->getReports())->isEmpty()
                ->array($runner->getObservers())->isEmpty()
            ->if($runner->addReport($report1)->addReport($report2))
            ->then
                ->array($runner->getReports())->isEqualTo([$report1, $report2])
                ->array($runner->getObservers())->isEqualTo([$report1, $report2])
            ->given(
                $firstReport = new \mock\atoum\atoum\report(),
                $secondReport = new \mock\atoum\atoum\report(),
                $overrideReport = new \mock\atoum\atoum\report(),
                $runner->removeReports()
            )
            ->if(
                $this->calling($firstReport)->isOverridableBy = function ($report) use ($overrideReport) {
                    return $report === $overrideReport;
                },
                $this->calling($secondReport)->isOverridableBy = function ($report) use ($overrideReport) {
                    return $report !== $overrideReport;
                },
                $runner->addReport($firstReport)
            )
            ->when($runner->removeReports($secondReport))
            ->then
                ->array($runner->getReports())->isEmpty
                ->array($runner->getObservers())->isEmpty
            ->if(
                $runner->addReport($firstReport),
                $runner->addReport($secondReport)
            )
            ->when($runner->removeReports($overrideReport))
            ->then
                ->array($runner->getReports())->isEqualTo([$firstReport])
                ->array($runner->getObservers())->isEqualTo([$firstReport])
        ;
    }

    public function testAddExtension()
    {
        $this
            ->if($runner = new testedClass())
            ->then
                ->object($runner->addExtension($extension = new \mock\atoum\atoum\extension()))->isIdenticalTo($runner)
                ->array(iterator_to_array($runner->getExtensions()))->isEqualTo([$extension])
                ->array($runner->getObservers())->contains($extension)
                ->mock($extension)
                    ->call('setRunner')->withArguments($runner)->once()
            ->if($this->resetMock($extension))
            ->then
                ->object($runner->addExtension($extension))->isIdenticalTo($runner)
                ->array(iterator_to_array($runner->getExtensions()))->isEqualTo([$extension])
                ->array($runner->getObservers())->contains($extension)
                ->mock($extension)
                    ->call('setRunner')->never();
        ;
    }

    public function testRemoveExtension()
    {
        $this
            ->if($runner = new testedClass())
            ->then
                ->object($runner->getExtensions())->isInstanceOf(atoum\extension\aggregator::class)
                ->sizeOf($runner->getExtensions())->isZero
                ->array($runner->getObservers())->isEmpty()
            ->if($extension = new \mock\atoum\atoum\extension())
            ->and(
                $this->mockClass('atoum\atoum\extension', 'otherMock', 'extension'),
                $otherExtension = new \otherMock\extension()
            )
            ->and($runner->addExtension($extension)->addExtension($otherExtension))
            ->then
                ->array(iterator_to_array($runner->getExtensions()))->isEqualTo([$extension, $otherExtension])
                ->array($runner->getObservers())->isEqualTo([$extension, $otherExtension])
                ->object($runner->removeExtension(new \mock\atoum\atoum\extension()))->isIdenticalTo($runner)
                ->array(iterator_to_array($runner->getExtensions()))->isEqualTo([$otherExtension])
                ->array($runner->getObservers())->isEqualTo([$otherExtension])
            ->if($runner->addExtension($extension))
            ->then
                ->array(iterator_to_array($runner->getExtensions()))->isEqualTo([$otherExtension, $extension])
                ->object($runner->removeExtension($extension))->isIdenticalTo($runner)
                ->array(iterator_to_array($runner->getExtensions()))->isEqualTo([$otherExtension])
                ->array($runner->getObservers())->isEqualTo([$otherExtension])
            ->if($runner->addExtension($extension))
            ->then
                ->array(iterator_to_array($runner->getExtensions()))->isEqualTo([$otherExtension, $extension])
                ->object($runner->removeExtension('mock\atoum\atoum\extension'))->isIdenticalTo($runner)
                ->array(iterator_to_array($runner->getExtensions()))->isEqualTo([$otherExtension])
                ->array($runner->getObservers())->isEqualTo([$otherExtension])
                ->object($runner->removeExtension($otherExtension))->isIdenticalTo($runner)
                ->object($runner->getExtensions())->isInstanceOf(atoum\extension\aggregator::class)
                ->sizeOf($runner->getExtensions())->isZero
                ->array($runner->getObservers())->isEmpty()
            ->if($extension = new \mock\atoum\atoum\extension())
            ->then
                ->exception(function () use ($runner, $extension) {
                    $runner->removeExtension($extension);
                })
                    ->isInstanceOf(atoum\exceptions\logic\invalidArgument::class)
                    ->hasMessage('Extension ' . get_class($extension) . ' is not loaded')
        ;
    }

    public function testRemoveExtensions()
    {
        $this
            ->if($runner = new testedClass())
            ->then
                ->object($runner->getExtensions())->isInstanceOf(atoum\extension\aggregator::class)
                ->sizeOf($runner->getExtensions())->isZero
                ->array($runner->getObservers())->isEmpty()
                ->object($runner->removeExtensions())->isIdenticalTo($runner)
                ->object($runner->getExtensions())->isInstanceOf(atoum\extension\aggregator::class)
                ->sizeOf($runner->getExtensions())->isZero
                ->array($runner->getObservers())->isEmpty()
            ->if($extension = new \mock\atoum\atoum\extension())
            ->and(
                $this->mockClass('atoum\atoum\extension', 'otherMock', 'extension'),
                $otherExtension = new \otherMock\extension()
            )
            ->and($runner->addExtension($extension)->addExtension($otherExtension))
            ->then
                ->array(iterator_to_array($runner->getExtensions()))->isEqualTo([$extension, $otherExtension])
                ->array($runner->getObservers())->isEqualTo([$extension, $otherExtension])
                ->object($runner->removeExtensions())->isIdenticalTo($runner)
                ->object($runner->getExtensions())->isInstanceOf(atoum\extension\aggregator::class)
                ->sizeOf($runner->getExtensions())->isZero
                ->array($runner->getObservers())->isEmpty()
        ;
    }

    public function testEnableCodeCoverage()
    {
        $this
            ->if($runner = new testedClass())
            ->and($runner->disableCodeCoverage())
            ->then
                ->boolean($runner->codeCoverageIsEnabled())->isFalse()
                ->object($runner->enableCodeCoverage())->isIdenticalTo($runner)
                ->boolean($runner->codeCoverageIsEnabled())->isTrue()
        ;
    }

    public function testDisableCodeCoverage()
    {
        $this
            ->if($runner = new testedClass())
            ->and($runner->enableCodeCoverage())
            ->then
                ->boolean($runner->codeCoverageIsEnabled())->isTrue()
                ->object($runner->disableCodeCoverage())->isIdenticalTo($runner)
                ->boolean($runner->codeCoverageIsEnabled())->isFalse()
        ;
    }

    public function testSetTestFactory()
    {
        $this
            ->if($runner = new testedClass())
            ->then
                ->variable($runner->getTestFactory())->isCallable
                ->object($runner->setTestFactory())->isIdenticalTo($runner)
                ->object($runner->getTestFactory())->isCallable
            ->if($factory = function () {
            })
            ->then
                ->object($runner->setTestFactory($factory))->isIdenticalTo($runner)
                ->object($runner->getTestFactory())->isCallable
            ->given($test = new \mock\atoum\atoum\test())
            ->and($generator = new \mock\atoum\atoum\test\mock\generator($test))
            ->and($test->setMockGenerator($generator))
            ->if($runner->disallowUsageOfUndefinedMethodInMock())
            ->and($runner->setTestFactory(function () use ($test) {
                return $test;
            }))
            ->and($factory = $runner->getTestFactory())
            ->then
                ->object($factory('mock\atoum\atoum\test'))->isIdenticalTo($test)
                ->mock($generator)
                    ->call('disallowUndefinedMethodUsage')->once
            ->if($this->resetMock($generator))
            ->if($runner->allowUsageOfUndefinedMethodInMock())
            ->then
                ->object($factory('mock\atoum\atoum\test'))->isIdenticalTo($test)
                ->mock($generator)
                    ->call('disallowUndefinedMethodUsage')->never
        ;
    }

    public function testSetPathAndVersionInScore()
    {
        $this
            ->if($php = new \mock\atoum\atoum\php())
            ->and($this->calling($php)->getBinaryPath = $phpPath = uniqid())
            ->and($this->calling($php)->run = $php)
            ->and($this->calling($php)->isRunning = false)
            ->and($this->calling($php)->getExitCode = 0)
            ->and($this->calling($php)->getStdout = $phpVersion = uniqid())
            ->and($adapter = new atoum\test\adapter())
            ->and($adapter->defined = true)
            ->and($adapter->constant = function ($constantName) use (& $atoumVersion, & $atoumDirectory) {
                switch ($constantName) {
                    case atoum\runner::atoumVersionConstant:
                        return $atoumVersion = uniqid();

                    case atoum\runner::atoumDirectoryConstant:
                        return $atoumDirectory = uniqid();
                }
            })
            ->and($runner = new testedClass())
            ->and($runner->setPhp($php))
            ->and($runner->setAdapter($adapter))
            ->and($runner->setScore($score = new \mock\atoum\atoum\runner\score()))
            ->then
                ->object($runner->setPathAndVersionInScore())->isIdenticalTo($runner)
                ->mock($score)
                    ->call('setAtoumVersion')->withArguments($atoumVersion)->once()
                    ->call('setAtoumPath')->withArguments($atoumDirectory)->once()
                    ->call('setPhpPath')->withArguments($phpPath)->once()
                    ->call('setPhpVersion')->withArguments($phpVersion)->once()
            ->if($adapter->defined = false)
            ->and($runner->setScore($score = new \mock\atoum\atoum\runner\score()))
            ->then
                ->object($runner->setPathAndVersionInScore())->isIdenticalTo($runner)
                ->mock($score)
                    ->call('setAtoumVersion')->withArguments(null)->once()
                    ->call('setAtoumPath')->withArguments(null)->once()
                    ->call('setPhpPath')->withArguments($phpPath)->once()
                    ->call('setPhpVersion')->withArguments($phpVersion)->once()
            ->if($this->calling($php)->getExitCode = rand(1, PHP_INT_MAX))
            ->and($runner->setScore($score = new \mock\atoum\atoum\runner\score()))
            ->then
                ->exception(function () use ($runner) {
                    $runner->setPathAndVersionInScore();
                })
                    ->isInstanceOf(atoum\exceptions\runtime::class)
                    ->hasMessage('Unable to get PHP version from \'' . $php . '\'')
        ;
    }

    public function testGetCoverage()
    {
        $this
            ->if($runner = new testedClass())
            ->then
                ->object($runner->getCoverage())->isIdenticalTo($runner->getScore()->getCoverage())
        ;
    }

    public function testAddTest()
    {
        $this
            ->if($runner = new testedClass())
            ->then
                ->object($runner->addTest($testPath1 = uniqid()))->isIdenticalTo($runner)
                ->array($runner->getTestPaths())->isEqualTo([$testPath1])
                ->object($runner->addTest($testPath2 = uniqid()))->isIdenticalTo($runner)
                ->array($runner->getTestPaths())->isEqualTo([$testPath1, $testPath2])
                ->object($runner->addTest($testPath1))->isIdenticalTo($runner)
                ->array($runner->getTestPaths())->isEqualTo([$testPath1, $testPath2])
                ->object($runner->addTest($testPath3 = new \splFileInfo(__FILE__)))->isIdenticalTo($runner)
                ->array($runner->getTestPaths())->isEqualTo([$testPath1, $testPath2, (string) $testPath3])
            ->if($runner->canNotAddTest())
            ->then
                ->object($runner->addTest($testPath4 = uniqid()))->isIdenticalTo($runner)
                ->array($runner->getTestPaths())->isEqualTo([$testPath1, $testPath2, (string) $testPath3])
            ->if($runner->canAddTest())
            ->then
                ->object($runner->addTest($testPath4 = uniqid()))->isIdenticalTo($runner)
                ->array($runner->getTestPaths())->isEqualTo([$testPath1, $testPath2, (string) $testPath3, $testPath4])
        ;
    }

    public function testCanAddTest()
    {
        $this
            ->if($runner = new testedClass())
            ->then
                ->object($runner->canAddTest())->isIdenticalTo($runner)
            ->if($runner->canNotAddTest())
            ->then
                ->object($runner->canAddTest())->isIdenticalTo($runner)
            ->if($runner->addTest(uniqid()))
            ->then
                ->array($runner->getTestPaths())->isNotEmpty()
        ;
    }

    public function testCanNotAddTest()
    {
        $this
            ->if($runner = new testedClass())
            ->then
                ->object($runner->canNotAddTest())->isIdenticalTo($runner)
            ->if($runner->addTest(uniqid()))
            ->then
                ->array($runner->getTestPaths())->isEmpty()
            ->if($runner->canAddTest())
            ->then
                ->object($runner->canNotAddTest())->isIdenticalTo($runner)
            ->if($runner->addTest(uniqid()))
            ->then
                ->array($runner->getTestPaths())->isEmpty()
        ;
    }

    public function testAcceptTestFileExtensions()
    {
        $this
            ->if($runner = new testedClass())
            ->and($runner->setTestDirectoryIterator($directoryIterator = new \mock\atoum\atoum\iterators\recursives\directory\factory()))
            ->then
                ->object($runner->acceptTestFileExtensions($testFileExtensions = [uniqid(), uniqid()]))->isIdenticalTo($runner)
                ->mock($directoryIterator)->call('acceptExtensions')->withArguments($testFileExtensions)->once()
        ;
    }
}
