From 0d15d09de29c8cce5e77ccc170147d7349005a34 Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Wed, 14 Dec 2022 22:00:27 +0100 Subject: [PATCH] Added test case for filtering methods based on access specifier in sequence diagrams --- .../visitor/translation_unit_visitor.cc | 38 ++++------------ src/common/clang_utils.cc | 21 +++++++++ src/common/clang_utils.h | 3 ++ .../visitor/translation_unit_visitor.cc | 4 ++ tests/t20027/.clang-uml | 16 +++++++ tests/t20027/t20027.cc | 22 ++++++++++ tests/t20027/test_case.h | 44 +++++++++++++++++++ tests/test_cases.cc | 1 + tests/test_cases.yaml | 3 ++ 9 files changed, 122 insertions(+), 30 deletions(-) create mode 100644 tests/t20027/.clang-uml create mode 100644 tests/t20027/t20027.cc create mode 100644 tests/t20027/test_case.h diff --git a/src/class_diagram/visitor/translation_unit_visitor.cc b/src/class_diagram/visitor/translation_unit_visitor.cc index 3aff1883..59cefb23 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.cc +++ b/src/class_diagram/visitor/translation_unit_visitor.cc @@ -41,28 +41,6 @@ using clanguml::common::model::namespace_; using clanguml::common::model::relationship; using clanguml::common::model::relationship_t; -namespace detail { -access_t access_specifier_to_access_t(clang::AccessSpecifier access_specifier) -{ - auto access = access_t::kPublic; - switch (access_specifier) { - case clang::AccessSpecifier::AS_public: - access = access_t::kPublic; - break; - case clang::AccessSpecifier::AS_private: - access = access_t::kPrivate; - break; - case clang::AccessSpecifier::AS_protected: - access = access_t::kProtected; - break; - default: - break; - } - - return access; -} -} - translation_unit_visitor::translation_unit_visitor(clang::SourceManager &sm, clanguml::class_diagram::model::diagram &diagram, const clanguml::config::class_diagram &config) @@ -668,7 +646,7 @@ void translation_unit_visitor::process_class_bases( cp.is_virtual(base.isVirtual()); cp.set_access( - detail::access_specifier_to_access_t(base.getAccessSpecifier())); + common::access_specifier_to_access_t(base.getAccessSpecifier())); LOG_DBG("Found base class {} [{}] for class {}", cp.name(), cp.id(), c.name()); @@ -725,7 +703,7 @@ void translation_unit_visitor::process_template_specialization_children( if (enum_decl->getNameAsString().empty()) { for (const auto *enum_const : enum_decl->enumerators()) { - class_member m{detail::access_specifier_to_access_t( + class_member m{common::access_specifier_to_access_t( enum_decl->getAccess()), enum_const->getNameAsString(), "enum"}; c.add_member(std::move(m)); @@ -787,7 +765,7 @@ void translation_unit_visitor::process_class_children( if (enum_decl->getNameAsString().empty()) { for (const auto *enum_const : enum_decl->enumerators()) { - class_member m{detail::access_specifier_to_access_t( + class_member m{common::access_specifier_to_access_t( enum_decl->getAccess()), enum_const->getNameAsString(), "enum"}; c.add_member(std::move(m)); @@ -818,7 +796,7 @@ void translation_unit_visitor::process_friend( if (diagram().should_include(friend_type_name)) { relationship r{relationship_t::kFriendship, common::to_id(*friend_type->getAsRecordDecl()), - detail::access_specifier_to_access_t(f.getAccess()), + common::access_specifier_to_access_t(f.getAccess()), "<>"}; c.add_relationship(std::move(r)); @@ -835,7 +813,7 @@ void translation_unit_visitor::process_method( if (mf.isDefaulted() && !mf.isExplicitlyDefaulted()) return; - class_method method{detail::access_specifier_to_access_t(mf.getAccess()), + class_method method{common::access_specifier_to_access_t(mf.getAccess()), util::trim(mf.getNameAsString()), common::to_string(mf.getReturnType(), mf.getASTContext())}; @@ -869,7 +847,7 @@ void translation_unit_visitor::process_template_method( !mf.getTemplatedDecl()->isExplicitlyDefaulted()) return; - class_method method{detail::access_specifier_to_access_t(mf.getAccess()), + class_method method{common::access_specifier_to_access_t(mf.getAccess()), util::trim(mf.getNameAsString()), mf.getTemplatedDecl()->getReturnType().getAsString()}; @@ -1133,7 +1111,7 @@ void translation_unit_visitor::process_static_field( type_name = "<>"; class_member field{ - detail::access_specifier_to_access_t(field_declaration.getAccess()), + common::access_specifier_to_access_t(field_declaration.getAccess()), field_declaration.getNameAsString(), type_name}; field.is_static(true); @@ -1981,7 +1959,7 @@ void translation_unit_visitor::process_field( const auto field_name = field_declaration.getNameAsString(); class_member field{ - detail::access_specifier_to_access_t(field_declaration.getAccess()), + common::access_specifier_to_access_t(field_declaration.getAccess()), field_name, common::to_string( field_type, field_declaration.getASTContext(), false)}; diff --git a/src/common/clang_utils.cc b/src/common/clang_utils.cc index 45c95493..c8f93d50 100644 --- a/src/common/clang_utils.cc +++ b/src/common/clang_utils.cc @@ -22,6 +22,27 @@ namespace clanguml::common { +model::access_t access_specifier_to_access_t( + clang::AccessSpecifier access_specifier) +{ + auto access = model::access_t::kPublic; + switch (access_specifier) { + case clang::AccessSpecifier::AS_public: + access = model::access_t::kPublic; + break; + case clang::AccessSpecifier::AS_private: + access = model::access_t::kPrivate; + break; + case clang::AccessSpecifier::AS_protected: + access = model::access_t::kProtected; + break; + default: + break; + } + + return access; +} + std::optional get_enclosing_namespace( const clang::DeclContext *decl) { diff --git a/src/common/clang_utils.h b/src/common/clang_utils.h index 09441e3c..5007bf9a 100644 --- a/src/common/clang_utils.h +++ b/src/common/clang_utils.h @@ -31,6 +31,9 @@ class NamespaceDecl; } namespace clanguml::common { +model::access_t access_specifier_to_access_t( + clang::AccessSpecifier access_specifier); + std::string get_tag_name(const clang::TagDecl &declaration); template std::string get_qualified_name(const T &declaration) diff --git a/src/sequence_diagram/visitor/translation_unit_visitor.cc b/src/sequence_diagram/visitor/translation_unit_visitor.cc index c5d34d77..3081677a 100644 --- a/src/sequence_diagram/visitor/translation_unit_visitor.cc +++ b/src/sequence_diagram/visitor/translation_unit_visitor.cc @@ -991,6 +991,10 @@ bool translation_unit_visitor::process_class_method_call_expression( diagram().should_include(callee_decl->getQualifiedNameAsString()))) return false; + if (!diagram().should_include( + common::access_specifier_to_access_t(method_decl->getAccess()))) + return false; + m.set_to(method_decl->getID()); m.set_message_name(method_decl->getNameAsString()); m.set_return_type( diff --git a/tests/t20027/.clang-uml b/tests/t20027/.clang-uml new file mode 100644 index 00000000..a107bba2 --- /dev/null +++ b/tests/t20027/.clang-uml @@ -0,0 +1,16 @@ +compilation_database_dir: .. +output_directory: puml +diagrams: + t20027_sequence: + type: sequence + glob: + - ../../tests/t20027/t20027.cc + include: + namespaces: + - clanguml::t20027 + access: + - public + using_namespace: + - clanguml::t20027 + start_from: + - function: "clanguml::t20027::tmain()" \ No newline at end of file diff --git a/tests/t20027/t20027.cc b/tests/t20027/t20027.cc new file mode 100644 index 00000000..e3ac5375 --- /dev/null +++ b/tests/t20027/t20027.cc @@ -0,0 +1,22 @@ +namespace clanguml { +namespace t20027 { + +class A { +public: + void a() { aa(); } + +protected: + void aa() { aaa(); } + +private: + void aaa() { } +}; + +void tmain() +{ + A a; + + a.a(); +} +} +} \ No newline at end of file diff --git a/tests/t20027/test_case.h b/tests/t20027/test_case.h new file mode 100644 index 00000000..70c54825 --- /dev/null +++ b/tests/t20027/test_case.h @@ -0,0 +1,44 @@ +/** + * tests/t20027/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("t20027", "[test-case][sequence]") +{ + auto [config, db] = load_config("t20027"); + + auto diagram = config.diagrams["t20027_sequence"]; + + REQUIRE(diagram->name == "t20027_sequence"); + + auto model = generate_sequence_diagram(*db, diagram); + + REQUIRE(model->name() == "t20027_sequence"); + + auto puml = generate_sequence_puml(diagram, *model); + AliasMatcher _A(puml); + + REQUIRE_THAT(puml, StartsWith("@startuml")); + REQUIRE_THAT(puml, EndsWith("@enduml\n")); + + // Check if all calls exist + REQUIRE_THAT(puml, HasCall(_A("tmain()"), _A("A"), "a()")); + REQUIRE_THAT(puml, !HasCall(_A("A"), _A("A"), "aa()")); + REQUIRE_THAT(puml, !HasCall(_A("A"), _A("A"), "aaa()")); + + 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 e3492193..b4fca775 100644 --- a/tests/test_cases.cc +++ b/tests/test_cases.cc @@ -273,6 +273,7 @@ using namespace clanguml::test::matchers; #include "t20024/test_case.h" #include "t20025/test_case.h" #include "t20026/test_case.h" +#include "t20027/test_case.h" /// /// Package diagram tests diff --git a/tests/test_cases.yaml b/tests/test_cases.yaml index f660db17..0c59ebca 100644 --- a/tests/test_cases.yaml +++ b/tests/test_cases.yaml @@ -226,6 +226,9 @@ test_cases: - name: t20026 title: Virtual method call sequence diagram test case description: + - name: t20027 + title: Filter call expressions based on access test case + description: Package diagrams: - name: t30001 title: Basic package diagram test case