Added test case for package dependency generation
This commit is contained in:
@@ -119,7 +119,7 @@ void generator::generate_alias(const enum_ &e, std::ostream &ostr) const
|
||||
void generator::generate(const class_ &c, std::ostream &ostr) const
|
||||
{
|
||||
|
||||
const auto uns = m_config.using_namespace;
|
||||
const auto& uns = m_config.using_namespace;
|
||||
|
||||
std::string class_type{"class"};
|
||||
if (c.is_abstract())
|
||||
|
||||
@@ -41,6 +41,9 @@ void element::add_relationship(relationship &&cr)
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DBG("Adding relationship: '{}' - {} - '{}'", cr.destination(),
|
||||
to_string(cr.type()), full_name(true));
|
||||
|
||||
auto it = std::find(relationships_.begin(), relationships_.end(), cr);
|
||||
if (it == relationships_.end())
|
||||
relationships_.emplace_back(std::move(cr));
|
||||
|
||||
@@ -79,6 +79,37 @@ std::string generator::name(relationship_t r) const
|
||||
}
|
||||
}
|
||||
|
||||
void generator::generate_relationships(
|
||||
const package &p, std::ostream &ostr) const
|
||||
{
|
||||
const auto &uns = m_config.using_namespace;
|
||||
|
||||
// Generate this packages relationship
|
||||
if (m_config.should_include_relationship("dependency")) {
|
||||
LOG_DBG("LOOKING FOR RELATIONSHIPS IN PACKAGE: {}", p.full_name(false));
|
||||
for (const auto &r : p.relationships()) {
|
||||
std::stringstream relstr;
|
||||
try {
|
||||
relstr << m_model.to_alias(ns_relative(uns, r.destination()))
|
||||
<< " <.. "
|
||||
<< m_model.to_alias(ns_relative(uns, p.full_name(false)))
|
||||
<< '\n';
|
||||
ostr << relstr.str();
|
||||
}
|
||||
catch (error::uml_alias_missing &e) {
|
||||
LOG_ERROR("=== Skipping dependency relation from {} to {} due "
|
||||
"to: {}",
|
||||
p.full_name(false), r.destination(), e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process it's subpackages relationships
|
||||
for (auto subpackage = p.cbegin(); subpackage != p.cend(); subpackage++) {
|
||||
generate_relationships(**subpackage, ostr);
|
||||
}
|
||||
}
|
||||
|
||||
void generator::generate(const package &p, std::ostream &ostr) const
|
||||
{
|
||||
const auto uns = m_config.using_namespace;
|
||||
@@ -109,9 +140,6 @@ void generator::generate(const package &p, std::ostream &ostr) const
|
||||
<< "end note\n";
|
||||
}
|
||||
}
|
||||
//
|
||||
// // Print relationships
|
||||
// ostr << all_relations_str.str();
|
||||
}
|
||||
|
||||
void generator::generate(std::ostream &ostr) const
|
||||
@@ -138,6 +166,12 @@ void generator::generate(std::ostream &ostr) const
|
||||
}
|
||||
}
|
||||
|
||||
// Process package relationships
|
||||
for (const auto &p : m_model) {
|
||||
generate_relationships(*p, ostr);
|
||||
ostr << '\n';
|
||||
}
|
||||
|
||||
// Process aliases in any of the puml directives
|
||||
for (const auto &b : m_config.puml.after) {
|
||||
std::string note{b};
|
||||
|
||||
@@ -58,6 +58,8 @@ public:
|
||||
|
||||
void generate_alias(const package &c, std::ostream &ostr) const;
|
||||
|
||||
void generate_relationships(const package &p, std::ostream &ostr) const;
|
||||
|
||||
void generate(const package &e, std::ostream &ostr) const;
|
||||
|
||||
void generate(std::ostream &ostr) const;
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <type_safe/optional_ref.hpp>
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
||||
@@ -133,4 +133,16 @@ clanguml::package_diagram::model::diagram &translation_unit_context::diagram()
|
||||
return diagram_;
|
||||
}
|
||||
|
||||
void translation_unit_context::set_current_package(
|
||||
type_safe::optional_ref<model::package> p)
|
||||
{
|
||||
current_package_ = p;
|
||||
}
|
||||
|
||||
type_safe::optional_ref<model::package>
|
||||
translation_unit_context::get_current_package() const
|
||||
{
|
||||
return current_package_;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -63,6 +63,10 @@ public:
|
||||
|
||||
clanguml::package_diagram::model::diagram &diagram();
|
||||
|
||||
void set_current_package(type_safe::optional_ref<model::package> p);
|
||||
|
||||
type_safe::optional_ref<model::package> get_current_package() const;
|
||||
|
||||
private:
|
||||
// Current visitor namespace
|
||||
std::vector<std::string> namespace_;
|
||||
@@ -83,6 +87,8 @@ private:
|
||||
// Map of discovered template aliases (declared with 'using' keyword)
|
||||
std::map<std::string, type_safe::object_ref<const cppast::cpp_type>>
|
||||
alias_template_index_;
|
||||
|
||||
type_safe::optional_ref<model::package> current_package_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -90,7 +90,9 @@ void translation_unit_visitor::operator()(const cppast::cpp_entity &file)
|
||||
if (!ns_declaration.is_anonymous() &&
|
||||
!ns_declaration.is_inline()) {
|
||||
|
||||
auto package_path = ctx.get_namespace();
|
||||
std::vector<std::string> package_parent =
|
||||
ctx.get_namespace();
|
||||
auto package_path = package_parent;
|
||||
package_path.push_back(e.name());
|
||||
|
||||
auto usn =
|
||||
@@ -100,12 +102,15 @@ void translation_unit_visitor::operator()(const cppast::cpp_entity &file)
|
||||
auto p = std::make_unique<package>(
|
||||
ctx.config().using_namespace);
|
||||
remove_prefix(package_path, usn);
|
||||
package_path.pop_back();
|
||||
remove_prefix(package_parent, usn);
|
||||
|
||||
p->set_name(e.name());
|
||||
p->set_namespace(package_path);
|
||||
p->set_namespace(package_parent);
|
||||
|
||||
ctx.diagram().add_package(
|
||||
package_path, std::move(p));
|
||||
package_parent, std::move(p));
|
||||
ctx.set_current_package(
|
||||
ctx.diagram().get_package(package_path));
|
||||
}
|
||||
|
||||
ctx.push_namespace(e.name());
|
||||
@@ -155,20 +160,6 @@ void translation_unit_visitor::operator()(const cppast::cpp_entity &file)
|
||||
cx::util::fully_prefixed(ctx.get_namespace(), cls)))
|
||||
process_class_declaration(cls);
|
||||
}
|
||||
// else if (e.kind() == cppast::cpp_entity_kind::enum_t)
|
||||
// {
|
||||
// LOG_DBG("========== Visiting '{}' - {}",
|
||||
// cx::util::full_name(ctx.get_namespace(), e),
|
||||
// cppast::to_string(e.kind()));
|
||||
//
|
||||
// auto &enm = static_cast<const cppast::cpp_enum
|
||||
// &>(e);
|
||||
//
|
||||
// if (ctx.config().should_include(
|
||||
// cx::util::fully_prefixed(ctx.get_namespace(),
|
||||
// enm)))
|
||||
// process_enum_declaration(enm);
|
||||
// }
|
||||
else if (e.kind() == cppast::cpp_entity_kind::type_alias_t) {
|
||||
LOG_DBG("========== Visiting '{}' - {}",
|
||||
cx::util::full_name(ctx.get_namespace(), e),
|
||||
@@ -191,27 +182,6 @@ void translation_unit_visitor::operator()(const cppast::cpp_entity &file)
|
||||
cppast::to_string(e.kind()));
|
||||
|
||||
auto &at = static_cast<const cppast::cpp_alias_template &>(e);
|
||||
|
||||
// if (at.type_alias().underlying_type().kind()
|
||||
// ==
|
||||
// cppast::cpp_type_kind::unexposed_t) {
|
||||
// LOG_WARN("Template alias has unexposed
|
||||
// underlying type: {}",
|
||||
// static_cast<const
|
||||
// cppast::cpp_unexposed_type &>(
|
||||
// at.type_alias().underlying_type())
|
||||
// .name());
|
||||
// }
|
||||
// else {
|
||||
// class_ tinst =
|
||||
// build_template_instantiation(static_cast<
|
||||
// const
|
||||
// cppast::cpp_template_instantiation_type
|
||||
// &>(
|
||||
// at.type_alias().underlying_type()));
|
||||
//
|
||||
// ctx.diagram().add_class(std::move(tinst));
|
||||
// }
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -220,39 +190,14 @@ void translation_unit_visitor::process_class_declaration(
|
||||
const cppast::cpp_class &cls,
|
||||
type_safe::optional_ref<const cppast::cpp_template_specialization> tspec)
|
||||
{
|
||||
auto current_package = ctx.get_current_package();
|
||||
|
||||
return;
|
||||
/*
|
||||
class_ c{ctx.config().using_namespace};
|
||||
c.is_struct(cls.class_kind() == cppast::cpp_class_kind::struct_t);
|
||||
c.set_name(cx::util::full_name(ctx.get_namespace(), cls));
|
||||
|
||||
if (cls.comment().has_value())
|
||||
c.add_decorators(decorators::parse(cls.comment().value()));
|
||||
if (!current_package)
|
||||
return;
|
||||
|
||||
cppast::cpp_access_specifier_kind last_access_specifier =
|
||||
cppast::cpp_access_specifier_kind::cpp_private;
|
||||
|
||||
// Process class documentation comment
|
||||
if (cppast::is_templated(cls)) {
|
||||
if (cls.parent().value().comment().has_value())
|
||||
c.add_decorators(
|
||||
decorators::parse(cls.parent().value().comment().value()));
|
||||
}
|
||||
else {
|
||||
if (cls.comment().has_value())
|
||||
c.add_decorators(decorators::parse(cls.comment().value()));
|
||||
}
|
||||
|
||||
if (c.skip())
|
||||
return;
|
||||
|
||||
c.set_style(c.style_spec());
|
||||
|
||||
// Process class child entities
|
||||
if (c.is_struct())
|
||||
last_access_specifier = cppast::cpp_access_specifier_kind::cpp_public;
|
||||
|
||||
for (auto &child : cls) {
|
||||
if (child.kind() == cppast::cpp_entity_kind::access_specifier_t) {
|
||||
auto &as = static_cast<const cppast::cpp_access_specifier &>(child);
|
||||
@@ -260,92 +205,46 @@ void translation_unit_visitor::process_class_declaration(
|
||||
}
|
||||
else if (child.kind() == cppast::cpp_entity_kind::member_variable_t) {
|
||||
auto &mv = static_cast<const cppast::cpp_member_variable &>(child);
|
||||
process_field(mv, c, last_access_specifier);
|
||||
}
|
||||
else if (child.kind() == cppast::cpp_entity_kind::variable_t) {
|
||||
auto &mv = static_cast<const cppast::cpp_variable &>(child);
|
||||
process_static_field(mv, c, last_access_specifier);
|
||||
}
|
||||
else if (child.kind() == cppast::cpp_entity_kind::member_function_t) {
|
||||
auto &mf = static_cast<const cppast::cpp_member_function &>(child);
|
||||
process_method(mf, c, last_access_specifier);
|
||||
}
|
||||
else if (child.kind() == cppast::cpp_entity_kind::function_t) {
|
||||
auto &mf = static_cast<const cppast::cpp_function &>(child);
|
||||
process_static_method(mf, c, last_access_specifier);
|
||||
}
|
||||
else if (child.kind() == cppast::cpp_entity_kind::function_template_t) {
|
||||
auto &tm =
|
||||
static_cast<const cppast::cpp_function_template &>(child);
|
||||
process_template_method(tm, c, last_access_specifier);
|
||||
}
|
||||
else if (child.kind() == cppast::cpp_entity_kind::constructor_t) {
|
||||
auto &mc = static_cast<const cppast::cpp_constructor &>(child);
|
||||
process_constructor(mc, c, last_access_specifier);
|
||||
}
|
||||
else if (child.kind() == cppast::cpp_entity_kind::destructor_t) {
|
||||
auto &mc = static_cast<const cppast::cpp_destructor &>(child);
|
||||
process_destructor(mc, c, last_access_specifier);
|
||||
}
|
||||
else if (child.kind() == cppast::cpp_entity_kind::enum_t) {
|
||||
auto &en = static_cast<const cppast::cpp_enum &>(child);
|
||||
if (en.name().empty()) {
|
||||
// Here we only want to handle anonymous enums, regular nested
|
||||
// enums are handled in the file-level visitor
|
||||
process_anonymous_enum(en, c, last_access_specifier);
|
||||
}
|
||||
}
|
||||
else if (child.kind() == cppast::cpp_entity_kind::friend_t) {
|
||||
auto &fr = static_cast<const cppast::cpp_friend &>(child);
|
||||
|
||||
LOG_DBG("Found friend declaration: {}, {}", child.name(),
|
||||
child.scope_name() ? child.scope_name().value().name()
|
||||
: "<no-scope>");
|
||||
|
||||
process_friend(fr, c);
|
||||
}
|
||||
else if (cppast::is_friended(child)) {
|
||||
auto &fr =
|
||||
static_cast<const cppast::cpp_friend &>(child.parent().value());
|
||||
|
||||
LOG_DBG("Found friend template: {}", child.name());
|
||||
|
||||
process_friend(fr, c);
|
||||
process_field(mv, current_package, last_access_specifier);
|
||||
}
|
||||
else {
|
||||
LOG_DBG("Found some other class child: {} ({})", child.name(),
|
||||
cppast::to_string(child.kind()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process class bases
|
||||
for (auto &base : cls.bases()) {
|
||||
class_parent cp;
|
||||
cp.set_name(
|
||||
clanguml::cx::util::fully_prefixed(ctx.get_namespace(), base));
|
||||
cp.is_virtual(base.is_virtual());
|
||||
void translation_unit_visitor::process_field(
|
||||
const cppast::cpp_member_variable &mv,
|
||||
type_safe::optional_ref<model::package> p,
|
||||
cppast::cpp_access_specifier_kind as)
|
||||
{
|
||||
auto &type = cx::util::unreferenced(cppast::remove_cv(mv.type()));
|
||||
auto type_ns =
|
||||
util::split(cx::util::full_name(type, ctx.entity_index(), false), "::");
|
||||
type_ns.pop_back();
|
||||
|
||||
switch (base.access_specifier()) {
|
||||
case cppast::cpp_access_specifier_kind::cpp_private:
|
||||
cp.set_access(access_t::kPrivate);
|
||||
break;
|
||||
case cppast::cpp_access_specifier_kind::cpp_public:
|
||||
cp.set_access(access_t::kPublic);
|
||||
break;
|
||||
case cppast::cpp_access_specifier_kind::cpp_protected:
|
||||
cp.set_access(access_t::kProtected);
|
||||
break;
|
||||
default:
|
||||
cp.set_access(access_t::kPublic);
|
||||
if (type.kind() == cppast::cpp_type_kind::user_defined_t) {
|
||||
LOG_DBG("Processing user defined type field {} {}",
|
||||
cppast::to_string(type), mv.name());
|
||||
|
||||
if (!starts_with(ctx.get_namespace(), type_ns) &&
|
||||
!starts_with(type_ns, ctx.get_namespace())) {
|
||||
relationship r{relationship_t::kDependency,
|
||||
fmt::format("{}", fmt::join(type_ns, "::"))};
|
||||
p.value().add_relationship(std::move(r));
|
||||
}
|
||||
|
||||
LOG_DBG("Found base class {} for class {}", cp.name(), c.name());
|
||||
|
||||
c.add_parent(std::move(cp));
|
||||
}
|
||||
|
||||
ctx.diagram().add_class(std::move(c));
|
||||
*/
|
||||
else if (type.kind() == cppast::cpp_type_kind::template_instantiation_t) {
|
||||
// template_instantiation_added_as_aggregation =
|
||||
// process_field_with_template_instantiation(
|
||||
// mv, resolve_alias(type), c, m, as);
|
||||
LOG_DBG("Processing template instantiation type {} {}",
|
||||
cppast::to_string(type), mv.name());
|
||||
}
|
||||
else {
|
||||
LOG_DBG("Skipping field type: {}", cppast::to_string(type));
|
||||
}
|
||||
}
|
||||
|
||||
const cppast::cpp_type &translation_unit_visitor::resolve_alias(
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <package_diagram/model/package.h>
|
||||
#include <string>
|
||||
|
||||
namespace clanguml::package_diagram::visitor {
|
||||
@@ -53,6 +54,9 @@ public:
|
||||
type_safe::optional_ref<const cppast::cpp_template_specialization>
|
||||
tspec = nullptr);
|
||||
|
||||
void process_field(const cppast::cpp_member_variable &mv,
|
||||
type_safe::optional_ref<model::package> p,
|
||||
cppast::cpp_access_specifier_kind as);
|
||||
private:
|
||||
/**
|
||||
* Try to resolve a type instance into a type referenced through an alias.
|
||||
|
||||
@@ -50,34 +50,6 @@ TEST_CASE("t30001", "[test-case][package]")
|
||||
REQUIRE_THAT(puml, Contains("component [AA]"));
|
||||
REQUIRE_THAT(puml, Contains("component [AAA]"));
|
||||
|
||||
REQUIRE_THAT(puml, Equals(R"(@startuml
|
||||
' t30001 test package diagram
|
||||
component [A] as C_0000000561 {
|
||||
component [AA] as C_0000000562 {
|
||||
component [AAA] as C_0000000563 {
|
||||
}
|
||||
component [BBB] as C_0000000564 {
|
||||
}
|
||||
}
|
||||
component [BB] as C_0000000565 {
|
||||
}
|
||||
}
|
||||
|
||||
component [B] as C_0000000566 {
|
||||
component [AA] as C_0000000567 {
|
||||
component [AAA] as C_0000000568 {
|
||||
}
|
||||
component [BBB] as C_0000000569 {
|
||||
}
|
||||
}
|
||||
component [BB] as C_0000000570 {
|
||||
}
|
||||
}
|
||||
|
||||
note right of C_0000000563: A AAA note...
|
||||
@enduml
|
||||
)"));
|
||||
|
||||
save_puml(
|
||||
"./" + config.output_directory + "/" + diagram->name + ".puml", puml);
|
||||
}
|
||||
|
||||
18
tests/t30002/.clang-uml
Normal file
18
tests/t30002/.clang-uml
Normal file
@@ -0,0 +1,18 @@
|
||||
compilation_database_dir: ..
|
||||
output_directory: puml
|
||||
diagrams:
|
||||
t30002_package:
|
||||
type: package
|
||||
glob:
|
||||
- ../../tests/t30002/t30002.cc
|
||||
include:
|
||||
namespaces:
|
||||
- clanguml::t30002
|
||||
exclude:
|
||||
namespaces:
|
||||
- clanguml::t30002::detail
|
||||
using_namespace:
|
||||
- clanguml::t30002
|
||||
plantuml:
|
||||
before:
|
||||
- "' t30002 test package diagram"
|
||||
15
tests/t30002/t30002.cc
Normal file
15
tests/t30002/t30002.cc
Normal file
@@ -0,0 +1,15 @@
|
||||
#include <memory>
|
||||
|
||||
namespace clanguml {
|
||||
namespace t30002 {
|
||||
namespace A::AA::AAA {
|
||||
struct CA {
|
||||
};
|
||||
}
|
||||
namespace B::BB::BBB {
|
||||
struct CBA {
|
||||
A::AA::AAA::CA *ca_;
|
||||
};
|
||||
}
|
||||
} // namespace t30002
|
||||
} // namespace clanguml
|
||||
61
tests/t30002/test_case.h
Normal file
61
tests/t30002/test_case.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* tests/t30002/test_case.cc
|
||||
*
|
||||
* Copyright (c) 2021-2022 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.
|
||||
*/
|
||||
|
||||
TEST_CASE("t30002", "[test-case][package]")
|
||||
{
|
||||
auto [config, db] = load_config("t30002");
|
||||
|
||||
auto diagram = config.diagrams["t30002_package"];
|
||||
|
||||
REQUIRE(diagram->include.namespaces.size() == 1);
|
||||
REQUIRE_THAT(diagram->include.namespaces,
|
||||
VectorContains(std::string{"clanguml::t30002"}));
|
||||
|
||||
REQUIRE(diagram->exclude.namespaces.size() == 1);
|
||||
REQUIRE_THAT(diagram->exclude.namespaces,
|
||||
VectorContains(std::string{"clanguml::t30002::detail"}));
|
||||
|
||||
REQUIRE(diagram->should_include("clanguml::t30002::A"));
|
||||
REQUIRE(!diagram->should_include("clanguml::t30002::detail::C"));
|
||||
REQUIRE(!diagram->should_include("std::vector"));
|
||||
|
||||
REQUIRE(diagram->name == "t30002_package");
|
||||
|
||||
auto model = generate_package_diagram(db, diagram);
|
||||
|
||||
REQUIRE(model.name() == "t30002_package");
|
||||
|
||||
auto puml = generate_package_puml(diagram, model);
|
||||
AliasMatcher _A(puml);
|
||||
|
||||
REQUIRE_THAT(puml, StartsWith("@startuml"));
|
||||
REQUIRE_THAT(puml, EndsWith("@enduml\n"));
|
||||
|
||||
REQUIRE_THAT(puml, Contains("component [A]"));
|
||||
REQUIRE_THAT(puml, Contains("component [AA]"));
|
||||
REQUIRE_THAT(puml, Contains("component [AAA]"));
|
||||
|
||||
REQUIRE_THAT(puml, Contains("component [B]"));
|
||||
REQUIRE_THAT(puml, Contains("component [BB]"));
|
||||
REQUIRE_THAT(puml, Contains("component [BBB]"));
|
||||
|
||||
//REQUIRE_THAT(puml, IsDependency(_A("BBB"), _A("AAA")));
|
||||
|
||||
save_puml(
|
||||
"./" + config.output_directory + "/" + diagram->name + ".puml", puml);
|
||||
}
|
||||
@@ -177,6 +177,7 @@ using namespace clanguml::test::matchers;
|
||||
// Package diagram tests
|
||||
//
|
||||
#include "t30001/test_case.h"
|
||||
#include "t30002/test_case.h"
|
||||
|
||||
//
|
||||
// Other tests (e.g. configuration file)
|
||||
|
||||
Reference in New Issue
Block a user