diff --git a/docs/test_cases.md b/docs/test_cases.md index 2c027fe4..95b075df 100644 --- a/docs/test_cases.md +++ b/docs/test_cases.md @@ -17,6 +17,7 @@ * [t00016](./test_cases/t00016.md) - Unnamed enums and empty templates * [t00017](./test_cases/t00017.md) - Test include relations also as members flag * [t00018](./test_cases/t00018.md) - Pimpl pattern + * [t00019](./test_cases/t00019.md) - Layercake pattern ## Sequence diagrams * [t20001](./test_cases/t20001.md) - Basic sequence diagram ## Configuration diagrams diff --git a/docs/test_cases/t00019.md b/docs/test_cases/t00019.md new file mode 100644 index 00000000..2fe6f35b --- /dev/null +++ b/docs/test_cases/t00019.md @@ -0,0 +1,160 @@ +# t00019 - Layercake pattern +## Config +```yaml +compilation_database_dir: .. +output_directory: puml +diagrams: + t00019_class: + type: class + glob: + - ../../tests/t00019/**.h + - ../../tests/t00019/**.cc + using_namespace: + - clanguml::t00019 + include: + namespaces: + - clanguml::t00019 + plantuml: + after: + - '@A(Base) <|-- @A(Layer3)' + - '@A(Layer3) <|-- @A(Layer2)' + - '@A(Layer2) <|-- @A(Layer1)' + +``` +## Source code +File t00019_layer2.h +```cpp +#pragma once + +namespace clanguml { +namespace t00019 { + +template class Layer2 : public LowerLayer { + + using LowerLayer::LowerLayer; + + using LowerLayer::m1; + + using LowerLayer::m2; + + int all_calls_count() const + { + return LowerLayer::m1_calls() + LowerLayer::m2_calls(); + } +}; +} +} + +``` +File t00019_base.h +```cpp +#pragma once + +#include + +namespace clanguml { +namespace t00019 { + +class Base { + + Base() = default; + + virtual ~Base() = default; + + virtual int m1() { return 2; } + + virtual std::string m2() { return "two"; } +}; +} +} + +``` +File t00019_layer1.h +```cpp +#pragma once + +#include +#include + +namespace clanguml { +namespace t00019 { + +template class Layer1 : public LowerLayer { + + using LowerLayer::LowerLayer; + + int m1() override + { + std::cout << "m1 called\n"; + return LowerLayer::m1(); + } + + std::string m2() override + { + std::cout << "m2 called\n"; + return LowerLayer::m2(); + } +}; +} +} + +``` +File t00019.cc +```cpp +#include "t00019_base.h" +#include "t00019_layer1.h" +#include "t00019_layer2.h" +#include "t00019_layer3.h" + +#include + +namespace clanguml { +namespace t00019 { + +class A { +public: + std::unique_ptr>>> layers; +}; +} +} + +``` +File t00019_layer3.h +```cpp +#pragma once + +#include + +namespace clanguml { +namespace t00019 { + +template class Layer3 : public LowerLayer { + + using LowerLayer::LowerLayer; + + virtual int m1() override + { + m_m1_calls++; + return LowerLayer::m1(); + } + + virtual std::string m2() override + { + m_m2_calls++; + return LowerLayer::m2(); + } + + int m1_calls() const { return m_m1_calls; } + + int m2_calls() const { return m_m2_calls; } + +private: + int m_m1_calls{}; + int m_m2_calls{}; +}; +} +} + +``` +## Generated UML diagrams +![t00019_class](./t00019_class.png "Layercake pattern") diff --git a/docs/test_cases/t00019_class.png b/docs/test_cases/t00019_class.png new file mode 100644 index 00000000..c2eb10e4 Binary files /dev/null and b/docs/test_cases/t00019_class.png differ diff --git a/tests/t00019/.clanguml b/tests/t00019/.clanguml new file mode 100644 index 00000000..16412e73 --- /dev/null +++ b/tests/t00019/.clanguml @@ -0,0 +1,18 @@ +compilation_database_dir: .. +output_directory: puml +diagrams: + t00019_class: + type: class + glob: + - ../../tests/t00019/**.h + - ../../tests/t00019/**.cc + using_namespace: + - clanguml::t00019 + include: + namespaces: + - clanguml::t00019 + plantuml: + after: + - '@A(Base) <|-- @A(Layer3)' + - '@A(Layer3) <|-- @A(Layer2)' + - '@A(Layer2) <|-- @A(Layer1)' diff --git a/tests/t00019/t00019.cc b/tests/t00019/t00019.cc new file mode 100644 index 00000000..e80e908d --- /dev/null +++ b/tests/t00019/t00019.cc @@ -0,0 +1,16 @@ +#include "t00019_base.h" +#include "t00019_layer1.h" +#include "t00019_layer2.h" +#include "t00019_layer3.h" + +#include + +namespace clanguml { +namespace t00019 { + +class A { +public: + std::unique_ptr>>> layers; +}; +} +} diff --git a/tests/t00019/t00019_base.h b/tests/t00019/t00019_base.h new file mode 100644 index 00000000..9838ed35 --- /dev/null +++ b/tests/t00019/t00019_base.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +namespace clanguml { +namespace t00019 { + +class Base { + + Base() = default; + + virtual ~Base() = default; + + virtual int m1() { return 2; } + + virtual std::string m2() { return "two"; } +}; +} +} diff --git a/tests/t00019/t00019_layer1.h b/tests/t00019/t00019_layer1.h new file mode 100644 index 00000000..19860aa9 --- /dev/null +++ b/tests/t00019/t00019_layer1.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include + +namespace clanguml { +namespace t00019 { + +template class Layer1 : public LowerLayer { + + using LowerLayer::LowerLayer; + + int m1() override + { + std::cout << "m1 called\n"; + return LowerLayer::m1(); + } + + std::string m2() override + { + std::cout << "m2 called\n"; + return LowerLayer::m2(); + } +}; +} +} diff --git a/tests/t00019/t00019_layer2.h b/tests/t00019/t00019_layer2.h new file mode 100644 index 00000000..b60b8237 --- /dev/null +++ b/tests/t00019/t00019_layer2.h @@ -0,0 +1,20 @@ +#pragma once + +namespace clanguml { +namespace t00019 { + +template class Layer2 : public LowerLayer { + + using LowerLayer::LowerLayer; + + using LowerLayer::m1; + + using LowerLayer::m2; + + int all_calls_count() const + { + return LowerLayer::m1_calls() + LowerLayer::m2_calls(); + } +}; +} +} diff --git a/tests/t00019/t00019_layer3.h b/tests/t00019/t00019_layer3.h new file mode 100644 index 00000000..607b17a2 --- /dev/null +++ b/tests/t00019/t00019_layer3.h @@ -0,0 +1,33 @@ +#pragma once + +#include + +namespace clanguml { +namespace t00019 { + +template class Layer3 : public LowerLayer { + + using LowerLayer::LowerLayer; + + virtual int m1() override + { + m_m1_calls++; + return LowerLayer::m1(); + } + + virtual std::string m2() override + { + m_m2_calls++; + return LowerLayer::m2(); + } + + int m1_calls() const { return m_m1_calls; } + + int m2_calls() const { return m_m2_calls; } + +private: + int m_m1_calls{}; + int m_m2_calls{}; +}; +} +} diff --git a/tests/t00019/test_case.h b/tests/t00019/test_case.h new file mode 100644 index 00000000..f6a9a39a --- /dev/null +++ b/tests/t00019/test_case.h @@ -0,0 +1,59 @@ +/** + * tests/t00019/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("t00019", "[test-case][class]") +{ + auto [config, db] = load_config("t00019"); + + auto diagram = config.diagrams["t00019_class"]; + + REQUIRE(diagram->name == "t00019_class"); + + REQUIRE(diagram->include.namespaces.size() == 1); + REQUIRE_THAT(diagram->include.namespaces, + VectorContains(std::string{"clanguml::t00019"})); + + REQUIRE(diagram->exclude.namespaces.size() == 0); + + REQUIRE(diagram->should_include("clanguml::t00019::Layer1")); + REQUIRE(diagram->should_include("clanguml::t00019::Layer2")); + REQUIRE(diagram->should_include("clanguml::t00019::Layer3")); + REQUIRE(diagram->should_include("clanguml::t00019::Base")); + + auto model = generate_class_diagram(db, diagram); + + REQUIRE(model.name == "t00019_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("Base"))); + REQUIRE_THAT(puml, IsClassTemplate("Layer1", "LowerLayer")); + REQUIRE_THAT(puml, IsClassTemplate("Layer2", "LowerLayer")); + REQUIRE_THAT(puml, IsClassTemplate("Layer3", "LowerLayer")); + REQUIRE_THAT(puml, IsBaseClass(_A("Base"), _A("Layer3"))); + REQUIRE_THAT( + puml, IsBaseClass(_A("Layer3"), _A("Layer2"))); + REQUIRE_THAT( + puml, IsBaseClass(_A("Layer2"), _A("Layer1"))); + + save_puml( + "./" + config.output_directory + "/" + diagram->name + ".puml", puml); +} diff --git a/tests/test_cases.cc b/tests/test_cases.cc index 270a7944..18ad5356 100644 --- a/tests/test_cases.cc +++ b/tests/test_cases.cc @@ -122,6 +122,7 @@ using namespace clanguml::test::matchers; #include "t00016/test_case.h" #include "t00017/test_case.h" #include "t00018/test_case.h" +#include "t00019/test_case.h" // // Sequence diagram tests diff --git a/tests/test_cases.yaml b/tests/test_cases.yaml index 4eceeaf3..35bc6e59 100644 --- a/tests/test_cases.yaml +++ b/tests/test_cases.yaml @@ -51,6 +51,9 @@ test_cases: - name: t00018 title: Pimpl pattern description: + - name: t00019 + title: Layercake pattern + description: Sequence diagrams: - name: t20001 title: Basic sequence diagram