From 82846dcc27dbcecb293d20f574bd24deb13583f0 Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Sat, 8 Jun 2024 13:29:22 +0200 Subject: [PATCH] Extended cli_handler test cases (#287) --- Makefile | 6 + src/cli/cli_handler.cc | 7 +- src/cli/cli_handler.h | 7 ++ tests/test_cli_handler.cc | 258 +++++++++++++++++++++++++++++++++++++- 4 files changed, 276 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index bfe2b285..3d55eecf 100644 --- a/Makefile +++ b/Makefile @@ -115,6 +115,12 @@ test: debug test_release: release CTEST_OUTPUT_ON_FAILURE=1 ctest --test-dir release +coverage_report: test + lcov -c -d debug -o coverage.info + lcov -e coverage.info "${PWD}/src/*" -o coverage-src.info + lcov -l coverage-src.info + genhtml coverage-src.info --output-directory debug/coverage_html + install: release make -C release install DESTDIR=${DESTDIR} diff --git a/src/cli/cli_handler.cc b/src/cli/cli_handler.cc index 8ff4b5f8..9841cc9e 100644 --- a/src/cli/cli_handler.cc +++ b/src/cli/cli_handler.cc @@ -389,6 +389,11 @@ runtime_config cli_handler::get_runtime_config() const return cfg; } +void cli_handler::set_config_path(const std::string &path) +{ + config_path = path; +} + cli_flow_t cli_handler::print_version() { ostr_ << "clang-uml " << clanguml::version::CLANG_UML_VERSION << '\n'; @@ -477,7 +482,7 @@ cli_flow_t cli_handler::create_config_file() { namespace fs = std::filesystem; - fs::path config_file{"./.clang-uml"}; + fs::path config_file{config_path}; if (fs::exists(config_file)) { ostr_ << "ERROR: .clang-uml file already exists\n"; diff --git a/src/cli/cli_handler.h b/src/cli/cli_handler.h index ae222369..81fafa0e 100644 --- a/src/cli/cli_handler.h +++ b/src/cli/cli_handler.h @@ -153,6 +153,13 @@ public: */ runtime_config get_runtime_config() const; + /** + * @brief Set the default config path + * + * @param path + */ + void set_config_path(const std::string &path); + std::string config_path{".clang-uml"}; std::optional compilation_database_dir{}; std::vector diagram_names{}; diff --git a/tests/test_cli_handler.cc b/tests/test_cli_handler.cc index 22e35b7c..d47ac55c 100644 --- a/tests/test_cli_handler.cc +++ b/tests/test_cli_handler.cc @@ -158,6 +158,154 @@ TEST_CASE("Test cli handler print_diagram_template") )"); } +TEST_CASE("Test cli handler print_diagram_template invalid template") +{ + using clanguml::cli::cli_flow_t; + using clanguml::cli::cli_handler; + + using clanguml::cli::cli_flow_t; + using clanguml::cli::cli_handler; + + std::vector argv = {"clang-uml", "--config", + "./test_config_data/simple.yml", "--show-template", "no_such_template"}; + + std::ostringstream ostr; + cli_handler cli{ostr, make_sstream_logger(ostr)}; + + auto res = cli.handle_options(argv.size(), argv.data()); + + REQUIRE(res == cli_flow_t::kError); +} + +TEST_CASE("Test cli handler properly adds new diagram configs from template") +{ + using clanguml::cli::cli_flow_t; + using clanguml::cli::cli_handler; + using clanguml::util::contains; + + std::string option_name; + std::string diagram_name; + + // First create initial config file + std::vector argv{"clang-uml", "--init"}; + + // Generate temporary file path + std::string config_file{std::tmpnam(nullptr)}; + + std::ostringstream ostr; + cli_handler cli{ostr, make_sstream_logger(ostr)}; + cli.set_config_path(config_file); + + auto res = cli.handle_options(argv.size(), argv.data()); + + REQUIRE(res == cli_flow_t::kExit); + + // Now add a template to the diagram + std::vector argv_add{ + "clang-uml", + "--add-diagram-from-template", + "class_context_tmpl", + "--template-var", + "diagram_name=A_context_diagram", + "--template-var", + "glob=test.cc", + "--template-var", + "using_namespace=ns1::ns2", + "--template-var", + "class_name=ns1::ns2::A", + "--template-var", + "namespace_name=ns1::ns2", + }; + + std::ostringstream ostr_add; + + cli_handler cli_add{ostr_add, make_sstream_logger(ostr_add)}; + cli_add.set_config_path(config_file); + + auto res_add = cli_add.handle_options(argv_add.size(), argv_add.data()); + REQUIRE(res_add == cli_flow_t::kExit); + + // Read contents of temp_file and check if they are valid + std::ifstream ifs(config_file); + std::string config_file_content{ + std::istreambuf_iterator(ifs), std::istreambuf_iterator()}; + + REQUIRE(config_file_content == + R"(compilation_database_dir: . +output_directory: docs/diagrams +diagrams: + example_class_diagram: + type: class + glob: + - src/*.cpp + using_namespace: + - myproject + include: + namespaces: + - myproject + exclude: + namespaces: + - myproject::detail + A_context_diagram: + type: class + glob: [test.cc] + using_namespace: ns1::ns2 + include: + context: [ns1::ns2::A] + namespaces: [ns1::ns2] +)"); +} + +TEST_CASE("Test cli handler properly reports error when adding config from " + "invalid template") +{ + using clanguml::cli::cli_flow_t; + using clanguml::cli::cli_handler; + using clanguml::util::contains; + + std::string option_name; + std::string diagram_name; + + // First create initial config file + std::vector argv{"clang-uml", "--init"}; + + // Generate temporary file path + std::string config_file{std::tmpnam(nullptr)}; + + std::ostringstream ostr; + cli_handler cli{ostr, make_sstream_logger(ostr)}; + cli.set_config_path(config_file); + + auto res = cli.handle_options(argv.size(), argv.data()); + + REQUIRE(res == cli_flow_t::kExit); + + // Now add a template to the diagram + std::vector argv_add{ + "clang-uml", + "--add-diagram-from-template", + "invalid_template_name", + "--template-var", + "diagram_name=A_context_diagram", + "--template-var", + "glob=test.cc", + "--template-var", + "using_namespace=ns1::ns2", + "--template-var", + "class_name=ns1::ns2::A", + "--template-var", + "namespace_name=ns1::ns2", + }; + + std::ostringstream ostr_add; + + cli_handler cli_add{ostr_add, make_sstream_logger(ostr_add)}; + cli_add.set_config_path(config_file); + + auto res_add = cli_add.handle_options(argv_add.size(), argv_add.data()); + REQUIRE(res_add == cli_flow_t::kError); +} + TEST_CASE("Test cli handler add_compile_flag and remove_compile_flag") { using clanguml::cli::cli_flow_t; @@ -209,4 +357,112 @@ TEST_CASE("Test cli handler puml config inheritance with render cmd") "mmdc -i output/{}.mmd -o output/{}.svg"); REQUIRE(cli.config.diagrams.at("class_main")->puml().after.at(0) == "' test comment"); -} \ No newline at end of file +} + +TEST_CASE("Test cli handler properly initializes new config file") +{ + using clanguml::cli::cli_flow_t; + using clanguml::cli::cli_handler; + using clanguml::util::contains; + + std::vector argv{"clang-uml", "--init"}; + + // Generate temporary file path + std::string config_file{std::tmpnam(nullptr)}; + + std::ostringstream ostr; + cli_handler cli{ostr, make_sstream_logger(ostr)}; + cli.set_config_path(config_file); + + auto res = cli.handle_options(argv.size(), argv.data()); + + // Read contents of temp_file and check if they are valid + std::ifstream ifs(config_file); + std::string config_file_content{ + std::istreambuf_iterator(ifs), std::istreambuf_iterator()}; + + REQUIRE(config_file_content == + R"(# Change to directory where compile_commands.json is +compilation_database_dir: . +# Change to directory where diagram should be written +output_directory: docs/diagrams +diagrams: + example_class_diagram: + type: class + glob: + - src/*.cpp + using_namespace: + - myproject + include: + namespaces: + - myproject + exclude: + namespaces: + - myproject::detail +)"); +} + +TEST_CASE("Test cli handler properly adds new diagram configs") +{ + using clanguml::cli::cli_flow_t; + using clanguml::cli::cli_handler; + using clanguml::util::contains; + + std::string option_name; + std::string diagram_name; + + SUBCASE("class") + { + option_name = "--add-class-diagram"; + diagram_name = "new_class_diagram"; + } + SUBCASE("sequence") + { + option_name = "--add-sequence-diagram"; + diagram_name = "new_sequence_diagram"; + } + SUBCASE("include") + { + option_name = "--add-include-diagram"; + diagram_name = "new_include_diagram"; + } + SUBCASE("package") + { + option_name = "--add-package-diagram"; + diagram_name = "new_package_diagram"; + } + + CAPTURE(option_name); + CAPTURE(diagram_name); + + std::vector argv{"clang-uml", "--init"}; + + // Generate temporary file path + std::string config_file{std::tmpnam(nullptr)}; + + std::ostringstream ostr; + cli_handler cli{ostr, make_sstream_logger(ostr)}; + cli.set_config_path(config_file); + + cli.handle_options(argv.size(), argv.data()); + + std::vector argv_add_diagram{ + "clang-uml", option_name.c_str(), diagram_name.c_str()}; + + std::ostringstream ostr_add_diagram; + cli_handler cli_add_diagram{ + ostr_add_diagram, make_sstream_logger(ostr_add_diagram)}; + cli_add_diagram.set_config_path(config_file); + + cli_add_diagram.handle_options( + argv_add_diagram.size(), argv_add_diagram.data()); + + // Read contents of temp_file and check if they are valid + std::ifstream ifs(config_file); + std::string config_file_content{ + std::istreambuf_iterator(ifs), std::istreambuf_iterator()}; + + // Verify that the config_file YAML file contains 'new_class_diagram' object + REQUIRE(clanguml::util::contains( + config_file_content, fmt::format("{}:", diagram_name))); +}