From 1644a201d05076ee0ad494def8113ae1326f1b0b Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Sun, 4 Dec 2022 22:51:57 +0100 Subject: [PATCH] Added function and method arguments in sequence diagrams --- src/common/model/source_location.h | 5 + .../visitor/translation_unit_visitor.cc | 1 + .../plantuml/sequence_diagram_generator.cc | 18 +- src/sequence_diagram/model/participant.cc | 16 +- src/sequence_diagram/model/participant.h | 42 ++++- .../visitor/translation_unit_visitor.cc | 159 +++++++++++------- .../visitor/translation_unit_visitor.h | 2 + tests/t20001/test_case.h | 10 +- tests/t20002/test_case.h | 6 +- tests/t20003/.clang-uml | 2 +- tests/t20003/t20003.cc | 4 - tests/t20003/test_case.h | 6 +- tests/t20004/test_case.h | 34 ++-- tests/t20005/.clang-uml | 2 +- tests/t20005/test_case.h | 4 +- tests/t20006/test_case.h | 6 +- tests/t20013/.clang-uml | 14 ++ tests/t20013/t20013.cc | 27 +++ tests/t20013/test_case.h | 50 ++++++ tests/test_cases.cc | 1 + tests/test_cases.h | 6 +- 21 files changed, 299 insertions(+), 116 deletions(-) create mode 100644 tests/t20013/.clang-uml create mode 100644 tests/t20013/t20013.cc create mode 100644 tests/t20013/test_case.h diff --git a/src/common/model/source_location.h b/src/common/model/source_location.h index 6e2c4a91..38e880ef 100644 --- a/src/common/model/source_location.h +++ b/src/common/model/source_location.h @@ -39,8 +39,13 @@ public: void set_line(const unsigned line) { line_ = line; } + unsigned int location_id() const { return hash_; } + + void set_location_id(unsigned int h) { hash_ = h; } + private: std::string file_; unsigned int line_{0}; + unsigned int hash_; }; } \ No newline at end of file diff --git a/src/common/visitor/translation_unit_visitor.cc b/src/common/visitor/translation_unit_visitor.cc index 6c42e29f..6f0b0099 100644 --- a/src/common/visitor/translation_unit_visitor.cc +++ b/src/common/visitor/translation_unit_visitor.cc @@ -70,6 +70,7 @@ void translation_unit_visitor::set_source_location( element.set_file(source_manager_.getFilename(decl.getLocation()).str()); element.set_line( source_manager_.getSpellingLineNumber(decl.getLocation())); + element.set_location_id(decl.getLocation().getHashValue()); } } diff --git a/src/sequence_diagram/generators/plantuml/sequence_diagram_generator.cc b/src/sequence_diagram/generators/plantuml/sequence_diagram_generator.cc index be064b43..e2afa867 100644 --- a/src/sequence_diagram/generators/plantuml/sequence_diagram_generator.cc +++ b/src/sequence_diagram/generators/plantuml/sequence_diagram_generator.cc @@ -56,10 +56,11 @@ void generator::generate_call(const message &m, std::ostream &ostr) const generate_participant(ostr, m.from); generate_participant(ostr, m.to); - auto message = m.message_name; - if (!message.empty()) { - message = m_config.using_namespace().relative(message); - message += "()"; + std::string message; + + if (to.value().type_name() == "method") { + message = dynamic_cast(to.value()) + .message_name(model::function::message_render_mode::full); } ostr << from.value().alias() << " " @@ -133,7 +134,7 @@ void generator::generate_participant(std::ostream &ostr, common::id_t id) const m_model.get_participant(participant_id).value(); if (participant.type_name() == "method") { - const auto &class_id = + const auto class_id = m_model.get_participant(participant_id) .value() .class_id(); @@ -197,6 +198,13 @@ void generator::generate(std::ostream &ostr) const const auto &from = m_model.get_participant(start_from); + if (!from.has_value()) { + LOG_WARN( + "Failed to find participant {} for start_from condition", + sf.location); + continue; + } + generate_participant(ostr, start_from); ostr << "activate " << from.value().alias() << std::endl; diff --git a/src/sequence_diagram/model/participant.cc b/src/sequence_diagram/model/participant.cc index dbe4642c..94ca51d8 100644 --- a/src/sequence_diagram/model/participant.cc +++ b/src/sequence_diagram/model/participant.cc @@ -163,16 +163,18 @@ function::function(const common::model::namespace_ &using_namespace) std::string function::full_name(bool relative) const { - return element::full_name(relative) + "()"; + return fmt::format("{}({}){}", element::full_name(relative), + fmt::join(parameters_, ","), is_const() ? " const" : ""); } std::string function::full_name_no_ns() const { - return element::full_name_no_ns() + "()"; + return fmt::format("{}({}){}", element::full_name_no_ns(), + fmt::join(parameters_, ","), is_const() ? " const" : ""); } method::method(const common::model::namespace_ &using_namespace) - : participant{using_namespace} + : function{using_namespace} { } @@ -185,7 +187,7 @@ std::string method::alias() const function_template::function_template( const common::model::namespace_ &using_namespace) - : participant{using_namespace} + : function{using_namespace} { } @@ -199,7 +201,8 @@ std::string function_template::full_name(bool relative) const ostr << name_and_ns(); render_template_params(ostr, using_namespace(), relative); - ostr << "()"; + ostr << fmt::format( + "({}){}", fmt::join(parameters(), ","), is_const() ? " const" : ""); std::string res; @@ -224,7 +227,8 @@ std::string function_template::full_name_no_ns() const render_template_params(ostr, using_namespace(), false); - ostr << "()"; + ostr << fmt::format( + "({}){}", fmt::join(parameters(), ","), is_const() ? " const" : ""); return ostr.str(); } diff --git a/src/sequence_diagram/model/participant.h b/src/sequence_diagram/model/participant.h index d2abc452..5a36411d 100644 --- a/src/sequence_diagram/model/participant.h +++ b/src/sequence_diagram/model/participant.h @@ -139,6 +139,8 @@ struct lambda : public class_ { }; struct function : public participant { + enum class message_render_mode { full, no_arguments }; + function(const common::model::namespace_ &using_namespace); function(const function &) = delete; @@ -151,9 +153,31 @@ struct function : public participant { std::string full_name(bool relative = true) const override; std::string full_name_no_ns() const override; + + virtual std::string message_name(message_render_mode mode) const + { + if (mode == message_render_mode::no_arguments) { + return fmt::format("{}(){}", name(), is_const() ? " const" : ""); + } + + return fmt::format("{}({}){}", name(), fmt::join(parameters_, ","), + is_const() ? " const" : ""); + } + + bool is_const() const { return is_const_; } + + void is_const(bool c) { is_const_ = c; } + + void add_parameter(const std::string &a) { parameters_.push_back(a); } + + const std::vector ¶meters() const { return parameters_; } + +private: + bool is_const_{false}; + std::vector parameters_; }; -struct method : public participant { +struct method : public function { method(const common::model::namespace_ &using_namespace); method(const function &) = delete; @@ -180,7 +204,19 @@ struct method : public participant { std::string full_name(bool /*relative*/) const override { - return class_full_name() + "::" + method_name(); + return fmt::format("{}::{}({}){}", class_full_name(), method_name(), + fmt::join(parameters(), ","), is_const() ? " const" : ""); + } + + std::string message_name(message_render_mode mode) const override + { + if (mode == message_render_mode::no_arguments) { + return fmt::format( + "{}(){}", method_name(), is_const() ? " const" : ""); + } + + return fmt::format("{}({}){}", method_name(), + fmt::join(parameters(), ","), is_const() ? " const" : ""); } diagram_element::id_t class_id() const { return class_id_; } @@ -197,7 +233,7 @@ private: std::string class_full_name_; }; -struct function_template : public participant, public template_trait { +struct function_template : public function, public template_trait { function_template(const common::model::namespace_ &using_namespace); function_template(const function_template &) = delete; diff --git a/src/sequence_diagram/visitor/translation_unit_visitor.cc b/src/sequence_diagram/visitor/translation_unit_visitor.cc index d047becd..59b87ac3 100644 --- a/src/sequence_diagram/visitor/translation_unit_visitor.cc +++ b/src/sequence_diagram/visitor/translation_unit_visitor.cc @@ -293,8 +293,6 @@ bool translation_unit_visitor::VisitCXXMethodDecl(clang::CXXMethodDecl *m) LOG_DBG("Getting method's class with local id {}", parent_decl->getID()); - set_unique_id(m->getID(), m_ptr->id()); - const auto &method_class = get_participant(parent_decl).value(); @@ -304,13 +302,20 @@ bool translation_unit_visitor::VisitCXXMethodDecl(clang::CXXMethodDecl *m) get_participant(m_ptr->class_id()).value().full_name_no_ns() + "::" + m->getNameAsString()); - m_ptr->set_id(common::to_id( - get_participant(m_ptr->class_id()).value().full_name(false) + - "::" + m->getNameAsString())); + for (const auto *param : m->parameters()) { + m_ptr->add_parameter(simplify_system_template( + common::to_string(param->getType(), m->getASTContext()))); + } - LOG_DBG("Set id {} for method name {}", m_ptr->id(), - get_participant(m_ptr->class_id()).value().full_name(false) + - "::" + m->getNameAsString()); + set_source_location(*m, *m_ptr); + + const auto method_full_name = m_ptr->full_name(false); + + m_ptr->set_id(common::to_id(method_full_name)); + + set_unique_id(m->getID(), m_ptr->id()); + + LOG_DBG("Set id {} for method name {}", m_ptr->id(), method_full_name); context().update(m); @@ -365,7 +370,13 @@ bool translation_unit_visitor::VisitFunctionDecl(clang::FunctionDecl *f) f_ptr->set_name(ns.name()); ns.pop_back(); f_ptr->set_namespace(ns); - f_ptr->set_id(common::to_id(function_name)); + + for (const auto *param : f->parameters()) { + f_ptr->add_parameter(simplify_system_template( + common::to_string(param->getType(), f->getASTContext()))); + } + + f_ptr->set_id(common::to_id(f_ptr->full_name(false))); context().update(f); @@ -401,9 +412,16 @@ bool translation_unit_visitor::VisitFunctionTemplateDecl( process_template_parameters(*function_template, *f_ptr); + for (const auto *param : + function_template->getTemplatedDecl()->parameters()) { + f_ptr->add_parameter(simplify_system_template(common::to_string( + param->getType(), function_template->getASTContext()))); + } + f_ptr->set_id(common::to_id(f_ptr->full_name(false))); context().update(function_template); + context().set_caller_id(f_ptr->id()); set_unique_id(function_template->getID(), f_ptr->id()); @@ -633,33 +651,9 @@ bool translation_unit_visitor::process_class_method_call_expression( diagram().should_include(callee_decl->getQualifiedNameAsString()))) return false; - const auto *callee_template_specialization = - clang::dyn_cast(callee_decl); + m.to = method_decl->getID(); + m.message_name = method_decl->getNameAsString(); - if (callee_template_specialization) { - LOG_DBG("Callee is a template specialization declaration {}", - callee_template_specialization->getQualifiedNameAsString()); - - const auto &specialization_participant = - diagram() - .get_participant( - get_unique_id(callee_template_specialization->getID()) - .value()) - .value(); - const auto specialization_method_name = - specialization_participant.full_name(false) + - "::" + method_decl->getNameAsString(); - - m.to = common::to_id(specialization_method_name); - m.message_name = method_decl->getNameAsString(); - } - else { - // TODO: The method can be called before it's declaration has been - // encountered by the visitor - for now it's not a problem - // as overloaded methods are not supported - m.to = common::to_id(method_decl->getQualifiedNameAsString()); - m.message_name = method_decl->getNameAsString(); - } m.return_type = method_call_expr->getCallReturnType(*context().get_ast_context()) .getAsString(); @@ -682,8 +676,7 @@ bool translation_unit_visitor::process_class_template_method_call_expression( expr->getCallee()); if (is_callee_valid_template_specialization(dependent_member_callee)) { - - const auto *primary_template = + const auto *template_declaration = dependent_member_callee->getBaseType() ->getAs() ->getTemplateName() @@ -693,33 +686,52 @@ bool translation_unit_visitor::process_class_template_method_call_expression( // First check if the primary template is already in the // participants map - if (get_participant(primary_template).has_value()) { + if (get_participant(template_declaration).has_value()) { callee_method_full_name = - get_participant(primary_template).value().full_name(false) + + get_participant(template_declaration).value().full_name(false) + "::" + dependent_member_callee->getMember().getAsString(); - } - else if (is_smart_pointer(primary_template)) { - // Otherwise check if it a smart pointer - primary_template->getTemplateParameters()->asArray().front(); - if (get_participant(primary_template).has_value()) { - callee_method_full_name = - get_participant(primary_template).value().full_name(false) + + for (const auto &[id, p] : diagram().participants) { + const auto p_full_name = p->full_name(false); + + if (p_full_name.find(callee_method_full_name + "(") == 0) { + // TODO: This selects the first matching template method + // without considering arguments!!! + m.to = id; + break; + } + } + } + else if (is_smart_pointer(template_declaration)) { + // Otherwise check if it is a smart pointer + template_declaration->getTemplateParameters()->asArray().front(); + + if (get_participant(template_declaration).has_value()) { + callee_method_full_name = get_participant(template_declaration) + .value() + .full_name(false) + "::" + dependent_member_callee->getMember().getAsString(); + + for (const auto &[id, p] : diagram().participants) { + const auto p_full_name = p->full_name(false); + if (p_full_name.find(callee_method_full_name + "(") == 0) { + // TODO: This selects the first matching template method + // without considering arguments!!! + m.to = id; + break; + } + } } else return false; } - auto callee_id = common::to_id(callee_method_full_name); - m.to = callee_id; - m.message_name = dependent_member_callee->getMember().getAsString(); m.return_type = ""; - if (get_unique_id(primary_template->getID())) + if (get_unique_id(template_declaration->getID())) diagram().add_active_participant( - get_unique_id(primary_template->getID()).value()); + get_unique_id(template_declaration->getID()).value()); } return true; @@ -744,7 +756,7 @@ bool translation_unit_visitor::process_function_call_expression( if (!get_unique_id(callee_function->getID()).has_value()) { // This is hopefully not an interesting call... - return false; + m.to = callee_function->getID(); } else { m.to = get_unique_id(callee_function->getID()).value(); @@ -774,17 +786,10 @@ bool translation_unit_visitor::process_unresolved_lookup_call_expression( clang::dyn_cast_or_null(decl); if (!get_unique_id(ftd->getID()).has_value()) - continue; - - m.to = get_unique_id(ftd->getID()).value(); - auto message_name = - diagram() - .get_participant(m.to) - .value() - .full_name(false) - .substr(); - m.message_name = - message_name.substr(0, message_name.size() - 2); + m.to = ftd->getID(); + else { + m.to = get_unique_id(ftd->getID()).value(); + } break; } @@ -1038,6 +1043,11 @@ translation_unit_visitor::build_function_template_instantiation( template_base_params, decl.getTemplateSpecializationArgs()->asArray(), template_instantiation, "", decl.getPrimaryTemplate()); + for (const auto *param : decl.parameters()) { + template_instantiation_ptr->add_parameter( + common::to_string(param->getType(), decl.getASTContext())); + } + return template_instantiation_ptr; } @@ -1644,6 +1654,16 @@ bool translation_unit_visitor::simplify_system_template( return false; } +std::string translation_unit_visitor::simplify_system_template( + const std::string &full_name) const +{ + if (config().type_aliases().count(full_name) > 0) { + return config().type_aliases().at(full_name); + } + + return full_name; +} + std::string translation_unit_visitor::make_lambda_name( const clang::CXXRecordDecl *cls) const { @@ -1672,6 +1692,19 @@ std::string translation_unit_visitor::make_lambda_name( void translation_unit_visitor::finalize() { + decltype(diagram().active_participants_) active_participants_unique; + + for (auto id : diagram().active_participants_) { + if (local_ast_id_map_.find(id) != local_ast_id_map_.end()) { + active_participants_unique.emplace(local_ast_id_map_.at(id)); + } + else { + active_participants_unique.emplace(id); + } + } + + diagram().active_participants_ = std::move(active_participants_unique); + for (auto &[id, activity] : diagram().sequences) { for (auto &m : activity.messages) { if (local_ast_id_map_.find(m.to) != local_ast_id_map_.end()) { diff --git a/src/sequence_diagram/visitor/translation_unit_visitor.h b/src/sequence_diagram/visitor/translation_unit_visitor.h index 3991b723..a14c0f35 100644 --- a/src/sequence_diagram/visitor/translation_unit_visitor.h +++ b/src/sequence_diagram/visitor/translation_unit_visitor.h @@ -195,6 +195,8 @@ private: bool simplify_system_template(class_diagram::model::template_parameter &ct, const std::string &full_name); + std::string simplify_system_template(const std::string &full_name) const; + std::string make_lambda_name(const clang::CXXRecordDecl *cls) const; bool is_smart_pointer(const clang::TemplateDecl *primary_template) const; diff --git a/tests/t20001/test_case.h b/tests/t20001/test_case.h index 6ee8363a..c50075e6 100644 --- a/tests/t20001/test_case.h +++ b/tests/t20001/test_case.h @@ -38,11 +38,11 @@ TEST_CASE("t20001", "[test-case][sequence]") REQUIRE_THAT(puml, StartsWith("@startuml")); REQUIRE_THAT(puml, EndsWith("@enduml\n")); - REQUIRE_THAT(puml, HasCall(_A("A"), "log_result")); - REQUIRE_THAT(puml, HasCall(_A("B"), _A("A"), "log_result")); - REQUIRE_THAT(puml, HasCallWithResponse(_A("B"), _A("A"), "add3")); - REQUIRE_THAT(puml, HasCall(_A("A"), "add")); - REQUIRE_THAT(puml, !HasCall(_A("A"), _A("detail::C"), "add")); + REQUIRE_THAT(puml, HasCall(_A("A"), "log_result(int)")); + REQUIRE_THAT(puml, HasCall(_A("B"), _A("A"), "log_result(int)")); + REQUIRE_THAT(puml, HasCallWithResponse(_A("B"), _A("A"), "add3(int,int,int)")); + REQUIRE_THAT(puml, HasCall(_A("A"), "add(int,int)")); + REQUIRE_THAT(puml, !HasCall(_A("A"), _A("detail::C"), "add(int,int)")); save_puml( "./" + config.output_directory() + "/" + diagram->name + ".puml", puml); diff --git a/tests/t20002/test_case.h b/tests/t20002/test_case.h index 09691b06..d053c8ac 100644 --- a/tests/t20002/test_case.h +++ b/tests/t20002/test_case.h @@ -34,9 +34,9 @@ TEST_CASE("t20002", "[test-case][sequence]") REQUIRE_THAT(puml, StartsWith("@startuml")); REQUIRE_THAT(puml, EndsWith("@enduml\n")); - REQUIRE_THAT(puml, HasCall(_A("m1()"), _A("m2()"), "m2")); - REQUIRE_THAT(puml, HasCall(_A("m2()"), _A("m3()"), "m3")); - REQUIRE_THAT(puml, HasCall(_A("m3()"), _A("m4()"), "m4")); + REQUIRE_THAT(puml, HasCall(_A("m1()"), _A("m2()"), "")); + REQUIRE_THAT(puml, HasCall(_A("m2()"), _A("m3()"), "")); + REQUIRE_THAT(puml, HasCall(_A("m3()"), _A("m4()"), "")); save_puml( "./" + config.output_directory() + "/" + diagram->name + ".puml", puml); diff --git a/tests/t20003/.clang-uml b/tests/t20003/.clang-uml index d592e566..9aae6b72 100644 --- a/tests/t20003/.clang-uml +++ b/tests/t20003/.clang-uml @@ -11,4 +11,4 @@ diagrams: using_namespace: - clanguml::t20003 start_from: - - function: "clanguml::t20003::m1()" + - function: "clanguml::t20003::m1(T)" diff --git a/tests/t20003/t20003.cc b/tests/t20003/t20003.cc index af9b75ba..00c33395 100644 --- a/tests/t20003/t20003.cc +++ b/tests/t20003/t20003.cc @@ -1,7 +1,3 @@ -#include -#include -#include - namespace clanguml { namespace t20003 { diff --git a/tests/t20003/test_case.h b/tests/t20003/test_case.h index 557ba525..7a17c539 100644 --- a/tests/t20003/test_case.h +++ b/tests/t20003/test_case.h @@ -34,9 +34,9 @@ TEST_CASE("t20003", "[test-case][sequence]") REQUIRE_THAT(puml, StartsWith("@startuml")); REQUIRE_THAT(puml, EndsWith("@enduml\n")); - REQUIRE_THAT(puml, HasCall(_A("m1()"), _A("m2()"), "m2")); - REQUIRE_THAT(puml, HasCall(_A("m2()"), _A("m3()"), "m3")); - REQUIRE_THAT(puml, HasCall(_A("m3()"), _A("m4()"), "m4")); + REQUIRE_THAT(puml, HasCall(_A("m1(T)"), _A("m2(T)"), "")); + REQUIRE_THAT(puml, HasCall(_A("m2(T)"), _A("m3(T)"), "")); + REQUIRE_THAT(puml, HasCall(_A("m3(T)"), _A("m4(T)"), "")); save_puml( "./" + config.output_directory() + "/" + diagram->name + ".puml", puml); diff --git a/tests/t20004/test_case.h b/tests/t20004/test_case.h index 615d3729..fc827d68 100644 --- a/tests/t20004/test_case.h +++ b/tests/t20004/test_case.h @@ -32,22 +32,28 @@ TEST_CASE("t20004", "[test-case][sequence]") AliasMatcher _A(puml); REQUIRE_THAT(puml, StartsWith("@startuml")); - REQUIRE_THAT(puml, HasCall(_A("main()"), _A("m1()"), "m1")); - REQUIRE_THAT(puml, !HasCall(_A("m1()"), _A("m1()"), "m2")); - REQUIRE_THAT(puml, !HasCall(_A("m1()"), _A("m1()"), "m2")); - - REQUIRE_THAT(puml, HasCall(_A("main()"), _A("m1()"), "m1")); - REQUIRE_THAT(puml, - HasCall(_A("m1()"), _A("m4()"), "m4")); - - REQUIRE_THAT(puml, HasCall(_A("main()"), _A("m1()"), "m1")); + REQUIRE_THAT(puml, HasCall(_A("main()"), _A("m1(float)"), "")); REQUIRE_THAT( - puml, HasCall(_A("m1()"), _A("m2()"), "m2")); + puml, !HasCall(_A("m1(float)"), _A("m1(float)"), "")); + REQUIRE_THAT( + puml, !HasCall(_A("m1(float)"), _A("m1(float)"), "")); - REQUIRE_THAT(puml, HasCall(_A("main()"), _A("m1()"), "m1")); - REQUIRE_THAT(puml, HasCall(_A("m1()"), _A("m2()"), "m2")); - REQUIRE_THAT(puml, HasCall(_A("m2()"), _A("m3()"), "m3")); - REQUIRE_THAT(puml, HasCall(_A("m3()"), _A("m4()"), "m4")); + REQUIRE_THAT(puml, + HasCall(_A("main()"), _A("m1(unsigned long)"), "")); + REQUIRE_THAT(puml, + HasCall(_A("m1(unsigned long)"), + _A("m4(unsigned long)"), "")); + + REQUIRE_THAT( + puml, HasCall(_A("main()"), _A("m1(std::string)"), "")); + REQUIRE_THAT(puml, + HasCall(_A("m1(std::string)"), + _A("m2(std::string)"), "")); + + REQUIRE_THAT(puml, HasCall(_A("main()"), _A("m1(int)"), "")); + REQUIRE_THAT(puml, HasCall(_A("m1(int)"), _A("m2(int)"), "")); + REQUIRE_THAT(puml, HasCall(_A("m2(int)"), _A("m3(int)"), "")); + REQUIRE_THAT(puml, HasCall(_A("m3(int)"), _A("m4(int)"), "")); REQUIRE_THAT(puml, EndsWith("@enduml\n")); save_puml( diff --git a/tests/t20005/.clang-uml b/tests/t20005/.clang-uml index d6248303..7fbd937a 100644 --- a/tests/t20005/.clang-uml +++ b/tests/t20005/.clang-uml @@ -11,4 +11,4 @@ diagrams: using_namespace: - clanguml::t20005 start_from: - - function: "clanguml::t20005::C::c" \ No newline at end of file + - function: "clanguml::t20005::C::c(T)" \ No newline at end of file diff --git a/tests/t20005/test_case.h b/tests/t20005/test_case.h index 2f8bed5d..476ee657 100644 --- a/tests/t20005/test_case.h +++ b/tests/t20005/test_case.h @@ -35,8 +35,8 @@ TEST_CASE("t20005", "[test-case][sequence]") REQUIRE_THAT(puml, EndsWith("@enduml\n")); // Check if all calls exist - REQUIRE_THAT(puml, HasCall(_A("C"), _A("B"), "b")); - REQUIRE_THAT(puml, HasCall(_A("B"), _A("A"), "a")); + REQUIRE_THAT(puml, HasCall(_A("C"), _A("B"), "b(T)")); + REQUIRE_THAT(puml, HasCall(_A("B"), _A("A"), "a(T)")); save_puml( "./" + config.output_directory() + "/" + diagram->name + ".puml", puml); diff --git a/tests/t20006/test_case.h b/tests/t20006/test_case.h index 77ba8a6b..be090ce1 100644 --- a/tests/t20006/test_case.h +++ b/tests/t20006/test_case.h @@ -35,10 +35,10 @@ TEST_CASE("t20006", "[test-case][sequence]") REQUIRE_THAT(puml, EndsWith("@enduml\n")); // Check if all calls exist - REQUIRE_THAT(puml, HasCall(_A("tmain()"), _A("B"), "b")); - REQUIRE_THAT(puml, HasCall(_A("B"), _A("A"), "a1")); + REQUIRE_THAT(puml, HasCall(_A("tmain()"), _A("B"), "b(int)")); + REQUIRE_THAT(puml, HasCall(_A("B"), _A("A"), "a1(int)")); - REQUIRE_THAT(puml, HasCall(_A("tmain()"), _A("B"), "b")); + REQUIRE_THAT(puml, HasCall(_A("tmain()"), _A("B"), "b(std::string)")); REQUIRE_THAT( puml, HasCall(_A("B"), _A("A"), "a2")); diff --git a/tests/t20013/.clang-uml b/tests/t20013/.clang-uml new file mode 100644 index 00000000..e37d69ee --- /dev/null +++ b/tests/t20013/.clang-uml @@ -0,0 +1,14 @@ +compilation_database_dir: .. +output_directory: puml +diagrams: + t20013_sequence: + type: sequence + glob: + - ../../tests/t20013/t20013.cc + include: + namespaces: + - clanguml::t20013 + using_namespace: + - clanguml::t20013 + start_from: + - function: "clanguml::t20013::tmain(int,char **)" \ No newline at end of file diff --git a/tests/t20013/t20013.cc b/tests/t20013/t20013.cc new file mode 100644 index 00000000..616c09a2 --- /dev/null +++ b/tests/t20013/t20013.cc @@ -0,0 +1,27 @@ +namespace clanguml { +namespace t20013 { + +struct A { + int a1(int i) { return i; } + double a2(double d) { return d; } + const char *a3(const char *s) { return s; } +}; + +struct B { + int b(int i) { return a.a1(i); } + double b(double d) { return a.a2(d); } + const char *b(const char *s) { return a.a3(s); } + + A a; +}; + +void tmain(int argc, char **argv) +{ + B b; + + b.b(1); + b.b(2.0); + b.b("three"); +} +} +} \ No newline at end of file diff --git a/tests/t20013/test_case.h b/tests/t20013/test_case.h new file mode 100644 index 00000000..0b100356 --- /dev/null +++ b/tests/t20013/test_case.h @@ -0,0 +1,50 @@ +/** + * tests/t20013/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("t20013", "[test-case][sequence]") +{ + auto [config, db] = load_config("t20013"); + + auto diagram = config.diagrams["t20013_sequence"]; + + REQUIRE(diagram->name == "t20013_sequence"); + + auto model = generate_sequence_diagram(*db, diagram); + + REQUIRE(model->name() == "t20013_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(int,char **)"), _A("B"), "b(int)")); + REQUIRE_THAT(puml, HasCall(_A("B"), _A("A"), "a1(int)")); + + REQUIRE_THAT(puml, HasCall(_A("tmain(int,char **)"), _A("B"), "b(double)")); + REQUIRE_THAT(puml, HasCall(_A("B"), _A("A"), "a2(double)")); + + REQUIRE_THAT( + puml, HasCall(_A("tmain(int,char **)"), _A("B"), "b(const char *)")); + REQUIRE_THAT(puml, HasCall(_A("B"), _A("A"), "a3(const char *)")); + + 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 af89443c..beb24b90 100644 --- a/tests/test_cases.cc +++ b/tests/test_cases.cc @@ -259,6 +259,7 @@ using namespace clanguml::test::matchers; #include "t20010/test_case.h" #include "t20011/test_case.h" #include "t20012/test_case.h" +#include "t20013/test_case.h" /// /// Package diagram tests diff --git a/tests/test_cases.h b/tests/test_cases.h index e1ec4aac..23baa2b4 100644 --- a/tests/test_cases.h +++ b/tests/test_cases.h @@ -123,7 +123,7 @@ ContainsMatcher HasCall(std::string const &from, std::string const &message, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) { return ContainsMatcher(CasedString( - fmt::format("{} -> {} : {}()", from, from, message), caseSensitivity)); + fmt::format("{} -> {} : {}", from, from, message), caseSensitivity)); } ContainsMatcher HasCall(std::string const &from, std::string const &to, @@ -131,7 +131,7 @@ ContainsMatcher HasCall(std::string const &from, std::string const &to, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) { return ContainsMatcher(CasedString( - fmt::format("{} -> {} : {}()", from, to, message), caseSensitivity)); + fmt::format("{} -> {} : {}", from, to, message), caseSensitivity)); } auto HasCallWithResponse(std::string const &from, std::string const &to, @@ -140,7 +140,7 @@ auto HasCallWithResponse(std::string const &from, std::string const &to, { return HasCallWithResultMatcher( CasedString( - fmt::format("{} -> {} : {}()", from, to, message), caseSensitivity), + fmt::format("{} -> {} : {}", from, to, message), caseSensitivity), CasedString(fmt::format("{} --> {}", to, from), caseSensitivity)); }