Added option to enable rendering return types in sequence diagrams (fixes #93)
This commit is contained in:
15
tests/t20032/.clang-uml
Normal file
15
tests/t20032/.clang-uml
Normal file
@@ -0,0 +1,15 @@
|
||||
compilation_database_dir: ..
|
||||
output_directory: puml
|
||||
diagrams:
|
||||
t20032_sequence:
|
||||
type: sequence
|
||||
glob:
|
||||
- ../../tests/t20032/t20032.cc
|
||||
include:
|
||||
namespaces:
|
||||
- clanguml::t20032
|
||||
using_namespace:
|
||||
- clanguml::t20032
|
||||
generate_return_types: true
|
||||
start_from:
|
||||
- function: "clanguml::t20032::tmain(int,char **)"
|
||||
27
tests/t20032/t20032.cc
Normal file
27
tests/t20032/t20032.cc
Normal file
@@ -0,0 +1,27 @@
|
||||
namespace clanguml {
|
||||
namespace t20032 {
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
79
tests/t20032/test_case.h
Normal file
79
tests/t20032/test_case.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* tests/t20032/test_case.h
|
||||
*
|
||||
* Copyright (c) 2021-2023 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("t20032", "[test-case][sequence]")
|
||||
{
|
||||
auto [config, db] = load_config("t20032");
|
||||
|
||||
auto diagram = config.diagrams["t20032_sequence"];
|
||||
|
||||
REQUIRE(diagram->name == "t20032_sequence");
|
||||
|
||||
auto model = generate_sequence_diagram(*db, diagram);
|
||||
|
||||
REQUIRE(model->name() == "t20032_sequence");
|
||||
|
||||
REQUIRE(model->name() == "t20032_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, HasResponse(_A("tmain(int,char **)"), _A("B"), "int"));
|
||||
|
||||
REQUIRE_THAT(puml, HasCall(_A("B"), _A("A"), "a1(int)"));
|
||||
REQUIRE_THAT(puml, HasResponse(_A("B"), _A("A"), "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, HasResponse(_A("B"), _A("A"), "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 *)"));
|
||||
REQUIRE_THAT(puml, HasResponse(_A("B"), _A("A"), "const char *"));
|
||||
|
||||
save_puml(
|
||||
config.output_directory() + "/" + diagram->name + ".puml", puml);
|
||||
}
|
||||
|
||||
{
|
||||
auto j = generate_sequence_json(diagram, *model);
|
||||
|
||||
using namespace json;
|
||||
|
||||
std::vector<int> messages = {
|
||||
FindMessage(j, "tmain(int,char **)", "B", "b(int)", "int"),
|
||||
FindMessage(j, "B", "A", "a1(int)", "int"),
|
||||
FindMessage(j, "tmain(int,char **)", "B", "b(double)"),
|
||||
FindMessage(j, "B", "A", "a2(double)"),
|
||||
FindMessage(j, "tmain(int,char **)", "B", "b(const char *)"),
|
||||
FindMessage(j, "B", "A", "a3(const char *)")};
|
||||
|
||||
REQUIRE(std::is_sorted(messages.begin(), messages.end()));
|
||||
|
||||
save_json(config.output_directory() + "/" + diagram->name + ".json", j);
|
||||
}
|
||||
}
|
||||
@@ -345,6 +345,7 @@ using namespace clanguml::test::matchers;
|
||||
#include "t20029/test_case.h"
|
||||
#include "t20030/test_case.h"
|
||||
#include "t20031/test_case.h"
|
||||
#include "t20032/test_case.h"
|
||||
|
||||
///
|
||||
/// Package diagram tests
|
||||
|
||||
@@ -134,12 +134,15 @@ struct HasCallWithResultMatcher : ContainsMatcher {
|
||||
|
||||
template <typename T> class HasCallMatcher : public Catch::MatcherBase<T> {
|
||||
T m_from, m_to, m_message;
|
||||
bool m_is_response;
|
||||
std::string call_pattern, response_pattern;
|
||||
|
||||
public:
|
||||
HasCallMatcher(T from, T to, T message)
|
||||
HasCallMatcher(T from, T to, T message, bool is_response = false)
|
||||
: m_from(from)
|
||||
, m_to{to}
|
||||
, m_message{message}
|
||||
, m_is_response{is_response}
|
||||
{
|
||||
util::replace_all(m_message, "(", "\\(");
|
||||
util::replace_all(m_message, ")", "\\)");
|
||||
@@ -147,15 +150,22 @@ public:
|
||||
util::replace_all(m_message, "[", "\\[");
|
||||
util::replace_all(m_message, "]", "\\]");
|
||||
util::replace_all(m_message, "+", "\\+");
|
||||
|
||||
call_pattern = fmt::format("{} -> {} "
|
||||
"(\\[\\[.*\\]\\] )?: {}",
|
||||
m_from, m_to, m_message);
|
||||
|
||||
response_pattern =
|
||||
fmt::format("{} --> {} : //{}//", m_from, m_to, m_message);
|
||||
}
|
||||
|
||||
bool match(T const &in) const override
|
||||
{
|
||||
std::istringstream fin(in);
|
||||
std::string line;
|
||||
std::regex r{fmt::format("{} -> {} "
|
||||
"(\\[\\[.*\\]\\] )?: {}",
|
||||
m_from, m_to, m_message)};
|
||||
|
||||
std::regex r{m_is_response ? response_pattern : call_pattern};
|
||||
|
||||
while (std::getline(fin, line)) {
|
||||
std::smatch base_match;
|
||||
std::regex_search(in, base_match, r);
|
||||
@@ -182,6 +192,13 @@ auto HasCall(std::string const &from, std::string const &to,
|
||||
return HasCallMatcher(from, to, message);
|
||||
}
|
||||
|
||||
auto HasResponse(std::string const &from, std::string const &to,
|
||||
std::string const &message,
|
||||
CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes)
|
||||
{
|
||||
return HasCallMatcher(to, from, message, true);
|
||||
}
|
||||
|
||||
auto HasCallInControlCondition(std::string const &from, std::string const &to,
|
||||
std::string const &message,
|
||||
CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes)
|
||||
@@ -955,7 +972,8 @@ bool IsFileParticipant(const nlohmann::json &j, const std::string &name)
|
||||
|
||||
namespace detail {
|
||||
int find_message_nested(const nlohmann::json &j, const std::string &from,
|
||||
const std::string &to, const std::string &msg, const nlohmann::json &from_p,
|
||||
const std::string &to, const std::string &msg,
|
||||
std::optional<std::string> return_type, const nlohmann::json &from_p,
|
||||
const nlohmann::json &to_p, int &count)
|
||||
{
|
||||
const auto &messages = j["messages"];
|
||||
@@ -965,23 +983,25 @@ int find_message_nested(const nlohmann::json &j, const std::string &from,
|
||||
for (const auto &m : messages) {
|
||||
if (m.contains("branches")) {
|
||||
for (const auto &b : m["branches"]) {
|
||||
auto nested_res =
|
||||
find_message_nested(b, from, to, msg, from_p, to_p, count);
|
||||
auto nested_res = find_message_nested(
|
||||
b, from, to, msg, return_type, from_p, to_p, count);
|
||||
|
||||
if (nested_res >= 0)
|
||||
return nested_res;
|
||||
}
|
||||
}
|
||||
else if (m.contains("messages")) {
|
||||
auto nested_res =
|
||||
find_message_nested(m, from, to, msg, from_p, to_p, count);
|
||||
auto nested_res = find_message_nested(
|
||||
m, from, to, msg, return_type, from_p, to_p, count);
|
||||
|
||||
if (nested_res >= 0)
|
||||
return nested_res;
|
||||
}
|
||||
else {
|
||||
if ((m["from"]["participant_id"] == from_p["id"]) &&
|
||||
(m["to"]["participant_id"] == to_p["id"]) && (m["name"] == msg))
|
||||
(m["to"]["participant_id"] == to_p["id"]) &&
|
||||
(m["name"] == msg) &&
|
||||
(!return_type || m["return_type"] == *return_type))
|
||||
return count;
|
||||
|
||||
count++;
|
||||
@@ -992,7 +1012,8 @@ int find_message_nested(const nlohmann::json &j, const std::string &from,
|
||||
}
|
||||
|
||||
int find_message_impl(const nlohmann::json &j, const std::string &from,
|
||||
const std::string &to, const std::string &msg)
|
||||
const std::string &to, const std::string &msg,
|
||||
std::optional<std::string> return_type)
|
||||
{
|
||||
|
||||
auto from_p = get_participant(j, from);
|
||||
@@ -1004,7 +1025,7 @@ int find_message_impl(const nlohmann::json &j, const std::string &from,
|
||||
int count{0};
|
||||
|
||||
auto res = detail::find_message_nested(
|
||||
sequence_0, from, to, msg, *from_p, *to_p, count);
|
||||
sequence_0, from, to, msg, return_type, *from_p, *to_p, count);
|
||||
|
||||
if (res >= 0)
|
||||
return res;
|
||||
@@ -1018,14 +1039,15 @@ int find_message_impl(const nlohmann::json &j, const std::string &from,
|
||||
int FindMessage(const nlohmann::json &j, const File &from, const File &to,
|
||||
const std::string &msg)
|
||||
{
|
||||
return detail::find_message_impl(j, from.file, to.file, msg);
|
||||
return detail::find_message_impl(j, from.file, to.file, msg, {});
|
||||
}
|
||||
|
||||
int FindMessage(const nlohmann::json &j, const std::string &from,
|
||||
const std::string &to, const std::string &msg)
|
||||
const std::string &to, const std::string &msg,
|
||||
std::optional<std::string> return_type = {})
|
||||
{
|
||||
return detail::find_message_impl(
|
||||
j, expand_name(j, from), expand_name(j, to), msg);
|
||||
j, expand_name(j, from), expand_name(j, to), msg, return_type);
|
||||
}
|
||||
|
||||
} // namespace json
|
||||
|
||||
@@ -292,6 +292,9 @@ test_cases:
|
||||
- name: t20031
|
||||
title: Callee type sequence diagram filter test case
|
||||
description:
|
||||
- name: t20032
|
||||
title: Return type generation option sequence diagram test case
|
||||
description:
|
||||
Package diagrams:
|
||||
- name: t30001
|
||||
title: Basic package diagram test case
|
||||
|
||||
@@ -323,3 +323,20 @@ TEST_CASE("Test config diagram_templates", "[unit-test]")
|
||||
REQUIRE(cfg.diagram_templates()["main_sequence_tmpl"].type ==
|
||||
clanguml::common::model::diagram_t::kSequence);
|
||||
}
|
||||
|
||||
TEST_CASE("Test config sequence inherited", "[unit-test]")
|
||||
{
|
||||
auto cfg = clanguml::config::load(
|
||||
"./test_config_data/sequence_inheritable_options.yml");
|
||||
|
||||
CHECK(cfg.diagrams.size() == 2);
|
||||
auto &def = *cfg.diagrams["sequence_default"];
|
||||
CHECK(def.type() == clanguml::common::model::diagram_t::kSequence);
|
||||
CHECK(def.combine_free_functions_into_file_participants() == true);
|
||||
CHECK(def.generate_return_types() == true);
|
||||
|
||||
def = *cfg.diagrams["sequence_default2"];
|
||||
CHECK(def.type() == clanguml::common::model::diagram_t::kSequence);
|
||||
CHECK(def.combine_free_functions_into_file_participants() == false);
|
||||
CHECK(def.generate_return_types() == false);
|
||||
}
|
||||
15
tests/test_config_data/sequence_inheritable_options.yml
Normal file
15
tests/test_config_data/sequence_inheritable_options.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
compilation_database_dir: debug
|
||||
output_directory: output
|
||||
combine_free_functions_into_file_participants: true
|
||||
generate_return_types: true
|
||||
diagrams:
|
||||
sequence_default:
|
||||
type: sequence
|
||||
glob:
|
||||
- src/*.cc
|
||||
sequence_default2:
|
||||
type: sequence
|
||||
glob:
|
||||
- src/*.cc
|
||||
combine_free_functions_into_file_participants: false
|
||||
generate_return_types: false
|
||||
Reference in New Issue
Block a user