From cadbeba82ce7a37beb46b935314cc41b60872474 Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Tue, 9 Jul 2024 18:22:40 +0200 Subject: [PATCH] Add more advanced mode diagram filter test cases --- src/common/model/diagram.cc | 5 + src/common/model/filters/diagram_filter.cc | 4 + src/common/model/filters/diagram_filter.h | 11 +- .../model/filters/diagram_filter_factory.h | 9 +- tests/t00082/.clang-uml | 20 +++ tests/t00082/t00082.cc | 30 ++++ tests/t00082/test_case.h | 46 ++++++ tests/t00083/.clang-uml | 16 ++ tests/t00083/t00083.cc | 30 ++++ tests/t00083/test_case.h | 46 ++++++ tests/test_cases.cc | 2 + tests/test_cases.yaml | 6 + tests/test_config_data/filters_advanced.yml | 61 ++++++- tests/test_filters_advanced.cc | 152 ++++++++++++++++++ 14 files changed, 433 insertions(+), 5 deletions(-) create mode 100644 tests/t00082/.clang-uml create mode 100644 tests/t00082/t00082.cc create mode 100644 tests/t00082/test_case.h create mode 100644 tests/t00083/.clang-uml create mode 100644 tests/t00083/t00083.cc create mode 100644 tests/t00083/test_case.h diff --git a/src/common/model/diagram.cc b/src/common/model/diagram.cc index 454cd62b..ab79be7d 100644 --- a/src/common/model/diagram.cc +++ b/src/common/model/diagram.cc @@ -66,6 +66,11 @@ bool diagram::should_include(const element &e) const if (filter_.get() == nullptr) return true; + if (!complete()) { + return filter_->should_include( + dynamic_cast(e)); + } + return filter_->should_include(e) && filter_->should_include(dynamic_cast(e)); } diff --git a/src/common/model/filters/diagram_filter.cc b/src/common/model/filters/diagram_filter.cc index 21ffe448..a89abd65 100644 --- a/src/common/model/filters/diagram_filter.cc +++ b/src/common/model/filters/diagram_filter.cc @@ -159,6 +159,10 @@ bool filter_visitor::is_exclusive() const filter_t filter_visitor::type() const { return type_; } +filter_mode_t filter_visitor::mode() const { return mode_; } + +void filter_visitor::set_mode(filter_mode_t mode) { mode_ = mode; } + anyof_filter::anyof_filter( filter_t type, std::vector> filters) : filter_visitor{type} diff --git a/src/common/model/filters/diagram_filter.h b/src/common/model/filters/diagram_filter.h index 424e3556..60730349 100644 --- a/src/common/model/filters/diagram_filter.h +++ b/src/common/model/filters/diagram_filter.h @@ -39,6 +39,7 @@ namespace clanguml::common::model { class diagram_filter_factory; using clanguml::common::eid_t; +using clanguml::config::filter_mode_t; /** * Diagram filters can be add in 2 modes: @@ -113,9 +114,12 @@ public: bool is_exclusive() const; filter_t type() const; + filter_mode_t mode() const; + void set_mode(filter_mode_t mode); private: filter_t type_; + filter_mode_t mode_{filter_mode_t::basic}; }; struct anyof_filter : public filter_visitor { @@ -155,8 +159,13 @@ private: template tvl::value_t match_anyof(const diagram &d, const E &element) const { - return tvl::any_of(filters_.begin(), filters_.end(), + auto result = tvl::any_of(filters_.begin(), filters_.end(), [&d, &element](const auto &f) { return f->match(d, element); }); + + if (mode() == filter_mode_t::advanced && !d.complete()) + return type() == filter_t::kInclusive; + + return result; } std::vector> filters_; diff --git a/src/common/model/filters/diagram_filter_factory.h b/src/common/model/filters/diagram_filter_factory.h index cedd70a3..0fb0576f 100644 --- a/src/common/model/filters/diagram_filter_factory.h +++ b/src/common/model/filters/diagram_filter_factory.h @@ -80,9 +80,12 @@ private: const std::vector &filter_config, std::vector> &result, Args &&...args) { - if (!filter_config.empty()) - result.emplace_back(std::make_unique( - filter_type, filter_config, std::forward(args)...)); + if (!filter_config.empty()) { + auto filter = std::make_unique( + filter_type, filter_config, std::forward(args)...); + filter->set_mode(filter_mode_t::advanced); + result.emplace_back(std::move(filter)); + } } }; diff --git a/tests/t00082/.clang-uml b/tests/t00082/.clang-uml new file mode 100644 index 00000000..54f1fc9f --- /dev/null +++ b/tests/t00082/.clang-uml @@ -0,0 +1,20 @@ +diagrams: + t00082_class: + type: class + glob: + - t00082.cc + generate_packages: true + filter_mode: advanced + include: + anyof: + subclasses: + - clanguml::t00082::ns1::nsA::A1 + namespaces: + - clanguml::t00082::ns2::nsB + context: + - clanguml::t00082::ns3::nsC::B3 + exclude: + allof: + elements: + - clanguml::t00082::ns1::nsA::A1 + using_namespace: clanguml::t00082 \ No newline at end of file diff --git a/tests/t00082/t00082.cc b/tests/t00082/t00082.cc new file mode 100644 index 00000000..d2f1c094 --- /dev/null +++ b/tests/t00082/t00082.cc @@ -0,0 +1,30 @@ +namespace clanguml::t00082 { +namespace ns1 { +namespace nsA { +struct A1 { }; +struct B1 : public A1 { }; +struct C1 : public B1 { }; +struct D1 { }; +} +} +namespace ns2 { +namespace nsB { +struct A2 { }; +struct B2 : public A2 { }; +struct C2 : public B2 { }; +} +} +namespace ns3 { +namespace nsC { +struct A3 { }; +struct B3 : public A3 { }; +struct C3 : public B3 { }; +struct D3 { }; +} +} +namespace ns4 { +namespace nsD { +struct A4 { }; +} +} +} \ No newline at end of file diff --git a/tests/t00082/test_case.h b/tests/t00082/test_case.h new file mode 100644 index 00000000..558ed375 --- /dev/null +++ b/tests/t00082/test_case.h @@ -0,0 +1,46 @@ +/** + * tests/t00082/test_case.h + * + * Copyright (c) 2021-2024 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("t00082") +{ + using namespace clanguml::test; + using namespace std::string_literals; + + auto [config, db, diagram, model] = + CHECK_CLASS_MODEL("t00082", "t00082_class"); + + CHECK_CLASS_DIAGRAM(*config, diagram, *model, [](const auto &src) { + REQUIRE(!IsClass(src, {"ns1::nsA", "A1"})); + REQUIRE(IsClass(src, {"ns1::nsA", "B1"})); + REQUIRE(IsClass(src, {"ns1::nsA", "C1"})); + REQUIRE(!IsClass(src, {"ns1::nsA", "D1"})); + + REQUIRE(IsClass(src, {"ns2::nsB", "A2"})); + REQUIRE(IsClass(src, {"ns2::nsB", "B2"})); + REQUIRE(IsClass(src, {"ns2::nsB", "C2"})); + + REQUIRE(IsClass(src, {"ns3::nsC", "A3"})); + REQUIRE(IsClass(src, {"ns3::nsC", "B3"})); + REQUIRE(IsClass(src, {"ns3::nsC", "C3"})); + REQUIRE(!IsClass(src, {"ns3::nsC", "D3"})); + + REQUIRE(!IsNamespacePackage(src, "ns4"s)); + + REQUIRE(!IsClass(src, {"ns4::nsD", "A4"})); + }); +} \ No newline at end of file diff --git a/tests/t00083/.clang-uml b/tests/t00083/.clang-uml new file mode 100644 index 00000000..de1d7f5b --- /dev/null +++ b/tests/t00083/.clang-uml @@ -0,0 +1,16 @@ +diagrams: + t00083_class: + type: class + glob: + - t00083.cc + generate_packages: true + filter_mode: advanced + exclude: + anyof: + subclasses: + - clanguml::t00083::ns1::nsA::A1 + namespaces: + - clanguml::t00083::ns2::nsB + context: + - clanguml::t00083::ns3::nsC::B3 + using_namespace: clanguml::t00083 \ No newline at end of file diff --git a/tests/t00083/t00083.cc b/tests/t00083/t00083.cc new file mode 100644 index 00000000..75073794 --- /dev/null +++ b/tests/t00083/t00083.cc @@ -0,0 +1,30 @@ +namespace clanguml::t00083 { +namespace ns1 { +namespace nsA { +struct A1 { }; +struct B1 : public A1 { }; +struct C1 : public B1 { }; +struct D1 { }; +} +} +namespace ns2 { +namespace nsB { +struct A2 { }; +struct B2 : public A2 { }; +struct C2 : public B2 { }; +} +} +namespace ns3 { +namespace nsC { +struct A3 { }; +struct B3 : public A3 { }; +struct C3 : public B3 { }; +struct D3 { }; +} +} +namespace ns4 { +namespace nsD { +struct A4 { }; +} +} +} \ No newline at end of file diff --git a/tests/t00083/test_case.h b/tests/t00083/test_case.h new file mode 100644 index 00000000..54a4e1e4 --- /dev/null +++ b/tests/t00083/test_case.h @@ -0,0 +1,46 @@ +/** + * tests/t00083/test_case.h + * + * Copyright (c) 2021-2024 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("t00083") +{ + using namespace clanguml::test; + using namespace std::string_literals; + + auto [config, db, diagram, model] = + CHECK_CLASS_MODEL("t00083", "t00083_class"); + + CHECK_CLASS_DIAGRAM(*config, diagram, *model, [](const auto &src) { + REQUIRE(!IsClass(src, {"ns1::nsA", "A1"})); + REQUIRE(!IsClass(src, {"ns1::nsA", "B1"})); + REQUIRE(!IsClass(src, {"ns1::nsA", "C1"})); + REQUIRE(IsClass(src, {"ns1::nsA", "D1"})); + + REQUIRE(!IsClass(src, {"ns2::nsB", "A2"})); + REQUIRE(!IsClass(src, {"ns2::nsB", "B2"})); + REQUIRE(!IsClass(src, {"ns2::nsB", "C2"})); + + REQUIRE(!IsClass(src, {"ns3::nsC", "A3"})); + REQUIRE(!IsClass(src, {"ns3::nsC", "B3"})); + REQUIRE(!IsClass(src, {"ns3::nsC", "C3"})); + REQUIRE(IsClass(src, {"ns3::nsC", "D3"})); + + REQUIRE(IsNamespacePackage(src, "ns4"s)); + + REQUIRE(IsClass(src, {"ns4::nsD", "A4"})); + }); +} \ No newline at end of file diff --git a/tests/test_cases.cc b/tests/test_cases.cc index 51c8860b..0121de51 100644 --- a/tests/test_cases.cc +++ b/tests/test_cases.cc @@ -555,6 +555,8 @@ void CHECK_INCLUDE_DIAGRAM(const clanguml::config::config &config, #include "t00079/test_case.h" #include "t00080/test_case.h" #include "t00081/test_case.h" +#include "t00082/test_case.h" +#include "t00083/test_case.h" /// /// Sequence diagram tests diff --git a/tests/test_cases.yaml b/tests/test_cases.yaml index 9442a596..98081ac8 100644 --- a/tests/test_cases.yaml +++ b/tests/test_cases.yaml @@ -240,6 +240,12 @@ test_cases: - name: t00081 title: Test case for class members relationships to std types description: + - name: t00082 + title: Test case for advanced diagram filter inclusion test with subclasses and namespaces + description: + - name: t00083 + title: Test case for advanced diagram filter exclusion test with subclasses and namespaces + description: Sequence diagrams: - name: t20001 title: Basic sequence diagram test case diff --git a/tests/test_config_data/filters_advanced.yml b/tests/test_config_data/filters_advanced.yml index 1990beac..31057c44 100644 --- a/tests/test_config_data/filters_advanced.yml +++ b/tests/test_config_data/filters_advanced.yml @@ -2,6 +2,25 @@ compilation_database_dir: debug output_directory: output filter_mode: advanced diagrams: + include_test: + type: include + relative_to: ../../../src + glob: + - src/**/*.cc + - src/**/*.h + include: + allof: + paths: + - class_d*/ + - common + - util/*.h + - util/*.cc + - main.cc + exclude: + allof: + paths: + - sequence_diagram + - util/error.h anyof_test: type: class relative_to: ../../../src @@ -25,4 +44,44 @@ diagrams: modules: - mod1::mod2 namespaces: - - ns1::ns2 \ No newline at end of file + - ns1::ns2 + method_type_include_test: + type: class + include: + anyof: + namespaces: + - ns1::ns2 + method_types: + - constructor + - operator + regex_elements_test: + type: class + include: + elements: + - ns1::ClassA + - r: 'ns1::ns2::Class.+' + - r: 'ns1::.+::ns3::.+' + exclude: + elements: + - ns1::ns2::ClassZ + regex_elements_and_namespaces: + type: class + include: + allof: + elements: + - ns1::ClassA + - r: 'ns1::ns2::Class.+' + - r: 'ns1::.+::ns3::.+' + namespaces: + - r: '.+ns2.+' + edge_filter_and_namespaces: + type: class + filter_mode: advanced + include: + anyof: + subclasses: + - ns1::nsA::A + namespaces: + - ns2::nsB + context: + - ns1::nsA::C \ No newline at end of file diff --git a/tests/test_filters_advanced.cc b/tests/test_filters_advanced.cc index acdcf6f9..f6cfd5ac 100644 --- a/tests/test_filters_advanced.cc +++ b/tests/test_filters_advanced.cc @@ -28,12 +28,41 @@ #include "sequence_diagram/model/diagram.h" #include +using clanguml::class_diagram::model::class_; +using clanguml::class_diagram::model::class_method; +using clanguml::common::eid_t; +using clanguml::common::model::access_t; using clanguml::common::model::diagram_filter; using clanguml::common::model::diagram_filter_factory; using clanguml::common::model::namespace_; +using clanguml::common::model::package; using clanguml::common::model::source_file; using clanguml::config::filter_mode_t; +TEST_CASE("Test diagram paths filter") +{ + auto cfg = + clanguml::config::load("./test_config_data/filters_advanced.yml"); + + auto &config = *cfg.diagrams["include_test"]; + clanguml::include_diagram::model::diagram diagram; + + auto filter_ptr = diagram_filter_factory::create(diagram, config); + diagram_filter &filter = *filter_ptr; + + auto make_path = [&](std::string_view p) { + return source_file{config.root_directory() / p}; + }; + + CHECK(filter.should_include( + make_path("class_diagram/visitor/translation_unit_visitor.h"))); + CHECK(filter.should_include(make_path("main.cc"))); + CHECK(filter.should_include(make_path("util/util.cc"))); + CHECK_FALSE(filter.should_include(make_path("util/error.h"))); + CHECK_FALSE(filter.should_include( + make_path("sequence_diagram/visitor/translation_unit_visitor.h"))); +} + TEST_CASE("Test advanced diagram filter anyof") { auto cfg = @@ -42,6 +71,8 @@ TEST_CASE("Test advanced diagram filter anyof") auto &config = *cfg.diagrams["anyof_test"]; clanguml::include_diagram::model::diagram diagram; + diagram.set_complete(true); + auto filter_ptr = diagram_filter_factory::create(diagram, config); diagram_filter &filter = *filter_ptr; @@ -67,6 +98,7 @@ TEST_CASE("Test advanced diagram filter modules") auto &config = *cfg.diagrams["modules_test"]; clanguml::include_diagram::model::diagram diagram; + diagram.set_complete(true); auto filter_ptr = diagram_filter_factory::create(diagram, config); diagram_filter &filter = *filter_ptr; @@ -93,6 +125,126 @@ TEST_CASE("Test advanced diagram filter modules") CHECK_FALSE(filter.should_include(e1)); } +TEST_CASE("Test method_types include filter") +{ + auto cfg = + clanguml::config::load("./test_config_data/filters_advanced.yml"); + + auto &config = *cfg.diagrams["method_type_include_test"]; + clanguml::class_diagram::model::diagram diagram; + diagram.set_complete(true); + + auto filter_ptr = diagram_filter_factory::create(diagram, config); + diagram_filter &filter = *filter_ptr; + + class_method cm{access_t::kPublic, "A", ""}; + cm.is_constructor(true); + + CHECK(filter.should_include(cm)); + + cm.is_constructor(false); + cm.is_destructor(true); + + CHECK_FALSE(filter.should_include(cm)); +} + +TEST_CASE("Test elements and namespaces regexp filter") +{ + auto cfg = + clanguml::config::load("./test_config_data/filters_advanced.yml"); + + auto &config = *cfg.diagrams["regex_elements_and_namespaces"]; + clanguml::class_diagram::model::diagram diagram; + + auto filter_ptr = diagram_filter_factory::create(diagram, config); + diagram_filter &filter = *filter_ptr; + + class_ c{{}}; + + c.set_namespace(namespace_{"ns1"}); + c.set_name("ClassA"); + + CHECK_FALSE(filter.should_include(c)); + + c.set_namespace(namespace_{"ns1::ns2"}); + c.set_name("ClassA"); + + CHECK(filter.should_include(c)); + + c.set_namespace(namespace_{"ns1::ns2"}); + c.set_name("ClassZ"); + + CHECK(filter.should_include(c)); + + c.set_namespace(namespace_{"ns1::ns5::ns3"}); + c.set_name("ClassA"); + + CHECK_FALSE(filter.should_include(c)); +} + +TEST_CASE("Test edge filter and namespaces filter") +{ + auto cfg = + clanguml::config::load("./test_config_data/filters_advanced.yml"); + + auto &config = *cfg.diagrams["edge_filter_and_namespaces"]; + clanguml::class_diagram::model::diagram diagram; + + auto filter_ptr = diagram_filter_factory::create(diagram, config); + diagram_filter &filter = *filter_ptr; + + diagram.set_complete(true); + + uint64_t id{1}; + + { + auto ns1 = std::make_unique(namespace_{}); + ns1->set_name("ns1"); + ns1->set_id(eid_t{id++}); + diagram.add(namespace_{}, std::move(ns1)); + } + { + auto ns1__nsA = std::make_unique(namespace_{}); + ns1__nsA->set_name("nsA"); + ns1__nsA->set_namespace(namespace_{"ns1"}); + ns1__nsA->set_id(eid_t{id++}); + diagram.add(namespace_{}, std::move(ns1__nsA)); + } + { + auto A = std::make_unique(namespace_{}); + A->set_namespace(namespace_{"ns1::nsA"}); + A->set_name("A"); + A->set_id(eid_t{id}); + diagram.add(namespace_{"ns1::nsA"}, std::move(A)); + } + + CHECK(filter.should_include(*diagram.get(eid_t{id}))); + + class_ c{{}}; + c.set_namespace(namespace_{"ns2::nsB"}); + c.set_name("B"); + + CHECK(filter.should_include(c)); + + { + auto C = std::make_unique(namespace_{}); + C->set_namespace(namespace_{"ns1::nsA"}); + C->set_name("C"); + C->set_id(eid_t{++id}); + diagram.add(namespace_{"ns1::nsA"}, std::move(C)); + } + + c.set_namespace(namespace_{"ns2::nsB"}); + c.set_name("C"); + + CHECK(filter.should_include(c)); + + c.set_namespace(namespace_{"ns1::nsA"}); + c.set_name("R"); + + CHECK_FALSE(filter.should_include(c)); +} + /// /// Main test function ///