No Description

ProgressBarTest.php 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Console\Tests\Helper;
  11. use Symfony\Component\Console\Helper\ProgressBar;
  12. use Symfony\Component\Console\Helper\Helper;
  13. use Symfony\Component\Console\Output\StreamOutput;
  14. class ProgressBarTest extends \PHPUnit_Framework_TestCase
  15. {
  16. public function testMultipleStart()
  17. {
  18. $bar = new ProgressBar($output = $this->getOutputStream());
  19. $bar->start();
  20. $bar->advance();
  21. $bar->start();
  22. rewind($output->getStream());
  23. $this->assertEquals(
  24. $this->generateOutput(' 0 [>---------------------------]').
  25. $this->generateOutput(' 1 [->--------------------------]').
  26. $this->generateOutput(' 0 [>---------------------------]'),
  27. stream_get_contents($output->getStream())
  28. );
  29. }
  30. public function testAdvance()
  31. {
  32. $bar = new ProgressBar($output = $this->getOutputStream());
  33. $bar->start();
  34. $bar->advance();
  35. rewind($output->getStream());
  36. $this->assertEquals(
  37. $this->generateOutput(' 0 [>---------------------------]').
  38. $this->generateOutput(' 1 [->--------------------------]'),
  39. stream_get_contents($output->getStream())
  40. );
  41. }
  42. public function testAdvanceWithStep()
  43. {
  44. $bar = new ProgressBar($output = $this->getOutputStream());
  45. $bar->start();
  46. $bar->advance(5);
  47. rewind($output->getStream());
  48. $this->assertEquals(
  49. $this->generateOutput(' 0 [>---------------------------]').
  50. $this->generateOutput(' 5 [----->----------------------]'),
  51. stream_get_contents($output->getStream())
  52. );
  53. }
  54. public function testAdvanceMultipleTimes()
  55. {
  56. $bar = new ProgressBar($output = $this->getOutputStream());
  57. $bar->start();
  58. $bar->advance(3);
  59. $bar->advance(2);
  60. rewind($output->getStream());
  61. $this->assertEquals(
  62. $this->generateOutput(' 0 [>---------------------------]').
  63. $this->generateOutput(' 3 [--->------------------------]').
  64. $this->generateOutput(' 5 [----->----------------------]'),
  65. stream_get_contents($output->getStream())
  66. );
  67. }
  68. public function testAdvanceOverMax()
  69. {
  70. $bar = new ProgressBar($output = $this->getOutputStream(), 10);
  71. $bar->setProgress(9);
  72. $bar->advance();
  73. $bar->advance();
  74. rewind($output->getStream());
  75. $this->assertEquals(
  76. $this->generateOutput(' 9/10 [=========================>--] 90%').
  77. $this->generateOutput(' 10/10 [============================] 100%').
  78. $this->generateOutput(' 11/11 [============================] 100%'),
  79. stream_get_contents($output->getStream())
  80. );
  81. }
  82. public function testCustomizations()
  83. {
  84. $bar = new ProgressBar($output = $this->getOutputStream(), 10);
  85. $bar->setBarWidth(10);
  86. $bar->setBarCharacter('_');
  87. $bar->setEmptyBarCharacter(' ');
  88. $bar->setProgressCharacter('/');
  89. $bar->setFormat(' %current%/%max% [%bar%] %percent:3s%%');
  90. $bar->start();
  91. $bar->advance();
  92. rewind($output->getStream());
  93. $this->assertEquals(
  94. $this->generateOutput(' 0/10 [/ ] 0%').
  95. $this->generateOutput(' 1/10 [_/ ] 10%'),
  96. stream_get_contents($output->getStream())
  97. );
  98. }
  99. public function testDisplayWithoutStart()
  100. {
  101. $bar = new ProgressBar($output = $this->getOutputStream(), 50);
  102. $bar->display();
  103. rewind($output->getStream());
  104. $this->assertEquals(
  105. $this->generateOutput(' 0/50 [>---------------------------] 0%'),
  106. stream_get_contents($output->getStream())
  107. );
  108. }
  109. public function testDisplayWithQuietVerbosity()
  110. {
  111. $bar = new ProgressBar($output = $this->getOutputStream(true, StreamOutput::VERBOSITY_QUIET), 50);
  112. $bar->display();
  113. rewind($output->getStream());
  114. $this->assertEquals(
  115. '',
  116. stream_get_contents($output->getStream())
  117. );
  118. }
  119. public function testFinishWithoutStart()
  120. {
  121. $bar = new ProgressBar($output = $this->getOutputStream(), 50);
  122. $bar->finish();
  123. rewind($output->getStream());
  124. $this->assertEquals(
  125. $this->generateOutput(' 50/50 [============================] 100%'),
  126. stream_get_contents($output->getStream())
  127. );
  128. }
  129. public function testPercent()
  130. {
  131. $bar = new ProgressBar($output = $this->getOutputStream(), 50);
  132. $bar->start();
  133. $bar->display();
  134. $bar->advance();
  135. $bar->advance();
  136. rewind($output->getStream());
  137. $this->assertEquals(
  138. $this->generateOutput(' 0/50 [>---------------------------] 0%').
  139. $this->generateOutput(' 0/50 [>---------------------------] 0%').
  140. $this->generateOutput(' 1/50 [>---------------------------] 2%').
  141. $this->generateOutput(' 2/50 [=>--------------------------] 4%'),
  142. stream_get_contents($output->getStream())
  143. );
  144. }
  145. public function testOverwriteWithShorterLine()
  146. {
  147. $bar = new ProgressBar($output = $this->getOutputStream(), 50);
  148. $bar->setFormat(' %current%/%max% [%bar%] %percent:3s%%');
  149. $bar->start();
  150. $bar->display();
  151. $bar->advance();
  152. // set shorter format
  153. $bar->setFormat(' %current%/%max% [%bar%]');
  154. $bar->advance();
  155. rewind($output->getStream());
  156. $this->assertEquals(
  157. $this->generateOutput(' 0/50 [>---------------------------] 0%').
  158. $this->generateOutput(' 0/50 [>---------------------------] 0%').
  159. $this->generateOutput(' 1/50 [>---------------------------] 2%').
  160. $this->generateOutput(' 2/50 [=>--------------------------] '),
  161. stream_get_contents($output->getStream())
  162. );
  163. }
  164. public function testStartWithMax()
  165. {
  166. $bar = new ProgressBar($output = $this->getOutputStream());
  167. $bar->setFormat('%current%/%max% [%bar%]');
  168. $bar->start(50);
  169. $bar->advance();
  170. rewind($output->getStream());
  171. $this->assertEquals(
  172. $this->generateOutput(' 0/50 [>---------------------------]').
  173. $this->generateOutput(' 1/50 [>---------------------------]'),
  174. stream_get_contents($output->getStream())
  175. );
  176. }
  177. public function testSetCurrentProgress()
  178. {
  179. $bar = new ProgressBar($output = $this->getOutputStream(), 50);
  180. $bar->start();
  181. $bar->display();
  182. $bar->advance();
  183. $bar->setProgress(15);
  184. $bar->setProgress(25);
  185. rewind($output->getStream());
  186. $this->assertEquals(
  187. $this->generateOutput(' 0/50 [>---------------------------] 0%').
  188. $this->generateOutput(' 0/50 [>---------------------------] 0%').
  189. $this->generateOutput(' 1/50 [>---------------------------] 2%').
  190. $this->generateOutput(' 15/50 [========>-------------------] 30%').
  191. $this->generateOutput(' 25/50 [==============>-------------] 50%'),
  192. stream_get_contents($output->getStream())
  193. );
  194. }
  195. /**
  196. */
  197. public function testSetCurrentBeforeStarting()
  198. {
  199. $bar = new ProgressBar($this->getOutputStream());
  200. $bar->setProgress(15);
  201. $this->assertNotNull($bar->getStartTime());
  202. }
  203. /**
  204. * @expectedException \LogicException
  205. * @expectedExceptionMessage You can't regress the progress bar
  206. */
  207. public function testRegressProgress()
  208. {
  209. $bar = new ProgressBar($output = $this->getOutputStream(), 50);
  210. $bar->start();
  211. $bar->setProgress(15);
  212. $bar->setProgress(10);
  213. }
  214. public function testRedrawFrequency()
  215. {
  216. $bar = $this->getMock('Symfony\Component\Console\Helper\ProgressBar', array('display'), array($output = $this->getOutputStream(), 6));
  217. $bar->expects($this->exactly(4))->method('display');
  218. $bar->setRedrawFrequency(2);
  219. $bar->start();
  220. $bar->setProgress(1);
  221. $bar->advance(2);
  222. $bar->advance(2);
  223. $bar->advance(1);
  224. }
  225. public function testMultiByteSupport()
  226. {
  227. if (!function_exists('mb_strlen') || (false === $encoding = mb_detect_encoding('■'))) {
  228. $this->markTestSkipped('The mbstring extension is needed for multi-byte support');
  229. }
  230. $bar = new ProgressBar($output = $this->getOutputStream());
  231. $bar->start();
  232. $bar->setBarCharacter('■');
  233. $bar->advance(3);
  234. rewind($output->getStream());
  235. $this->assertEquals(
  236. $this->generateOutput(' 0 [>---------------------------]').
  237. $this->generateOutput(' 3 [■■■>------------------------]'),
  238. stream_get_contents($output->getStream())
  239. );
  240. }
  241. public function testClear()
  242. {
  243. $bar = new ProgressBar($output = $this->getOutputStream(), 50);
  244. $bar->start();
  245. $bar->setProgress(25);
  246. $bar->clear();
  247. rewind($output->getStream());
  248. $this->assertEquals(
  249. $this->generateOutput(' 0/50 [>---------------------------] 0%').
  250. $this->generateOutput(' 25/50 [==============>-------------] 50%').
  251. $this->generateOutput(' '),
  252. stream_get_contents($output->getStream())
  253. );
  254. }
  255. public function testPercentNotHundredBeforeComplete()
  256. {
  257. $bar = new ProgressBar($output = $this->getOutputStream(), 200);
  258. $bar->start();
  259. $bar->display();
  260. $bar->advance(199);
  261. $bar->advance();
  262. rewind($output->getStream());
  263. $this->assertEquals(
  264. $this->generateOutput(' 0/200 [>---------------------------] 0%').
  265. $this->generateOutput(' 0/200 [>---------------------------] 0%').
  266. $this->generateOutput(' 199/200 [===========================>] 99%').
  267. $this->generateOutput(' 200/200 [============================] 100%'),
  268. stream_get_contents($output->getStream())
  269. );
  270. }
  271. public function testNonDecoratedOutput()
  272. {
  273. $bar = new ProgressBar($output = $this->getOutputStream(false), 200);
  274. $bar->start();
  275. for ($i = 0; $i < 200; $i++) {
  276. $bar->advance();
  277. }
  278. $bar->finish();
  279. rewind($output->getStream());
  280. $this->assertEquals(
  281. " 0/200 [>---------------------------] 0%\n".
  282. " 20/200 [==>-------------------------] 10%\n".
  283. " 40/200 [=====>----------------------] 20%\n".
  284. " 60/200 [========>-------------------] 30%\n".
  285. " 80/200 [===========>----------------] 40%\n".
  286. " 100/200 [==============>-------------] 50%\n".
  287. " 120/200 [================>-----------] 60%\n".
  288. " 140/200 [===================>--------] 70%\n".
  289. " 160/200 [======================>-----] 80%\n".
  290. " 180/200 [=========================>--] 90%\n".
  291. " 200/200 [============================] 100%",
  292. stream_get_contents($output->getStream())
  293. );
  294. }
  295. public function testNonDecoratedOutputWithClear()
  296. {
  297. $bar = new ProgressBar($output = $this->getOutputStream(false), 50);
  298. $bar->start();
  299. $bar->setProgress(25);
  300. $bar->clear();
  301. $bar->setProgress(50);
  302. $bar->finish();
  303. rewind($output->getStream());
  304. $this->assertEquals(
  305. " 0/50 [>---------------------------] 0%\n".
  306. " 25/50 [==============>-------------] 50%\n".
  307. " 50/50 [============================] 100%",
  308. stream_get_contents($output->getStream())
  309. );
  310. }
  311. public function testNonDecoratedOutputWithoutMax()
  312. {
  313. $bar = new ProgressBar($output = $this->getOutputStream(false));
  314. $bar->start();
  315. $bar->advance();
  316. rewind($output->getStream());
  317. $this->assertEquals(
  318. " 0 [>---------------------------]\n".
  319. " 1 [->--------------------------]",
  320. stream_get_contents($output->getStream())
  321. );
  322. }
  323. public function testParallelBars()
  324. {
  325. $output = $this->getOutputStream();
  326. $bar1 = new ProgressBar($output, 2);
  327. $bar2 = new ProgressBar($output, 3);
  328. $bar2->setProgressCharacter('#');
  329. $bar3 = new ProgressBar($output);
  330. $bar1->start();
  331. $output->write("\n");
  332. $bar2->start();
  333. $output->write("\n");
  334. $bar3->start();
  335. for ($i = 1; $i <= 3; $i++) {
  336. // up two lines
  337. $output->write("\033[2A");
  338. if ($i <= 2) {
  339. $bar1->advance();
  340. }
  341. $output->write("\n");
  342. $bar2->advance();
  343. $output->write("\n");
  344. $bar3->advance();
  345. }
  346. $output->write("\033[2A");
  347. $output->write("\n");
  348. $output->write("\n");
  349. $bar3->finish();
  350. rewind($output->getStream());
  351. $this->assertEquals(
  352. $this->generateOutput(' 0/2 [>---------------------------] 0%')."\n".
  353. $this->generateOutput(' 0/3 [#---------------------------] 0%')."\n".
  354. rtrim($this->generateOutput(' 0 [>---------------------------]')).
  355. "\033[2A".
  356. $this->generateOutput(' 1/2 [==============>-------------] 50%')."\n".
  357. $this->generateOutput(' 1/3 [=========#------------------] 33%')."\n".
  358. rtrim($this->generateOutput(' 1 [->--------------------------]')).
  359. "\033[2A".
  360. $this->generateOutput(' 2/2 [============================] 100%')."\n".
  361. $this->generateOutput(' 2/3 [==================#---------] 66%')."\n".
  362. rtrim($this->generateOutput(' 2 [-->-------------------------]')).
  363. "\033[2A".
  364. "\n".
  365. $this->generateOutput(' 3/3 [============================] 100%')."\n".
  366. rtrim($this->generateOutput(' 3 [--->------------------------]')).
  367. "\033[2A".
  368. "\n".
  369. "\n".
  370. rtrim($this->generateOutput(' 3 [============================]')),
  371. stream_get_contents($output->getStream())
  372. );
  373. }
  374. public function testWithoutMax()
  375. {
  376. $output = $this->getOutputStream();
  377. $bar = new ProgressBar($output);
  378. $bar->start();
  379. $bar->advance();
  380. $bar->advance();
  381. $bar->advance();
  382. $bar->finish();
  383. rewind($output->getStream());
  384. $this->assertEquals(
  385. rtrim($this->generateOutput(' 0 [>---------------------------]')).
  386. rtrim($this->generateOutput(' 1 [->--------------------------]')).
  387. rtrim($this->generateOutput(' 2 [-->-------------------------]')).
  388. rtrim($this->generateOutput(' 3 [--->------------------------]')).
  389. rtrim($this->generateOutput(' 3 [============================]')),
  390. stream_get_contents($output->getStream())
  391. );
  392. }
  393. public function testAddingPlaceholderFormatter()
  394. {
  395. ProgressBar::setPlaceholderFormatterDefinition('remaining_steps', function (ProgressBar $bar) {
  396. return $bar->getMaxSteps() - $bar->getProgress();
  397. });
  398. $bar = new ProgressBar($output = $this->getOutputStream(), 3);
  399. $bar->setFormat(' %remaining_steps% [%bar%]');
  400. $bar->start();
  401. $bar->advance();
  402. $bar->finish();
  403. rewind($output->getStream());
  404. $this->assertEquals(
  405. $this->generateOutput(' 3 [>---------------------------]').
  406. $this->generateOutput(' 2 [=========>------------------]').
  407. $this->generateOutput(' 0 [============================]'),
  408. stream_get_contents($output->getStream())
  409. );
  410. }
  411. public function testMultilineFormat()
  412. {
  413. $bar = new ProgressBar($output = $this->getOutputStream(), 3);
  414. $bar->setFormat("%bar%\nfoobar");
  415. $bar->start();
  416. $bar->advance();
  417. $bar->clear();
  418. $bar->finish();
  419. rewind($output->getStream());
  420. $this->assertEquals(
  421. $this->generateOutput(">---------------------------\nfoobar").
  422. $this->generateOutput("=========>------------------\nfoobar ").
  423. $this->generateOutput(" \n ").
  424. $this->generateOutput("============================\nfoobar "),
  425. stream_get_contents($output->getStream())
  426. );
  427. }
  428. public function testAnsiColorsAndEmojis()
  429. {
  430. $bar = new ProgressBar($output = $this->getOutputStream(), 15);
  431. ProgressBar::setPlaceholderFormatterDefinition('memory', function (ProgressBar $bar) {
  432. static $i = 0;
  433. $mem = 100000 * $i;
  434. $colors = $i++ ? '41;37' : '44;37';
  435. return "\033[".$colors."m ".Helper::formatMemory($mem)." \033[0m";
  436. });
  437. $bar->setFormat(" \033[44;37m %title:-37s% \033[0m\n %current%/%max% %bar% %percent:3s%%\n 🏁 %remaining:-10s% %memory:37s%");
  438. $bar->setBarCharacter($done = "\033[32m●\033[0m");
  439. $bar->setEmptyBarCharacter($empty = "\033[31m●\033[0m");
  440. $bar->setProgressCharacter($progress = "\033[32m➤ \033[0m");
  441. $bar->setMessage('Starting the demo... fingers crossed', 'title');
  442. $bar->start();
  443. $bar->setMessage('Looks good to me...', 'title');
  444. $bar->advance(4);
  445. $bar->setMessage('Thanks, bye', 'title');
  446. $bar->finish();
  447. rewind($output->getStream());
  448. $this->assertEquals(
  449. $this->generateOutput(
  450. " \033[44;37m Starting the demo... fingers crossed \033[0m\n".
  451. " 0/15 ".$progress.str_repeat($empty, 26)." 0%\n".
  452. " \xf0\x9f\x8f\x81 1 sec \033[44;37m 0 B \033[0m"
  453. ).
  454. $this->generateOutput(
  455. " \033[44;37m Looks good to me... \033[0m\n".
  456. " 4/15 ".str_repeat($done, 7).$progress.str_repeat($empty, 19)." 26%\n".
  457. " \xf0\x9f\x8f\x81 1 sec \033[41;37m 97 KiB \033[0m"
  458. ).
  459. $this->generateOutput(
  460. " \033[44;37m Thanks, bye \033[0m\n".
  461. " 15/15 ".str_repeat($done, 28)." 100%\n".
  462. " \xf0\x9f\x8f\x81 1 sec \033[41;37m 195 KiB \033[0m"
  463. ),
  464. stream_get_contents($output->getStream())
  465. );
  466. }
  467. public function testSetFormat()
  468. {
  469. $bar = new ProgressBar($output = $this->getOutputStream());
  470. $bar->setFormat('normal');
  471. $bar->start();
  472. rewind($output->getStream());
  473. $this->assertEquals(
  474. $this->generateOutput(' 0 [>---------------------------]'),
  475. stream_get_contents($output->getStream())
  476. );
  477. $bar = new ProgressBar($output = $this->getOutputStream(), 10);
  478. $bar->setFormat('normal');
  479. $bar->start();
  480. rewind($output->getStream());
  481. $this->assertEquals(
  482. $this->generateOutput(' 0/10 [>---------------------------] 0%'),
  483. stream_get_contents($output->getStream())
  484. );
  485. }
  486. /**
  487. * @dataProvider provideFormat
  488. */
  489. public function testFormatsWithoutMax($format)
  490. {
  491. $bar = new ProgressBar($output = $this->getOutputStream());
  492. $bar->setFormat($format);
  493. $bar->start();
  494. rewind($output->getStream());
  495. $this->assertNotEmpty(stream_get_contents($output->getStream()));
  496. }
  497. /**
  498. * Provides each defined format
  499. *
  500. * @return array
  501. */
  502. public function provideFormat()
  503. {
  504. return array(
  505. array('normal'),
  506. array('verbose'),
  507. array('very_verbose'),
  508. array('debug'),
  509. );
  510. }
  511. protected function getOutputStream($decorated = true, $verbosity = StreamOutput::VERBOSITY_NORMAL)
  512. {
  513. return new StreamOutput(fopen('php://memory', 'r+', false), $verbosity, $decorated);
  514. }
  515. protected function generateOutput($expected)
  516. {
  517. $count = substr_count($expected, "\n");
  518. return "\x0D".($count ? sprintf("\033[%dA", $count) : '').$expected;
  519. }
  520. }