First working version of JSON sequence diagram generator

This commit is contained in:
Bartek Kryza
2023-03-19 18:29:45 +01:00
parent e620c86f31
commit f0497e934d
42 changed files with 1165 additions and 56 deletions

View File

@@ -12,7 +12,7 @@ YAML configuration files. The main idea behind the
project is to easily maintain up-to-date diagrams within a code-base or document
legacy code. The configuration file or files for `clang-uml` define the
type and contents of each generated diagram.
Currently, the diagrams are generated in [PlantUML](https://plantuml.com) format.
Currently, the diagrams are generated in [PlantUML](https://plantuml.com) and JSON formats.
`clang-uml` currently supports C++ up to version 17 and partial support for C++ 20.

View File

@@ -20,59 +20,6 @@
#include "util/error.h"
namespace clanguml::common::model {
using nlohmann::json;
void to_json(nlohmann::json &j, const source_location &sl)
{
j = json{{"file", sl.file_relative()}, {"line", sl.line()}};
}
void to_json(nlohmann::json &j, const element &c)
{
j = json{{"id", std::to_string(c.id())}, {"name", c.name()},
{"namespace", c.get_namespace().to_string()}, {"type", c.type_name()},
{"display_name", c.full_name(false)}};
if (const auto &comment = c.comment(); comment)
j["comment"] = comment.value();
if (!c.file().empty()) {
j["source_location"] =
dynamic_cast<const common::model::source_location &>(c);
}
}
void to_json(nlohmann::json &j, const template_parameter &c)
{
j["kind"] = to_string(c.kind());
if (c.type())
j["type"] = c.type().value();
if (c.name())
j["name"] = c.name().value();
if (c.default_value())
j["default"] = c.default_value().value();
j["is_variadic"] = c.is_variadic();
}
void to_json(nlohmann::json &j, const relationship &c)
{
j["type"] = to_string(c.type());
j["destination"] = std::to_string(c.destination());
if (!c.multiplicity_source().empty())
j["multiplicity_source"] = c.multiplicity_source();
if (!c.multiplicity_destination().empty())
j["multiplicity_destination"] = c.multiplicity_destination();
if (c.access() != access_t::kNone)
j["access"] = to_string(c.access());
if (!c.label().empty())
j["label"] = c.label();
if (const auto &comment = c.comment(); comment)
j["comment"] = comment.value();
}
} // namespace clanguml::common::model
namespace clanguml::class_diagram::model {
using nlohmann::json;
void to_json(nlohmann::json &j, const class_element &c)
@@ -170,6 +117,9 @@ void generator::generate(std::ostream &ostr) const
generate_relationships(json_);
json_["name"] = m_model.name();
json_["diagram_type"] = "class";
ostr << json_;
}

View File

@@ -60,6 +60,8 @@ class generator : public common_generator<diagram_config, diagram_model> {
public:
generator(diagram_config &config, diagram_model &model);
void generate(std::ostream &ostr) const override;
void generate(const class_ &c, nlohmann::json &parent) const;
void generate(const enum_ &c, nlohmann::json &parent) const;
@@ -70,8 +72,6 @@ public:
void generate_top_level_elements(nlohmann::json &parent) const;
void generate(std::ostream &ostr) const override;
void generate_relationships(nlohmann::json &parent) const;
void generate_relationships(const class_ &c, nlohmann::json &parent) const;

View File

@@ -25,6 +25,7 @@
#include "config/config.h"
#include "include_diagram/generators/plantuml/include_diagram_generator.h"
#include "package_diagram/generators/plantuml/package_diagram_generator.h"
#include "sequence_diagram/generators/json/sequence_diagram_generator.h"
#include "sequence_diagram/generators/plantuml/sequence_diagram_generator.h"
#include "util/util.h"
#include "version.h"

View File

@@ -0,0 +1,72 @@
/**
* src/common/generators/json/generator.cc
*
* 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.
*/
#include "generator.h"
namespace clanguml::common::model {
using nlohmann::json;
void to_json(nlohmann::json &j, const source_location &sl)
{
j = json{{"file", sl.file_relative()}, {"line", sl.line()}};
}
void to_json(nlohmann::json &j, const element &c)
{
j = json{{"id", std::to_string(c.id())}, {"name", c.name()},
{"namespace", c.get_namespace().to_string()}, {"type", c.type_name()},
{"display_name", c.full_name(false)}};
if (const auto &comment = c.comment(); comment)
j["comment"] = comment.value();
if (!c.file().empty()) {
j["source_location"] =
dynamic_cast<const common::model::source_location &>(c);
}
}
void to_json(nlohmann::json &j, const template_parameter &c)
{
j["kind"] = to_string(c.kind());
if (c.type())
j["type"] = c.type().value();
if (c.name())
j["name"] = c.name().value();
if (c.default_value())
j["default"] = c.default_value().value();
j["is_variadic"] = c.is_variadic();
}
void to_json(nlohmann::json &j, const relationship &c)
{
j["type"] = to_string(c.type());
j["destination"] = std::to_string(c.destination());
if (!c.multiplicity_source().empty())
j["multiplicity_source"] = c.multiplicity_source();
if (!c.multiplicity_destination().empty())
j["multiplicity_destination"] = c.multiplicity_destination();
if (c.access() != access_t::kNone)
j["access"] = to_string(c.access());
if (!c.label().empty())
j["label"] = c.label();
if (const auto &comment = c.comment(); comment)
j["comment"] = comment.value();
}
} // namespace clanguml::common::model

View File

@@ -29,6 +29,18 @@
#include <ostream>
namespace clanguml::common::model {
using nlohmann::json;
void to_json(nlohmann::json &j, const source_location &sl);
void to_json(nlohmann::json &j, const element &c);
void to_json(nlohmann::json &j, const template_parameter &c);
void to_json(nlohmann::json &j, const relationship &c);
} // namespace clanguml::common::model
namespace clanguml::common::generators::json {
using clanguml::common::model::access_t;

View File

@@ -136,6 +136,19 @@ std::string to_string(const diagram_t t)
}
}
std::string to_string(const message_scope_t t)
{
switch (t) {
case message_scope_t::kNormal:
return "normal";
case message_scope_t::kCondition:
return "condition";
default:
assert(false);
return "";
}
}
diagram_t from_string(const std::string &s)
{
if (s == "class")

View File

@@ -80,6 +80,8 @@ std::string to_string(message_t m);
std::string to_string(diagram_t r);
std::string to_string(message_scope_t);
diagram_t from_string(const std::string &s);
} // namespace clanguml::common::model

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,81 @@
/**
* src/sequence_diagram/generators/json/sequence_diagram_generator.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.
*/
#pragma once
#include "common/generators/json/generator.h"
#include "config/config.h"
#include "sequence_diagram/model/diagram.h"
#include "util/util.h"
#include <glob/glob.hpp>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <optional>
#include <sstream>
namespace clanguml::sequence_diagram::generators::json {
using diagram_config = clanguml::config::sequence_diagram;
using diagram_model = clanguml::sequence_diagram::model::diagram;
template <typename C, typename D>
using common_generator = clanguml::common::generators::json::generator<C, D>;
class generator : public common_generator<diagram_config, diagram_model> {
public:
generator(diagram_config &config, diagram_model &model);
void generate_call(const sequence_diagram::model::message &m,
nlohmann::json &parent) const;
common::id_t generate_participant(
nlohmann::json &parent, common::id_t id, bool force = false) const;
void generate_participant(
nlohmann::json &parent, const std::string &name) const;
void generate_activity(common::model::diagram_element::id_t activity_id,
const sequence_diagram::model::activity &a, nlohmann::json &parent,
std::vector<common::model::diagram_element::id_t> &visited,
std::optional<nlohmann::json> nested_block) const;
void generate(std::ostream &ostr) const override;
nlohmann::json &current_block_statement() const
{
assert(!block_statements_stack_.empty());
return block_statements_stack_.back().get();
}
private:
bool is_participant_generated(common::id_t id) const;
std::string render_name(std::string name) const;
mutable std::set<common::id_t> generated_participants_;
mutable nlohmann::json json_;
mutable std::vector<std::reference_wrapper<nlohmann::json>>
block_statements_stack_;
};
} // namespace clanguml::sequence_diagram::generators::json

View File

@@ -433,6 +433,10 @@ void generator::generate(std::ostream &ostr) const
render_mode =
model::function::message_render_mode::no_arguments;
// For methods or functions in diagrams where they are combined into
// file participants, we need to add an 'entry' point call to know
// which method relates to the first activity for this 'start_from'
// condition
if (from.value().type_name() == "method" ||
m_config.combine_free_functions_into_file_participants()) {
ostr << "[->"

View File

@@ -47,4 +47,20 @@ TEST_CASE("t20001", "[test-case][sequence]")
REQUIRE_THAT(puml, HasComment("t20001 test diagram of type sequence"));
save_puml(config.output_directory() + "/" + diagram->name + ".puml", puml);
auto j = generate_sequence_json(diagram, *model);
REQUIRE(j["participants"][0]["name"] == "clanguml::t20001::tmain()");
REQUIRE(j["participants"][1]["name"] == "clanguml::t20001::A");
REQUIRE(j["participants"][2]["name"] == "clanguml::t20001::B");
auto &messages = j["sequences"][0]["messages"];
REQUIRE(messages[0]["name"] == "add(int,int)");
REQUIRE(messages[1]["name"] == "wrap_add3(int,int,int)");
REQUIRE(messages[2]["name"] == "add3(int,int,int)");
REQUIRE(messages[3]["name"] == "add(int,int)");
REQUIRE(messages[4]["name"] == "__log_result(int)__");
REQUIRE(messages[5]["name"] == "__log_result(int)__");
save_json(config.output_directory() + "/" + diagram->name + ".json", j);
}

View File

@@ -39,4 +39,10 @@ TEST_CASE("t20002", "[test-case][sequence]")
REQUIRE_THAT(puml, HasCall(_A("m3()"), _A("m4()"), ""));
save_puml(config.output_directory() + "/" + diagram->name + ".puml", puml);
auto j = generate_sequence_json(diagram, *model);
// REQUIRE(j == nlohmann::json::parse(expected_json));
save_json(config.output_directory() + "/" + diagram->name + ".json", j);
}

View File

@@ -39,4 +39,10 @@ TEST_CASE("t20003", "[test-case][sequence]")
REQUIRE_THAT(puml, HasCall(_A("m3<T>(T)"), _A("m4<T>(T)"), ""));
save_puml(config.output_directory() + "/" + diagram->name + ".puml", puml);
auto j = generate_sequence_json(diagram, *model);
// REQUIRE(j == nlohmann::json::parse(expected_json));
save_json(config.output_directory() + "/" + diagram->name + ".json", j);
}

