Skip to content

add ProgressOutputNodeProcessor #5

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
4 changes: 3 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
},
"require-dev": {
"behat/behat": "^3.12.0",
"phpunit/phpunit": "^9.6.6"
"phpunit/phpunit": "^9.6.6",
"symfony/console": "^5.0 || ^6.0",
"dg/bypass-finals": "^1.4"
},
"autoload": {
"psr-4": {
Expand Down
3 changes: 3 additions & 0 deletions phpunit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
bootstrap="vendor/autoload.php"
colors="true"
>
<extensions>
<extension class="Netlogix\XmlProcessor\Tests\Hooks\BypassFinalHook"/>
</extensions>
<coverage>
<include>
<directory suffix=".php">src/</directory>
Expand Down
27 changes: 27 additions & 0 deletions src/Factory/NodeProcessorProgressBarFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);

namespace Netlogix\XmlProcessor\Factory;

use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class NodeProcessorProgressBarFactory
{
public function createProgressBar(?OutputInterface $output = NULL): ?ProgressBar
{
if (!($output instanceof ConsoleOutputInterface)) {
return NULL;
}
$progressBar = new ProgressBar($output);
$progressBar->setBarCharacter('<fg=green>⚬</>');
$progressBar->setEmptyBarCharacter("<fg=red>⚬</>");
$progressBar->setProgressCharacter("<fg=green>➤</>");
$progressBar->setFormat(
"<comment>%node%</comment>\n%current% [%bar%]\n %memory:6s%\n"
);

return $progressBar;
}
}
63 changes: 63 additions & 0 deletions src/NodeProcessor/ProgressOutputNodeProcessor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php
declare(strict_types=1);

namespace Netlogix\XmlProcessor\NodeProcessor;


use Netlogix\XmlProcessor\Factory\NodeProcessorProgressBarFactory;
use Netlogix\XmlProcessor\NodeProcessor\Context\OpenContext;
use Netlogix\XmlProcessor\XmlProcessor;
use Netlogix\XmlProcessor\XmlProcessorContext;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class ProgressOutputNodeProcessor implements NodeProcessorInterface, OpenNodeProcessorInterface
{
protected ?OutputInterface $output = NULL;

protected ?ProgressBar $progressBar = NULL;

protected NodeProcessorProgressBarFactory $progressBarFactory;

function __construct(?NodeProcessorProgressBarFactory $progressBarFactory = NULL)
{
$this->progressBarFactory = $progressBarFactory ?? new NodeProcessorProgressBarFactory();
}

public function SetOutput(?ConsoleOutputInterface $output = NULL): void
{
$this->output = $output;
$this->progressBar = NULL;
}

function getSubscribedEvents(string $nodePath, XmlProcessorContext $context): \Generator
{
if ($this->progressBar === NULL) {
yield XmlProcessor::EVENT_OPEN_FILE => [$this, 'openFile'];
} else {
yield 'NodeType_' . \XMLReader::ELEMENT => [$this, 'openElement'];
yield XmlProcessor::EVENT_END_OF_FILE => [$this, 'endOfFile'];
}
}

function openFile(): void
{
if ($this->progressBar !== NULL) {
$this->progressBar->finish();
}
$this->progressBar = $this->progressBarFactory->createProgressBar($this->output);
}

function openElement(OpenContext $context): void
{
$this->progressBar->advance();
$this->progressBar->setMessage($context->getNodePath(), 'node');
}

function endOfFile(): void
{
$this->progressBar->finish();
$this->progressBar = NULL;
}
}
18 changes: 18 additions & 0 deletions tests/Hooks/BypassFinalHook.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php
declare(strict_types=1);

namespace Netlogix\XmlProcessor\Tests\Hooks;

use DG\BypassFinals;
use PHPUnit\Runner\BeforeTestHook;

final class BypassFinalHook implements BeforeTestHook
{
public function executeBeforeTest(string $test): void
{
BypassFinals::enable();
BypassFinals::setWhitelist([
'*/vendor/symfony/console/Helper/*',
]);
}
}
36 changes: 36 additions & 0 deletions tests/Unit/Factory/NodeProcessorProgressBarFactoryTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);

namespace Netlogix\XmlProcessor\Tests\Unit;

use Netlogix\XmlProcessor\Factory\NodeProcessorProgressBarFactory;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class NodeProcessorProgressBarFactoryTest extends TestCase
{
function test__construct(): void
{
$nodeProcessorProgressBarFactory = new NodeProcessorProgressBarFactoryTest();
self::assertInstanceOf(NodeProcessorProgressBarFactoryTest::class, $nodeProcessorProgressBarFactory);
}

function testCreateProgressBar(): void
{
$nodeProcessorProgressBarFactory = new NodeProcessorProgressBarFactory();
$progressBar = $nodeProcessorProgressBarFactory->createProgressBar();
self::assertNull($progressBar);

$progressBar = $nodeProcessorProgressBarFactory->createProgressBar($this->getMockBuilder(OutputInterface::class)->getMock());
self::assertNull($progressBar);

$output = $this::getMockForAbstractClass(ConsoleOutputInterface::class);
$output->method('getErrorOutput')->willReturn($output);
$output->method('isDecorated')->willReturn(true);

$progressBar = $nodeProcessorProgressBarFactory->createProgressBar($output);
self::assertInstanceOf(ProgressBar::class, $progressBar);
}
}
135 changes: 135 additions & 0 deletions tests/Unit/NodeProcessor/ProgressOutputNodeProcessorTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
<?php
declare(strict_types=1);

