From 788c57dd4be7a23d6b748a07a44476dc7b87cebf Mon Sep 17 00:00:00 2001 From: Gareth Ellis Date: Tue, 1 Dec 2015 12:02:51 +0000 Subject: [PATCH] Add progress helper --- lib/Cake/Console/Helper/ProgressHelper.php | 113 +++++++++ lib/Cake/Console/Helper/ShellHelper.php | 37 +++ .../Console/Helper/ProgressHelperTest.php | 217 ++++++++++++++++++ lib/Cake/TestSuite/Stub/ConsoleOutputStub.php | 79 +++++++ 4 files changed, 446 insertions(+) create mode 100644 lib/Cake/Console/Helper/ProgressHelper.php create mode 100644 lib/Cake/Console/Helper/ShellHelper.php create mode 100644 lib/Cake/Test/Case/Console/Helper/ProgressHelperTest.php create mode 100644 lib/Cake/TestSuite/Stub/ConsoleOutputStub.php diff --git a/lib/Cake/Console/Helper/ProgressHelper.php b/lib/Cake/Console/Helper/ProgressHelper.php new file mode 100644 index 00000000000..52024bf7227 --- /dev/null +++ b/lib/Cake/Console/Helper/ProgressHelper.php @@ -0,0 +1,113 @@ + null]; + if (isset($args[0])) { + $args['callback'] = $args[0]; + } + if (!$args['callback'] || !is_callable($args['callback'])) { + throw new RuntimeException('Callback option must be a callable.'); + } + $this->init($args); + $callback = $args['callback']; + while ($this->_progress < $this->_total) { + $callback($this); + $this->draw(); + } + $this->_consoleOutput->write(''); + } + + /** + * Initialize the progress bar for use. + * + * - `total` The total number of items in the progress bar. Defaults + * to 100. + * - `width` The width of the progress bar. Defaults to 80. + * + * @param array $args The initialization data. + * @return void + */ + public function init(array $args = []) + { + $args += ['total' => 100, 'width' => 80]; + $this->_progress = 0; + $this->_width = $args['width']; + $this->_total = $args['total']; + } + + + /** + * Increment the progress bar. + * + * @param int $num The amount of progress to advance by. + * @return void + */ + public function increment($num = 1) + { + $this->_progress = min(max(0, $this->_progress + $num), $this->_total); + } + + /** + * Render the progress bar based on the current state. + * + * @return void + */ + public function draw() + { + $numberLen = strlen(' 100%'); + $complete = round($this->_progress / $this->_total, 2); + $barLen = ($this->_width - $numberLen) * ($this->_progress / $this->_total); + $bar = ''; + if ($barLen > 1) { + $bar = str_repeat('=', $barLen - 1) . '>'; + } + $pad = ceil($this->_width - $numberLen - $barLen); + if ($pad > 0) { + $bar .= str_repeat(' ', $pad); + } + $percent = ($complete * 100) . '%'; + $bar .= str_pad($percent, $numberLen, ' ', STR_PAD_LEFT); + $this->_consoleOutput->overwrite($bar, 0); + } +} \ No newline at end of file diff --git a/lib/Cake/Console/Helper/ShellHelper.php b/lib/Cake/Console/Helper/ShellHelper.php new file mode 100644 index 00000000000..519464532a9 --- /dev/null +++ b/lib/Cake/Console/Helper/ShellHelper.php @@ -0,0 +1,37 @@ +_consoleOutput = $consoleOutput; + } + + /** + * This method should output content using `$this->_consoleOutput`. + * + * @param array $args The arguments for the helper. + * @return void + */ + abstract public function output($args); +} \ No newline at end of file diff --git a/lib/Cake/Test/Case/Console/Helper/ProgressHelperTest.php b/lib/Cake/Test/Case/Console/Helper/ProgressHelperTest.php new file mode 100644 index 00000000000..256be6f027c --- /dev/null +++ b/lib/Cake/Test/Case/Console/Helper/ProgressHelperTest.php @@ -0,0 +1,217 @@ +consoleOutput = new ConsoleOutputStub(); + $this->helper = new ProgressHelper($this->consoleOutput); + } + + /** + * Test that a callback is required.* + * @expectedException \RuntimeException + */ + public function testOutputFailure() + { + $this->helper->output(['not a callback']); + } + + /** + * Test that the callback is invoked until 100 is reached. + * + * @return void + */ + public function testOutputSuccess() + { + $this->helper->output([function ($progress) { + $progress->increment(20); + }]); + $expected = [ + '', + '==============> 20%', + '', + '=============================> 40%', + '', + '============================================> 60%', + '', + '===========================================================> 80%', + '', + '==========================================================================> 100%', + '', + ]; + $this->assertEquals($expected, $this->consoleOutput->messages()); + } + + /** + * Test output with options + * + * @return void + */ + public function testOutputSuccessOptions() + { + $this->helper->output([ + 'total' => 10, + 'width' => 20, + 'callback' => function ($progress) { + $progress->increment(2); + } + ]); + $expected = [ + '', + '==> 20%', + '', + '=====> 40%', + '', + '========> 60%', + '', + '===========> 80%', + '', + '==============> 100%', + '', + ]; + $this->assertEquals($expected, $this->consoleOutput->messages()); + } + + /** + * Test using the helper manually. + * + * @return void + */ + public function testIncrementAndRender() + { + $this->helper->init(); + $this->helper->increment(20); + $this->helper->draw(); + $this->helper->increment(40); + $this->helper->draw(); + $this->helper->increment(40); + $this->helper->draw(); + $expected = [ + '', + '==============> 20%', + '', + '============================================> 60%', + '', + '==========================================================================> 100%', + ]; + $this->assertEquals($expected, $this->consoleOutput->messages()); + } + + /** + * Test negative numbers + * + * @return void + */ + public function testIncrementWithNegatives() + { + $this->helper->init(); + $this->helper->increment(40); + $this->helper->draw(); + $this->helper->increment(-60); + $this->helper->draw(); + $this->helper->increment(80); + $this->helper->draw(); + $expected = [ + '', + '=============================> 40%', + '', + ' 0%', + '', + '===========================================================> 80%', + ]; + $this->assertEquals($expected, $this->consoleOutput->messages()); + } + + /** + * Test increment and draw with options + * + * @return void + */ + public function testIncrementWithOptions() + { + $this->helper->init([ + 'total' => 10, + 'width' => 20, + ]); + $this->helper->increment(4); + $this->helper->draw(); + $this->helper->increment(4); + $this->helper->draw(); + $this->helper->increment(4); + $this->helper->draw(); + + $expected = [ + '', + '=====> 40%', + '', + '===========> 80%', + '', + '==============> 100%', + ]; + $this->assertEquals($expected, $this->consoleOutput->messages()); + } + + /** + * Test increment and draw with value that makes the pad + * be a float + * + * @return void + */ + public function testIncrementFloatPad() + { + $this->helper->init([ + 'total' => 50 + ]); + $this->helper->increment(7); + $this->helper->draw(); + $this->helper->increment(7); + $this->helper->draw(); + $this->helper->increment(7); + $this->helper->draw(); + $this->helper->increment(7); + $this->helper->draw(); + $this->helper->increment(7); + $this->helper->draw(); + $this->helper->increment(3); + $this->helper->draw(); + $this->helper->increment(4); + $this->helper->draw(); + $this->helper->increment(8); + $this->helper->draw(); + $expected = [ + '', + '=========> 14%', + '', + '====================> 28%', + '', + '==============================> 42%', + '', + '=========================================> 56%', + '', + '===================================================> 70%', + '', + '========================================================> 76%', + '', + '==============================================================> 84%', + '', + '==========================================================================> 100%', + ]; + $this->assertEquals($expected, $this->consoleOutput->messages()); + } +} \ No newline at end of file diff --git a/lib/Cake/TestSuite/Stub/ConsoleOutputStub.php b/lib/Cake/TestSuite/Stub/ConsoleOutputStub.php new file mode 100644 index 00000000000..cef3043906b --- /dev/null +++ b/lib/Cake/TestSuite/Stub/ConsoleOutputStub.php @@ -0,0 +1,79 @@ +_out[] = $line; + $this->_lastWritten = strlen($line); + } + $newlines--; + while ($newlines > 0) { + $this->_out[] = ''; + $this->_lastWritten = 0; + $newlines--; + } + } + +/** + * Overwrite output already written to the buffer. + * + * @param array|string $message The message to output. + * @param int $newlines Number of newlines to append. + * @param int $size The number of bytes to overwrite. Defaults to the + * length of the last message output. + * @return void + */ + public function overwrite($message, $newlines = 1, $size = null) + { + //insert an empty array to mock deletion of existing output + $this->_out[] = ""; + //append new message to output + $this->write($message, $newlines); + } + + /** +* Get the buffered output. +* +* @return array +*/ + public function messages() + { + return $this->_out; + } +} \ No newline at end of file