View File

@@ -57,4 +57,10 @@ TEST_CASE("t20004", "[test-case][sequence]")
REQUIRE_THAT(puml, EndsWith("@enduml\n"));
save_puml(config.output_directory() + "/" + diagram->name + ".puml", puml);
auto j = generate_sequence_json(diagram, *model);
// REQUIRE(j == nlohmann::json::parse(expected_json));
save_json(config.output_directory() + "/" + diagram->name + ".json", j);
}

View File

@@ -40,4 +40,10 @@ TEST_CASE("t20005", "[test-case][sequence]")
REQUIRE_THAT(puml, HasCall(_A("B<T>"), _A("A<T>"), "a(T)"));
REQUIRE_THAT(puml, HasExitpoint(_A("C<T>")));
save_puml(config.output_directory() + "/" + diagram->name + ".puml", puml);
auto j = generate_sequence_json(diagram, *model);
// REQUIRE(j == nlohmann::json::parse(expected_json));
save_json(config.output_directory() + "/" + diagram->name + ".json", j);
}

View File

@@ -70,4 +70,10 @@ TEST_CASE("t20006", "[test-case][sequence]")
REQUIRE_THAT(puml, HasCall(_A("BB<int,float>"), _A("AA<int>"), "aa2(int)"));
save_puml(config.output_directory() + "/" + diagram->name + ".puml", puml);
auto j = generate_sequence_json(diagram, *model);
// REQUIRE(j == nlohmann::json::parse(expected_json));
save_json(config.output_directory() + "/" + diagram->name + ".json", j);
}

