From 381994df99a37c2c6746fa709c6bca43af4f53a3 Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Mon, 5 Sep 2022 23:34:42 +0200 Subject: [PATCH] Added test case for configurable type aliases --- .../plantuml/class_diagram_generator.cc | 14 +++-- .../visitor/translation_unit_visitor.cc | 5 +- src/common/clang_utils.cc | 1 + src/config/config.cc | 12 ++++ src/config/config.h | 2 + tests/t00049/.clang-uml | 15 +++++ tests/t00049/t00049.cc | 23 +++++++ tests/t00049/test_case.h | 62 +++++++++++++++++++ tests/test_cases.cc | 13 ++-- tests/test_cases.yaml | 3 + 10 files changed, 137 insertions(+), 13 deletions(-) create mode 100644 tests/t00049/.clang-uml create mode 100644 tests/t00049/t00049.cc create mode 100644 tests/t00049/test_case.h diff --git a/src/class_diagram/generators/plantuml/class_diagram_generator.cc b/src/class_diagram/generators/plantuml/class_diagram_generator.cc index 0f8ae605..77269e6b 100644 --- a/src/class_diagram/generators/plantuml/class_diagram_generator.cc +++ b/src/class_diagram/generators/plantuml/class_diagram_generator.cc @@ -73,7 +73,8 @@ void generator::generate_alias(const class_ &c, std::ostream &ostr) const assert(!full_name.empty()); - ostr << class_type << " \"" << render_name(full_name); + ostr << class_type << " \"" + << m_config.simplify_template_type(render_name(full_name)); ostr << "\" as " << c.alias() << '\n'; @@ -130,7 +131,8 @@ void generator::generate(const class_ &c, std::ostream &ostr) const if (m.is_static()) ostr << "{static} "; - std::string type{m.type()}; + std::string type{ + uns.relative(m_config.simplify_template_type(m.type()))}; ostr << plantuml_common::to_plantuml(m.access()) << m.name(); @@ -140,7 +142,8 @@ void generator::generate(const class_ &c, std::ostream &ostr) const std::vector params; std::transform(m.parameters().cbegin(), m.parameters().cend(), std::back_inserter(params), [this](const auto &mp) { - return mp.to_string(m_config.using_namespace()); + return m_config.simplify_template_type( + mp.to_string(m_config.using_namespace())); }); auto args_string = fmt::format("{}", fmt::join(params, ", ")); if (m_config.generate_method_arguments() == @@ -162,7 +165,7 @@ void generator::generate(const class_ &c, std::ostream &ostr) const if (m.is_defaulted()) ostr << " = default"; - ostr << " : " << uns.relative(type); + ostr << " : " << type; if (m_config.generate_links) { generate_link(ostr, m); @@ -231,7 +234,8 @@ void generator::generate(const class_ &c, std::ostream &ostr) const ostr << "{static} "; ostr << plantuml_common::to_plantuml(m.access()) << m.name() << " : " - << render_name(uns.relative(m.type())); + << render_name( + uns.relative(m_config.simplify_template_type(m.type()))); if (m_config.generate_links) { generate_link(ostr, m); diff --git a/src/class_diagram/visitor/translation_unit_visitor.cc b/src/class_diagram/visitor/translation_unit_visitor.cc index da30df25..9b432b46 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.cc +++ b/src/class_diagram/visitor/translation_unit_visitor.cc @@ -701,7 +701,8 @@ void translation_unit_visitor::process_method( return; class_method method{detail::access_specifier_to_access_t(mf.getAccess()), - util::trim(mf.getNameAsString()), mf.getReturnType().getAsString()}; + util::trim(mf.getNameAsString()), + common::to_string(mf.getReturnType(), mf.getASTContext())}; method.is_pure_virtual(mf.isPure()); method.is_virtual(mf.isVirtual()); @@ -865,7 +866,7 @@ void translation_unit_visitor::process_function_parameter( if (parameter.skip()) return; - parameter.set_type(p.getType().getAsString()); + parameter.set_type(common::to_string(p.getType(), p.getASTContext())); if (p.hasDefaultArg()) { const auto *default_arg = p.getDefaultArg(); diff --git a/src/common/clang_utils.cc b/src/common/clang_utils.cc index 5297cb1a..aaaeea0a 100644 --- a/src/common/clang_utils.cc +++ b/src/common/clang_utils.cc @@ -105,6 +105,7 @@ std::string to_string(const clang::QualType &type, const clang::ASTContext &ctx, // Remove trailing spaces after commas in template arguments clanguml::util::replace_all(result, ", ", ","); + clanguml::util::replace_all(result, "> >", ">>"); return result; } diff --git a/src/config/config.cc b/src/config/config.cc index 5a286b9d..019c2672 100644 --- a/src/config/config.cc +++ b/src/config/config.cc @@ -102,6 +102,18 @@ void inheritable_diagram_options::inherit( relative_to.override(parent.relative_to); } +std::string inheritable_diagram_options::simplify_template_type( + std::string full_name) const +{ + const auto &aliases = template_aliases(); + + for (const auto &[pattern, replacement] : aliases) { + util::replace_all(full_name, pattern, replacement); + } + + return full_name; +} + std::vector diagram::get_translation_units( const std::filesystem::path &root_directory) const { diff --git a/src/config/config.h b/src/config/config.h index e4f1699c..d4d82845 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -140,6 +140,8 @@ struct inheritable_diagram_options { option template_aliases{"template_aliases"}; void inherit(const inheritable_diagram_options &parent); + + std::string simplify_template_type(std::string full_name) const; }; struct diagram : public inheritable_diagram_options { diff --git a/tests/t00049/.clang-uml b/tests/t00049/.clang-uml new file mode 100644 index 00000000..53ad8a79 --- /dev/null +++ b/tests/t00049/.clang-uml @@ -0,0 +1,15 @@ +compilation_database_dir: .. +output_directory: puml +diagrams: + t00049_class: + type: class + using_namespace: clanguml::t00049 + template_aliases: + "std::vector": string_vector + "std::basic_string": thestring + "std::map": intmap + glob: + - ../../tests/t00049/t00049.cc + include: + namespaces: + - clanguml::t00049 \ No newline at end of file diff --git a/tests/t00049/t00049.cc b/tests/t00049/t00049.cc new file mode 100644 index 00000000..2bbd92b2 --- /dev/null +++ b/tests/t00049/t00049.cc @@ -0,0 +1,23 @@ +#include +#include +#include + +namespace clanguml { +namespace t00049 { +template struct A { + T a; + + T &get_a() { return a; } +}; + +struct R { + A> a_string; + A> a_vector_string; + A> a_int_map; + + A> get_int_map() { return a_int_map; } + + void set_int_map(A> &&int_map) { a_int_map = int_map; } +}; +} +} \ No newline at end of file diff --git a/tests/t00049/test_case.h b/tests/t00049/test_case.h new file mode 100644 index 00000000..55dbae28 --- /dev/null +++ b/tests/t00049/test_case.h @@ -0,0 +1,62 @@ +/** + * tests/t00049/test_case.h + * + * Copyright (c) 2021-2022 Bartek Kryza + * + * 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("t00049", "[test-case][class]") +{ + auto [config, db] = load_config("t00049"); + + auto diagram = config.diagrams["t00049_class"]; + + REQUIRE(diagram->name == "t00049_class"); + + auto model = generate_class_diagram(*db, diagram); + + REQUIRE(model->name() == "t00049_class"); + + auto puml = generate_class_puml(diagram, *model); + AliasMatcher _A(puml); + + REQUIRE_THAT(puml, StartsWith("@startuml")); + REQUIRE_THAT(puml, EndsWith("@enduml\n")); + + // Check if all classes exist + REQUIRE_THAT(puml, IsClass(_A("R"))); + + // Check if class templates exist + REQUIRE_THAT(puml, IsClassTemplate("A", "T")); + + // Check if all methods exist + REQUIRE_THAT(puml, (IsMethod("get_int_map", "A"))); + REQUIRE_THAT(puml, + (IsMethod("set_int_map", "void", "A && int_map"))); + + // Check if all fields exist + REQUIRE_THAT(puml, (IsField("a_string", "A"))); + REQUIRE_THAT( + puml, (IsField("a_vector_string", "A"))); + REQUIRE_THAT(puml, (IsField("a_int_map", "A"))); + + // Check if all relationships exist + REQUIRE_THAT( + puml, IsInstantiation(_A("A"), _A("A>"))); + REQUIRE_THAT(puml, IsInstantiation(_A("A"), _A("A"))); + REQUIRE_THAT(puml, IsInstantiation(_A("A"), _A("A"))); + + save_puml( + "./" + config.output_directory() + "/" + diagram->name + ".puml", puml); +} \ No newline at end of file diff --git a/tests/test_cases.cc b/tests/test_cases.cc index ff8a159c..5cf6c2b6 100644 --- a/tests/test_cases.cc +++ b/tests/test_cases.cc @@ -191,9 +191,9 @@ void save_puml(const std::string &path, const std::string &puml) using namespace clanguml::test::matchers; -// -// Class diagram tests -// +/// +/// Class diagram tests +/// #include "t00002/test_case.h" #include "t00003/test_case.h" #include "t00004/test_case.h" @@ -241,13 +241,14 @@ using namespace clanguml::test::matchers; #include "t00046/test_case.h" #include "t00047/test_case.h" #include "t00048/test_case.h" +#include "t00049/test_case.h" //// //// Sequence diagram tests //// #include "t20001/test_case.h" #include "t20002/test_case.h" -// + //// //// Package diagram tests //// @@ -259,14 +260,14 @@ using namespace clanguml::test::matchers; #include "t30006/test_case.h" #include "t30007/test_case.h" #include "t30008/test_case.h" -// + //// //// Include diagram tests //// #include "t40001/test_case.h" #include "t40002/test_case.h" #include "t40003/test_case.h" -// + //// //// Other tests (e.g. configuration file) //// diff --git a/tests/test_cases.yaml b/tests/test_cases.yaml index 8315a996..1ca8818f 100644 --- a/tests/test_cases.yaml +++ b/tests/test_cases.yaml @@ -141,6 +141,9 @@ test_cases: - name: t00048 title: Test case for unique entity id with multiple translation units description: + - name: t00049 + title: Test case configurable type aliases + description: Sequence diagrams: - name: t20001 title: Basic sequence diagram test case