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 testFormat()
103 ' 0/10 [>---------------------------] 0%'.
104 $this->generateOutput(' 10/10 [============================] 100%').
105 $this->generateOutput(' 10/10 [============================] 100%')
108 // max in construct, no format
109 $bar = new ProgressBar($output = $this->getOutputStream(), 10);
114 rewind($output->getStream());
115 $this->assertEquals($expected, stream_get_contents($output->getStream()));
117 // max in start, no format
118 $bar = new ProgressBar($output = $this->getOutputStream());
123 rewind($output->getStream());
124 $this->assertEquals($expected, stream_get_contents($output->getStream()));
126 // max in construct, explicit format before
127 $bar = new ProgressBar($output = $this->getOutputStream(), 10);
128 $bar->setFormat('normal');
133 rewind($output->getStream());
134 $this->assertEquals($expected, stream_get_contents($output->getStream()));
136 // max in start, explicit format before
137 $bar = new ProgressBar($output = $this->getOutputStream());
138 $bar->setFormat('normal');
143 rewind($output->getStream());
144 $this->assertEquals($expected, stream_get_contents($output->getStream()));
147 public function testCustomizations()
149 $bar = new ProgressBar($output = $this->getOutputStream(), 10);
150 $bar->setBarWidth(10);
151 $bar->setBarCharacter('_');
152 $bar->setEmptyBarCharacter(' ');
153 $bar->setProgressCharacter('/');
154 $bar->setFormat(' %current%/%max% [%bar%] %percent:3s%%');
158 rewind($output->getStream());
161 $this->generateOutput(' 1/10 [_/ ] 10%'),
162 stream_get_contents($output->getStream())
166 public function testDisplayWithoutStart()
168 $bar = new ProgressBar($output = $this->getOutputStream(), 50);
171 rewind($output->getStream());
173 ' 0/50 [>---------------------------] 0%',
174 stream_get_contents($output->getStream())
178 public function testDisplayWithQuietVerbosity()
180 $bar = new ProgressBar($output = $this->getOutputStream(true, StreamOutput::VERBOSITY_QUIET), 50);
183 rewind($output->getStream());
186 stream_get_contents($output->getStream())
190 public function testFinishWithoutStart()
192 $bar = new ProgressBar($output = $this->getOutputStream(), 50);
195 rewind($output->getStream());
197 ' 50/50 [============================] 100%',
198 stream_get_contents($output->getStream())
202 public function testPercent()
204 $bar = new ProgressBar($output = $this->getOutputStream(), 50);
210 rewind($output->getStream());
212 ' 0/50 [>---------------------------] 0%'.
213 $this->generateOutput(' 0/50 [>---------------------------] 0%').
214 $this->generateOutput(' 1/50 [>---------------------------] 2%').
215 $this->generateOutput(' 2/50 [=>--------------------------] 4%'),
216 stream_get_contents($output->getStream())
220 public function testOverwriteWithShorterLine()
222 $bar = new ProgressBar($output = $this->getOutputStream(), 50);
223 $bar->setFormat(' %current%/%max% [%bar%] %percent:3s%%');
228 // set shorter format
229 $bar->setFormat(' %current%/%max% [%bar%]');
232 rewind($output->getStream());
234 ' 0/50 [>---------------------------] 0%'.
235 $this->generateOutput(' 0/50 [>---------------------------] 0%').
236 $this->generateOutput(' 1/50 [>---------------------------] 2%').
237 $this->generateOutput(' 2/50 [=>--------------------------]'),
238 stream_get_contents($output->getStream())
242 public function testStartWithMax()
244 $bar = new ProgressBar($output = $this->getOutputStream());
245 $bar->setFormat('%current%/%max% [%bar%]');
249 rewind($output->getStream());
251 ' 0/50 [>---------------------------]'.
252 $this->generateOutput(' 1/50 [>---------------------------]'),
253 stream_get_contents($output->getStream())
257 public function testSetCurrentProgress()
259 $bar = new ProgressBar($output = $this->getOutputStream(), 50);
263 $bar->setProgress(15);
264 $bar->setProgress(25);
266 rewind($output->getStream());
268 ' 0/50 [>---------------------------] 0%'.
269 $this->generateOutput(' 0/50 [>---------------------------] 0%').
270 $this->generateOutput(' 1/50 [>---------------------------] 2%').
271 $this->generateOutput(' 15/50 [========>-------------------] 30%').
272 $this->generateOutput(' 25/50 [==============>-------------] 50%'),
273 stream_get_contents($output->getStream())
277 public function testSetCurrentBeforeStarting()
279 $bar = new ProgressBar($this->getOutputStream());
280 $bar->setProgress(15);
281 $this->assertNotNull($bar->getStartTime());
285 * @expectedException \LogicException
286 * @expectedExceptionMessage You can't regress the progress bar
288 public function testRegressProgress()
290 $bar = new ProgressBar($output = $this->getOutputStream(), 50);
292 $bar->setProgress(15);
293 $bar->setProgress(10);
296 public function testRedrawFrequency()
298 $bar = $this->getMockBuilder('Symfony\Component\Console\Helper\ProgressBar')->setMethods(array('display'))->setConstructorArgs(array($this->getOutputStream(), 6))->getMock();
299 $bar->expects($this->exactly(4))->method('display');
301 $bar->setRedrawFrequency(2);
303 $bar->setProgress(1);
309 public function testRedrawFrequencyIsAtLeastOneIfZeroGiven()
311 $bar = $this->getMockBuilder('Symfony\Component\Console\Helper\ProgressBar')->setMethods(array('display'))->setConstructorArgs(array($this->getOutputStream()))->getMock();
313 $bar->expects($this->exactly(2))->method('display');
314 $bar->setRedrawFrequency(0);
319 public function testRedrawFrequencyIsAtLeastOneIfSmallerOneGiven()
321 $bar = $this->getMockBuilder('Symfony\Component\Console\Helper\ProgressBar')->setMethods(array('display'))->setConstructorArgs(array($this->getOutputStream()))->getMock();
323 $bar->expects($this->exactly(2))->method('display');
324 $bar->setRedrawFrequency(0.9);
329 public function testMultiByteSupport()
331 $bar = new ProgressBar($output = $this->getOutputStream());
333 $bar->setBarCharacter('■');
336 rewind($output->getStream());
338 ' 0 [>---------------------------]'.
339 $this->generateOutput(' 3 [■■■>------------------------]'),
340 stream_get_contents($output->getStream())
344 public function testClear()
346 $bar = new ProgressBar($output = $this->getOutputStream(), 50);
348 $bar->setProgress(25);
351 rewind($output->getStream());
353 ' 0/50 [>---------------------------] 0%'.
354 $this->generateOutput(' 25/50 [==============>-------------] 50%').
355 $this->generateOutput(''),
356 stream_get_contents($output->getStream())
360 public function testPercentNotHundredBeforeComplete()
362 $bar = new ProgressBar($output = $this->getOutputStream(), 200);
368 rewind($output->getStream());
370 ' 0/200 [>---------------------------] 0%'.
371 $this->generateOutput(' 0/200 [>---------------------------] 0%').
372 $this->generateOutput(' 199/200 [===========================>] 99%').
373 $this->generateOutput(' 200/200 [============================] 100%'),
374 stream_get_contents($output->getStream())
378 public function testNonDecoratedOutput()
380 $bar = new ProgressBar($output = $this->getOutputStream(false), 200);
383 for ($i = 0; $i < 200; ++$i) {
389 rewind($output->getStream());
391 ' 0/200 [>---------------------------] 0%'.PHP_EOL.
392 ' 20/200 [==>-------------------------] 10%'.PHP_EOL.
393 ' 40/200 [=====>----------------------] 20%'.PHP_EOL.
394 ' 60/200 [========>-------------------] 30%'.PHP_EOL.
395 ' 80/200 [===========>----------------] 40%'.PHP_EOL.
396 ' 100/200 [==============>-------------] 50%'.PHP_EOL.
397 ' 120/200 [================>-----------] 60%'.PHP_EOL.
398 ' 140/200 [===================>--------] 70%'.PHP_EOL.
399 ' 160/200 [======================>-----] 80%'.PHP_EOL.
400 ' 180/200 [=========================>--] 90%'.PHP_EOL.
401 ' 200/200 [============================] 100%',
402 stream_get_contents($output->getStream())
406 public function testNonDecoratedOutputWithClear()
408 $bar = new ProgressBar($output = $this->getOutputStream(false), 50);
410 $bar->setProgress(25);
412 $bar->setProgress(50);
415 rewind($output->getStream());
417 ' 0/50 [>---------------------------] 0%'.PHP_EOL.
418 ' 25/50 [==============>-------------] 50%'.PHP_EOL.
419 ' 50/50 [============================] 100%',
420 stream_get_contents($output->getStream())
424 public function testNonDecoratedOutputWithoutMax()
426 $bar = new ProgressBar($output = $this->getOutputStream(false));
430 rewind($output->getStream());
432 ' 0 [>---------------------------]'.PHP_EOL.
433 ' 1 [->--------------------------]',
434 stream_get_contents($output->getStream())
438 public function testParallelBars()
440 $output = $this->getOutputStream();
441 $bar1 = new ProgressBar($output, 2);
442 $bar2 = new ProgressBar($output, 3);
443 $bar2->setProgressCharacter('#');
444 $bar3 = new ProgressBar($output);
447 $output->write("\n");
449 $output->write("\n");
452 for ($i = 1; $i <= 3; ++$i) {
454 $output->write("\033[2A");
458 $output->write("\n");
460 $output->write("\n");
463 $output->write("\033[2A");
464 $output->write("\n");
465 $output->write("\n");
468 rewind($output->getStream());
470 ' 0/2 [>---------------------------] 0%'."\n".
471 ' 0/3 [#---------------------------] 0%'."\n".
472 rtrim(' 0 [>---------------------------]').
475 $this->generateOutput(' 1/2 [==============>-------------] 50%')."\n".
476 $this->generateOutput(' 1/3 [=========#------------------] 33%')."\n".
477 rtrim($this->generateOutput(' 1 [->--------------------------]')).
480 $this->generateOutput(' 2/2 [============================] 100%')."\n".
481 $this->generateOutput(' 2/3 [==================#---------] 66%')."\n".
482 rtrim($this->generateOutput(' 2 [-->-------------------------]')).
486 $this->generateOutput(' 3/3 [============================] 100%')."\n".
487 rtrim($this->generateOutput(' 3 [--->------------------------]')).
492 rtrim($this->generateOutput(' 3 [============================]')),
493 stream_get_contents($output->getStream())
497 public function testWithoutMax()
499 $output = $this->getOutputStream();
501 $bar = new ProgressBar($output);
508 rewind($output->getStream());
510 rtrim(' 0 [>---------------------------]').
511 rtrim($this->generateOutput(' 1 [->--------------------------]')).
512 rtrim($this->generateOutput(' 2 [-->-------------------------]')).
513 rtrim($this->generateOutput(' 3 [--->------------------------]')).
514 rtrim($this->generateOutput(' 3 [============================]')),
515 stream_get_contents($output->getStream())
519 public function testAddingPlaceholderFormatter()
521 ProgressBar::setPlaceholderFormatterDefinition('remaining_steps', function (ProgressBar $bar) {
522 return $bar->getMaxSteps() - $bar->getProgress();
524 $bar = new ProgressBar($output = $this->getOutputStream(), 3);
525 $bar->setFormat(' %remaining_steps% [%bar%]');
531 rewind($output->getStream());
533 ' 3 [>---------------------------]'.
534 $this->generateOutput(' 2 [=========>------------------]').
535 $this->generateOutput(' 0 [============================]'),
536 stream_get_contents($output->getStream())
540 public function testMultilineFormat()
542 $bar = new ProgressBar($output = $this->getOutputStream(), 3);
543 $bar->setFormat("%bar%\nfoobar");
550 rewind($output->getStream());
552 ">---------------------------\nfoobar".
553 $this->generateOutput("=========>------------------\nfoobar").
554 "\x0D\x1B[2K\x1B[1A\x1B[2K".
555 $this->generateOutput("============================\nfoobar"),
556 stream_get_contents($output->getStream())
560 public function testAnsiColorsAndEmojis()
562 $bar = new ProgressBar($output = $this->getOutputStream(), 15);
563 ProgressBar::setPlaceholderFormatterDefinition('memory', function (ProgressBar $bar) {
566 $colors = $i++ ? '41;37' : '44;37';
568 return "\033[".$colors.'m '.Helper::formatMemory($mem)." \033[0m";
570 $bar->setFormat(" \033[44;37m %title:-37s% \033[0m\n %current%/%max% %bar% %percent:3s%%\n 🏁 %remaining:-10s% %memory:37s%");
571 $bar->setBarCharacter($done = "\033[32m●\033[0m");
572 $bar->setEmptyBarCharacter($empty = "\033[31m●\033[0m");
573 $bar->setProgressCharacter($progress = "\033[32m➤ \033[0m");
575 $bar->setMessage('Starting the demo... fingers crossed', 'title');
577 $bar->setMessage('Looks good to me...', 'title');
579 $bar->setMessage('Thanks, bye', 'title');
582 rewind($output->getStream());
584 " \033[44;37m Starting the demo... fingers crossed \033[0m\n".
585 ' 0/15 '.$progress.str_repeat($empty, 26)." 0%\n".
586 " \xf0\x9f\x8f\x81 < 1 sec \033[44;37m 0 B \033[0m"
588 $this->generateOutput(
589 " \033[44;37m Looks good to me... \033[0m\n".
590 ' 4/15 '.str_repeat($done, 7).$progress.str_repeat($empty, 19)." 26%\n".
591 " \xf0\x9f\x8f\x81 < 1 sec \033[41;37m 97 KiB \033[0m"
593 $this->generateOutput(
594 " \033[44;37m Thanks, bye \033[0m\n".
595 ' 15/15 '.str_repeat($done, 28)." 100%\n".
596 " \xf0\x9f\x8f\x81 < 1 sec \033[41;37m 195 KiB \033[0m"
598 stream_get_contents($output->getStream())
602 public function testSetFormat()
604 $bar = new ProgressBar($output = $this->getOutputStream());
605 $bar->setFormat('normal');
607 rewind($output->getStream());
609 ' 0 [>---------------------------]',
610 stream_get_contents($output->getStream())
613 $bar = new ProgressBar($output = $this->getOutputStream(), 10);
614 $bar->setFormat('normal');
616 rewind($output->getStream());
618 ' 0/10 [>---------------------------] 0%',
619 stream_get_contents($output->getStream())
624 * @dataProvider provideFormat
626 public function testFormatsWithoutMax($format)
628 $bar = new ProgressBar($output = $this->getOutputStream());
629 $bar->setFormat($format);
632 rewind($output->getStream());
633 $this->assertNotEmpty(stream_get_contents($output->getStream()));
637 * Provides each defined format.
641 public function provideFormat()
646 array('very_verbose'),
651 protected function getOutputStream($decorated = true, $verbosity = StreamOutput::VERBOSITY_NORMAL)
653 return new StreamOutput(fopen('php://memory', 'r+', false), $verbosity, $decorated);
656 protected function generateOutput($expected)
658 $count = substr_count($expected, "\n");
660 return "\x0D\x1B[2K".($count ? str_repeat("\x1B[1A\x1B[2K", $count) : '').$expected;