View File

@@ -45,4 +45,10 @@ TEST_CASE("t20007", "[test-case][sequence]")
"add(std::string &&,std::string &&,std::string &&)"));
save_puml(config.output_directory() + "/" + diagram->name + ".puml", puml);
auto j = generate_sequence_json(diagram, *model);
// REQUIRE(j == nlohmann::json::parse(expected_json));
save_json(config.output_directory() + "/" + diagram->name + ".json", j);
}

View File

@@ -52,4 +52,10 @@ TEST_CASE("t20008", "[test-case][sequence]")
HasCall(_A("B<std::string>"), _A("A<std::string>"), "a3(std::string)"));
save_puml(config.output_directory() + "/" + diagram->name + ".puml", puml);
auto j = generate_sequence_json(diagram, *model);
// REQUIRE(j == nlohmann::json::parse(expected_json));
save_json(config.output_directory() + "/" + diagram->name + ".json", j);
}

View File

@@ -46,4 +46,10 @@ TEST_CASE("t20009", "[test-case][sequence]")
REQUIRE_THAT(puml, HasCall(_A("tmain()"), _A("B<float>"), "b(float)"));
REQUIRE_THAT(puml, HasCall(_A("B<float>"), _A("A<float>"), "a(float)"));
save_puml(config.output_directory() + "/" + diagram->name + ".puml", puml);
auto j = generate_sequence_json(diagram, *model);
// REQUIRE(j == nlohmann::json::parse(expected_json));
save_json(config.output_directory() + "/" + diagram->name + ".json", j);
}

