Improved test coverage (#287)

This commit is contained in:
Bartek Kryza
2024-06-09 21:09:59 +02:00
parent 82846dcc27
commit 94f5f445ee
21 changed files with 252 additions and 83 deletions

View File

@@ -45,7 +45,8 @@ jobs:
- name: Run coverage
run: |
lcov -c -d debug -o coverage.info
lcov -e coverage.info "$PWD/src/*" -o coverage-src.info
lcov -r coverage.info -o coverage-src.info "$PWD/src/main.cc" "$PWD/src/common/generators/generators.cc"
lcov -e coverage-src.info -o coverage-src.info "$PWD/src/*"
lcov -l coverage-src.info
- name: Upload coverage
uses: codecov/codecov-action@v3

View File

@@ -50,7 +50,7 @@ DESTDIR ?=
.PHONY: clean
clean:
rm -rf debug release debug_tidy
rm -rf debug release debug_tidy coverage.info coverage-src.info
debug/CMakeLists.txt:
cmake -S . -B debug \
@@ -117,7 +117,8 @@ test_release: release
coverage_report: test
lcov -c -d debug -o coverage.info
lcov -e coverage.info "${PWD}/src/*" -o coverage-src.info
lcov -r coverage.info -o coverage-src.info "${PWD}/src/main.cc" "${PWD}/src/common/generators/generators.cc"
lcov -e coverage-src.info -o coverage-src.info "${PWD}/src/*"
lcov -l coverage-src.info
genhtml coverage-src.info --output-directory debug/coverage_html

View File

@@ -166,6 +166,8 @@ void generate_diagram_impl(const std::string &name,
runtime_config.output_directory, name, diagram, model);
}
// Convert plantuml or mermaid to an image using command provided
// in the command line arguments
if (runtime_config.render_diagrams) {
render_diagram(generator_type, diagram);
}

View File

@@ -23,6 +23,12 @@
namespace clanguml::common::generators {
progress_indicator::progress_indicator()
: progress_indicator(std::cout)
{
}
progress_indicator::progress_indicator(std::ostream &ostream)
: ostream_(ostream)
{
progress_bars_.set_option(indicators::option::HideBarWhenComplete{false});
}
@@ -36,6 +42,7 @@ void progress_indicator::add_progress_bar(
const auto kPrefixTextWidth = 25U;
auto bar = std::make_shared<indicators::ProgressBar>(
indicators::option::Stream{ostream_},
indicators::option::BarWidth{kBarWidth},
indicators::option::ForegroundColor{color},
indicators::option::ShowElapsedTime{true},
@@ -78,6 +85,7 @@ void progress_indicator::increment(const std::string &name)
bar.set_progress((p.progress * kASTTraverseProgressPercent) / p.max);
bar.set_option(indicators::option::PostfixText{
fmt::format("{}/{}", p.progress, p.max)});
progress_bars_mutex_.unlock();
}
@@ -106,6 +114,8 @@ void progress_indicator::complete(const std::string &name)
auto &p = progress_bar_index_.at(name);
auto &bar = progress_bars_[p.index];
p.progress = p.max;
bar.set_progress(kCompleteProgressPercent);
#if _MSC_VER
@@ -124,6 +134,12 @@ void progress_indicator::complete(const std::string &name)
void progress_indicator::fail(const std::string &name)
{
progress_bars_mutex_.lock();
if (progress_bar_index_.count(name) == 0) {
progress_bars_mutex_.unlock();
return;
}
auto &p = progress_bar_index_.at(name);
auto &bar = progress_bars_[p.index];

View File

@@ -45,6 +45,8 @@ public:
progress_indicator();
progress_indicator(std::ostream &ostream);
/**
* Add a new progress bar to the indicator set
*
@@ -86,5 +88,6 @@ private:
std::vector<std::shared_ptr<indicators::ProgressBar>> bars_;
std::map<std::string, progress_state> progress_bar_index_;
std::mutex progress_bars_mutex_;
std::ostream &ostream_;
};
} // namespace clanguml::common::generators

View File

@@ -17,6 +17,7 @@
*/
#include "clang_visitor.h"
#include "util/util.h"
#if LLVM_VERSION_MAJOR > 17
#define CLANG_UML_LLVM_COMMENT_KIND(COMMENT_KIND) \
@@ -191,7 +192,7 @@ void clang_visitor::visit_param_command(
inja::json param = inja::json::object();
param["name"] = name;
param["description"] = description;
param["description"] = util::trim(description);
cmt["param"].push_back(std::move(param));
}
}
@@ -226,7 +227,7 @@ void clang_visitor::visit_tparam_command(
inja::json param = inja::json::object();
param["name"] = name;
param["description"] = description;
param["description"] = util::trim(description);
cmt["tparam"].push_back(std::move(param));
}
}

View File

@@ -98,25 +98,6 @@ void generator::generate(const source_file &f, std::ostream &ostr) const
}
}
void generator::generate_notes(
std::ostream &ostr, const common::model::diagram_element &element) const
{
const auto &config =
common_generator<diagram_config, diagram_model>::config();
for (const auto &decorator : element.decorators()) {
auto note = std::dynamic_pointer_cast<decorators::note>(decorator);
if (note && note->applies_to_diagram(config.name)) {
auto note_id_str = fmt::format("N_{}", note_id_++);
ostr << indent(1) << note_id_str << "(" << note->text << ")\n";
ostr << indent(1) << note_id_str << "-.-" << element.alias()
<< '\n';
}
}
}
void generator::generate_diagram(std::ostream &ostr) const
{
// Generate files and folders

View File

@@ -79,15 +79,6 @@ public:
*/
void generate_relationships(const source_file &p, std::ostream &ostr) const;
/**
* @brief Generate notes attached to files
*
* @param ostr Output stream
* @param element Element with a note
*/
void generate_notes(std::ostream &ostr,
const common::model::diagram_element &element) const override;
/**
* @brief Generate diagram element
*
@@ -95,9 +86,6 @@ public:
* @param parent Output stream
*/
void generate(const source_file &e, std::ostream &ostr) const;
private:
mutable uint64_t note_id_{0UL};
};
} // namespace clanguml::include_diagram::generators::mermaid

