From 4ee1aee0e7bbd0e291df2643b0d864d461ee07c6 Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Thu, 29 Jul 2021 19:19:24 +0200 Subject: [PATCH] Added note decorator test case --- src/puml/class_diagram_generator.h | 18 +++++++- src/uml/class_diagram_visitor.cc | 15 ++++++- src/uml/decorators.cc | 2 +- tests/CMakeLists.txt | 2 +- tests/t00028/.clang-uml | 12 ++++++ tests/t00028/t00028.cc | 54 ++++++++++++++++++++++++ tests/t00028/test_case.h | 66 ++++++++++++++++++++++++++++++ tests/test_cases.cc | 1 + tests/test_cases.h | 9 ++++ tests/test_cases.yaml | 3 ++ tests/test_decorator_parser.cc | 14 ++++++- 11 files changed, 189 insertions(+), 7 deletions(-) create mode 100644 tests/t00028/.clang-uml create mode 100644 tests/t00028/t00028.cc create mode 100644 tests/t00028/test_case.h diff --git a/src/puml/class_diagram_generator.h b/src/puml/class_diagram_generator.h index 245eea7b..f6b66644 100644 --- a/src/puml/class_diagram_generator.h +++ b/src/puml/class_diagram_generator.h @@ -289,8 +289,9 @@ public: for (auto decorator : c.decorators) { auto note = std::dynamic_pointer_cast(decorator); if (note) { - ostr << "note " << note->position << " of " << c.alias() - << " : " << note->text << '\n'; + ostr << "note " << note->position << " of " << c.alias() << '\n' + << note->text << '\n' + << "end note\n"; } } @@ -339,6 +340,7 @@ public: relstr << " : " << r.label; relstr << '\n'; + ostr << relstr.str(); } catch (error::uml_alias_missing &ex) { @@ -347,6 +349,18 @@ public: to_string(r.type), e.name, destination, ex.what()); } } + + // + // Process notes + // + for (auto decorator : e.decorators) { + auto note = std::dynamic_pointer_cast(decorator); + if (note) { + ostr << "note " << note->position << " of " << e.alias() << '\n' + << note->text << '\n' + << "end note\n"; + } + } } void generate(std::ostream &ostr) const diff --git a/src/uml/class_diagram_visitor.cc b/src/uml/class_diagram_visitor.cc index 1a617ea9..1df06d72 100644 --- a/src/uml/class_diagram_visitor.cc +++ b/src/uml/class_diagram_visitor.cc @@ -193,6 +193,10 @@ void tu_visitor::process_enum_declaration(const cppast::cpp_enum &enm) enum_ e; e.name = cx::util::full_name(ctx.namespace_, enm); + // Process enum documentation comment + if (enm.comment().has_value()) + e.decorators = decorators::parse(enm.comment().value()); + for (const auto &ev : enm) { if (ev.kind() == cppast::cpp_entity_kind::enum_value_t) { e.constants.push_back(ev.name()); @@ -230,8 +234,15 @@ void tu_visitor::process_class_declaration(const cppast::cpp_class &cls, cppast::cpp_access_specifier_kind::cpp_private; // Process class documentation comment - if(cls.comment().has_value()) - c.decorators = decorators::parse(cls.comment().value()); + if (cppast::is_templated(cls)) { + if (cls.parent().value().comment().has_value()) + c.decorators = + decorators::parse(cls.parent().value().comment().value()); + } + else { + if (cls.comment().has_value()) + c.decorators = decorators::parse(cls.comment().value()); + } // Process class child entities if (c.is_struct) diff --git a/src/uml/decorators.cc b/src/uml/decorators.cc index c26cbeb1..a50d9128 100644 --- a/src/uml/decorators.cc +++ b/src/uml/decorators.cc @@ -68,7 +68,7 @@ std::shared_ptr note::from_string(std::string_view c) std::advance(it, note_position.size() + 1); } - else if (*it == ' ') { + else if (std::isspace(*it)) { std::advance(it, 1); } else { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index dfcb8c25..e04c7ff0 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -74,5 +74,5 @@ foreach(TEST_CASE_CONFIG ${TEST_CASE_CONFIGS}) endforeach() add_test(NAME test_util COMMAND test_util) -add_test(NAME test_command_parser COMMAND test_command_parser) +add_test(NAME test_decorator_parser COMMAND test_decorator_parser) add_test(NAME test_cases COMMAND test_cases) diff --git a/tests/t00028/.clang-uml b/tests/t00028/.clang-uml new file mode 100644 index 00000000..f62109b1 --- /dev/null +++ b/tests/t00028/.clang-uml @@ -0,0 +1,12 @@ +compilation_database_dir: .. +output_directory: puml +diagrams: + t00028_class: + type: class + glob: + - ../../tests/t00028/t00028.cc + using_namespace: + - clanguml::t00028 + include: + namespaces: + - clanguml::t00028 diff --git a/tests/t00028/t00028.cc b/tests/t00028/t00028.cc new file mode 100644 index 00000000..4d343e6a --- /dev/null +++ b/tests/t00028/t00028.cc @@ -0,0 +1,54 @@ +#include +#include + +namespace clanguml { +namespace t00028 { + +/// \clanguml{note[top] A class note.} +class A { +}; + +/// \clanguml{note[] B class note.} +class B { +}; + +/// +/// @clanguml{note[bottom] C class note.} +/// This is class C. +class C { +}; + +/// \clanguml{note +/// D +/// class +/// note.} +class D { +}; + +/// \clanguml{note E template class note.} +template class E { + T param; +}; + +/// @clanguml{note[ bottom ] F enum note.} +enum class F { + one, + two, + three +}; + +/// \clanguml{note[right] R class note.} +class R { + A aaa; + + B *bbb; + + C &ccc; + + std::vector> ddd; + + E eee; +}; + +} // namespace t00028 +} // namespace clanguml diff --git a/tests/t00028/test_case.h b/tests/t00028/test_case.h new file mode 100644 index 00000000..159b9208 --- /dev/null +++ b/tests/t00028/test_case.h @@ -0,0 +1,66 @@ +/** + * tests/t00028/test_case.cc + * + * Copyright (c) 2021 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("t00028", "[test-case][class]") +{ + auto [config, db] = load_config("t00028"); + + auto diagram = config.diagrams["t00028_class"]; + + REQUIRE(diagram->name == "t00028_class"); + + REQUIRE(diagram->include.namespaces.size() == 1); + REQUIRE_THAT(diagram->include.namespaces, + VectorContains(std::string{"clanguml::t00028"})); + + REQUIRE(diagram->exclude.namespaces.size() == 0); + + REQUIRE(diagram->should_include("clanguml::t00028::A")); + + auto model = generate_class_diagram(db, diagram); + + REQUIRE(model.name == "t00028_class"); + + auto puml = generate_class_puml(diagram, model); + AliasMatcher _A(puml); + + REQUIRE_THAT(puml, StartsWith("@startuml")); + REQUIRE_THAT(puml, EndsWith("@enduml\n")); + + REQUIRE_THAT(puml, IsClass(_A("A"))); + REQUIRE_THAT(puml, IsClass(_A("B"))); + REQUIRE_THAT(puml, IsClass(_A("C"))); + REQUIRE_THAT(puml, IsClass(_A("D"))); + REQUIRE_THAT(puml, IsClassTemplate("E", "T")); + REQUIRE_THAT(puml, IsEnum(_A("F"))); + REQUIRE_THAT(puml, IsClass(_A("R"))); + REQUIRE_THAT(puml, HasNote(_A("A"), "top", "A class note.")); + REQUIRE_THAT(puml, HasNote(_A("B"), "left", "B class note.")); + REQUIRE_THAT(puml, HasNote(_A("C"), "bottom", "C class note.")); + const auto d_note = R"( +D +class +note.)"; + REQUIRE_THAT(puml, HasNote(_A("D"), "left", d_note)); + REQUIRE_THAT(puml, HasNote(_A("E"), "left", "E template class note.")); + REQUIRE_THAT(puml, HasNote(_A("F"), "bottom", "F enum note.")); + REQUIRE_THAT(puml, HasNote(_A("R"), "right", "R class note.")); + + save_puml( + "./" + config.output_directory + "/" + diagram->name + ".puml", puml); +} diff --git a/tests/test_cases.cc b/tests/test_cases.cc index c7455c3e..74d04769 100644 --- a/tests/test_cases.cc +++ b/tests/test_cases.cc @@ -131,6 +131,7 @@ using namespace clanguml::test::matchers; #include "t00025/test_case.h" #include "t00026/test_case.h" #include "t00027/test_case.h" +#include "t00028/test_case.h" // // Sequence diagram tests diff --git a/tests/test_cases.h b/tests/test_cases.h index 610d1178..630a9abd 100644 --- a/tests/test_cases.h +++ b/tests/test_cases.h @@ -262,6 +262,15 @@ ContainsMatcher IsDependency(std::string const &from, std::string const &to, CasedString(fmt::format("{} ..> {}", from, to), caseSensitivity)); } +ContainsMatcher HasNote(std::string const &cls, std::string const &position, + std::string const ¬e, + CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) +{ + return ContainsMatcher( + CasedString(fmt::format("note {} of {}", position, cls), + caseSensitivity)); +} + template ContainsMatcher IsMethod(std::string const &name, std::string const &type = "void", diff --git a/tests/test_cases.yaml b/tests/test_cases.yaml index dcf4f62a..ce369e04 100644 --- a/tests/test_cases.yaml +++ b/tests/test_cases.yaml @@ -78,6 +78,9 @@ test_cases: - name: t00027 title: Template decorator pattern description: + - name: t00028 + title: PlantUML note decorator test case + description: Sequence diagrams: - name: t20001 title: Basic sequence diagram diff --git a/tests/test_decorator_parser.cc b/tests/test_decorator_parser.cc index 3f964503..bc340866 100644 --- a/tests/test_decorator_parser.cc +++ b/tests/test_decorator_parser.cc @@ -56,16 +56,20 @@ TEST_CASE("Test decorator parser on note", "[unit-test]") @clanguml{note[ top ] This is a comment } + \clanguml{note This is a comment} + \clanguml{note[] This is a comment} )"; using namespace clanguml::decorators; auto decorators = parse(comment); - CHECK(decorators.size() == 2); + CHECK(decorators.size() == 4); auto n1 = std::dynamic_pointer_cast(decorators.at(0)); auto n2 = std::dynamic_pointer_cast(decorators.at(1)); + auto n3 = std::dynamic_pointer_cast(decorators.at(2)); + auto n4 = std::dynamic_pointer_cast(decorators.at(3)); CHECK(n1); CHECK(n1->position == "left"); @@ -74,6 +78,14 @@ TEST_CASE("Test decorator parser on note", "[unit-test]") CHECK(n2); CHECK(n2->position == "top"); CHECK(n2->text == "This is a comment"); + + CHECK(n3); + CHECK(n3->position == "left"); + CHECK(n3->text == "This is a comment"); + + CHECK(n4); + CHECK(n4->position == "left"); + CHECK(n4->text == "This is a comment"); } TEST_CASE("Test decorator parser on style", "[unit-test]")