Added function and method arguments in sequence diagrams

This commit is contained in:
Bartek Kryza
2022-12-04 22:51:57 +01:00
parent 5a8b739eba
commit 1644a201d0
21 changed files with 299 additions and 116 deletions

View File

@@ -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_;
};
}

View File

@@ -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());
}
}

View File

@@ -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<const model::function &>(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<model::participant>(participant_id).value();
if (participant.type_name() == "method") {
const auto &class_id =
const auto class_id =
m_model.get_participant<model::method>(participant_id)
.value()
.class_id();
@@ -197,6 +198,13 @@ void generator::generate(std::ostream &ostr) const
const auto &from =
m_model.get_participant<model::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;

View File

@@ -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();
}

View File

@@ -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<std::string> &parameters() const { return parameters_; }
private:
bool is_const_{false};
std::vector<std::string> 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;

View File

@@ -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<model::class_>(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<clang::ClassTemplateSpecializationDecl>(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<model::class_>(
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<clang::TemplateSpecializationType>()
->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<clang::FunctionTemplateDecl>(decl);
if (!get_unique_id(ftd->getID()).has_value())
continue;
m.to = get_unique_id(ftd->getID()).value();
auto message_name =
diagram()
.get_participant<model::function_template>(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()) {

View File

@@ -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;

View File

@@ -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);

View File

@@ -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);

View File

@@ -11,4 +11,4 @@ diagrams:
using_namespace:
- clanguml::t20003
start_from:
- function: "clanguml::t20003::m1<T>()"
- function: "clanguml::t20003::m1<T>(T)"

View File

@@ -1,7 +1,3 @@
#include <algorithm>
#include <numeric>
#include <vector>
namespace clanguml {
namespace t20003 {

View File

@@ -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<T>()"), _A("m2<T>()"), "m2<T>"));
REQUIRE_THAT(puml, HasCall(_A("m2<T>()"), _A("m3<T>()"), "m3<T>"));
REQUIRE_THAT(puml, HasCall(_A("m3<T>()"), _A("m4<T>()"), "m4<T>"));
REQUIRE_THAT(puml, HasCall(_A("m1<T>(T)"), _A("m2<T>(T)"), ""));
REQUIRE_THAT(puml, HasCall(_A("m2<T>(T)"), _A("m3<T>(T)"), ""));
REQUIRE_THAT(puml, HasCall(_A("m3<T>(T)"), _A("m4<T>(T)"), ""));
save_puml(
"./" + config.output_directory() + "/" + diagram->name + ".puml", puml);

View File

@@ -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<float>()"), "m1"));
REQUIRE_THAT(puml, !HasCall(_A("m1<float>()"), _A("m1<float>()"), "m2"));
REQUIRE_THAT(puml, !HasCall(_A("m1<float>()"), _A("m1<float>()"), "m2"));
REQUIRE_THAT(puml, HasCall(_A("main()"), _A("m1<unsigned long>()"), "m1"));
REQUIRE_THAT(puml,
HasCall(_A("m1<unsigned long>()"), _A("m4<unsigned long>()"), "m4"));
REQUIRE_THAT(puml, HasCall(_A("main()"), _A("m1<std::string>()"), "m1"));
REQUIRE_THAT(puml, HasCall(_A("main()"), _A("m1<float>(float)"), ""));
REQUIRE_THAT(
puml, HasCall(_A("m1<std::string>()"), _A("m2<std::string>()"), "m2"));
puml, !HasCall(_A("m1<float>(float)"), _A("m1<float>(float)"), ""));
REQUIRE_THAT(
puml, !HasCall(_A("m1<float>(float)"), _A("m1<float>(float)"), ""));
REQUIRE_THAT(puml, HasCall(_A("main()"), _A("m1<int>()"), "m1"));
REQUIRE_THAT(puml, HasCall(_A("m1<int>()"), _A("m2<int>()"), "m2"));
REQUIRE_THAT(puml, HasCall(_A("m2<int>()"), _A("m3<int>()"), "m3"));
REQUIRE_THAT(puml, HasCall(_A("m3<int>()"), _A("m4<int>()"), "m4"));
REQUIRE_THAT(puml,
HasCall(_A("main()"), _A("m1<unsigned long>(unsigned long)"), ""));
REQUIRE_THAT(puml,
HasCall(_A("m1<unsigned long>(unsigned long)"),
_A("m4<unsigned long>(unsigned long)"), ""));
REQUIRE_THAT(
puml, HasCall(_A("main()"), _A("m1<std::string>(std::string)"), ""));
REQUIRE_THAT(puml,
HasCall(_A("m1<std::string>(std::string)"),
_A("m2<std::string>(std::string)"), ""));
REQUIRE_THAT(puml, HasCall(_A("main()"), _A("m1<int>(int)"), ""));
REQUIRE_THAT(puml, HasCall(_A("m1<int>(int)"), _A("m2<int>(int)"), ""));
REQUIRE_THAT(puml, HasCall(_A("m2<int>(int)"), _A("m3<int>(int)"), ""));
REQUIRE_THAT(puml, HasCall(_A("m3<int>(int)"), _A("m4<int>(int)"), ""));
REQUIRE_THAT(puml, EndsWith("@enduml\n"));
save_puml(

View File

@@ -11,4 +11,4 @@ diagrams:
using_namespace:
- clanguml::t20005
start_from:
- function: "clanguml::t20005::C<T>::c"
- function: "clanguml::t20005::C<T>::c(T)"

View File

@@ -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<T>"), _A("B<T>"), "b"));
REQUIRE_THAT(puml, HasCall(_A("B<T>"), _A("A<T>"), "a"));
REQUIRE_THAT(puml, HasCall(_A("C<T>"), _A("B<T>"), "b(T)"));
REQUIRE_THAT(puml, HasCall(_A("B<T>"), _A("A<T>"), "a(T)"));
save_puml(
"./" + config.output_directory() + "/" + diagram->name + ".puml", puml);

View File

@@ -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<int>"), "b"));
REQUIRE_THAT(puml, HasCall(_A("B<int>"), _A("A<int>"), "a1"));
REQUIRE_THAT(puml, HasCall(_A("tmain()"), _A("B<int>"), "b(int)"));
REQUIRE_THAT(puml, HasCall(_A("B<int>"), _A("A<int>"), "a1(int)"));
REQUIRE_THAT(puml, HasCall(_A("tmain()"), _A("B<std::string>"), "b"));
REQUIRE_THAT(puml, HasCall(_A("tmain()"), _A("B<std::string>"), "b(std::string)"));
REQUIRE_THAT(
puml, HasCall(_A("B<std::string>"), _A("A<std::string>"), "a2"));

14
tests/t20013/.clang-uml Normal file
View File

@@ -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 **)"

27
tests/t20013/t20013.cc Normal file
View File

@@ -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");
}
}
}

50
tests/t20013/test_case.h Normal file
View File

@@ -0,0 +1,50 @@
/**
* tests/t20013/test_case.h
*
* 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("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);
}

View File

@@ -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

View File

@@ -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));
}