View File

@@ -48,4 +48,10 @@ TEST_CASE("t20010", "[test-case][sequence]")
REQUIRE_THAT(puml, HasCall(_A("B<int>"), _A("A"), "a4()"));
save_puml(config.output_directory() + "/" + diagram->name + ".puml", puml);
auto j = generate_sequence_json(diagram, *model);
// REQUIRE(j == nlohmann::json::parse(expected_json));
save_json(config.output_directory() + "/" + diagram->name + ".json", j);
}

View File

@@ -44,4 +44,10 @@ TEST_CASE("t20011", "[test-case][sequence]")
REQUIRE_THAT(puml, HasCall(_A("A"), _A("A"), "b(int)"));
save_puml(config.output_directory() + "/" + diagram->name + ".puml", puml);
auto j = generate_sequence_json(diagram, *model);
// REQUIRE(j == nlohmann::json::parse(expected_json));
save_json(config.output_directory() + "/" + diagram->name + ".json", j);
}

View File

@@ -77,4 +77,10 @@ TEST_CASE("t20012", "[test-case][sequence]")
REQUIRE_THAT(puml, HasCall(_A("tmain()"), _A("D"), "add5(int)"));
save_puml(config.output_directory() + "/" + diagram->name + ".puml", puml);
auto j = generate_sequence_json(diagram, *model);
// REQUIRE(j == nlohmann::json::parse(expected_json));
save_json(config.output_directory() + "/" + diagram->name + ".json", j);
}

View File

