ProgressBarTest.php 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899
  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 PHPUnit\Framework\TestCase;
  12. use Symfony\Component\Console\Formatter\OutputFormatter;
  13. use Symfony\Component\Console\Helper\Helper;
  14. use Symfony\Component\Console\Helper\ProgressBar;
  15. use Symfony\Component\Console\Output\ConsoleSectionOutput;
  16. use Symfony\Component\Console\Output\StreamOutput;
  17. /**
  18. * @group time-sensitive
  19. */
  20. class ProgressBarTest extends TestCase
  21. {
  22. public function testMultipleStart()
  23. {
  24. $bar = new ProgressBar($output = $this->getOutputStream());
  25. $bar->start();
  26. $bar->advance();
  27. $bar->start();
  28. rewind($output->getStream());
  29. $this->assertEquals(
  30. ' 0 [>---------------------------]'.
  31. $this->generateOutput(' 1 [->--------------------------]').
  32. $this->generateOutput(' 0 [>---------------------------]'),
  33. stream_get_contents($output->getStream())
  34. );
  35. }
  36. public function testAdvance()
  37. {
  38. $bar = new ProgressBar($output = $this->getOutputStream());
  39. $bar->start();
  40. $bar->advance();
  41. rewind($output->getStream());
  42. $this->assertEquals(
  43. ' 0 [>---------------------------]'.
  44. $this->generateOutput(' 1 [->--------------------------]'),
  45. stream_get_contents($output->getStream())
  46. );
  47. }
  48. public function testAdvanceWithStep()
  49. {
  50. $bar = new ProgressBar($output = $this->getOutputStream());
  51. $bar->start();
  52. $bar->advance(5);
  53. rewind($output->getStream());
  54. $this->assertEquals(
  55. ' 0 [>---------------------------]'.
  56. $this->generateOutput(' 5 [----->----------------------]'),
  57. stream_get_contents($output->getStream())
  58. );
  59. }
  60. public function testAdvanceMultipleTimes()
  61. {
  62. $bar = new ProgressBar($output = $this->getOutputStream());
  63. $bar->start();
  64. $bar->advance(3);
  65. $bar->advance(2);
  66. rewind($output->getStream());
  67. $this->assertEquals(
  68. ' 0 [>---------------------------]'.
  69. $this->generateOutput(' 3 [--->------------------------]').
  70. $this->generateOutput(' 5 [----->----------------------]'),
  71. stream_get_contents($output->getStream())
  72. );
  73. }
  74. public function testAdvanceOverMax()
  75. {
  76. $bar = new ProgressBar($output = $this->getOutputStream(), 10);
  77. $bar->setProgress(9);
  78. $bar->advance();
  79. $bar->advance();
  80. rewind($output->getStream());
  81. $this->assertEquals(
  82. ' 9/10 [=========================>--] 90%'.
  83. $this->generateOutput(' 10/10 [============================] 100%').
  84. $this->generateOutput(' 11/11 [============================] 100%'),
  85. stream_get_contents($output->getStream())
  86. );
  87. }
  88. public function testRegress()
  89. {
  90. $bar = new ProgressBar($output = $this->getOutputStream());
  91. $bar->start();
  92. $bar->advance();
  93. $bar->advance();
  94. $bar->advance(-1);
  95. rewind($output->getStream());
  96. $this->assertEquals(
  97. ' 0 [>---------------------------]'.
  98. $this->generateOutput(' 1 [->--------------------------]').
  99. $this->generateOutput(' 2 [-->-------------------------]').
  100. $this->generateOutput(' 1 [->--------------------------]'),
  101. stream_get_contents($output->getStream())
  102. );
  103. }
  104. public function testRegressWithStep()
  105. {
  106. $bar = new ProgressBar($output = $this->getOutputStream());
  107. $bar->start();
  108. $bar->advance(4);
  109. $bar->advance(4);
  110. $bar->advance(-2);
  111. rewind($output->getStream());
  112. $this->assertEquals(
  113. ' 0 [>---------------------------]'.
  114. $this->generateOutput(' 4 [---->-----------------------]').
  115. $this->generateOutput(' 8 [-------->-------------------]').
  116. $this->generateOutput(' 6 [------>---------------------]'),
  117. stream_get_contents($output->getStream())
  118. );
  119. }
  120. public function testRegressMultipleTimes()
  121. {
  122. $bar = new ProgressBar($output = $this->getOutputStream());
  123. $bar->start();
  124. $bar->advance(3);
  125. $bar->advance(3);
  126. $bar->advance(-1);
  127. $bar->advance(-2);
  128. rewind($output->getStream());
  129. $this->assertEquals(
  130. ' 0 [>---------------------------]'.
  131. $this->generateOutput(' 3 [--->------------------------]').
  132. $this->generateOutput(' 6 [------>---------------------]').
  133. $this->generateOutput(' 5 [----->----------------------]').
  134. $this->generateOutput(' 3 [--->------------------------]'),
  135. stream_get_contents($output->getStream())
  136. );
  137. }
  138. public function testRegressBelowMin()
  139. {
  140. $bar = new ProgressBar($output = $this->getOutputStream(), 10);
  141. $bar->setProgress(1);
  142. $bar->advance(-1);
  143. $bar->advance(-1);
  144. rewind($output->getStream());
  145. $this->assertEquals(
  146. ' 1/10 [==>-------------------------] 10%'.
  147. $this->generateOutput(' 0/10 [>---------------------------] 0%'),
  148. stream_get_contents($output->getStream())
  149. );
  150. }
  151. public function testFormat()
  152. {
  153. $expected =
  154. ' 0/10 [>---------------------------] 0%'.
  155. $this->generateOutput(' 10/10 [============================] 100%').
  156. $this->generateOutput(' 10/10 [============================] 100%')
  157. ;
  158. // max in construct, no format
  159. $bar = new ProgressBar($output = $this->getOutputStream(), 10);
  160. $bar->start();
  161. $bar->advance(10);
  162. $bar->finish();
  163. rewind($output->getStream());
  164. $this->assertEquals($expected, stream_get_contents($output->getStream()));
  165. // max in start, no format
  166. $bar = new ProgressBar($output = $this->getOutputStream());
  167. $bar->start(10);
  168. $bar->advance(10);
  169. $bar->finish();
  170. rewind($output->getStream());
  171. $this->assertEquals($expected, stream_get_contents($output->getStream()));
  172. // max in construct, explicit format before
  173. $bar = new ProgressBar($output = $this->getOutputStream(), 10);
  174. $bar->setFormat('normal');
  175. $bar->start();
  176. $bar->advance(10);
  177. $bar->finish();
  178. rewind($output->getStream());
  179. $this->assertEquals($expected, stream_get_contents($output->getStream()));
  180. // max in start, explicit format before
  181. $bar = new ProgressBar($output = $this->getOutputStream());
  182. $bar->setFormat('normal');
  183. $bar->start(10);
  184. $bar->advance(10);
  185. $bar->finish();
  186. rewind($output->getStream());
  187. $this->assertEquals($expected, stream_get_contents($output->getStream()));
  188. }
  189. public function testCustomizations()
  190. {
  191. $bar = new ProgressBar($output = $this->getOutputStream(), 10);
  192. $bar->setBarWidth(10);
  193. $bar->setBarCharacter('_');
  194. $bar->setEmptyBarCharacter(' ');
  195. $bar->setProgressCharacter('/');
  196. $bar->setFormat(' %current%/%max% [%bar%] %percent:3s%%');
  197. $bar->start();
  198. $bar->advance();
  199. rewind($output->getStream());
  200. $this->assertEquals(
  201. ' 0/10 [/ ] 0%'.
  202. $this->generateOutput(' 1/10 [_/ ] 10%'),
  203. stream_get_contents($output->getStream())
  204. );
  205. }
  206. public function testDisplayWithoutStart()
  207. {
  208. $bar = new ProgressBar($output = $this->getOutputStream(), 50);
  209. $bar->display();
  210. rewind($output->getStream());
  211. $this->assertEquals(
  212. ' 0/50 [>---------------------------] 0%',
  213. stream_get_contents($output->getStream())
  214. );
  215. }
  216. public function testDisplayWithQuietVerbosity()
  217. {
  218. $bar = new ProgressBar($output = $this->getOutputStream(true, StreamOutput::VERBOSITY_QUIET), 50);
  219. $bar->display();
  220. rewind($output->getStream());
  221. $this->assertEquals(
  222. '',
  223. stream_get_contents($output->getStream())
  224. );
  225. }
  226. public function testFinishWithoutStart()
  227. {
  228. $bar = new ProgressBar($output = $this->getOutputStream(), 50);
  229. $bar->finish();
  230. rewind($output->getStream());
  231. $this->assertEquals(
  232. ' 50/50 [============================] 100%',
  233. stream_get_contents($output->getStream())
  234. );
  235. }
  236. public function testPercent()
  237. {
  238. $bar = new ProgressBar($output = $this->getOutputStream(), 50);
  239. $bar->start();
  240. $bar->display();
  241. $bar->advance();
  242. $bar->advance();
  243. rewind($output->getStream());
  244. $this->assertEquals(
  245. ' 0/50 [>---------------------------] 0%'.
  246. $this->generateOutput(' 0/50 [>---------------------------] 0%').
  247. $this->generateOutput(' 1/50 [>---------------------------] 2%').
  248. $this->generateOutput(' 2/50 [=>--------------------------] 4%'),
  249. stream_get_contents($output->getStream())
  250. );
  251. }
  252. public function testOverwriteWithShorterLine()
  253. {
  254. $bar = new ProgressBar($output = $this->getOutputStream(), 50);
  255. $bar->setFormat(' %current%/%max% [%bar%] %percent:3s%%');
  256. $bar->start();
  257. $bar->display();
  258. $bar->advance();
  259. // set shorter format
  260. $bar->setFormat(' %current%/%max% [%bar%]');
  261. $bar->advance();
  262. rewind($output->getStream());
  263. $this->assertEquals(
  264. ' 0/50 [>---------------------------] 0%'.
  265. $this->generateOutput(' 0/50 [>---------------------------] 0%').
  266. $this->generateOutput(' 1/50 [>---------------------------] 2%').
  267. $this->generateOutput(' 2/50 [=>--------------------------]'),
  268. stream_get_contents($output->getStream())
  269. );
  270. }
  271. public function testOverwriteWithSectionOutput()
  272. {
  273. $sections = [];
  274. $stream = $this->getOutputStream(true);
  275. $output = new ConsoleSectionOutput($stream->getStream(), $sections, $stream->getVerbosity(), $stream->isDecorated(), new OutputFormatter());
  276. $bar = new ProgressBar($output, 50);
  277. $bar->start();
  278. $bar->display();
  279. $bar->advance();
  280. $bar->advance();
  281. rewind($output->getStream());
  282. $this->assertEquals(
  283. ' 0/50 [>---------------------------] 0%'.PHP_EOL.
  284. "\x1b[1A\x1b[0J".' 0/50 [>---------------------------] 0%'.PHP_EOL.
  285. "\x1b[1A\x1b[0J".' 1/50 [>---------------------------] 2%'.PHP_EOL.
  286. "\x1b[1A\x1b[0J".' 2/50 [=>--------------------------] 4%'.PHP_EOL,
  287. stream_get_contents($output->getStream())
  288. );
  289. }
  290. public function testOverwriteMultipleProgressBarsWithSectionOutputs()
  291. {
  292. $sections = [];
  293. $stream = $this->getOutputStream(true);
  294. $output1 = new ConsoleSectionOutput($stream->getStream(), $sections, $stream->getVerbosity(), $stream->isDecorated(), new OutputFormatter());
  295. $output2 = new ConsoleSectionOutput($stream->getStream(), $sections, $stream->getVerbosity(), $stream->isDecorated(), new OutputFormatter());
  296. $progress = new ProgressBar($output1, 50);
  297. $progress2 = new ProgressBar($output2, 50);
  298. $progress->start();
  299. $progress2->start();
  300. $progress2->advance();
  301. $progress->advance();
  302. rewind($stream->getStream());
  303. $this->assertEquals(
  304. ' 0/50 [>---------------------------] 0%'.PHP_EOL.
  305. ' 0/50 [>---------------------------] 0%'.PHP_EOL.
  306. "\x1b[1A\x1b[0J".' 1/50 [>---------------------------] 2%'.PHP_EOL.
  307. "\x1b[2A\x1b[0J".' 1/50 [>---------------------------] 2%'.PHP_EOL.
  308. "\x1b[1A\x1b[0J".' 1/50 [>---------------------------] 2%'.PHP_EOL.
  309. ' 1/50 [>---------------------------] 2%'.PHP_EOL,
  310. stream_get_contents($stream->getStream())
  311. );
  312. }
  313. public function testMultipleSectionsWithCustomFormat()
  314. {
  315. $sections = [];
  316. $stream = $this->getOutputStream(true);
  317. $output1 = new ConsoleSectionOutput($stream->getStream(), $sections, $stream->getVerbosity(), $stream->isDecorated(), new OutputFormatter());
  318. $output2 = new ConsoleSectionOutput($stream->getStream(), $sections, $stream->getVerbosity(), $stream->isDecorated(), new OutputFormatter());
  319. ProgressBar::setFormatDefinition('test', '%current%/%max% [%bar%] %percent:3s%% Fruitcake marzipan toffee. Cupcake gummi bears tart dessert ice cream chupa chups cupcake chocolate bar sesame snaps. Croissant halvah cookie jujubes powder macaroon. Fruitcake bear claw bonbon jelly beans oat cake pie muffin Fruitcake marzipan toffee.');
  320. $progress = new ProgressBar($output1, 50);
  321. $progress2 = new ProgressBar($output2, 50);
  322. $progress2->setFormat('test');
  323. $progress->start();
  324. $progress2->start();
  325. $progress->advance();
  326. $progress2->advance();
  327. rewind($stream->getStream());
  328. $this->assertEquals(' 0/50 [>---------------------------] 0%'.PHP_EOL.
  329. ' 0/50 [>] 0% Fruitcake marzipan toffee. Cupcake gummi bears tart dessert ice cream chupa chups cupcake chocolate bar sesame snaps. Croissant halvah cookie jujubes powder macaroon. Fruitcake bear claw bonbon jelly beans oat cake pie muffin Fruitcake marzipan toffee.'.PHP_EOL.
  330. "\x1b[4A\x1b[0J".' 0/50 [>] 0% Fruitcake marzipan toffee. Cupcake gummi bears tart dessert ice cream chupa chups cupcake chocolate bar sesame snaps. Croissant halvah cookie jujubes powder macaroon. Fruitcake bear claw bonbon jelly beans oat cake pie muffin Fruitcake marzipan toffee.'.PHP_EOL.
  331. "\x1b[3A\x1b[0J".' 1/50 [>---------------------------] 2%'.PHP_EOL.
  332. ' 0/50 [>] 0% Fruitcake marzipan toffee. Cupcake gummi bears tart dessert ice cream chupa chups cupcake chocolate bar sesame snaps. Croissant halvah cookie jujubes powder macaroon. Fruitcake bear claw bonbon jelly beans oat cake pie muffin Fruitcake marzipan toffee.'.PHP_EOL.
  333. "\x1b[3A\x1b[0J".' 1/50 [>] 2% Fruitcake marzipan toffee. Cupcake gummi bears tart dessert ice cream chupa chups cupcake chocolate bar sesame snaps. Croissant halvah cookie jujubes powder macaroon. Fruitcake bear claw bonbon jelly beans oat cake pie muffin Fruitcake marzipan toffee.'.PHP_EOL,
  334. stream_get_contents($stream->getStream())
  335. );
  336. }
  337. public function testStartWithMax()
  338. {
  339. $bar = new ProgressBar($output = $this->getOutputStream());
  340. $bar->setFormat('%current%/%max% [%bar%]');
  341. $bar->start(50);
  342. $bar->advance();
  343. rewind($output->getStream());
  344. $this->assertEquals(
  345. ' 0/50 [>---------------------------]'.
  346. $this->generateOutput(' 1/50 [>---------------------------]'),
  347. stream_get_contents($output->getStream())
  348. );
  349. }
  350. public function testSetCurrentProgress()
  351. {
  352. $bar = new ProgressBar($output = $this->getOutputStream(), 50);
  353. $bar->start();
  354. $bar->display();
  355. $bar->advance();
  356. $bar->setProgress(15);
  357. $bar->setProgress(25);
  358. rewind($output->getStream());
  359. $this->assertEquals(
  360. ' 0/50 [>---------------------------] 0%'.
  361. $this->generateOutput(' 0/50 [>---------------------------] 0%').
  362. $this->generateOutput(' 1/50 [>---------------------------] 2%').
  363. $this->generateOutput(' 15/50 [========>-------------------] 30%').
  364. $this->generateOutput(' 25/50 [==============>-------------] 50%'),
  365. stream_get_contents($output->getStream())
  366. );
  367. }
  368. public function testSetCurrentBeforeStarting()
  369. {
  370. $bar = new ProgressBar($this->getOutputStream());
  371. $bar->setProgress(15);
  372. $this->assertNotNull($bar->getStartTime());
  373. }
  374. public function testRedrawFrequency()
  375. {
  376. $bar = new ProgressBar($output = $this->getOutputStream(), 6);
  377. $bar->setRedrawFrequency(2);
  378. $bar->start();
  379. $bar->setProgress(1);
  380. $bar->advance(2);
  381. $bar->advance(2);
  382. $bar->advance(1);
  383. rewind($output->getStream());
  384. $this->assertEquals(
  385. ' 0/6 [>---------------------------] 0%'.
  386. $this->generateOutput(' 3/6 [==============>-------------] 50%').
  387. $this->generateOutput(' 5/6 [=======================>----] 83%').
  388. $this->generateOutput(' 6/6 [============================] 100%'),
  389. stream_get_contents($output->getStream())
  390. );
  391. }
  392. public function testRedrawFrequencyIsAtLeastOneIfZeroGiven()
  393. {
  394. $bar = new ProgressBar($output = $this->getOutputStream());
  395. $bar->setRedrawFrequency(0);
  396. $bar->start();
  397. $bar->advance();
  398. rewind($output->getStream());
  399. $this->assertEquals(
  400. ' 0 [>---------------------------]'.
  401. $this->generateOutput(' 1 [->--------------------------]'),
  402. stream_get_contents($output->getStream())
  403. );
  404. }
  405. public function testRedrawFrequencyIsAtLeastOneIfSmallerOneGiven()
  406. {
  407. $bar = new ProgressBar($output = $this->getOutputStream());
  408. $bar->setRedrawFrequency(0.9);
  409. $bar->start();
  410. $bar->advance();
  411. rewind($output->getStream());
  412. $this->assertEquals(
  413. ' 0 [>---------------------------]'.
  414. $this->generateOutput(' 1 [->--------------------------]'),
  415. stream_get_contents($output->getStream())
  416. );
  417. }
  418. public function testMultiByteSupport()
  419. {
  420. $bar = new ProgressBar($output = $this->getOutputStream());
  421. $bar->start();
  422. $bar->setBarCharacter('■');
  423. $bar->advance(3);
  424. rewind($output->getStream());
  425. $this->assertEquals(
  426. ' 0 [>---------------------------]'.
  427. $this->generateOutput(' 3 [■■■>------------------------]'),
  428. stream_get_contents($output->getStream())
  429. );
  430. }
  431. public function testClear()
  432. {
  433. $bar = new ProgressBar($output = $this->getOutputStream(), 50);
  434. $bar->start();
  435. $bar->setProgress(25);
  436. $bar->clear();
  437. rewind($output->getStream());
  438. $this->assertEquals(
  439. ' 0/50 [>---------------------------] 0%'.
  440. $this->generateOutput(' 25/50 [==============>-------------] 50%').
  441. $this->generateOutput(''),
  442. stream_get_contents($output->getStream())
  443. );
  444. }
  445. public function testPercentNotHundredBeforeComplete()
  446. {
  447. $bar = new ProgressBar($output = $this->getOutputStream(), 200);
  448. $bar->start();
  449. $bar->display();
  450. $bar->advance(199);
  451. $bar->advance();
  452. rewind($output->getStream());
  453. $this->assertEquals(
  454. ' 0/200 [>---------------------------] 0%'.
  455. $this->generateOutput(' 0/200 [>---------------------------] 0%').
  456. $this->generateOutput(' 199/200 [===========================>] 99%').
  457. $this->generateOutput(' 200/200 [============================] 100%'),
  458. stream_get_contents($output->getStream())
  459. );
  460. }
  461. public function testNonDecoratedOutput()
  462. {
  463. $bar = new ProgressBar($output = $this->getOutputStream(false), 200);
  464. $bar->start();
  465. for ($i = 0; $i < 200; ++$i) {
  466. $bar->advance();
  467. }
  468. $bar->finish();
  469. rewind($output->getStream());
  470. $this->assertEquals(
  471. ' 0/200 [>---------------------------] 0%'.PHP_EOL.
  472. ' 20/200 [==>-------------------------] 10%'.PHP_EOL.
  473. ' 40/200 [=====>----------------------] 20%'.PHP_EOL.
  474. ' 60/200 [========>-------------------] 30%'.PHP_EOL.
  475. ' 80/200 [===========>----------------] 40%'.PHP_EOL.
  476. ' 100/200 [==============>-------------] 50%'.PHP_EOL.
  477. ' 120/200 [================>-----------] 60%'.PHP_EOL.
  478. ' 140/200 [===================>--------] 70%'.PHP_EOL.
  479. ' 160/200 [======================>-----] 80%'.PHP_EOL.
  480. ' 180/200 [=========================>--] 90%'.PHP_EOL.
  481. ' 200/200 [============================] 100%',
  482. stream_get_contents($output->getStream())
  483. );
  484. }
  485. public function testNonDecoratedOutputWithClear()
  486. {
  487. $bar = new ProgressBar($output = $this->getOutputStream(false), 50);
  488. $bar->start();
  489. $bar->setProgress(25);
  490. $bar->clear();
  491. $bar->setProgress(50);
  492. $bar->finish();
  493. rewind($output->getStream());
  494. $this->assertEquals(
  495. ' 0/50 [>---------------------------] 0%'.PHP_EOL.
  496. ' 25/50 [==============>-------------] 50%'.PHP_EOL.
  497. ' 50/50 [============================] 100%',
  498. stream_get_contents($output->getStream())
  499. );
  500. }
  501. public function testNonDecoratedOutputWithoutMax()
  502. {
  503. $bar = new ProgressBar($output = $this->getOutputStream(false));
  504. $bar->start();
  505. $bar->advance();
  506. rewind($output->getStream());
  507. $this->assertEquals(
  508. ' 0 [>---------------------------]'.PHP_EOL.
  509. ' 1 [->--------------------------]',
  510. stream_get_contents($output->getStream())
  511. );
  512. }
  513. public function testParallelBars()
  514. {
  515. $output = $this->getOutputStream();
  516. $bar1 = new ProgressBar($output, 2);
  517. $bar2 = new ProgressBar($output, 3);
  518. $bar2->setProgressCharacter('#');
  519. $bar3 = new ProgressBar($output);
  520. $bar1->start();
  521. $output->write("\n");
  522. $bar2->start();
  523. $output->write("\n");
  524. $bar3->start();
  525. for ($i = 1; $i <= 3; ++$i) {
  526. // up two lines
  527. $output->write("\033[2A");
  528. if ($i <= 2) {
  529. $bar1->advance();
  530. }
  531. $output->write("\n");
  532. $bar2->advance();
  533. $output->write("\n");
  534. $bar3->advance();
  535. }
  536. $output->write("\033[2A");
  537. $output->write("\n");
  538. $output->write("\n");
  539. $bar3->finish();
  540. rewind($output->getStream());
  541. $this->assertEquals(
  542. ' 0/2 [>---------------------------] 0%'."\n".
  543. ' 0/3 [#---------------------------] 0%'."\n".
  544. rtrim(' 0 [>---------------------------]').
  545. "\033[2A".
  546. $this->generateOutput(' 1/2 [==============>-------------] 50%')."\n".
  547. $this->generateOutput(' 1/3 [=========#------------------] 33%')."\n".
  548. rtrim($this->generateOutput(' 1 [->--------------------------]')).
  549. "\033[2A".
  550. $this->generateOutput(' 2/2 [============================] 100%')."\n".
  551. $this->generateOutput(' 2/3 [==================#---------] 66%')."\n".
  552. rtrim($this->generateOutput(' 2 [-->-------------------------]')).
  553. "\033[2A".
  554. "\n".
  555. $this->generateOutput(' 3/3 [============================] 100%')."\n".
  556. rtrim($this->generateOutput(' 3 [--->------------------------]')).
  557. "\033[2A".
  558. "\n".
  559. "\n".
  560. rtrim($this->generateOutput(' 3 [============================]')),
  561. stream_get_contents($output->getStream())
  562. );
  563. }
  564. public function testWithoutMax()
  565. {
  566. $output = $this->getOutputStream();
  567. $bar = new ProgressBar($output);
  568. $bar->start();
  569. $bar->advance();
  570. $bar->advance();
  571. $bar->advance();
  572. $bar->finish();
  573. rewind($output->getStream());
  574. $this->assertEquals(
  575. rtrim(' 0 [>---------------------------]').
  576. rtrim($this->generateOutput(' 1 [->--------------------------]')).
  577. rtrim($this->generateOutput(' 2 [-->-------------------------]')).
  578. rtrim($this->generateOutput(' 3 [--->------------------------]')).
  579. rtrim($this->generateOutput(' 3 [============================]')),
  580. stream_get_contents($output->getStream())
  581. );
  582. }
  583. public function testSettingMaxStepsDuringProgressing()
  584. {
  585. $output = $this->getOutputStream();
  586. $bar = new ProgressBar($output);
  587. $bar->start();
  588. $bar->setProgress(2);
  589. $bar->setMaxSteps(10);
  590. $bar->setProgress(5);
  591. $bar->setMaxSteps(100);
  592. $bar->setProgress(10);
  593. $bar->finish();
  594. rewind($output->getStream());
  595. $this->assertEquals(
  596. rtrim(' 0 [>---------------------------]').
  597. rtrim($this->generateOutput(' 2 [-->-------------------------]')).
  598. rtrim($this->generateOutput(' 5/10 [==============>-------------] 50%')).
  599. rtrim($this->generateOutput(' 10/100 [==>-------------------------] 10%')).
  600. rtrim($this->generateOutput(' 100/100 [============================] 100%')),
  601. stream_get_contents($output->getStream())
  602. );
  603. }
  604. public function testWithSmallScreen()
  605. {
  606. $output = $this->getOutputStream();
  607. $bar = new ProgressBar($output);
  608. putenv('COLUMNS=12');
  609. $bar->start();
  610. $bar->advance();
  611. putenv('COLUMNS=120');
  612. rewind($output->getStream());
  613. $this->assertEquals(
  614. ' 0 [>---]'.
  615. $this->generateOutput(' 1 [->--]'),
  616. stream_get_contents($output->getStream())
  617. );
  618. }
  619. public function testAddingPlaceholderFormatter()
  620. {
  621. ProgressBar::setPlaceholderFormatterDefinition('remaining_steps', function (ProgressBar $bar) {
  622. return $bar->getMaxSteps() - $bar->getProgress();
  623. });
  624. $bar = new ProgressBar($output = $this->getOutputStream(), 3);
  625. $bar->setFormat(' %remaining_steps% [%bar%]');
  626. $bar->start();
  627. $bar->advance();
  628. $bar->finish();
  629. rewind($output->getStream());
  630. $this->assertEquals(
  631. ' 3 [>---------------------------]'.
  632. $this->generateOutput(' 2 [=========>------------------]').
  633. $this->generateOutput(' 0 [============================]'),
  634. stream_get_contents($output->getStream())
  635. );
  636. }
  637. public function testMultilineFormat()
  638. {
  639. $bar = new ProgressBar($output = $this->getOutputStream(), 3);
  640. $bar->setFormat("%bar%\nfoobar");
  641. $bar->start();
  642. $bar->advance();
  643. $bar->clear();
  644. $bar->finish();
  645. rewind($output->getStream());
  646. $this->assertEquals(
  647. ">---------------------------\nfoobar".
  648. $this->generateOutput("=========>------------------\nfoobar").
  649. "\x0D\x1B[2K\x1B[1A\x1B[2K".
  650. $this->generateOutput("============================\nfoobar"),
  651. stream_get_contents($output->getStream())
  652. );
  653. }
  654. public function testAnsiColorsAndEmojis()
  655. {
  656. putenv('COLUMNS=156');
  657. $bar = new ProgressBar($output = $this->getOutputStream(), 15);
  658. ProgressBar::setPlaceholderFormatterDefinition('memory', function (ProgressBar $bar) {
  659. static $i = 0;
  660. $mem = 100000 * $i;
  661. $colors = $i++ ? '41;37' : '44;37';
  662. return "\033[".$colors.'m '.Helper::formatMemory($mem)." \033[0m";
  663. });
  664. $bar->setFormat(" \033[44;37m %title:-37s% \033[0m\n %current%/%max% %bar% %percent:3s%%\n 🏁 %remaining:-10s% %memory:37s%");
  665. $bar->setBarCharacter($done = "\033[32m●\033[0m");
  666. $bar->setEmptyBarCharacter($empty = "\033[31m●\033[0m");
  667. $bar->setProgressCharacter($progress = "\033[32m➤ \033[0m");
  668. $bar->setMessage('Starting the demo... fingers crossed', 'title');
  669. $bar->start();
  670. rewind($output->getStream());
  671. $this->assertEquals(
  672. " \033[44;37m Starting the demo... fingers crossed \033[0m\n".
  673. ' 0/15 '.$progress.str_repeat($empty, 26)." 0%\n".
  674. " \xf0\x9f\x8f\x81 < 1 sec \033[44;37m 0 B \033[0m",
  675. stream_get_contents($output->getStream())
  676. );
  677. ftruncate($output->getStream(), 0);
  678. rewind($output->getStream());
  679. $bar->setMessage('Looks good to me...', 'title');
  680. $bar->advance(4);
  681. rewind($output->getStream());
  682. $this->assertEquals(
  683. $this->generateOutput(
  684. " \033[44;37m Looks good to me... \033[0m\n".
  685. ' 4/15 '.str_repeat($done, 7).$progress.str_repeat($empty, 19)." 26%\n".
  686. " \xf0\x9f\x8f\x81 < 1 sec \033[41;37m 97 KiB \033[0m"
  687. ),
  688. stream_get_contents($output->getStream())
  689. );
  690. ftruncate($output->getStream(), 0);
  691. rewind($output->getStream());
  692. $bar->setMessage('Thanks, bye', 'title');
  693. $bar->finish();
  694. rewind($output->getStream());
  695. $this->assertEquals(
  696. $this->generateOutput(
  697. " \033[44;37m Thanks, bye \033[0m\n".
  698. ' 15/15 '.str_repeat($done, 28)." 100%\n".
  699. " \xf0\x9f\x8f\x81 < 1 sec \033[41;37m 195 KiB \033[0m"
  700. ),
  701. stream_get_contents($output->getStream())
  702. );
  703. putenv('COLUMNS=120');
  704. }
  705. public function testSetFormat()
  706. {
  707. $bar = new ProgressBar($output = $this->getOutputStream());
  708. $bar->setFormat('normal');
  709. $bar->start();
  710. rewind($output->getStream());
  711. $this->assertEquals(
  712. ' 0 [>---------------------------]',
  713. stream_get_contents($output->getStream())
  714. );
  715. $bar = new ProgressBar($output = $this->getOutputStream(), 10);
  716. $bar->setFormat('normal');
  717. $bar->start();
  718. rewind($output->getStream());
  719. $this->assertEquals(
  720. ' 0/10 [>---------------------------] 0%',
  721. stream_get_contents($output->getStream())
  722. );
  723. }
  724. /**
  725. * @dataProvider provideFormat
  726. */
  727. public function testFormatsWithoutMax($format)
  728. {
  729. $bar = new ProgressBar($output = $this->getOutputStream());
  730. $bar->setFormat($format);
  731. $bar->start();
  732. rewind($output->getStream());
  733. $this->assertNotEmpty(stream_get_contents($output->getStream()));
  734. }
  735. /**
  736. * Provides each defined format.
  737. *
  738. * @return array
  739. */
  740. public function provideFormat()
  741. {
  742. return [
  743. ['normal'],
  744. ['verbose'],
  745. ['very_verbose'],
  746. ['debug'],
  747. ];
  748. }
  749. protected function getOutputStream($decorated = true, $verbosity = StreamOutput::VERBOSITY_NORMAL)
  750. {
  751. return new StreamOutput(fopen('php://memory', 'r+', false), $verbosity, $decorated);
  752. }
  753. protected function generateOutput($expected)
  754. {
  755. $count = substr_count($expected, "\n");
  756. return "\x0D\x1B[2K".($count ? str_repeat("\x1B[1A\x1B[2K", $count) : '').$expected;
  757. }
  758. public function testBarWidthWithMultilineFormat()
  759. {
  760. putenv('COLUMNS=10');
  761. $bar = new ProgressBar($output = $this->getOutputStream());
  762. $bar->setFormat("%bar%\n0123456789");
  763. // before starting
  764. $bar->setBarWidth(5);
  765. $this->assertEquals(5, $bar->getBarWidth());
  766. // after starting
  767. $bar->start();
  768. rewind($output->getStream());
  769. $this->assertEquals(5, $bar->getBarWidth(), stream_get_contents($output->getStream()));
  770. putenv('COLUMNS=120');
  771. }
  772. }