Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
lib: use custom formatter for CLI usage output
gtest: add test for CLI help formatting
  • Loading branch information
alaaeddineelamri committed Jul 11, 2022
1 parent 4962da4 commit 5197e94
Show file tree
Hide file tree
Showing 3 changed files with 175 additions and 1 deletion.
102 changes: 101 additions & 1 deletion core/src/lib/cli.cc
Expand Up @@ -24,6 +24,106 @@
#include "lib/bnet_network_dump.h"
#include "lib/version.h"
#include "lib/message.h"
#include <regex>

class BareosCliFormatter : public CLI::Formatter {
public:
std::string make_option_opts(const CLI::Option* opt) const override
{
std::stringstream out;

if (!opt->get_option_text().empty()) {
out << " " << opt->get_option_text();
} else {
if (opt->get_type_size() != 0) {
if (!opt->get_type_name().empty()) {
out << " " << get_label(opt->get_type_name());
}
if (opt->get_expected_max() == CLI::detail::expected_max_vector_size) {
out << " ...";
} else if (opt->get_expected_min() > 1) {
out << " x " << opt->get_expected();
}
if (!opt->get_default_str().empty()) {
out << "\n" << indent << indent;
out << "Default: " << opt->get_default_str();
}
if (opt->get_required()) {
out << "\n" << indent << indent;
out << get_label("REQUIRED");
}
}
if (!opt->get_envname().empty()) {
out << "\n" << indent << indent;
out << get_label("Env") << ": " << opt->get_envname();
}
if (!opt->get_needs().empty()) {
out << "\n" << indent << indent;
out << get_label("Needs") << ":";
for (const CLI::Option* op : opt->get_needs())
out << " " << op->get_name();
}
if (!opt->get_excludes().empty()) {
out << "\n" << indent << indent;
out << get_label("Excludes") << ":";
for (const CLI::Option* op : opt->get_excludes())
out << " " << op->get_name();
}
}
return out.str();
}

std::string make_option(const CLI::Option* opt,
bool is_positional) const override
{
std::stringstream out;

std::string name = make_option_name(opt, is_positional);
// remove option values from string, eg.
// -s{false},--no-signals{false}
// => -s,--no-signals
name = std::regex_replace(name, std::regex("\\{[^}]*\\}"), "");
out << indent << name;

out << make_option_opts(opt);
out << std::endl;

std::string description = make_option_desc(opt);
if (!description.empty()) {
format_paragraph(out, description, indent + indent);
}
out << std::endl;

return out.str();
}

protected:
std::string indent = std::string(" ");
std::size_t max_line_length = 79;

std::ostream& format_paragraph(std::ostream& out,
const std::string& text,
const std::string& indent) const
{
std::istringstream text_iss(text);

std::string word;
unsigned characters_written = indent.size();

out << indent;
while (text_iss >> word) {
if (word.size() + characters_written > max_line_length) {
out << "\n";
out << indent;
characters_written = indent.size();
}
out << word << " ";
characters_written += word.size() + 1;
}
out << std::endl;
return out;
}
};

void InitCLIApp(CLI::App& app, std::string description, int fsfyear)
{
Expand All @@ -38,7 +138,7 @@ void InitCLIApp(CLI::App& app, std::string description, int fsfyear)
app.description(description);
app.set_help_flag("-h,--help,-?", "Print this help message and exit.");
app.set_version_flag("--version", kBareosVersionStrings.Full);
app.get_formatter()->column_width(40);
app.formatter(std::make_shared<BareosCliFormatter>());
#ifdef HAVE_WIN32
app.allow_windows_style_options();
#endif
Expand Down
3 changes: 3 additions & 0 deletions core/src/tests/CMakeLists.txt
Expand Up @@ -167,6 +167,9 @@ if(NOT client-only)
$<$<BOOL:HAVE_PAM>:${PAM_LIBRARIES}> GTest::gtest_main
SKIP_GTEST
)

bareos_add_test(cli_test LINK_LIBRARIES bareos CLI11::CLI11 GTest::gtest_main)

bareos_add_test(
configure LINK_LIBRARIES testing_common dird_objects bareosfind bareossql
GTest::gtest_main
Expand Down
71 changes: 71 additions & 0 deletions core/src/tests/cli_test.cc
@@ -0,0 +1,71 @@
/*
BAREOS® - Backup Archiving REcovery Open Sourced
Copyright (C) 2022-2022 Bareos GmbH & Co. KG
This program is Free Software; you can redistribute it and/or
modify it under the terms of version three of the GNU Affero General Public
License as published by the Free Software Foundation and included
in the file LICENSE.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
*/

#if defined(HAVE_MINGW)
# include "include/bareos.h"
# include "gtest/gtest.h"
#else
# include "gtest/gtest.h"
# include "include/bareos.h"
#endif

#include "lib/cli.h"

TEST(CLI, HelpMessageDisplaysWithCorrectFormat)
{
CLI::App app;
InitCLIApp(app, "test app");
std::string random_default{"random default"};
std::string random_option_text{"a random option."};
bool random_flag;
auto xarg = app.add_flag("!-x", random_flag, random_option_text);

app.add_option("-y", random_default, random_option_text)
->required()
->capture_default_str()
->needs(xarg)
->excludes(xarg)
->expected(5);


/* clang-format off */
std::string expected_help{
"test app\n"
"Usage: [OPTIONS]\n\n"
"Options:\n"
" -h,-?,--help\n"
" Print this help message and exit. \n\n"
" --version\n"
" Display program version information and exit \n\n"
" -x\n"
" Excludes: -y\n"
" "+random_option_text+" \n\n"
" -y TEXT x 5\n"
" Default: "+random_default+"\n"
" REQUIRED\n"
" Needs: -x\n"
" Excludes: -x\n"
" "+random_option_text+" \n\n\n"};
/* clang-format on */

EXPECT_STREQ(app.get_description().c_str(), "test app");
EXPECT_STREQ(app.help().c_str(), expected_help.c_str());
}

0 comments on commit 5197e94

Please sign in to comment.