Added option to include if and loop condition text in the diagram (fixes #162)
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
* [Lambda expressions in sequence diagrams](#lambda-expressions-in-sequence-diagrams)
|
||||
* [Customizing participants order](#customizing-participants-order)
|
||||
* [Generating return types](#generating-return-types)
|
||||
* [Generating condition statements](#generating-condition-statements)
|
||||
|
||||
<!-- tocstop -->
|
||||
|
||||
@@ -263,3 +264,15 @@ generate_return_types: true
|
||||
This option only affects the `plantuml` generation, in `json` generator
|
||||
`return_type` property is always present in the message nodes.
|
||||
|
||||
|
||||
## Generating condition statements
|
||||
Sometimes, it is useful to include actual condition statements (for instance
|
||||
contents of the `if()` condition in the `alt` or `loop` blocks in the sequence
|
||||
diagrams, to make them more readable.
|
||||
|
||||
This can be enabled using the following option:
|
||||
|
||||
```yaml
|
||||
generate_condition_statements: true
|
||||
```
|
||||
|
||||
|
||||
@@ -564,6 +564,75 @@ bool is_type_token(const std::string &t)
|
||||
(is_identifier(t) && !is_qualifier(t) && !is_bracket(t));
|
||||
}
|
||||
|
||||
std::string format_condition_text(const std::string &condition_text)
|
||||
{
|
||||
std::string result{condition_text};
|
||||
|
||||
if (result.size() < 2)
|
||||
return {};
|
||||
|
||||
std::vector<std::string> text_lines = util::split(result, "\n", true);
|
||||
|
||||
// Trim each line
|
||||
for (auto &line : text_lines) {
|
||||
line = util::trim(line);
|
||||
}
|
||||
|
||||
result = util::join(" ", text_lines);
|
||||
|
||||
if (result.at(0) == '(' && result.back() == ')')
|
||||
return result.substr(1, result.size() - 2);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string get_condition_text(clang::SourceManager &sm, clang::IfStmt *stmt)
|
||||
{
|
||||
auto condition_range =
|
||||
clang::SourceRange(stmt->getLParenLoc(), stmt->getRParenLoc());
|
||||
|
||||
return format_condition_text(get_source_text(condition_range, sm));
|
||||
}
|
||||
|
||||
std::string get_condition_text(clang::SourceManager &sm, clang::WhileStmt *stmt)
|
||||
{
|
||||
auto condition_range =
|
||||
clang::SourceRange(stmt->getLParenLoc(), stmt->getRParenLoc());
|
||||
|
||||
return format_condition_text(get_source_text(condition_range, sm));
|
||||
}
|
||||
|
||||
std::string get_condition_text(
|
||||
clang::SourceManager &sm, clang::CXXForRangeStmt *stmt)
|
||||
{
|
||||
auto condition_range = stmt->getRangeStmt()->getSourceRange();
|
||||
|
||||
return format_condition_text(get_source_text(condition_range, sm));
|
||||
}
|
||||
|
||||
std::string get_condition_text(clang::SourceManager &sm, clang::ForStmt *stmt)
|
||||
{
|
||||
auto condition_range =
|
||||
clang::SourceRange(stmt->getLParenLoc(), stmt->getRParenLoc());
|
||||
|
||||
return format_condition_text(get_source_text(condition_range, sm));
|
||||
}
|
||||
|
||||
std::string get_condition_text(clang::SourceManager &sm, clang::DoStmt *stmt)
|
||||
{
|
||||
auto condition_range = stmt->getCond()->getSourceRange();
|
||||
|
||||
return format_condition_text(get_source_text(condition_range, sm));
|
||||
}
|
||||
|
||||
std::string get_condition_text(
|
||||
clang::SourceManager &sm, clang::ConditionalOperator *stmt)
|
||||
{
|
||||
auto condition_range = stmt->getCond()->getSourceRange();
|
||||
|
||||
return format_condition_text(get_source_text(condition_range, sm));
|
||||
}
|
||||
|
||||
clang::QualType dereference(clang::QualType type)
|
||||
{
|
||||
auto res = type;
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "types.h"
|
||||
#include "util/util.h"
|
||||
|
||||
#include <clang/AST/Expr.h>
|
||||
#include <clang/AST/RecursiveASTVisitor.h>
|
||||
|
||||
#include <deque>
|
||||
@@ -248,6 +249,23 @@ bool is_qualified_identifier(const std::string &t);
|
||||
|
||||
bool is_type_token(const std::string &t);
|
||||
|
||||
std::string format_condition_text(const std::string &condition_text);
|
||||
|
||||
std::string get_condition_text(clang::SourceManager &sm, clang::IfStmt *stmt);
|
||||
|
||||
std::string get_condition_text(
|
||||
clang::SourceManager &sm, clang::WhileStmt *stmt);
|
||||
|
||||
std::string get_condition_text(
|
||||
clang::SourceManager &sm, clang::CXXForRangeStmt *stmt);
|
||||
|
||||
std::string get_condition_text(clang::SourceManager &sm, clang::ForStmt *stmt);
|
||||
|
||||
std::string get_condition_text(clang::SourceManager &sm, clang::DoStmt *stmt);
|
||||
|
||||
std::string get_condition_text(
|
||||
clang::SourceManager &sm, clang::ConditionalOperator *stmt);
|
||||
|
||||
clang::QualType dereference(clang::QualType type);
|
||||
|
||||
/**
|
||||
|
||||
@@ -168,6 +168,8 @@ void inheritable_diagram_options::inherit(
|
||||
combine_free_functions_into_file_participants.override(
|
||||
parent.combine_free_functions_into_file_participants);
|
||||
generate_return_types.override(parent.generate_return_types);
|
||||
generate_condition_statements.override(
|
||||
parent.generate_condition_statements);
|
||||
debug_mode.override(parent.debug_mode);
|
||||
generate_metadata.override(parent.generate_metadata);
|
||||
}
|
||||
|
||||
@@ -446,6 +446,8 @@ struct inheritable_diagram_options {
|
||||
option<bool> combine_free_functions_into_file_participants{
|
||||
"combine_free_functions_into_file_participants", false};
|
||||
option<bool> generate_return_types{"generate_return_types", false};
|
||||
option<bool> generate_condition_statements{
|
||||
"generate_condition_statements", false};
|
||||
option<std::vector<std::string>> participants_order{"participants_order"};
|
||||
option<bool> debug_mode{"debug_mode", false};
|
||||
option<bool> generate_metadata{"generate_metadata", true};
|
||||
|
||||
@@ -568,6 +568,7 @@ template <> struct convert<sequence_diagram> {
|
||||
get_option(node, rhs.start_from);
|
||||
get_option(node, rhs.combine_free_functions_into_file_participants);
|
||||
get_option(node, rhs.generate_return_types);
|
||||
get_option(node, rhs.generate_condition_statements);
|
||||
get_option(node, rhs.relative_to);
|
||||
get_option(node, rhs.participants_order);
|
||||
get_option(node, rhs.generate_method_arguments);
|
||||
@@ -756,6 +757,7 @@ template <> struct convert<config> {
|
||||
get_option(node, rhs.generate_metadata);
|
||||
get_option(node, rhs.combine_free_functions_into_file_participants);
|
||||
get_option(node, rhs.generate_return_types);
|
||||
get_option(node, rhs.generate_condition_statements);
|
||||
|
||||
rhs.base_directory.set(node["__parent_path"].as<std::string>());
|
||||
get_option(node, rhs.relative_to);
|
||||
|
||||
@@ -290,6 +290,7 @@ YAML::Emitter &operator<<(
|
||||
out << c.comment_parser;
|
||||
out << c.combine_free_functions_into_file_participants;
|
||||
out << c.generate_return_types;
|
||||
out << c.generate_condition_statements;
|
||||
out << c.participants_order;
|
||||
out << c.debug_mode;
|
||||
|
||||
|
||||
@@ -231,7 +231,7 @@ void generator::generate_activity(const activity &a,
|
||||
process_conditional_message(m);
|
||||
break;
|
||||
case message_t::kConditionalElse:
|
||||
process_conditional_else_message();
|
||||
process_conditional_else_message(m);
|
||||
break;
|
||||
case message_t::kConditionalEnd:
|
||||
process_end_conditional_message();
|
||||
@@ -279,6 +279,8 @@ void generator::process_while_message(const message &m) const
|
||||
while_block["type"] = "loop";
|
||||
while_block["name"] = "while";
|
||||
while_block["activity_id"] = std::to_string(m.from());
|
||||
if (auto text = m.condition_text(); text.has_value())
|
||||
while_block["condition_text"] = *text;
|
||||
|
||||
current_block_statement()["messages"].push_back(std::move(while_block));
|
||||
|
||||
@@ -298,6 +300,8 @@ void generator::process_for_message(const message &m) const
|
||||
for_block["type"] = "loop";
|
||||
for_block["name"] = "for";
|
||||
for_block["activity_id"] = std::to_string(m.from());
|
||||
if (auto text = m.condition_text(); text.has_value())
|
||||
for_block["condition_text"] = *text;
|
||||
|
||||
current_block_statement()["messages"].push_back(std::move(for_block));
|
||||
|
||||
@@ -317,6 +321,8 @@ void generator::process_do_message(const message &m) const
|
||||
do_block["type"] = "loop";
|
||||
do_block["name"] = "do";
|
||||
do_block["activity_id"] = std::to_string(m.from());
|
||||
if (auto text = m.condition_text(); text.has_value())
|
||||
do_block["condition_text"] = *text;
|
||||
|
||||
current_block_statement()["messages"].push_back(std::move(do_block));
|
||||
|
||||
@@ -413,6 +419,8 @@ void generator::process_conditional_message(const message &m) const
|
||||
if_block["type"] = "alt";
|
||||
if_block["name"] = "conditional";
|
||||
if_block["activity_id"] = std::to_string(m.from());
|
||||
if (auto text = m.condition_text(); text.has_value())
|
||||
if_block["condition_text"] = *text;
|
||||
|
||||
current_block_statement()["messages"].push_back(std::move(if_block));
|
||||
|
||||
@@ -427,13 +435,15 @@ void generator::process_conditional_message(const message &m) const
|
||||
std::ref(current_block_statement()["branches"].back()));
|
||||
}
|
||||
|
||||
void generator::process_conditional_else_message() const
|
||||
void generator::process_conditional_else_message(const message &m) const
|
||||
{
|
||||
// remove previous branch from the stack
|
||||
block_statements_stack_.pop_back();
|
||||
|
||||
nlohmann::json branch;
|
||||
branch["type"] = "alternative";
|
||||
if (auto text = m.condition_text(); text.has_value())
|
||||
branch["condition_text"] = *text;
|
||||
current_block_statement()["branches"].push_back(std::move(branch));
|
||||
|
||||
block_statements_stack_.push_back(
|
||||
@@ -477,6 +487,8 @@ void generator::process_if_message(const message &m) const
|
||||
if_block["type"] = "alt";
|
||||
if_block["name"] = "if";
|
||||
if_block["activity_id"] = std::to_string(m.from());
|
||||
if (auto text = m.condition_text(); text.has_value())
|
||||
if_block["condition_text"] = *text;
|
||||
|
||||
current_block_statement()["messages"].push_back(std::move(if_block));
|
||||
|
||||
|
||||
@@ -158,8 +158,10 @@ private:
|
||||
|
||||
/**
|
||||
* @brief Process conditional else statement message
|
||||
*
|
||||
* @param m Message model
|
||||
*/
|
||||
void process_conditional_else_message() const;
|
||||
void process_conditional_else_message(const model::message &m) const;
|
||||
|
||||
/**
|
||||
* @brief Process `switch` statement message
|
||||
|
||||
@@ -178,11 +178,17 @@ void generator::generate_activity(const activity &a, std::ostream &ostr,
|
||||
}
|
||||
else if (m.type() == message_t::kIf) {
|
||||
print_debug(m, ostr);
|
||||
ostr << "alt\n";
|
||||
ostr << "alt";
|
||||
if (m.condition_text())
|
||||
ostr << " " << m.condition_text().value();
|
||||
ostr << '\n';
|
||||
}
|
||||
else if (m.type() == message_t::kElseIf) {
|
||||
print_debug(m, ostr);
|
||||
ostr << "else\n";
|
||||
ostr << "else";
|
||||
if (m.condition_text())
|
||||
ostr << " " << m.condition_text().value();
|
||||
ostr << '\n';
|
||||
}
|
||||
else if (m.type() == message_t::kElse) {
|
||||
print_debug(m, ostr);
|
||||
@@ -193,21 +199,30 @@ void generator::generate_activity(const activity &a, std::ostream &ostr,
|
||||
}
|
||||
else if (m.type() == message_t::kWhile) {
|
||||
print_debug(m, ostr);
|
||||
ostr << "loop\n";
|
||||
ostr << "loop";
|
||||
if (m.condition_text())
|
||||
ostr << " " << m.condition_text().value();
|
||||
ostr << '\n';
|
||||
}
|
||||
else if (m.type() == message_t::kWhileEnd) {
|
||||
ostr << "end\n";
|
||||
}
|
||||
else if (m.type() == message_t::kFor) {
|
||||
print_debug(m, ostr);
|
||||
ostr << "loop\n";
|
||||
ostr << "loop";
|
||||
if (m.condition_text())
|
||||
ostr << " " << m.condition_text().value();
|
||||
ostr << '\n';
|
||||
}
|
||||
else if (m.type() == message_t::kForEnd) {
|
||||
ostr << "end\n";
|
||||
}
|
||||
else if (m.type() == message_t::kDo) {
|
||||
print_debug(m, ostr);
|
||||
ostr << "loop\n";
|
||||
ostr << "loop";
|
||||
if (m.condition_text())
|
||||
ostr << " " << m.condition_text().value();
|
||||
ostr << '\n';
|
||||
}
|
||||
else if (m.type() == message_t::kDoEnd) {
|
||||
ostr << "end\n";
|
||||
@@ -237,7 +252,10 @@ void generator::generate_activity(const activity &a, std::ostream &ostr,
|
||||
}
|
||||
else if (m.type() == message_t::kConditional) {
|
||||
print_debug(m, ostr);
|
||||
ostr << "alt\n";
|
||||
ostr << "alt";
|
||||
if (m.condition_text())
|
||||
ostr << " " << m.condition_text().value();
|
||||
ostr << '\n';
|
||||
}
|
||||
else if (m.type() == message_t::kConditionalElse) {
|
||||
print_debug(m, ostr);
|
||||
|
||||
@@ -57,4 +57,17 @@ void message::set_message_scope(common::model::message_scope_t scope)
|
||||
|
||||
common::model::message_scope_t message::message_scope() const { return scope_; }
|
||||
|
||||
void message::condition_text(const std::string &condition_text)
|
||||
{
|
||||
if (condition_text.empty())
|
||||
condition_text_ = std::nullopt;
|
||||
else
|
||||
condition_text_ = condition_text;
|
||||
}
|
||||
|
||||
std::optional<std::string> message::condition_text() const
|
||||
{
|
||||
return condition_text_;
|
||||
}
|
||||
|
||||
} // namespace clanguml::sequence_diagram::model
|
||||
|
||||
@@ -128,6 +128,20 @@ public:
|
||||
*/
|
||||
common::model::message_scope_t message_scope() const;
|
||||
|
||||
/**
|
||||
* @brief Set condition text for block statements (e.g. if(<THIS TEXT>))
|
||||
*
|
||||
* @param condition_text Condition text
|
||||
*/
|
||||
void condition_text(const std::string &condition_text);
|
||||
|
||||
/**
|
||||
* @brief Get condition text
|
||||
*
|
||||
* @return Block statement condition text
|
||||
*/
|
||||
std::optional<std::string> condition_text() const;
|
||||
|
||||
private:
|
||||
common::model::message_t type_{common::model::message_t::kNone};
|
||||
|
||||
@@ -143,6 +157,8 @@ private:
|
||||
std::string message_name_{};
|
||||
|
||||
std::string return_type_{};
|
||||
|
||||
std::optional<std::string> condition_text_;
|
||||
};
|
||||
|
||||
} // namespace clanguml::sequence_diagram::model
|
||||
|
||||
@@ -307,15 +307,25 @@ bool call_expression_context::is_expr_in_current_control_statement_condition(
|
||||
const clang::Stmt *stmt) const
|
||||
{
|
||||
if (current_ifstmt() != nullptr) {
|
||||
if (common::is_subexpr_of(current_ifstmt()->getCond(), stmt)) {
|
||||
if (common::is_subexpr_of(current_ifstmt()->getCond(), stmt))
|
||||
return true;
|
||||
|
||||
if (const auto *condition_decl_stmt = current_ifstmt()->getInit();
|
||||
condition_decl_stmt != nullptr) {
|
||||
if (common::is_subexpr_of(condition_decl_stmt, stmt))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (current_elseifstmt() != nullptr) {
|
||||
if (common::is_subexpr_of(current_elseifstmt()->getCond(), stmt)) {
|
||||
if (common::is_subexpr_of(current_elseifstmt()->getCond(), stmt))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (current_conditionaloperator() != nullptr) {
|
||||
if (common::is_subexpr_of(
|
||||
current_conditionaloperator()->getCond(), stmt))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (const auto *loop_stmt = current_loopstmt(); loop_stmt != nullptr) {
|
||||
|
||||
@@ -584,6 +584,10 @@ bool translation_unit_visitor::TraverseIfStmt(clang::IfStmt *stmt)
|
||||
const auto current_caller_id = context().caller_id();
|
||||
const auto *current_ifstmt = context().current_ifstmt();
|
||||
|
||||
std::string condition_text;
|
||||
if (config().generate_condition_statements())
|
||||
condition_text = common::get_condition_text(source_manager(), stmt);
|
||||
|
||||
// Check if this is a beginning of a new if statement, or an
|
||||
// else if condition of the current if statement
|
||||
if (current_ifstmt != nullptr) {
|
||||
@@ -601,6 +605,7 @@ bool translation_unit_visitor::TraverseIfStmt(clang::IfStmt *stmt)
|
||||
|
||||
message m{message_t::kElseIf, current_caller_id};
|
||||
set_source_location(*stmt, m);
|
||||
m.condition_text(condition_text);
|
||||
diagram().add_block_message(std::move(m));
|
||||
}
|
||||
else {
|
||||
@@ -608,6 +613,7 @@ bool translation_unit_visitor::TraverseIfStmt(clang::IfStmt *stmt)
|
||||
|
||||
message m{message_t::kIf, current_caller_id};
|
||||
set_source_location(*stmt, m);
|
||||
m.condition_text(condition_text);
|
||||
diagram().add_block_message(std::move(m));
|
||||
}
|
||||
}
|
||||
@@ -631,10 +637,15 @@ bool translation_unit_visitor::TraverseWhileStmt(clang::WhileStmt *stmt)
|
||||
|
||||
const auto current_caller_id = context().caller_id();
|
||||
|
||||
std::string condition_text;
|
||||
if (config().generate_condition_statements())
|
||||
condition_text = common::get_condition_text(source_manager(), stmt);
|
||||
|
||||
if (current_caller_id != 0) {
|
||||
context().enter_loopstmt(stmt);
|
||||
message m{message_t::kWhile, current_caller_id};
|
||||
set_source_location(*stmt, m);
|
||||
m.condition_text(condition_text);
|
||||
diagram().add_block_message(std::move(m));
|
||||
}
|
||||
RecursiveASTVisitor<translation_unit_visitor>::TraverseWhileStmt(stmt);
|
||||
@@ -656,10 +667,15 @@ bool translation_unit_visitor::TraverseDoStmt(clang::DoStmt *stmt)
|
||||
|
||||
const auto current_caller_id = context().caller_id();
|
||||
|
||||
std::string condition_text;
|
||||
if (config().generate_condition_statements())
|
||||
condition_text = common::get_condition_text(source_manager(), stmt);
|
||||
|
||||
if (current_caller_id != 0) {
|
||||
context().enter_loopstmt(stmt);
|
||||
message m{message_t::kDo, current_caller_id};
|
||||
set_source_location(*stmt, m);
|
||||
m.condition_text(condition_text);
|
||||
diagram().add_block_message(std::move(m));
|
||||
}
|
||||
|
||||
@@ -682,10 +698,15 @@ bool translation_unit_visitor::TraverseForStmt(clang::ForStmt *stmt)
|
||||
|
||||
const auto current_caller_id = context().caller_id();
|
||||
|
||||
std::string condition_text;
|
||||
if (config().generate_condition_statements())
|
||||
condition_text = common::get_condition_text(source_manager(), stmt);
|
||||
|
||||
if (current_caller_id != 0) {
|
||||
context().enter_loopstmt(stmt);
|
||||
message m{message_t::kFor, current_caller_id};
|
||||
set_source_location(*stmt, m);
|
||||
m.condition_text(condition_text);
|
||||
diagram().add_block_message(std::move(m));
|
||||
}
|
||||
|
||||
@@ -761,10 +782,15 @@ bool translation_unit_visitor::TraverseCXXForRangeStmt(
|
||||
|
||||
const auto current_caller_id = context().caller_id();
|
||||
|
||||
std::string condition_text;
|
||||
if (config().generate_condition_statements())
|
||||
condition_text = common::get_condition_text(source_manager(), stmt);
|
||||
|
||||
if (current_caller_id != 0) {
|
||||
context().enter_loopstmt(stmt);
|
||||
message m{message_t::kFor, current_caller_id};
|
||||
set_source_location(*stmt, m);
|
||||
m.condition_text(condition_text);
|
||||
diagram().add_block_message(std::move(m));
|
||||
}
|
||||
|
||||
@@ -847,10 +873,15 @@ bool translation_unit_visitor::TraverseConditionalOperator(
|
||||
|
||||
const auto current_caller_id = context().caller_id();
|
||||
|
||||
std::string condition_text;
|
||||
if (config().generate_condition_statements())
|
||||
condition_text = common::get_condition_text(source_manager(), stmt);
|
||||
|
||||
if (current_caller_id != 0) {
|
||||
context().enter_conditionaloperator(stmt);
|
||||
model::message m{message_t::kConditional, current_caller_id};
|
||||
set_source_location(*stmt, m);
|
||||
m.condition_text(condition_text);
|
||||
diagram().add_block_message(std::move(m));
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,8 @@ TEST_CASE("t20028", "[test-case][sequence]")
|
||||
REQUIRE_THAT(puml, EndsWith("@enduml\n"));
|
||||
|
||||
// Check if all calls exist
|
||||
REQUIRE_THAT(puml, HasCall(_A("tmain()"), _A("A"), "a()"));
|
||||
REQUIRE_THAT(
|
||||
puml, HasCallInControlCondition(_A("tmain()"), _A("A"), "a()"));
|
||||
REQUIRE_THAT(puml, HasCall(_A("tmain()"), _A("A"), "b()"));
|
||||
REQUIRE_THAT(puml, HasCall(_A("tmain()"), _A("A"), "c()"));
|
||||
REQUIRE_THAT(puml, HasCall(_A("tmain()"), _A("A"), "d()"));
|
||||
|
||||
15
tests/t20033/.clang-uml
Normal file
15
tests/t20033/.clang-uml
Normal file
@@ -0,0 +1,15 @@
|
||||
compilation_database_dir: ..
|
||||
output_directory: puml
|
||||
diagrams:
|
||||
t20033_sequence:
|
||||
type: sequence
|
||||
glob:
|
||||
- ../../tests/t20033/t20033.cc
|
||||
include:
|
||||
namespaces:
|
||||
- clanguml::t20033
|
||||
using_namespace:
|
||||
- clanguml::t20033
|
||||
generate_condition_statements: true
|
||||
start_from:
|
||||
- function: "clanguml::t20033::tmain()"
|
||||
65
tests/t20033/t20033.cc
Normal file
65
tests/t20033/t20033.cc
Normal file
@@ -0,0 +1,65 @@
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace clanguml {
|
||||
namespace t20033 {
|
||||
struct A {
|
||||
int a1() { return 0; }
|
||||
int a2() { return 1; }
|
||||
int a3() { return 2; }
|
||||
int a4() { return 3; }
|
||||
};
|
||||
|
||||
int tmain()
|
||||
{
|
||||
A a;
|
||||
|
||||
int result{};
|
||||
// clang-format off
|
||||
if(false) {
|
||||
result = 0;
|
||||
}
|
||||
else if (reinterpret_cast<uint64_t>(&a) % 100 == 0ULL) {
|
||||
result = a.a1();
|
||||
}
|
||||
else if (reinterpret_cast<uint64_t>(&a) % 64 == 0ULL) {
|
||||
result = a.a2();
|
||||
}
|
||||
else if(a.a2() == 2 &&
|
||||
a.a3() == 3) {
|
||||
result = a.a3();
|
||||
}
|
||||
else {
|
||||
result = a.a4();
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
if (int i = a.a2(); i != 2) {
|
||||
result += a.a3();
|
||||
}
|
||||
|
||||
for (int i = 0; i < a.a2(); i++) {
|
||||
result += i * a.a3();
|
||||
}
|
||||
|
||||
int retry_count = a.a3();
|
||||
while (retry_count--) {
|
||||
result -= a.a2();
|
||||
}
|
||||
|
||||
do {
|
||||
result += a.a4();
|
||||
} while (retry_count++ < a.a3());
|
||||
|
||||
result = a.a4() % 6 ? result * 2 : result;
|
||||
|
||||
std::vector<int> ints;
|
||||
for (auto i : ints) {
|
||||
result += a.a4();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
58
tests/t20033/test_case.h
Normal file
58
tests/t20033/test_case.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* tests/t20033/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("t20033", "[test-case][sequence]")
|
||||
{
|
||||
auto [config, db] = load_config("t20033");
|
||||
|
||||
auto diagram = config.diagrams["t20033_sequence"];
|
||||
|
||||
REQUIRE(diagram->name == "t20033_sequence");
|
||||
|
||||
auto model = generate_sequence_diagram(*db, diagram);
|
||||
|
||||
REQUIRE(model->name() == "t20033_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"), "a1()"));
|
||||
REQUIRE_THAT(
|
||||
puml, HasCallInControlCondition(_A("tmain()"), _A("A"), "a2()"));
|
||||
REQUIRE_THAT(
|
||||
puml, HasCallInControlCondition(_A("tmain()"), _A("A"), "a3()"));
|
||||
REQUIRE_THAT(
|
||||
puml, HasCallInControlCondition(_A("tmain()"), _A("A"), "a4()"));
|
||||
|
||||
save_puml(
|
||||
config.output_directory() + "/" + diagram->name + ".puml", puml);
|
||||
}
|
||||
|
||||
{
|
||||
auto j = generate_sequence_json(diagram, *model);
|
||||
|
||||
using namespace json;
|
||||
|
||||
save_json(config.output_directory() + "/" + diagram->name + ".json", j);
|
||||
}
|
||||
}
|
||||
@@ -346,6 +346,7 @@ using namespace clanguml::test::matchers;
|
||||
#include "t20030/test_case.h"
|
||||
#include "t20031/test_case.h"
|
||||
#include "t20032/test_case.h"
|
||||
#include "t20033/test_case.h"
|
||||
|
||||
///
|
||||
/// Package diagram tests
|
||||
|
||||
@@ -295,6 +295,9 @@ test_cases:
|
||||
- name: t20032
|
||||
title: Return type generation option sequence diagram test case
|
||||
description:
|
||||
- name: t20033
|
||||
title: Control statement text in sequence diagram test case
|
||||
description:
|
||||
Package diagrams:
|
||||
- name: t30001
|
||||
title: Basic package diagram test case
|
||||
|
||||
Reference in New Issue
Block a user