@@ -46,4 +46,10 @@ TEST_CASE("t20013", "[test-case][sequence]")
REQUIRE_THAT(puml, HasCall(_A("B"), _A("A"), "a3(const char *)"));
save_puml(config.output_directory() + "/" + diagram->name + ".puml", puml);
auto j = generate_sequence_json(diagram, *model);
// REQUIRE(j == nlohmann::json::parse(expected_json));
save_json(config.output_directory() + "/" + diagram->name + ".json", j);
}

View File

@@ -45,4 +45,10 @@ TEST_CASE("t20014", "[test-case][sequence]")
REQUIRE_THAT(puml, HasCall(_A("C<B,int>"), _A("B"), "b1(int,int)"));
save_puml(config.output_directory() + "/" + diagram->name + ".puml", puml);
auto j = generate_sequence_json(diagram, *model);
// REQUIRE(j == nlohmann::json::parse(expected_json));
save_json(config.output_directory() + "/" + diagram->name + ".json", j);
}

View File

@@ -47,4 +47,10 @@ TEST_CASE("t20015", "[test-case][sequence]")
REQUIRE_THAT(puml, !HasCall(_A("B"), _A("B"), "set_z(int)"));
save_puml(config.output_directory() + "/" + diagram->name + ".puml", puml);
auto j = generate_sequence_json(diagram, *model);
// REQUIRE(j == nlohmann::json::parse(expected_json));
save_json(config.output_directory() + "/" + diagram->name + ".json", j);
}

View File

@@ -42,4 +42,10 @@ TEST_CASE("t20016", "[test-case][sequence]")
REQUIRE_THAT(puml, HasCall(_A("B<long>"), _A("A"), "a2(const long &)"));
save_puml(config.output_directory() + "/" + diagram->name + ".puml", puml);
auto j = generate_sequence_json(diagram, *model);
// REQUIRE(j == nlohmann::json::parse(expected_json));
save_json(config.output_directory() + "/" + diagram->name + ".json", j);
}

View File

@@ -49,4 +49,10 @@ TEST_CASE("t20017", "[test-case][sequence]")
REQUIRE_THAT(puml, HasExitpoint(_A("t20017.cc")));
save_puml(config.output_directory() + "/" + diagram->name + ".puml", puml);
auto j = generate_sequence_json(diagram, *model);
// REQUIRE(j == nlohmann::json::parse(expected_json));
save_json(config.output_directory() + "/" + diagram->name + ".json", j);
}

View File

@@ -52,4 +52,10 @@ TEST_CASE("t20018", "[test-case][sequence]")
HasCall(_A("Factorial<1>"), _A("Factorial<0>"), "__print(int)__"));
save_puml(config.output_directory() + "/" + diagram->name + ".puml", puml);
auto j = generate_sequence_json(diagram, *model);
// REQUIRE(j == nlohmann::json::parse(expected_json));
save_json(config.output_directory() + "/" + diagram->name + ".json", j);
}

View File

@@ -41,4 +41,10 @@ TEST_CASE("t20019", "[test-case][sequence]")
REQUIRE_THAT(puml, HasCall(_A("Base<D2>"), _A("D2"), "impl()"));
save_puml(config.output_directory() + "/" + diagram->name + ".puml", puml);
auto j = generate_sequence_json(diagram, *model);
// REQUIRE(j == nlohmann::json::parse(expected_json));
save_json(config.output_directory() + "/" + diagram->name + ".json", j);
}

View File

@@ -51,4 +51,10 @@ TEST_CASE("t20020", "[test-case][sequence]")
puml, HasCallInControlCondition(_A("tmain()"), _A("C"), "c3(int)"));
save_puml(config.output_directory() + "/" + diagram->name + ".puml", puml);
auto j = generate_sequence_json(diagram, *model);
// REQUIRE(j == nlohmann::json::parse(expected_json));
save_json(config.output_directory() + "/" + diagram->name + ".json", j);
}

