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; + } +}