namespace Netlogix\XmlProcessor\Tests\Unit\NodeProcessor;

use Netlogix\XmlProcessor\Factory\NodeProcessorProgressBarFactory;
use Netlogix\XmlProcessor\NodeProcessor\Context\OpenContext;
use Netlogix\XmlProcessor\NodeProcessor\ProgressOutputNodeProcessor;
use Netlogix\XmlProcessor\XmlProcessor;
use Netlogix\XmlProcessor\XmlProcessorContext;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Output\ConsoleOutputInterface;

class ProgressOutputNodeProcessorTest extends TestCase
{
protected function setUp(): void
{
parent::setUp();

$reflectionClass = new \ReflectionClass(ProgressOutputNodeProcessor::class);
$this->progressBarProperty = $reflectionClass->getProperty('progressBar');
$this->progressBarProperty->setAccessible(true);
}

public function test__construct(): void
{
$nodeProcessor = new ProgressOutputNodeProcessor();
self::assertInstanceOf(ProgressOutputNodeProcessor::class, $nodeProcessor);
}

public function testGetSubscribedEvents(): void
{
$progressBarFactory = $this::getMockBuilder(NodeProcessorProgressBarFactory::class)
->disableOriginalConstructor()
->getMock();
$progressBarFactory->method('createProgressBar')->willReturn($this->createMock(ProgressBar::class));
$nodeProcessor = new ProgressOutputNodeProcessor($progressBarFactory);
$context = $this->getMockBuilder(XmlProcessorContext::class)
->disableOriginalConstructor()
->getMock();

self::assertEquals(
[
XmlProcessor::EVENT_OPEN_FILE => [$nodeProcessor, 'openFile']
],
iterator_to_array($nodeProcessor->getSubscribedEvents('test', $context))
);

$nodeProcessor->setOutput($this->getOutputMock());
$nodeProcessor->openFile();

self::assertEquals(
[
'NodeType_' . \XMLReader::ELEMENT => [$nodeProcessor, 'openElement'],
XmlProcessor::EVENT_END_OF_FILE => [$nodeProcessor, 'endOfFile']
],
iterator_to_array($nodeProcessor->getSubscribedEvents('test', $context))
);
}

public function testOpenFile(): void
{
$nodeProcessor = new ProgressOutputNodeProcessor();
$nodeProcessor->openFile();
self::assertNull($this->progressBarProperty->getValue($nodeProcessor));

$progressBar = $this->getMockBuilder(ProgressBar::class)->disableOriginalConstructor()
->getMock();
$progressBar->expects($this->once())->method('finish');
$progressBarFactory = $this::getMockBuilder(NodeProcessorProgressBarFactory::class)
->disableOriginalConstructor()
->getMock();
$progressBarFactory->method('createProgressBar')->willReturn($progressBar);

$nodeProcessor = new ProgressOutputNodeProcessor($progressBarFactory);
$nodeProcessor->setOutput($this->getOutputMock());
$nodeProcessor->openFile();
self::assertInstanceOf(ProgressBar::class, $this->progressBarProperty->getValue($nodeProcessor));
$nodeProcessor->openFile();
self::assertInstanceOf(ProgressBar::class, $this->progressBarProperty->getValue($nodeProcessor));
}

public function testOpenElement(): void
{

$progressBar = $this->getMockBuilder(ProgressBar::class)->disableOriginalConstructor()
->getMock();

$progressBar->expects($this->once())->method('advance')->with(1);
$progressBar->expects($this->once())->method('setMessage')->with('foo/bar', 'node');
$progressBar->expects($this->once())->method('finish');
$progressBarFactory = $this::getMockBuilder(NodeProcessorProgressBarFactory::class)
->disableOriginalConstructor()
->getMock();
$progressBarFactory->expects($this->exactly(2))->method('createProgressBar')->willReturn($progressBar);

$nodeProcessor = new ProgressOutputNodeProcessor($progressBarFactory);
$nodeProcessor->setOutput($this->getOutputMock());
$nodeProcessor->openFile();

$openContext = $this->createMock(OpenContext::class);
$openContext->method('getNodePath')->willReturn('foo/bar');

$nodeProcessor->openElement($openContext);
$nodeProcessor->openFile();
}

function testEndOfFile(): void
{
$progressBar = $this->getMockBuilder(ProgressBar::class)->disableOriginalConstructor()
->getMock();
$progressBar->expects($this->once())->method('finish');
$progressBarFactory = $this::getMockBuilder(NodeProcessorProgressBarFactory::class)
->disableOriginalConstructor()
->getMock();
$progressBarFactory->method('createProgressBar')->willReturn($progressBar);

$nodeProcessor = new ProgressOutputNodeProcessor($progressBarFactory);
$nodeProcessor->setOutput($this->getOutputMock());
$nodeProcessor->openFile();

$nodeProcessor->endOfFile();
$progressBar = $this->progressBarProperty;
self::assertNull($progressBar->getValue($nodeProcessor));
}

private function getOutputMock(): ConsoleOutputInterface
{
$output = $this::getMockForAbstractClass(ConsoleOutputInterface::class);
$output->method('getErrorOutput')->willReturn($output);
$output->method('isDecorated')->willReturn(true);
return $output;
}
}
Loading