View File

@@ -58,4 +58,10 @@ TEST_CASE("t20021", "[test-case][sequence]")
puml, HasCallInControlCondition(_A("tmain()"), _A("C"), "contents()"));
save_puml(config.output_directory() + "/" + diagram->name + ".puml", puml);
auto j = generate_sequence_json(diagram, *model);
// REQUIRE(j == nlohmann::json::parse(expected_json));
save_json(config.output_directory() + "/" + diagram->name + ".json", j);
}

View File

@@ -39,4 +39,10 @@ TEST_CASE("t20022", "[test-case][sequence]")
REQUIRE_THAT(puml, HasCall(_A("A"), _A("B"), "b()"));
save_puml(config.output_directory() + "/" + diagram->name + ".puml", puml);
auto j = generate_sequence_json(diagram, *model);
// REQUIRE(j == nlohmann::json::parse(expected_json));
save_json(config.output_directory() + "/" + diagram->name + ".json", j);
}

View File

@@ -42,4 +42,10 @@ TEST_CASE("t20023", "[test-case][sequence]")
REQUIRE_THAT(puml, HasCall(_A("A"), _A("A"), "a4()"));
save_puml(config.output_directory() + "/" + diagram->name + ".puml", puml);
auto j = generate_sequence_json(diagram, *model);
// REQUIRE(j == nlohmann::json::parse(expected_json));
save_json(config.output_directory() + "/" + diagram->name + ".json", j);
}

View File

@@ -47,4 +47,10 @@ TEST_CASE("t20024", "[test-case][sequence]")
REQUIRE_THAT(puml, HasCall(_A("B"), _A("B"), "green()"));
save_puml(config.output_directory() + "/" + diagram->name + ".puml", puml);
auto j = generate_sequence_json(diagram, *model);
// REQUIRE(j == nlohmann::json::parse(expected_json));
save_json(config.output_directory() + "/" + diagram->name + ".json", j);
}

View File

@@ -42,4 +42,10 @@ TEST_CASE("t20025", "[test-case][sequence]")
REQUIRE_THAT(puml, !HasCall(_A("tmain()"), _A("add2(int,int)"), ""));
save_puml(config.output_directory() + "/" + diagram->name + ".puml", puml);
auto j = generate_sequence_json(diagram, *model);
// REQUIRE(j == nlohmann::json::parse(expected_json));
save_json(config.output_directory() + "/" + diagram->name + ".json", j);
}

View File

@@ -38,4 +38,10 @@ TEST_CASE("t20026", "[test-case][sequence]")
REQUIRE_THAT(puml, HasCall(_A("tmain()"), _A("A"), "a()"));
save_puml(config.output_directory() + "/" + diagram->name + ".puml", puml);
auto j = generate_sequence_json(diagram, *model);
// REQUIRE(j == nlohmann::json::parse(expected_json));
save_json(config.output_directory() + "/" + diagram->name + ".json", j);
}

View File

@@ -40,4 +40,10 @@ TEST_CASE("t20027", "[test-case][sequence]")
REQUIRE_THAT(puml, !HasCall(_A("A"), _A("A"), "aaa()"));
save_puml(config.output_directory() + "/" + diagram->name + ".puml", puml);
auto j = generate_sequence_json(diagram, *model);
// REQUIRE(j == nlohmann::json::parse(expected_json));
save_json(config.output_directory() + "/" + diagram->name + ".json", j);
}

View File

@@ -42,4 +42,10 @@ TEST_CASE("t20028", "[test-case][sequence]")
REQUIRE_THAT(puml, !HasCall(_A("tmain()"), _A("B"), "e()"));
save_puml(config.output_directory() + "/" + diagram->name + ".puml", puml);
auto j = generate_sequence_json(diagram, *model);
// REQUIRE(j == nlohmann::json::parse(expected_json));
save_json(config.output_directory() + "/" + diagram->name + ".json", j);
}

