ProgressTest.php 13 KB

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