diff --git a/composer.json b/composer.json
index dd19a77..377f70d 100644
--- a/composer.json
+++ b/composer.json
@@ -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": {
diff --git a/phpunit.xml b/phpunit.xml
index ad0eeee..05d225e 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -4,6 +4,9 @@
bootstrap="vendor/autoload.php"
colors="true"
>
+
+
+
src/
diff --git a/src/Factory/NodeProcessorProgressBarFactory.php b/src/Factory/NodeProcessorProgressBarFactory.php
new file mode 100644
index 0000000..3629d68
--- /dev/null
+++ b/src/Factory/NodeProcessorProgressBarFactory.php
@@ -0,0 +1,27 @@
+setBarCharacter('⚬>');
+ $progressBar->setEmptyBarCharacter("⚬>");
+ $progressBar->setProgressCharacter("➤>");
+ $progressBar->setFormat(
+ "%node%\n%current% [%bar%]\n %memory:6s%\n"
+ );
+
+ return $progressBar;
+ }
+}
\ No newline at end of file
diff --git a/src/NodeProcessor/ProgressOutputNodeProcessor.php b/src/NodeProcessor/ProgressOutputNodeProcessor.php
new file mode 100644
index 0000000..008b8a7
--- /dev/null
+++ b/src/NodeProcessor/ProgressOutputNodeProcessor.php
@@ -0,0 +1,63 @@
+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;
+ }
+}
diff --git a/tests/Hooks/BypassFinalHook.php b/tests/Hooks/BypassFinalHook.php
new file mode 100644
index 0000000..bb51820
--- /dev/null
+++ b/tests/Hooks/BypassFinalHook.php
@@ -0,0 +1,18 @@
+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);
+ }
+}
\ No newline at end of file
diff --git a/tests/Unit/NodeProcessor/ProgressOutputNodeProcessorTest.php b/tests/Unit/NodeProcessor/ProgressOutputNodeProcessorTest.php
new file mode 100644
index 0000000..4255631
--- /dev/null
+++ b/tests/Unit/NodeProcessor/ProgressOutputNodeProcessorTest.php
@@ -0,0 +1,135 @@
+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;
+ }
+}