View File

@@ -57,4 +57,231 @@ TEST_CASE("t20029", "[test-case][sequence]")
!HasCall(_A("ConnectionPool"), _A("ConnectionPool"), "connect_impl()"));
save_puml(config.output_directory() + "/" + diagram->name + ".puml", puml);
const std::string expected_json = R"###(
{
"diagram_type": "sequence",
"name": "t20029_sequence",
"participants": [
{
"id": "2091374738808319642",
"name": "clanguml::t20029::tmain()",
"source_location": {
"file": "../../tests/t20029/t20029.cc",
"line": 55
},
"type": "function"
},
{
"id": "1673261195873192383",
"name": "clanguml::t20029::Encoder<clanguml::t20029::Retrier<clanguml::t20029::ConnectionPool>>",
"source_location": {
"file": "../../tests/t20029/t20029.cc",
"line": 11
},
"type": "class"
},
{
"id": "658058855590948094",
"name": "clanguml::t20029::Retrier<clanguml::t20029::ConnectionPool>",
"source_location": {
"file": "../../tests/t20029/t20029.cc",
"line": 22
},
"type": "class"
},
{
"id": "1896406205097618937",
"name": "clanguml::t20029::ConnectionPool",
"source_location": {
"file": "../../tests/t20029/t20029.cc",
"line": 39
},
"type": "class"
},
{
"id": "1362646431260879440",
"name": "clanguml::t20029::encode_b64(std::string &&)",
"source_location": {
"file": "../../tests/t20029/t20029.cc",
"line": 9
},
"type": "function"
}
],
"sequences": [
{
"messages": [
{
"from": {
"id": "2091374738808319642",
"name": "clanguml::t20029::tmain()"
},
"name": "connect()",
"return_type": "void",
"scope": "normal",
"source_location": {
"file": "../../tests/t20029/t20029.cc",
"line": 59
},
"to": {
"activity_id": "940428568182104530",
"activity_name": "clanguml::t20029::ConnectionPool::connect()",
"participant_id": "1896406205097618937",
"participant_name": "clanguml::t20029::ConnectionPool"
},
"type": "message"
},
{
"activity_id": "2091374738808319642",
"messages": [
{
"activity_id": "2091374738808319642",
"branches": [
{
"messages": [
{
"from": {
"id": "2091374738808319642",
"name": "clanguml::t20029::tmain()"
},
"name": "send(std::string &&)",
"return_type": "_Bool",
"scope": "condition",
"source_location": {
"file": "../../tests/t20029/t20029.cc",
"line": 62
},
"to": {
"activity_id": "2026763864005979273",
"activity_name": "clanguml::t20029::Encoder<clanguml::t20029::Retrier<clanguml::t20029::ConnectionPool>>::send(std::string &&)",
"participant_id": "1673261195873192383",
"participant_name": "clanguml::t20029::Encoder<clanguml::t20029::Retrier<clanguml::t20029::ConnectionPool>>"
},
"type": "message"
},
{
"from": {
"id": "2026763864005979273",
"name": "clanguml::t20029::Encoder<clanguml::t20029::Retrier<clanguml::t20029::ConnectionPool>>::send(std::string &&)"
},
"name": "encode(std::string &&)",
"return_type": "std::string",
"scope": "normal",
"source_location": {
"file": "../../tests/t20029/t20029.cc",
"line": 15
},
"to": {
"activity_id": "1468258269466480773",
"activity_name": "clanguml::t20029::Encoder<clanguml::t20029::Retrier<clanguml::t20029::ConnectionPool>>::encode(std::string &&)",
"participant_id": "1673261195873192383",
"participant_name": "clanguml::t20029::Encoder<clanguml::t20029::Retrier<clanguml::t20029::ConnectionPool>>"
},
"type": "message"
},
{
"from": {
"id": "1468258269466480773",
"name": "clanguml::t20029::Encoder<clanguml::t20029::Retrier<clanguml::t20029::ConnectionPool>>::encode(std::string &&)"
},
"name": "",
"return_type": "",
"scope": "normal",
"source_location": {
"file": "../../tests/t20029/t20029.cc",
"line": 19
},
"to": {
"activity_id": "1362646431260879440",
"activity_name": "clanguml::t20029::encode_b64(std::string &&)"
},
"type": "message"
},
{
"from": {
"id": "2026763864005979273",
"name": "clanguml::t20029::Encoder<clanguml::t20029::Retrier<clanguml::t20029::ConnectionPool>>::send(std::string &&)"
},
"name": "send(std::string &&)",
"return_type": "_Bool",
"scope": "normal",
"source_location": {
"file": "../../tests/t20029/t20029.cc",
"line": 15
},
"to": {
"activity_id": "30515971485361302",
"activity_name": "clanguml::t20029::Retrier<clanguml::t20029::ConnectionPool>::send(std::string &&)",
"participant_id": "658058855590948094",
"participant_name": "clanguml::t20029::Retrier<clanguml::t20029::ConnectionPool>"
},
"type": "message"
},
{
"activity_id": "30515971485361302",
"messages": [
{
"activity_id": "30515971485361302",
"branches": [
{
"messages": [
{
"from": {
"id": "30515971485361302",
"name": "clanguml::t20029::Retrier<clanguml::t20029::ConnectionPool>::send(std::string &&)"
},
"name": "send(const std::string &)",
"return_type": "_Bool",
"scope": "condition",
"source_location": {
"file": "../../tests/t20029/t20029.cc",
"line": 31
},
"to": {
"activity_id": "972625940114169157",
"activity_name": "clanguml::t20029::ConnectionPool::send(const std::string &)",
"participant_id": "1896406205097618937",
"participant_name": "clanguml::t20029::ConnectionPool"
},
"type": "message"
}
],
"type": "consequent"
}
],
"name": "if",
"type": "alt"
}
],
"name": "while",
"type": "loop"
}
],
"type": "consequent"
}
],
"name": "if",
"type": "alt"
}
],
"name": "for",
"type": "loop"
}
],
"start_from": {
"id": 2091374738808319700,
"location": "clanguml::t20029::tmain()"
}
}
],
"using_namespace": "clanguml::t20029"
}
)###";
auto j = generate_sequence_json(diagram, *model);
REQUIRE(j == nlohmann::json::parse(expected_json));
save_json(config.output_directory() + "/" + diagram->name + ".json", j);
}

