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\Helper;
16 use Symfony\Component\Console\Helper\ProgressBar;
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 = new ProgressBar($output = $this->getOutputStream(), 6);
358 $bar->setRedrawFrequency(2);
360 $bar->setProgress(1);
365 rewind($output->getStream());
367 ' 0/6 [>---------------------------] 0%'.
368 $this->generateOutput(' 3/6 [==============>-------------] 50%').
369 $this->generateOutput(' 5/6 [=======================>----] 83%').
370 $this->generateOutput(' 6/6 [============================] 100%'),
371 stream_get_contents($output->getStream())
375 public function testRedrawFrequencyIsAtLeastOneIfZeroGiven()
377 $bar = new ProgressBar($output = $this->getOutputStream());
378 $bar->setRedrawFrequency(0);
382 rewind($output->getStream());
384 ' 0 [>---------------------------]'.
385 $this->generateOutput(' 1 [->--------------------------]'),
386 stream_get_contents($output->getStream())
390 public function testRedrawFrequencyIsAtLeastOneIfSmallerOneGiven()
392 $bar = new ProgressBar($output = $this->getOutputStream());
393 $bar->setRedrawFrequency(0.9);
397 rewind($output->getStream());
399 ' 0 [>---------------------------]'.
400 $this->generateOutput(' 1 [->--------------------------]'),
401 stream_get_contents($output->getStream())
405 public function testMultiByteSupport()
407 $bar = new ProgressBar($output = $this->getOutputStream());
409 $bar->setBarCharacter('■');
412 rewind($output->getStream());
414 ' 0 [>---------------------------]'.
415 $this->generateOutput(' 3 [■■■>------------------------]'),
416 stream_get_contents($output->getStream())
420 public function testClear()
422 $bar = new ProgressBar($output = $this->getOutputStream(), 50);
424 $bar->setProgress(25);
427 rewind($output->getStream());
429 ' 0/50 [>---------------------------] 0%'.
430 $this->generateOutput(' 25/50 [==============>-------------] 50%').
431 $this->generateOutput(''),
432 stream_get_contents($output->getStream())
436 public function testPercentNotHundredBeforeComplete()
438 $bar = new ProgressBar($output = $this->getOutputStream(), 200);
444 rewind($output->getStream());
446 ' 0/200 [>---------------------------] 0%'.
447 $this->generateOutput(' 0/200 [>---------------------------] 0%').
448 $this->generateOutput(' 199/200 [===========================>] 99%').
449 $this->generateOutput(' 200/200 [============================] 100%'),
450 stream_get_contents($output->getStream())
454 public function testNonDecoratedOutput()
456 $bar = new ProgressBar($output = $this->getOutputStream(false), 200);
459 for ($i = 0; $i < 200; ++$i) {
465 rewind($output->getStream());
467 ' 0/200 [>---------------------------] 0%'.PHP_EOL.
468 ' 20/200 [==>-------------------------] 10%'.PHP_EOL.
469 ' 40/200 [=====>----------------------] 20%'.PHP_EOL.
470 ' 60/200 [========>-------------------] 30%'.PHP_EOL.
471 ' 80/200 [===========>----------------] 40%'.PHP_EOL.
472 ' 100/200 [==============>-------------] 50%'.PHP_EOL.
473 ' 120/200 [================>-----------] 60%'.PHP_EOL.
474 ' 140/200 [===================>--------] 70%'.PHP_EOL.
475 ' 160/200 [======================>-----] 80%'.PHP_EOL.
476 ' 180/200 [=========================>--] 90%'.PHP_EOL.
477 ' 200/200 [============================] 100%',
478 stream_get_contents($output->getStream())
482 public function testNonDecoratedOutputWithClear()
484 $bar = new ProgressBar($output = $this->getOutputStream(false), 50);
486 $bar->setProgress(25);
488 $bar->setProgress(50);
491 rewind($output->getStream());
493 ' 0/50 [>---------------------------] 0%'.PHP_EOL.
494 ' 25/50 [==============>-------------] 50%'.PHP_EOL.
495 ' 50/50 [============================] 100%',
496 stream_get_contents($output->getStream())
500 public function testNonDecoratedOutputWithoutMax()
502 $bar = new ProgressBar($output = $this->getOutputStream(false));
506 rewind($output->getStream());
508 ' 0 [>---------------------------]'.PHP_EOL.
509 ' 1 [->--------------------------]',
510 stream_get_contents($output->getStream())
514 public function testParallelBars()
516 $output = $this->getOutputStream();
517 $bar1 = new ProgressBar($output, 2);
518 $bar2 = new ProgressBar($output, 3);
519 $bar2->setProgressCharacter('#');
520 $bar3 = new ProgressBar($output);
523 $output->write("\n");
525 $output->write("\n");
528 for ($i = 1; $i <= 3; ++$i) {
530 $output->write("\033[2A");
534 $output->write("\n");
536 $output->write("\n");
539 $output->write("\033[2A");
540 $output->write("\n");
541 $output->write("\n");
544 rewind($output->getStream());
546 ' 0/2 [>---------------------------] 0%'."\n".
547 ' 0/3 [#---------------------------] 0%'."\n".
548 rtrim(' 0 [>---------------------------]').
551 $this->generateOutput(' 1/2 [==============>-------------] 50%')."\n".
552 $this->generateOutput(' 1/3 [=========#------------------] 33%')."\n".
553 rtrim($this->generateOutput(' 1 [->--------------------------]')).
556 $this->generateOutput(' 2/2 [============================] 100%')."\n".
557 $this->generateOutput(' 2/3 [==================#---------] 66%')."\n".
558 rtrim($this->generateOutput(' 2 [-->-------------------------]')).
562 $this->generateOutput(' 3/3 [============================] 100%')."\n".
563 rtrim($this->generateOutput(' 3 [--->------------------------]')).
568 rtrim($this->generateOutput(' 3 [============================]')),
569 stream_get_contents($output->getStream())
573 public function testWithoutMax()
575 $output = $this->getOutputStream();
577 $bar = new ProgressBar($output);
584 rewind($output->getStream());
586 rtrim(' 0 [>---------------------------]').
587 rtrim($this->generateOutput(' 1 [->--------------------------]')).
588 rtrim($this->generateOutput(' 2 [-->-------------------------]')).
589 rtrim($this->generateOutput(' 3 [--->------------------------]')).
590 rtrim($this->generateOutput(' 3 [============================]')),
591 stream_get_contents($output->getStream())
595 public function testWithSmallScreen()
597 $output = $this->getOutputStream();
599 $bar = new ProgressBar($output);
600 putenv('COLUMNS=12');
603 putenv('COLUMNS=120');
605 rewind($output->getStream());
608 $this->generateOutput(' 1 [->--]'),
609 stream_get_contents($output->getStream())
613 public function testAddingPlaceholderFormatter()
615 ProgressBar::setPlaceholderFormatterDefinition('remaining_steps', function (ProgressBar $bar) {
616 return $bar->getMaxSteps() - $bar->getProgress();
618 $bar = new ProgressBar($output = $this->getOutputStream(), 3);
619 $bar->setFormat(' %remaining_steps% [%bar%]');
625 rewind($output->getStream());
627 ' 3 [>---------------------------]'.
628 $this->generateOutput(' 2 [=========>------------------]').
629 $this->generateOutput(' 0 [============================]'),
630 stream_get_contents($output->getStream())
634 public function testMultilineFormat()
636 $bar = new ProgressBar($output = $this->getOutputStream(), 3);
637 $bar->setFormat("%bar%\nfoobar");
644 rewind($output->getStream());
646 ">---------------------------\nfoobar".
647 $this->generateOutput("=========>------------------\nfoobar").
648 "\x0D\x1B[2K\x1B[1A\x1B[2K".
649 $this->generateOutput("============================\nfoobar"),
650 stream_get_contents($output->getStream())
654 public function testAnsiColorsAndEmojis()
656 putenv('COLUMNS=156');
658 $bar = new ProgressBar($output = $this->getOutputStream(), 15);
659 ProgressBar::setPlaceholderFormatterDefinition('memory', function (ProgressBar $bar) {
662 $colors = $i++ ? '41;37' : '44;37';
664 return "\033[".$colors.'m '.Helper::formatMemory($mem)." \033[0m";
666 $bar->setFormat(" \033[44;37m %title:-37s% \033[0m\n %current%/%max% %bar% %percent:3s%%\n 🏁 %remaining:-10s% %memory:37s%");
667 $bar->setBarCharacter($done = "\033[32m●\033[0m");
668 $bar->setEmptyBarCharacter($empty = "\033[31m●\033[0m");
669 $bar->setProgressCharacter($progress = "\033[32m➤ \033[0m");
671 $bar->setMessage('Starting the demo... fingers crossed', 'title');
674 rewind($output->getStream());
676 " \033[44;37m Starting the demo... fingers crossed \033[0m\n".
677 ' 0/15 '.$progress.str_repeat($empty, 26)." 0%\n".
678 " \xf0\x9f\x8f\x81 < 1 sec \033[44;37m 0 B \033[0m",
679 stream_get_contents($output->getStream())
681 ftruncate($output->getStream(), 0);
682 rewind($output->getStream());
684 $bar->setMessage('Looks good to me...', 'title');
687 rewind($output->getStream());
689 $this->generateOutput(
690 " \033[44;37m Looks good to me... \033[0m\n".
691 ' 4/15 '.str_repeat($done, 7).$progress.str_repeat($empty, 19)." 26%\n".
692 " \xf0\x9f\x8f\x81 < 1 sec \033[41;37m 97 KiB \033[0m"
694 stream_get_contents($output->getStream())
696 ftruncate($output->getStream(), 0);
697 rewind($output->getStream());
699 $bar->setMessage('Thanks, bye', 'title');
702 rewind($output->getStream());
704 $this->generateOutput(
705 " \033[44;37m Thanks, bye \033[0m\n".
706 ' 15/15 '.str_repeat($done, 28)." 100%\n".
707 " \xf0\x9f\x8f\x81 < 1 sec \033[41;37m 195 KiB \033[0m"
709 stream_get_contents($output->getStream())
711 putenv('COLUMNS=120');
714 public function testSetFormat()
716 $bar = new ProgressBar($output = $this->getOutputStream());
717 $bar->setFormat('normal');
719 rewind($output->getStream());
721 ' 0 [>---------------------------]',
722 stream_get_contents($output->getStream())
725 $bar = new ProgressBar($output = $this->getOutputStream(), 10);
726 $bar->setFormat('normal');
728 rewind($output->getStream());
730 ' 0/10 [>---------------------------] 0%',
731 stream_get_contents($output->getStream())
736 * @dataProvider provideFormat
738 public function testFormatsWithoutMax($format)
740 $bar = new ProgressBar($output = $this->getOutputStream());
741 $bar->setFormat($format);
744 rewind($output->getStream());
745 $this->assertNotEmpty(stream_get_contents($output->getStream()));
749 * Provides each defined format.
753 public function provideFormat()
758 array('very_verbose'),
763 protected function getOutputStream($decorated = true, $verbosity = StreamOutput::VERBOSITY_NORMAL)
765 return new StreamOutput(fopen('php://memory', 'r+', false), $verbosity, $decorated);
768 protected function generateOutput($expected)
770 $count = substr_count($expected, "\n");
772 return "\x0D\x1B[2K".($count ? str_repeat("\x1B[1A\x1B[2K", $count) : '').$expected;
775 public function testBarWidthWithMultilineFormat()
777 putenv('COLUMNS=10');
779 $bar = new ProgressBar($output = $this->getOutputStream());
780 $bar->setFormat("%bar%\n0123456789");
783 $bar->setBarWidth(5);
784 $this->assertEquals(5, $bar->getBarWidth());
788 rewind($output->getStream());
789 $this->assertEquals(5, $bar->getBarWidth(), stream_get_contents($output->getStream()));
790 putenv('COLUMNS=120');