4 * This file is part of the Symfony package.
6 * (c) Fabien Potencier <fabien@symfony.com>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Symfony\Component\Console\Tests\Helper;
14 use PHPUnit\Framework\TestCase;
15 use Symfony\Component\Console\Helper\ProgressBar;
16 use Symfony\Component\Console\Helper\Helper;
17 use Symfony\Component\Console\Output\StreamOutput;
20 * @group time-sensitive
22 class ProgressBarTest extends TestCase
24 public function testMultipleStart()
26 $bar = new ProgressBar($output = $this->getOutputStream());
31 rewind($output->getStream());
33 ' 0 [>---------------------------]'.
34 $this->generateOutput(' 1 [->--------------------------]').
35 $this->generateOutput(' 0 [>---------------------------]'),
36 stream_get_contents($output->getStream())
40 public function testAdvance()
42 $bar = new ProgressBar($output = $this->getOutputStream());
46 rewind($output->getStream());
48 ' 0 [>---------------------------]'.
49 $this->generateOutput(' 1 [->--------------------------]'),
50 stream_get_contents($output->getStream())
54 public function testAdvanceWithStep()
56 $bar = new ProgressBar($output = $this->getOutputStream());
60 rewind($output->getStream());
62 ' 0 [>---------------------------]'.
63 $this->generateOutput(' 5 [----->----------------------]'),
64 stream_get_contents($output->getStream())
68 public function testAdvanceMultipleTimes()
70 $bar = new ProgressBar($output = $this->getOutputStream());
75 rewind($output->getStream());
77 ' 0 [>---------------------------]'.
78 $this->generateOutput(' 3 [--->------------------------]').
79 $this->generateOutput(' 5 [----->----------------------]'),
80 stream_get_contents($output->getStream())
84 public function testAdvanceOverMax()
86 $bar = new ProgressBar($output = $this->getOutputStream(), 10);
91 rewind($output->getStream());
93 ' 9/10 [=========================>--] 90%'.
94 $this->generateOutput(' 10/10 [============================] 100%').
95 $this->generateOutput(' 11/11 [============================] 100%'),
96 stream_get_contents($output->getStream())
100 public function testRegress()
102 $bar = new ProgressBar($output = $this->getOutputStream());
108 rewind($output->getStream());
110 ' 0 [>---------------------------]'.
111 $this->generateOutput(' 1 [->--------------------------]').
112 $this->generateOutput(' 2 [-->-------------------------]').
113 $this->generateOutput(' 1 [->--------------------------]'),
114 stream_get_contents($output->getStream())
118 public function testRegressWithStep()
120 $bar = new ProgressBar($output = $this->getOutputStream());
126 rewind($output->getStream());
128 ' 0 [>---------------------------]'.
129 $this->generateOutput(' 4 [---->-----------------------]').
130 $this->generateOutput(' 8 [-------->-------------------]').
131 $this->generateOutput(' 6 [------>---------------------]'),
132 stream_get_contents($output->getStream())
136 public function testRegressMultipleTimes()
138 $bar = new ProgressBar($output = $this->getOutputStream());
145 rewind($output->getStream());
147 ' 0 [>---------------------------]'.
148 $this->generateOutput(' 3 [--->------------------------]').
149 $this->generateOutput(' 6 [------>---------------------]').
150 $this->generateOutput(' 5 [----->----------------------]').
151 $this->generateOutput(' 3 [--->------------------------]'),
152 stream_get_contents($output->getStream())
156 public function testRegressBelowMin()
158 $bar = new ProgressBar($output = $this->getOutputStream(), 10);
159 $bar->setProgress(1);
163 rewind($output->getStream());
165 ' 1/10 [==>-------------------------] 10%'.
166 $this->generateOutput(' 0/10 [>---------------------------] 0%'),
167 stream_get_contents($output->getStream())
171 public function testFormat()
174 ' 0/10 [>---------------------------] 0%'.
175 $this->generateOutput(' 10/10 [============================] 100%').
176 $this->generateOutput(' 10/10 [============================] 100%')
179 // max in construct, no format
180 $bar = new ProgressBar($output = $this->getOutputStream(), 10);
185 rewind($output->getStream());
186 $this->assertEquals($expected, stream_get_contents($output->getStream()));
188 // max in start, no format
189 $bar = new ProgressBar($output = $this->getOutputStream());
194 rewind($output->getStream());
195 $this->assertEquals($expected, stream_get_contents($output->getStream()));
197 // max in construct, explicit format before
198 $bar = new ProgressBar($output = $this->getOutputStream(), 10);
199 $bar->setFormat('normal');
204 rewind($output->getStream());
205 $this->assertEquals($expected, stream_get_contents($output->getStream()));
207 // max in start, explicit format before
208 $bar = new ProgressBar($output = $this->getOutputStream());
209 $bar->setFormat('normal');
214 rewind($output->getStream());
215 $this->assertEquals($expected, stream_get_contents($output->getStream()));
218 public function testCustomizations()
220 $bar = new ProgressBar($output = $this->getOutputStream(), 10);
221 $bar->setBarWidth(10);
222 $bar->setBarCharacter('_');
223 $bar->setEmptyBarCharacter(' ');
224 $bar->setProgressCharacter('/');
225 $bar->setFormat(' %current%/%max% [%bar%] %percent:3s%%');
229 rewind($output->getStream());
232 $this->generateOutput(' 1/10 [_/ ] 10%'),
233 stream_get_contents($output->getStream())
237 public function testDisplayWithoutStart()
239 $bar = new ProgressBar($output = $this->getOutputStream(), 50);
242 rewind($output->getStream());
244 ' 0/50 [>---------------------------] 0%',
245 stream_get_contents($output->getStream())
249 public function testDisplayWithQuietVerbosity()
251 $bar = new ProgressBar($output = $this->getOutputStream(true, StreamOutput::VERBOSITY_QUIET), 50);
254 rewind($output->getStream());
257 stream_get_contents($output->getStream())
261 public function testFinishWithoutStart()
263 $bar = new ProgressBar($output = $this->getOutputStream(), 50);
266 rewind($output->getStream());
268 ' 50/50 [============================] 100%',
269 stream_get_contents($output->getStream())
273 public function testPercent()
275 $bar = new ProgressBar($output = $this->getOutputStream(), 50);
281 rewind($output->getStream());
283 ' 0/50 [>---------------------------] 0%'.
284 $this->generateOutput(' 0/50 [>---------------------------] 0%').
285 $this->generateOutput(' 1/50 [>---------------------------] 2%').
286 $this->generateOutput(' 2/50 [=>--------------------------] 4%'),
287 stream_get_contents($output->getStream())
291 public function testOverwriteWithShorterLine()
293 $bar = new ProgressBar($output = $this->getOutputStream(), 50);
294 $bar->setFormat(' %current%/%max% [%bar%] %percent:3s%%');
299 // set shorter format
300 $bar->setFormat(' %current%/%max% [%bar%]');
303 rewind($output->getStream());
305 ' 0/50 [>---------------------------] 0%'.
306 $this->generateOutput(' 0/50 [>---------------------------] 0%').
307 $this->generateOutput(' 1/50 [>---------------------------] 2%').
308 $this->generateOutput(' 2/50 [=>--------------------------]'),
309 stream_get_contents($output->getStream())
313 public function testStartWithMax()
315 $bar = new ProgressBar($output = $this->getOutputStream());
316 $bar->setFormat('%current%/%max% [%bar%]');
320 rewind($output->getStream());
322 ' 0/50 [>---------------------------]'.
323 $this->generateOutput(' 1/50 [>---------------------------]'),
324 stream_get_contents($output->getStream())
328 public function testSetCurrentProgress()
330 $bar = new ProgressBar($output = $this->getOutputStream(), 50);
334 $bar->setProgress(15);
335 $bar->setProgress(25);
337 rewind($output->getStream());
339 ' 0/50 [>---------------------------] 0%'.
340 $this->generateOutput(' 0/50 [>---------------------------] 0%').
341 $this->generateOutput(' 1/50 [>---------------------------] 2%').
342 $this->generateOutput(' 15/50 [========>-------------------] 30%').
343 $this->generateOutput(' 25/50 [==============>-------------] 50%'),
344 stream_get_contents($output->getStream())
348 public function testSetCurrentBeforeStarting()
350 $bar = new ProgressBar($this->getOutputStream());
351 $bar->setProgress(15);
352 $this->assertNotNull($bar->getStartTime());
355 public function testRedrawFrequency()
357 $bar = $this->getMockBuilder('Symfony\Component\Console\Helper\ProgressBar')->setMethods(array('display'))->setConstructorArgs(array($this->getOutputStream(), 6))->getMock();
358 $bar->expects($this->exactly(4))->method('display');
360 $bar->setRedrawFrequency(2);
362 $bar->setProgress(1);
368 public function testRedrawFrequencyIsAtLeastOneIfZeroGiven()
370 $bar = $this->getMockBuilder('Symfony\Component\Console\Helper\ProgressBar')->setMethods(array('display'))->setConstructorArgs(array($this->getOutputStream()))->getMock();
372 $bar->expects($this->exactly(2))->method('display');
373 $bar->setRedrawFrequency(0);
378 public function testRedrawFrequencyIsAtLeastOneIfSmallerOneGiven()
380 $bar = $this->getMockBuilder('Symfony\Component\Console\Helper\ProgressBar')->setMethods(array('display'))->setConstructorArgs(array($this->getOutputStream()))->getMock();
382 $bar->expects($this->exactly(2))->method('display');
383 $bar->setRedrawFrequency(0.9);
388 public function testMultiByteSupport()
390 $bar = new ProgressBar($output = $this->getOutputStream());
392 $bar->setBarCharacter('■');
395 rewind($output->getStream());
397 ' 0 [>---------------------------]'.
398 $this->generateOutput(' 3 [■■■>------------------------]'),
399 stream_get_contents($output->getStream())
403 public function testClear()
405 $bar = new ProgressBar($output = $this->getOutputStream(), 50);
407 $bar->setProgress(25);
410 rewind($output->getStream());
412 ' 0/50 [>---------------------------] 0%'.
413 $this->generateOutput(' 25/50 [==============>-------------] 50%').
414 $this->generateOutput(''),
415 stream_get_contents($output->getStream())
419 public function testPercentNotHundredBeforeComplete()
421 $bar = new ProgressBar($output = $this->getOutputStream(), 200);
427 rewind($output->getStream());
429 ' 0/200 [>---------------------------] 0%'.
430 $this->generateOutput(' 0/200 [>---------------------------] 0%').
431 $this->generateOutput(' 199/200 [===========================>] 99%').
432 $this->generateOutput(' 200/200 [============================] 100%'),
433 stream_get_contents($output->getStream())
437 public function testNonDecoratedOutput()
439 $bar = new ProgressBar($output = $this->getOutputStream(false), 200);
442 for ($i = 0; $i < 200; ++$i) {
448 rewind($output->getStream());
450 ' 0/200 [>---------------------------] 0%'.PHP_EOL.
451 ' 20/200 [==>-------------------------] 10%'.PHP_EOL.
452 ' 40/200 [=====>----------------------] 20%'.PHP_EOL.
453 ' 60/200 [========>-------------------] 30%'.PHP_EOL.
454 ' 80/200 [===========>----------------] 40%'.PHP_EOL.
455 ' 100/200 [==============>-------------] 50%'.PHP_EOL.
456 ' 120/200 [================>-----------] 60%'.PHP_EOL.
457 ' 140/200 [===================>--------] 70%'.PHP_EOL.
458 ' 160/200 [======================>-----] 80%'.PHP_EOL.
459 ' 180/200 [=========================>--] 90%'.PHP_EOL.
460 ' 200/200 [============================] 100%',
461 stream_get_contents($output->getStream())
465 public function testNonDecoratedOutputWithClear()
467 $bar = new ProgressBar($output = $this->getOutputStream(false), 50);
469 $bar->setProgress(25);
471 $bar->setProgress(50);
474 rewind($output->getStream());
476 ' 0/50 [>---------------------------] 0%'.PHP_EOL.
477 ' 25/50 [==============>-------------] 50%'.PHP_EOL.
478 ' 50/50 [============================] 100%',
479 stream_get_contents($output->getStream())
483 public function testNonDecoratedOutputWithoutMax()
485 $bar = new ProgressBar($output = $this->getOutputStream(false));
489 rewind($output->getStream());
491 ' 0 [>---------------------------]'.PHP_EOL.
492 ' 1 [->--------------------------]',
493 stream_get_contents($output->getStream())
497 public function testParallelBars()
499 $output = $this->getOutputStream();
500 $bar1 = new ProgressBar($output, 2);
501 $bar2 = new ProgressBar($output, 3);
502 $bar2->setProgressCharacter('#');
503 $bar3 = new ProgressBar($output);
506 $output->write("\n");
508 $output->write("\n");
511 for ($i = 1; $i <= 3; ++$i) {
513 $output->write("\033[2A");
517 $output->write("\n");
519 $output->write("\n");
522 $output->write("\033[2A");
523 $output->write("\n");
524 $output->write("\n");
527 rewind($output->getStream());
529 ' 0/2 [>---------------------------] 0%'."\n".
530 ' 0/3 [#---------------------------] 0%'."\n".
531 rtrim(' 0 [>---------------------------]').
534 $this->generateOutput(' 1/2 [==============>-------------] 50%')."\n".
535 $this->generateOutput(' 1/3 [=========#------------------] 33%')."\n".
536 rtrim($this->generateOutput(' 1 [->--------------------------]')).
539 $this->generateOutput(' 2/2 [============================] 100%')."\n".
540 $this->generateOutput(' 2/3 [==================#---------] 66%')."\n".
541 rtrim($this->generateOutput(' 2 [-->-------------------------]')).
545 $this->generateOutput(' 3/3 [============================] 100%')."\n".
546 rtrim($this->generateOutput(' 3 [--->------------------------]')).
551 rtrim($this->generateOutput(' 3 [============================]')),
552 stream_get_contents($output->getStream())
556 public function testWithoutMax()
558 $output = $this->getOutputStream();
560 $bar = new ProgressBar($output);
567 rewind($output->getStream());
569 rtrim(' 0 [>---------------------------]').
570 rtrim($this->generateOutput(' 1 [->--------------------------]')).
571 rtrim($this->generateOutput(' 2 [-->-------------------------]')).
572 rtrim($this->generateOutput(' 3 [--->------------------------]')).
573 rtrim($this->generateOutput(' 3 [============================]')),
574 stream_get_contents($output->getStream())
578 public function testWithSmallScreen()
580 $output = $this->getOutputStream();
582 $bar = new ProgressBar($output);
583 putenv('COLUMNS=12');
586 putenv('COLUMNS=120');
588 rewind($output->getStream());
591 $this->generateOutput(' 1 [->--]'),
592 stream_get_contents($output->getStream())
596 public function testAddingPlaceholderFormatter()
598 ProgressBar::setPlaceholderFormatterDefinition('remaining_steps', function (ProgressBar $bar) {
599 return $bar->getMaxSteps() - $bar->getProgress();
601 $bar = new ProgressBar($output = $this->getOutputStream(), 3);
602 $bar->setFormat(' %remaining_steps% [%bar%]');
608 rewind($output->getStream());
610 ' 3 [>---------------------------]'.
611 $this->generateOutput(' 2 [=========>------------------]').
612 $this->generateOutput(' 0 [============================]'),
613 stream_get_contents($output->getStream())
617 public function testMultilineFormat()
619 $bar = new ProgressBar($output = $this->getOutputStream(), 3);
620 $bar->setFormat("%bar%\nfoobar");
627 rewind($output->getStream());
629 ">---------------------------\nfoobar".
630 $this->generateOutput("=========>------------------\nfoobar").
631 "\x0D\x1B[2K\x1B[1A\x1B[2K".
632 $this->generateOutput("============================\nfoobar"),
633 stream_get_contents($output->getStream())
637 public function testAnsiColorsAndEmojis()
639 putenv('COLUMNS=156');
641 $bar = new ProgressBar($output = $this->getOutputStream(), 15);
642 ProgressBar::setPlaceholderFormatterDefinition('memory', function (ProgressBar $bar) {
645 $colors = $i++ ? '41;37' : '44;37';
647 return "\033[".$colors.'m '.Helper::formatMemory($mem)." \033[0m";
649 $bar->setFormat(" \033[44;37m %title:-37s% \033[0m\n %current%/%max% %bar% %percent:3s%%\n 🏁 %remaining:-10s% %memory:37s%");
650 $bar->setBarCharacter($done = "\033[32m●\033[0m");
651 $bar->setEmptyBarCharacter($empty = "\033[31m●\033[0m");
652 $bar->setProgressCharacter($progress = "\033[32m➤ \033[0m");
654 $bar->setMessage('Starting the demo... fingers crossed', 'title');
657 rewind($output->getStream());
659 " \033[44;37m Starting the demo... fingers crossed \033[0m\n".
660 ' 0/15 '.$progress.str_repeat($empty, 26)." 0%\n".
661 " \xf0\x9f\x8f\x81 < 1 sec \033[44;37m 0 B \033[0m",
662 stream_get_contents($output->getStream())
664 ftruncate($output->getStream(), 0);
665 rewind($output->getStream());
667 $bar->setMessage('Looks good to me...', 'title');
670 rewind($output->getStream());
672 $this->generateOutput(
673 " \033[44;37m Looks good to me... \033[0m\n".
674 ' 4/15 '.str_repeat($done, 7).$progress.str_repeat($empty, 19)." 26%\n".
675 " \xf0\x9f\x8f\x81 < 1 sec \033[41;37m 97 KiB \033[0m"
677 stream_get_contents($output->getStream())
679 ftruncate($output->getStream(), 0);
680 rewind($output->getStream());
682 $bar->setMessage('Thanks, bye', 'title');
685 rewind($output->getStream());
687 $this->generateOutput(
688 " \033[44;37m Thanks, bye \033[0m\n".
689 ' 15/15 '.str_repeat($done, 28)." 100%\n".
690 " \xf0\x9f\x8f\x81 < 1 sec \033[41;37m 195 KiB \033[0m"
692 stream_get_contents($output->getStream())
694 putenv('COLUMNS=120');
697 public function testSetFormat()
699 $bar = new ProgressBar($output = $this->getOutputStream());
700 $bar->setFormat('normal');
702 rewind($output->getStream());
704 ' 0 [>---------------------------]',
705 stream_get_contents($output->getStream())
708 $bar = new ProgressBar($output = $this->getOutputStream(), 10);
709 $bar->setFormat('normal');
711 rewind($output->getStream());
713 ' 0/10 [>---------------------------] 0%',
714 stream_get_contents($output->getStream())
719 * @dataProvider provideFormat
721 public function testFormatsWithoutMax($format)
723 $bar = new ProgressBar($output = $this->getOutputStream());
724 $bar->setFormat($format);
727 rewind($output->getStream());
728 $this->assertNotEmpty(stream_get_contents($output->getStream()));
732 * Provides each defined format.
736 public function provideFormat()
741 array('very_verbose'),
746 protected function getOutputStream($decorated = true, $verbosity = StreamOutput::VERBOSITY_NORMAL)
748 return new StreamOutput(fopen('php://memory', 'r+', false), $verbosity, $decorated);
751 protected function generateOutput($expected)
753 $count = substr_count($expected, "\n");
755 return "\x0D\x1B[2K".($count ? str_repeat("\x1B[1A\x1B[2K", $count) : '').$expected;
758 public function testBarWidthWithMultilineFormat()
760 putenv('COLUMNS=10');
762 $bar = new ProgressBar($output = $this->getOutputStream());
763 $bar->setFormat("%bar%\n0123456789");
766 $bar->setBarWidth(5);
767 $this->assertEquals(5, $bar->getBarWidth());
771 rewind($output->getStream());
772 $this->assertEquals(5, $bar->getBarWidth(), stream_get_contents($output->getStream()));
773 putenv('COLUMNS=120');