View File

@@ -142,6 +142,20 @@ std::string generate_sequence_puml(
return ss.str();
}
nlohmann::json generate_sequence_json(
std::shared_ptr<clanguml::config::diagram> config,
clanguml::sequence_diagram::model::diagram &model)
{
using namespace clanguml::sequence_diagram::generators::json;
std::stringstream ss;
ss << generator(
dynamic_cast<clanguml::config::sequence_diagram &>(*config), model);
return nlohmann::json::parse(ss.str());
}
std::string generate_class_puml(
std::shared_ptr<clanguml::config::diagram> config,
clanguml::class_diagram::model::diagram &model)

View File

@@ -68,3 +68,12 @@ with open(r'tests/test_cases.yaml') as f:
copyfile(f'debug/tests/puml/{diagram_name}.svg',
f'docs/test_cases/{diagram_name}.svg')
tc.write(f'![{diagram_name}](./{diagram_name}.svg "{test_case["title"]}")\n')
tc.write("## Generated JSON models\n")
for diagram_name, _ in config_dict['diagrams'].items():
if os.path.exists(f'debug/tests/puml/{diagram_name}.json'):
with open(f'debug/tests/puml/{diagram_name}.json') as f:
contents = f.read()
tc.write("```json\n")
tc.write(contents)
tc.write("\n```\n")