diff --git a/src/cpp-utils/CMakeLists.txt b/src/cpp-utils/CMakeLists.txt index a6d285ca..500e6ec1 100644 --- a/src/cpp-utils/CMakeLists.txt +++ b/src/cpp-utils/CMakeLists.txt @@ -23,6 +23,7 @@ set(SOURCES io/IOStreamConsole.cpp io/NoninteractiveConsole.cpp io/pipestream.cpp + io/ProgressBar.cpp thread/LoopThread.cpp thread/ThreadSystem.cpp thread/debugging_nonwindows.cpp diff --git a/src/cpp-utils/io/ProgressBar.cpp b/src/cpp-utils/io/ProgressBar.cpp new file mode 100644 index 00000000..97aa499a --- /dev/null +++ b/src/cpp-utils/io/ProgressBar.cpp @@ -0,0 +1,35 @@ +#include "ProgressBar.h" +#include +#include +#include +#include "IOStreamConsole.h" + +using std::string; + +namespace cpputils { + +ProgressBar::ProgressBar(const char* preamble, uint64_t max_value) +: ProgressBar(std::make_shared(), preamble, max_value) {} + +ProgressBar::ProgressBar(std::shared_ptr console, const char* preamble, uint64_t max_value) +: _console(std::move(console)) +, _preamble(string("\r") + preamble + " ") +, _max_value(max_value) +, _lastPercentage(std::numeric_limits::max()) { + ASSERT(_max_value > 0, "Progress bar can't handle max_value of 0"); + + _console->print("\n"); + + // show progress bar. _lastPercentage is different to zero, so it shows. + update(0); +} + +void ProgressBar::update(uint64_t value) { + const size_t percentage = (100 * value) / _max_value; + if (percentage != _lastPercentage) { + _console->print(_preamble + std::to_string(percentage) + "%"); + _lastPercentage = percentage; + } +} + +} diff --git a/src/cpp-utils/io/ProgressBar.h b/src/cpp-utils/io/ProgressBar.h new file mode 100644 index 00000000..1bbd7841 --- /dev/null +++ b/src/cpp-utils/io/ProgressBar.h @@ -0,0 +1,31 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_IO_PROGRESSBAR_H +#define MESSMER_CPPUTILS_IO_PROGRESSBAR_H + +#include +#include +#include +#include "Console.h" + +namespace cpputils { + +class ProgressBar final { +public: + explicit ProgressBar(std::shared_ptr console, const char* preamble, uint64_t max_value); + explicit ProgressBar(const char* preamble, uint64_t max_value); + + void update(uint64_t value); + +private: + + std::shared_ptr _console; + std::string _preamble; + uint64_t _max_value; + size_t _lastPercentage; + + DISALLOW_COPY_AND_ASSIGN(ProgressBar); +}; + +} + +#endif diff --git a/test/cpp-utils/CMakeLists.txt b/test/cpp-utils/CMakeLists.txt index 0a2a5fe5..831683c9 100644 --- a/test/cpp-utils/CMakeLists.txt +++ b/test/cpp-utils/CMakeLists.txt @@ -29,6 +29,7 @@ set(SOURCES io/ConsoleTest_Print.cpp io/ConsoleTest_Ask.cpp io/ConsoleTest_AskPassword.cpp + io/ProgressBarTest.cpp random/RandomIncludeTest.cpp lock/LockPoolIncludeTest.cpp lock/ConditionBarrierIncludeTest.cpp diff --git a/test/cpp-utils/io/ProgressBarTest.cpp b/test/cpp-utils/io/ProgressBarTest.cpp new file mode 100644 index 00000000..97ca2211 --- /dev/null +++ b/test/cpp-utils/io/ProgressBarTest.cpp @@ -0,0 +1,66 @@ +#include +#include + +using cpputils::ProgressBar; +using std::make_shared; + +class MockConsole final: public cpputils::Console { +public: + void EXPECT_OUTPUT(const char* expected) { + EXPECT_EQ(expected, _output); + _output = ""; + } + + void print(const std::string& text) override { + _output += text; + } + + unsigned int ask(const std::string&, const std::vector&) override { + EXPECT_TRUE(false); + return 0; + } + + bool askYesNo(const std::string&, bool) override { + EXPECT_TRUE(false); + return false; + } + + std::string askPassword(const std::string&) override { + EXPECT_TRUE(false); + return ""; + } + +private: + std::string _output; +}; + +TEST(ProgressBarTest, testProgressBar) { + auto console = make_shared(); + + ProgressBar bar(console, "Preamble", 2000); + console->EXPECT_OUTPUT("\n\rPreamble 0%"); + + // when updating to 0, doesn't reprint + bar.update(0); + console->EXPECT_OUTPUT(""); + + // update to half + bar.update(1000); + console->EXPECT_OUTPUT("\rPreamble 50%"); + + // when updating to same value, doesn't reprint + bar.update(1000); + console->EXPECT_OUTPUT(""); + + // when updating to value with same percentage, doesn't reprint + bar.update(1001); + console->EXPECT_OUTPUT(""); + + // update to 0 + bar.update(0); + console->EXPECT_OUTPUT("\rPreamble 0%"); + + // update to full + bar.update(2000); + console->EXPECT_OUTPUT("\rPreamble 100%"); +}