View File

@@ -89,25 +89,6 @@ void diagram::add_file(std::unique_ptr<common::model::source_file> &&f)
add_element(p, std::move(f));
}
std::string diagram::to_alias(const std::string &full_name) const
{
LOG_DBG("Looking for alias for {}", full_name);
auto path = common::model::filesystem_path{full_name};
if (path.is_empty())
throw error::uml_alias_missing(
fmt::format("Missing alias for '{}'", path.to_string()));
auto source_file = get_element<common::model::source_file>(path);
if (!source_file)
throw error::uml_alias_missing(
fmt::format("Missing alias for '{}'", path.to_string()));
return source_file.value().alias();
}
const common::reference_vector<common::model::source_file> &
diagram::files() const
{

View File

@@ -103,17 +103,6 @@ public:
*/
template <typename ElementT> opt_ref<ElementT> find(eid_t id) const;
/**
* @brief Convert element id to PlantUML alias.
*
* @todo This method does not belong here - refactor to PlantUML specific
* code.
*
* @param full_name Full name of the diagram element.
* @return PlantUML alias.
*/
std::string to_alias(const std::string &full_name) const;
/**
* @brief Get list of references to files in the diagram model.
*

View File

@@ -94,7 +94,8 @@ set(TEST_NAMES
test_cli_handler
test_filters
test_thread_pool_executor
test_query_driver_output_extractor)
test_query_driver_output_extractor
test_progress_indicator)
foreach(TEST_NAME ${TEST_NAMES})
add_executable(${TEST_NAME})

View File

@@ -9,7 +9,13 @@ diagrams:
- clanguml::t00020
plantuml:
after:
- '@A(ProductA1) <.. @A(Factory1)'
- '@A(ProductB1) <.. @A(Factory1)'
- '{{ alias("ProductA2") }} <.. {{ alias("Factory2") }}'
- '{{ alias("ProductB2") }} <.. {{ alias("Factory2") }}'
- '@A(Factory1) ..> @A(ProductA1)'
- '@A(Factory1) ..> @A(ProductB1)'
- '{{ alias("Factory2") }} ..> {{ alias("ProductA2") }}'
- '{{ alias("Factory2") }} ..> {{ alias("ProductB2") }}'
mermaid:
after:
- '@A(Factory1) ..> @A(ProductA1)'
- '@A(Factory1) ..> @A(ProductB1)'
- '{{ alias("Factory2") }} ..> {{ alias("ProductA2") }}'
- '{{ alias("Factory2") }} ..> {{ alias("ProductB2") }}'

View File

@@ -23,15 +23,29 @@ TEST_CASE("t00020")
auto [config, db, diagram, model] =
CHECK_CLASS_MODEL("t00020", "t00020_class");
CHECK_CLASS_DIAGRAM(config, diagram, *model, [](const auto &src) {
REQUIRE(IsAbstractClass(src, "AbstractFactory"));
REQUIRE(IsAbstractClass(src, "ProductA"));
REQUIRE(IsAbstractClass(src, "ProductB"));
REQUIRE(IsClass(src, "ProductA1"));
REQUIRE(IsClass(src, "ProductA2"));
REQUIRE(IsClass(src, "ProductB1"));
REQUIRE(IsClass(src, "ProductB2"));
REQUIRE(IsClass(src, "Factory1"));
REQUIRE(IsClass(src, "Factory2"));
});
CHECK_CLASS_DIAGRAM(
config, diagram, *model,
[](const auto &src) {
REQUIRE(IsAbstractClass(src, "AbstractFactory"));
REQUIRE(IsAbstractClass(src, "ProductA"));
REQUIRE(IsAbstractClass(src, "ProductB"));
REQUIRE(IsClass(src, "ProductA1"));
REQUIRE(IsClass(src, "ProductA2"));
REQUIRE(IsClass(src, "ProductB1"));
REQUIRE(IsClass(src, "ProductB2"));
REQUIRE(IsClass(src, "Factory1"));
REQUIRE(IsClass(src, "Factory2"));
},
[](const plantuml_t &src) {
REQUIRE(IsDependency(src, "Factory1", "ProductA1"));
REQUIRE(IsDependency(src, "Factory1", "ProductB1"));
REQUIRE(IsDependency(src, "Factory2", "ProductA2"));
REQUIRE(IsDependency(src, "Factory2", "ProductB2"));
},
[](const mermaid_t &src) {
REQUIRE(IsDependency(src, "Factory1", "ProductA1"));
REQUIRE(IsDependency(src, "Factory1", "ProductB1"));
REQUIRE(IsDependency(src, "Factory2", "ProductA2"));
REQUIRE(IsDependency(src, "Factory2", "ProductB2"));
});
}

View File

@@ -11,4 +11,9 @@ diagrams:
dependencies:
- clanguml::t00043::dependencies::J
relationships:
- dependency
- dependency
exclude:
dependants:
- clanguml::t00043::dependants::EE
dependencies:
- clanguml::t00043::dependencies::II

View File

@@ -24,6 +24,14 @@ struct E {
void e(D *d) { }
};
struct EE {
void ee(E *e) { }
};
struct EEE {
void eee(EE *e) { }
};
struct F { };
} // namespace dependants
@@ -46,8 +54,18 @@ struct I {
void i(H *h) { }
};
struct II;
struct III {
void iii(II *i) { }
};
struct II {
void ii() { }
};
struct J {
void i(I *i) { }
void ii(II *ii) { }
};
} // namespace dependencies

View File

@@ -30,6 +30,9 @@ TEST_CASE("t00043")
REQUIRE(IsClass(src, {"dependants", "D"}));
REQUIRE(IsClass(src, {"dependants", "BB"}));
REQUIRE(IsClass(src, {"dependants", "E"}));
REQUIRE(!IsClass(src, {"dependants", "EE"}));
REQUIRE(!IsClass(src, {"dependants", "EEE"}));
REQUIRE(IsDependency(src, {"dependants", "B"}, {"dependants", "A"}));
REQUIRE(IsDependency(src, {"dependants", "BB"}, {"dependants", "A"}));
REQUIRE(IsDependency(src, {"dependants", "C"}, {"dependants", "B"}));
@@ -40,6 +43,8 @@ TEST_CASE("t00043")
REQUIRE(IsClass(src, {"dependencies", "GG"}));
REQUIRE(IsClass(src, {"dependencies", "H"}));
REQUIRE(!IsClass(src, {"dependencies", "HH"}));
REQUIRE(!IsClass(src, {"dependencies", "II"}));
REQUIRE(!IsClass(src, {"dependencies", "III"}));
REQUIRE(
IsDependency(src, {"dependencies", "J"}, {"dependencies", "I"}));

View File

@@ -81,6 +81,11 @@ enum class E { E1, E2, E3 };
template <typename T, typename V, int N> class F {
T t[N];
V v;
/// \brief Set value of v
///
/// \param v_ New value for v
V set_value(V v_) const { return v = v_; }
};
/// This is a short description of class G.

View File

@@ -68,12 +68,21 @@ TEST_CASE("Test compilation_database should work")
.make_preferred()
.string()));
REQUIRE_EQ(db->guess_language_from_filename("file.cpp"), "c++");
REQUIRE_EQ(db->guess_language_from_filename("file.cc"), "c++");
REQUIRE_EQ(db->guess_language_from_filename("file.c"), "c");
auto ccs = db->getAllCompileCommands();
REQUIRE(contains(ccs.at(0).CommandLine, "-Wno-error"));
REQUIRE(contains(ccs.at(0).CommandLine, "-Wno-unknown-warning-option"));
REQUIRE(
!contains(ccs.at(0).CommandLine, "-Wno-deprecated-declarations"));
REQUIRE_EQ(
db->count_matching_commands({"./src/class_diagram/model/class.cc"}),
1);
}
catch (clanguml::error::compilation_database_error &e) {
REQUIRE(false);

View File

@@ -22,6 +22,7 @@
#include "class_diagram/model/class.h"
#include "common/model/namespace.h"
#include "common/model/package.h"
#include "common/model/path.h"
#include "common/model/template_parameter.h"
TEST_CASE("Test namespace_")
@@ -468,4 +469,19 @@ TEST_CASE("Test common::model::package full_name")
CHECK(pkg.full_name(false) == "A.B.C:D");
CHECK(pkg.full_name(true) == ":D");
}
}
TEST_CASE("Test path_type")
{
using namespace clanguml::common::model;
REQUIRE_EQ(to_string(path_type::kModule), "module");
REQUIRE_EQ(to_string(path_type::kFilesystem), "directory");
REQUIRE_EQ(to_string(path_type::kNamespace), "namespace");
// Check that assiging a namespace path to a filesystem path throws
auto p1 = path{"A::B::C", path_type::kNamespace};
auto p2 = path{"A/B/C/D", path_type::kFilesystem};
REQUIRE_THROWS_AS(p1 = p2, std::runtime_error);
}

View File

@@ -0,0 +1,105 @@
/**
* @file tests/test_progress_indicator.cc
*
* Copyright (c) 2021-2024 Bartek Kryza <bkryza@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include "doctest/doctest.h"
#include "common/generators/progress_indicator.h"
#include "util/util.h"
TEST_CASE("Test progress indicator")
{
using namespace clanguml::common::generators;
using namespace std::string_literals;
std::stringstream sstr;
progress_indicator pi{sstr};
pi.add_progress_bar("One", 100, indicators::Color::green);
// Check if progress indicator has been displayed on the terminal
pi.increment("One");
pi.complete("One");
pi.stop();
std::vector<std::string> output_lines;
std::string line;
while (std::getline(sstr, line, '\r'))
output_lines.emplace_back(std::move(line));
REQUIRE_EQ(output_lines[0], "");
REQUIRE_EQ(output_lines[1],
"One [█----------------------------------] "
"[00:00s] 0/100");
REQUIRE_EQ(output_lines[2],
"One [█----------------------------------] "
"[00m:00s] 1/100");
REQUIRE_EQ(output_lines[3],
"One [███████████████████████████████████] "
"[00m:00s] 100/100 ✔");
}
TEST_CASE("Test progress indicator fail")
{
using namespace clanguml::common::generators;
using namespace std::string_literals;
std::stringstream sstr;
progress_indicator pi{sstr};
pi.add_progress_bar("One", 100, indicators::Color::green);
// Check if progress indicator has been displayed on the terminal
pi.increment("One");
pi.increment("Two"); // This shouldn't lock the progress bar or change it
pi.complete("Two"); // This shouldn't lock the progress bar or change it
pi.fail("Two"); // This shouldn't lock the progress bar or change it
pi.fail("One");
pi.stop();
std::vector<std::string> output_lines;
std::string line;
while (std::getline(sstr, line, '\r'))
output_lines.emplace_back(std::move(line));
REQUIRE_EQ(output_lines[0], "");
REQUIRE_EQ(output_lines[1],
"One [█----------------------------------] "
"[00:00s] 0/100");
REQUIRE_EQ(output_lines[2],
"One [█----------------------------------] "
"[00m:00s] 1/100");
REQUIRE_EQ(output_lines[3],
"One [█----------------------------------] "
"[00m:00s] 1/100 ✗");
}

View File

@@ -40,4 +40,26 @@ TEST_CASE("Test eid_t")
REQUIRE(global_id.is_global());
REQUIRE(local_id != global_id);
REQUIRE(local_id != 101);
}
TEST_CASE("Test to_string")
{
using namespace clanguml::common;
std::string t1{"abcd"};
REQUIRE_EQ(t1, to_string(t1));
string_or_regex t2{"abcdef"};
REQUIRE_EQ(to_string(t2), "abcdef");
REQUIRE_EQ(to_string(t2), t2.to_string());
string_or_regex t3{std::regex{"ab.*"}, "ab.*"};
REQUIRE_EQ(to_string(t3), "ab.*");
REQUIRE_EQ(to_string(t3), t3.to_string());
REQUIRE_EQ(to_string(generator_type_t::plantuml), "plantuml");
REQUIRE_EQ(to_string(generator_type_t::mermaid), "mermaid");
REQUIRE_EQ(to_string(generator_type_t::json), "json");
}