<?php

/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://phing.info>.
 */

namespace Phing\Test;

use Phing\Exception\BuildException;
use Phing\RuntimeConfigurable;
use Phing\Target;
use Phing\Task;
use Phing\Task\System\EchoTask;
use Phing\Test\Support\BuildFileTest;

/**
 * UTs for Target component.
 *
 * @author Victor Farazdagi <simple.square@gmail.com>
 * @author Daniel Holmes
 *
 * @internal
 */
class TargetTest extends BuildFileTest
{
    /** @var Target */
    private $target;

    public function setUp(): void
    {
        $this->configureProject(
            PHING_TEST_BASE
            . '/etc/components/Target/Target.xml'
        );

        $this->target = new Target();
        $this->target->setProject($this->project);
        $this->target->setName('MyTarget');
    }

    public function testHiddenTargets(): void
    {
        $phingExecutable = '"' . PHING_TEST_BASE . '/../bin/phing"';
        $buildFile = '"' . PHING_TEST_BASE . '/etc/components/Target/HiddenTargets.xml"';
        $cmd = $phingExecutable . ' -l -f ' . $buildFile;
        exec($cmd, $out);
        $out = implode("\n", $out);
        $offset = strpos($out, 'Subtargets:');
        $this->assertFalse(strpos($out, 'HideInListTarget', $offset));
        $this->assertTrue(false !== strpos($out, 'ShowInListTarget', $offset));
    }

    /**
     * @dataProvider setDependsValidDataProvider
     */
    #[\PHPUnit\Framework\Attributes\DataProvider('setDependsValidDataProvider')]
    public function testSetDependsValid(array $expectedDepends, string $depends): void
    {
        $this->target->setDepends($depends);

        $this->assertEquals($expectedDepends, $this->target->getDependencies());
    }

    public static function setDependsValidDataProvider(): array
    {
        return [
            [['target1'], 'target1'],
            [['target1', 'target2'], 'target1,target2'],
        ];
    }

    /**
     * @dataProvider setDependsInvalidDataProvider
     */
    #[\PHPUnit\Framework\Attributes\DataProvider('setDependsInvalidDataProvider')]
    public function testSetDependsInvalid(string $depends): void
    {
        $this->expectException(BuildException::class);
        $this->expectExceptionMessage('Syntax Error: Depend attribute for target MyTarget is malformed.');

        $this->target->setDepends($depends);
    }

    public static function setDependsInvalidDataProvider(): array
    {
        return [
            [''],
            ['target1,'],
        ];
    }

    public function testGetTasksReturnsCorrectTasks(): void
    {
        $task = new EchoTask();
        $task->setMessage('Hello World');
        $this->target->addTask($task);
        $this->target->addDataType('dataType');

        $tasks = $this->target->getTasks();

        $this->assertEquals([$task], $tasks);
    }

    public function testGetTasksClonesTasks(): void
    {
        $task = new EchoTask();
        $task->setMessage('Hello World');
        $this->target->addTask($task);

        $tasks = $this->target->getTasks();

        $this->assertNotSame($task, $tasks[0]);
    }

    public function testMainAppliesConfigurables(): void
    {
        $configurable = $this->getMockBuilder(RuntimeConfigurable::class)
            ->disableOriginalConstructor()
            ->getMock()
        ;
        $configurable->expects($this->once())->method('maybeConfigure')->with($this->project);
        $this->target->addDataType($configurable);

        $this->target->main();
    }

    public function testMainFalseIfDoesntApplyConfigurable(): void
    {
        $this->project->setProperty('ifProperty', null);
        $this->target->setIf('ifProperty');

        $configurable = $this->getMockBuilder(RuntimeConfigurable::class)
            ->disableOriginalConstructor()
            ->getMock()
        ;
        $configurable->expects($this->never())->method('maybeConfigure');
        $this->target->addDataType($configurable);

        $this->target->main();
    }

    public function testMainTrueUnlessDoesntApplyConfigurable(): void
    {
        $this->project->setProperty('unlessProperty', 'someValue');
        $this->target->setUnless('unlessProperty');

        $configurable = $this->getMockBuilder(RuntimeConfigurable::class)
            ->disableOriginalConstructor()
            ->getMock()
        ;
        $configurable->expects($this->never())->method('maybeConfigure');
        $this->target->addDataType($configurable);

        $this->target->main();
    }

    public function testMainPerformsTasks(): void
    {
        $task = $this->createMock(Task::class);
        $task->expects($this->once())->method('perform');
        $this->target->addTask($task);

        $this->target->main();
    }

    public function testMainFalseIfDoesntPerformTasks(): void
    {
        $this->project->setProperty('ifProperty', null);
        $this->target->setIf('ifProperty');

        $task = $this->createMock(Task::class);
        $task->expects($this->never())->method('perform');
        $this->target->addTask($task);

        $this->target->main();
    }

    public function testMainTrueUnlessDoesntPerformTasks(): void
    {
        $this->project->setProperty('unlessProperty', 'someValue');
        $this->target->setUnless('unlessProperty');

        $task = $this->createMock(Task::class);
        $task->expects($this->never())->method('perform');
        $this->target->addTask($task);

        $this->target->main();
    }
}
