ProgressTest.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. <?php
  2. namespace League\CLImate\Tests;
  3. use League\CLImate\Exceptions\UnexpectedValueException;
  4. class ProgressTest extends TestBase
  5. {
  6. /**
  7. * The string length of the bar when at 100%
  8. *
  9. * @var integer $bar_str_len
  10. */
  11. protected $bar_str_len;
  12. /**
  13. * @param integer $length
  14. * @return string
  15. */
  16. private function repeat($length)
  17. {
  18. if (!$this->bar_str_len) {
  19. // Subtract 10 because of the '> 100%' plus some padding, max 100
  20. $this->bar_str_len = min($this->util->width() - 10, 100);
  21. }
  22. $repeat = ($length / 100) * $this->bar_str_len;
  23. $bar = str_repeat('=', $repeat);
  24. $bar .= '>';
  25. $bar .= str_repeat(' ', max($this->bar_str_len - $repeat, 0));
  26. return $bar;
  27. }
  28. /** @test */
  29. public function it_can_output_a_progress_bar()
  30. {
  31. $this->shouldWrite('');
  32. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(0)} 0%\e[0m");
  33. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(10)} 10%\e[0m");
  34. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(20)} 20%\e[0m");
  35. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(30)} 30%\e[0m");
  36. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(40)} 40%\e[0m");
  37. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(50)} 50%\e[0m");
  38. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(60)} 60%\e[0m");
  39. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(70)} 70%\e[0m");
  40. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(80)} 80%\e[0m");
  41. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(90)} 90%\e[0m");
  42. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(100)} 100%\e[0m");
  43. $progress = $this->cli->progress()->total(10);
  44. for ($i = 0; $i <= 10; $i++) {
  45. $progress->current($i);
  46. }
  47. }
  48. /** @test */
  49. public function it_can_output_a_progress_bar_via_constructor()
  50. {
  51. $this->shouldWrite('');
  52. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(0)} 0%\e[0m");
  53. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(10)} 10%\e[0m");
  54. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(20)} 20%\e[0m");
  55. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(30)} 30%\e[0m");
  56. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(40)} 40%\e[0m");
  57. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(50)} 50%\e[0m");
  58. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(60)} 60%\e[0m");
  59. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(70)} 70%\e[0m");
  60. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(80)} 80%\e[0m");
  61. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(90)} 90%\e[0m");
  62. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(100)} 100%\e[0m");
  63. $progress = $this->cli->progress(10);
  64. for ($i = 0; $i <= 10; $i++) {
  65. $progress->current($i);
  66. }
  67. }
  68. /** @test */
  69. public function it_can_output_a_progress_bar_with_current_labels()
  70. {
  71. $this->shouldWrite('');
  72. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(0)} 0%\n\r\e[Kzeroth\e[0m");
  73. $this->shouldWrite("\e[m\e[2A\r\e[K{$this->repeat(10)} 10%\n\r\e[Kfirst\e[0m");
  74. $this->shouldWrite("\e[m\e[2A\r\e[K{$this->repeat(20)} 20%\n\r\e[Ksecond\e[0m");
  75. $this->shouldWrite("\e[m\e[2A\r\e[K{$this->repeat(30)} 30%\n\r\e[Kthird\e[0m");
  76. $this->shouldWrite("\e[m\e[2A\r\e[K{$this->repeat(40)} 40%\n\r\e[Kfourth\e[0m");
  77. $this->shouldWrite("\e[m\e[2A\r\e[K{$this->repeat(50)} 50%\n\r\e[Kfifth\e[0m");
  78. $this->shouldWrite("\e[m\e[2A\r\e[K{$this->repeat(60)} 60%\n\r\e[Ksixth\e[0m");
  79. $this->shouldWrite("\e[m\e[2A\r\e[K{$this->repeat(70)} 70%\n\r\e[Kseventh\e[0m");
  80. $this->shouldWrite("\e[m\e[2A\r\e[K{$this->repeat(80)} 80%\n\r\e[Keighth\e[0m");
  81. $this->shouldWrite("\e[m\e[2A\r\e[K{$this->repeat(90)} 90%\n\r\e[Kninth\e[0m");
  82. $this->shouldWrite("\e[m\e[2A\r\e[K{$this->repeat(100)} 100%\n\r\e[Ktenth\e[0m");
  83. $progress = $this->cli->progress(10);
  84. $labels = [
  85. 'zeroth',
  86. 'first',
  87. 'second',
  88. 'third',
  89. 'fourth',
  90. 'fifth',
  91. 'sixth',
  92. 'seventh',
  93. 'eighth',
  94. 'ninth',
  95. 'tenth',
  96. ];
  97. for ($i = 0; $i <= 10; $i++) {
  98. $progress->current($i, $labels[$i]);
  99. }
  100. }
  101. /** @test */
  102. public function it_can_output_a_progress_bar_with_current_optional_labels()
  103. {
  104. $this->shouldWrite('');
  105. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(0)} 0%\n\r\e[Kzeroth\e[0m");
  106. $this->shouldWrite("\e[m\e[2A\r\e[K{$this->repeat(10)} 10%\n\r\e[K\e[0m");
  107. $this->shouldWrite("\e[m\e[2A\r\e[K{$this->repeat(20)} 20%\n\r\e[Ksecond\e[0m");
  108. $this->shouldWrite("\e[m\e[2A\r\e[K{$this->repeat(30)} 30%\n\r\e[Kthird\e[0m");
  109. $progress = $this->cli->progress(10);
  110. $labels = [
  111. 'zeroth',
  112. '',
  113. 'second',
  114. 'third',
  115. ];
  116. for ($i = 0; $i <= 3; $i++) {
  117. $progress->current($i, $labels[$i]);
  118. }
  119. }
  120. /** @test */
  121. public function it_can_output_a_styled_progress_bar()
  122. {
  123. $this->shouldWrite('');
  124. $this->shouldWrite("\e[31m\e[1A\r\e[K{$this->repeat(0)} 0%\e[0m");
  125. $this->shouldWrite("\e[31m\e[1A\r\e[K{$this->repeat(10)} 10%\e[0m");
  126. $this->shouldWrite("\e[31m\e[1A\r\e[K{$this->repeat(20)} 20%\e[0m");
  127. $this->shouldWrite("\e[31m\e[1A\r\e[K{$this->repeat(30)} 30%\e[0m");
  128. $this->shouldWrite("\e[31m\e[1A\r\e[K{$this->repeat(40)} 40%\e[0m");
  129. $this->shouldWrite("\e[31m\e[1A\r\e[K{$this->repeat(50)} 50%\e[0m");
  130. $this->shouldWrite("\e[31m\e[1A\r\e[K{$this->repeat(60)} 60%\e[0m");
  131. $this->shouldWrite("\e[31m\e[1A\r\e[K{$this->repeat(70)} 70%\e[0m");
  132. $this->shouldWrite("\e[31m\e[1A\r\e[K{$this->repeat(80)} 80%\e[0m");
  133. $this->shouldWrite("\e[31m\e[1A\r\e[K{$this->repeat(90)} 90%\e[0m");
  134. $this->shouldWrite("\e[31m\e[1A\r\e[K{$this->repeat(100)} 100%\e[0m");
  135. $progress = $this->cli->redProgress(10);
  136. for ($i = 0; $i <= 10; $i++) {
  137. $progress->current($i);
  138. }
  139. }
  140. /** @test */
  141. public function it_can_output_a_styled_progress_bar_and_resets_the_style()
  142. {
  143. $this->shouldWrite('');
  144. $this->shouldWrite("\e[31m\e[1A\r\e[K{$this->repeat(0)} 0%\e[0m");
  145. $this->shouldWrite("\e[31m\e[1A\r\e[K{$this->repeat(10)} 10%\e[0m");
  146. $this->shouldWrite("\e[31m\e[1A\r\e[K{$this->repeat(20)} 20%\e[0m");
  147. $this->shouldWrite("\e[31m\e[1A\r\e[K{$this->repeat(30)} 30%\e[0m");
  148. $this->shouldWrite("\e[31m\e[1A\r\e[K{$this->repeat(40)} 40%\e[0m");
  149. $this->shouldWrite("\e[31m\e[1A\r\e[K{$this->repeat(50)} 50%\e[0m");
  150. $this->shouldWrite("\e[31m\e[1A\r\e[K{$this->repeat(60)} 60%\e[0m");
  151. $this->shouldWrite("\e[31m\e[1A\r\e[K{$this->repeat(70)} 70%\e[0m");
  152. $this->shouldWrite("\e[31m\e[1A\r\e[K{$this->repeat(80)} 80%\e[0m");
  153. $this->shouldWrite("\e[31m\e[1A\r\e[K{$this->repeat(90)} 90%\e[0m");
  154. $this->shouldWrite("\e[31m\e[1A\r\e[K{$this->repeat(100)} 100%\e[0m");
  155. $this->shouldWrite("\e[mand back to normal\e[0m");
  156. $this->shouldHavePersisted();
  157. $progress = $this->cli->redProgress(10);
  158. for ($i = 0; $i <= 10; $i++) {
  159. $progress->current($i);
  160. }
  161. $this->cli->out('and back to normal');
  162. }
  163. /**
  164. * @test
  165. */
  166. public function it_can_throws_an_exception_for_a_zero_total_progress_bar()
  167. {
  168. $this->expectException(UnexpectedValueException::class);
  169. $this->expectExceptionMessage("The progress total must be greater than zero.");
  170. $progress = $this->cli->progress();
  171. $progress->current(0);
  172. }
  173. /**
  174. * @test
  175. */
  176. public function it_can_throws_an_exception_when_the_current_is_greater_than_the_total()
  177. {
  178. $this->expectException(UnexpectedValueException::class);
  179. $this->expectExceptionMessage("The current is greater than the total.");
  180. $progress = $this->cli->progress(1);
  181. $progress->current(2);
  182. }
  183. /** @test */
  184. public function it_can_output_a_progress_bar_using_increments()
  185. {
  186. $this->shouldWrite('');
  187. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(10)} 10%\e[0m");
  188. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(20)} 20%\e[0m");
  189. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(70)} 70%\e[0m");
  190. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(50)} 50%\e[0m");
  191. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(100)} 100%\e[0m");
  192. $progress = $this->cli->progress()->total(10);
  193. $progress->advance();
  194. $progress->advance(1);
  195. $progress->advance(5);
  196. $progress->advance(-2);
  197. $progress->advance(5);
  198. }
  199. /** @test */
  200. public function it_can_output_a_progress_bar_using_increments_with_label()
  201. {
  202. $this->shouldWrite('');
  203. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(10)} 10%\n\r\e[Kstart\e[0m");
  204. $this->shouldWrite("\e[m\e[2A\r\e[K{$this->repeat(20)} 20%\n\r\e[Knext\e[0m");
  205. $this->shouldWrite("\e[m\e[2A\r\e[K{$this->repeat(100)} 100%\n\r\e[Kfinal\e[0m");
  206. $progress = $this->cli->progress()->total(10);
  207. $progress->advance(1, 'start');
  208. $progress->advance(1, 'next');
  209. $progress->advance(8, 'final');
  210. }
  211. /** @test */
  212. public function it_will_force_a_redraw_if_specified()
  213. {
  214. $this->shouldWrite('');
  215. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(20)} 20%\e[0m");
  216. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(40)} 40%\e[0m");
  217. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(40)} 40%\e[0m");
  218. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(60)} 60%\e[0m");
  219. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(80)} 80%\e[0m");
  220. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(100)} 100%\e[0m");
  221. $progress = $this->cli->progress()->total(5);
  222. $progress->forceRedraw();
  223. $items = [1, 2, 2, 3, 4, 5];
  224. foreach ($items as $item) {
  225. $progress->current($item);
  226. }
  227. }
  228. /** @test */
  229. public function it_will_not_force_a_redraw_if_disabled()
  230. {
  231. $this->shouldWrite('');
  232. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(20)} 20%\e[0m");
  233. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(40)} 40%\e[0m");
  234. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(60)} 60%\e[0m");
  235. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(80)} 80%\e[0m");
  236. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(100)} 100%\e[0m");
  237. $progress = $this->cli->progress()->total(5);
  238. $progress->forceRedraw(false);
  239. $items = [1, 2, 2, 3, 4, 5];
  240. foreach ($items as $item) {
  241. $progress->current($item);
  242. }
  243. }
  244. public function testEach1()
  245. {
  246. $this->shouldWrite('');
  247. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(50)} 50%\e[0m");
  248. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(100)} 100%\e[0m");
  249. $this->cli->progress()->each([1, 2]);
  250. }
  251. public function testEach2()
  252. {
  253. $this->shouldWrite('');
  254. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(50)} 50%\e[0m");
  255. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(100)} 100%\e[0m");
  256. $items = [];
  257. $this->cli->progress()->each(["two", "one"], function ($item) use (&$items) {
  258. $items[] = $item;
  259. });
  260. $this->assertSame(["two", "one"], $items);
  261. }
  262. public function testEach3()
  263. {
  264. $this->shouldWrite('');
  265. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(50)} 50%\e[0m");
  266. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(100)} 100%\e[0m");
  267. $items = [];
  268. $this->cli->progress()->each(["key2" => "two", "key1" => "one"], function ($item, $key) use (&$items) {
  269. $items[$key] = $item;
  270. });
  271. $this->assertSame(["key2" => "two", "key1" => "one"], $items);
  272. }
  273. public function testEach4()
  274. {
  275. $this->shouldWrite('');
  276. $this->shouldWrite("\e[m\e[1A\r\e[K{$this->repeat(20)} 20%\n\r\e[Kone\e[0m");
  277. $this->shouldWrite("\e[m\e[2A\r\e[K{$this->repeat(40)} 40%\n\r\e[Ktwo\e[0m");
  278. $this->shouldWrite("\e[m\e[2A\r\e[K{$this->repeat(60)} 60%\n\r\e[Kthree\e[0m");
  279. $this->shouldWrite("\e[m\e[2A\r\e[K{$this->repeat(80)} 80%\n\r\e[Kfour\e[0m");
  280. $this->shouldWrite("\e[m\e[2A\r\e[K{$this->repeat(100)} 100%\n\r\e[Kfive\e[0m");
  281. $this->cli->progress()->each(["one", "two", "three", "four", "five"], function ($item) {
  282. return $item;
  283. });
  284. }
  285. }