Add more advanced mode diagram filter test cases

This commit is contained in:
Bartek Kryza
2024-07-09 18:22:40 +02:00
parent 01c6264923
commit cadbeba82c
14 changed files with 433 additions and 5 deletions

View File

@@ -66,6 +66,11 @@ bool diagram::should_include(const element &e) const
if (filter_.get() == nullptr) if (filter_.get() == nullptr)
return true; return true;
if (!complete()) {
return filter_->should_include(
dynamic_cast<const source_location &>(e));
}
return filter_->should_include(e) && return filter_->should_include(e) &&
filter_->should_include(dynamic_cast<const source_location &>(e)); filter_->should_include(dynamic_cast<const source_location &>(e));
} }

View File

@@ -159,6 +159,10 @@ bool filter_visitor::is_exclusive() const
filter_t filter_visitor::type() const { return type_; } 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( anyof_filter::anyof_filter(
filter_t type, std::vector<std::unique_ptr<filter_visitor>> filters) filter_t type, std::vector<std::unique_ptr<filter_visitor>> filters)
: filter_visitor{type} : filter_visitor{type}

View File

@@ -39,6 +39,7 @@ namespace clanguml::common::model {
class diagram_filter_factory; class diagram_filter_factory;
using clanguml::common::eid_t; using clanguml::common::eid_t;
using clanguml::config::filter_mode_t;
/** /**
* Diagram filters can be add in 2 modes: * Diagram filters can be add in 2 modes:
@@ -113,9 +114,12 @@ public:
bool is_exclusive() const; bool is_exclusive() const;
filter_t type() const; filter_t type() const;
filter_mode_t mode() const;
void set_mode(filter_mode_t mode);
private: private:
filter_t type_; filter_t type_;
filter_mode_t mode_{filter_mode_t::basic};
}; };
struct anyof_filter : public filter_visitor { struct anyof_filter : public filter_visitor {
@@ -155,8 +159,13 @@ private:
template <typename E> template <typename E>
tvl::value_t match_anyof(const diagram &d, const E &element) const 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); }); [&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<std::unique_ptr<filter_visitor>> filters_; std::vector<std::unique_ptr<filter_visitor>> filters_;

View File

@@ -80,9 +80,12 @@ private:
const std::vector<T> &filter_config, const std::vector<T> &filter_config,
std::vector<std::unique_ptr<filter_visitor>> &result, Args &&...args) std::vector<std::unique_ptr<filter_visitor>> &result, Args &&...args)
{ {
if (!filter_config.empty()) if (!filter_config.empty()) {
result.emplace_back(std::make_unique<FT>( auto filter = std::make_unique<FT>(
filter_type, filter_config, std::forward<Args>(args)...)); filter_type, filter_config, std::forward<Args>(args)...);
filter->set_mode(filter_mode_t::advanced);
result.emplace_back(std::move(filter));
}
} }
}; };

20
tests/t00082/.clang-uml Normal file
View File

@@ -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

30
tests/t00082/t00082.cc Normal file
View File

@@ -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 { };
}
}
}

46
tests/t00082/test_case.h Normal file
View File

@@ -0,0 +1,46 @@
/**
* tests/t00082/test_case.h
*
* Copyright (c) 2021-2024 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("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"}));
});
}

16
tests/t00083/.clang-uml Normal file
View File

@@ -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

30
tests/t00083/t00083.cc Normal file
View File

@@ -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 { };
}
}
}

46
tests/t00083/test_case.h Normal file
View File

@@ -0,0 +1,46 @@
/**
* tests/t00083/test_case.h
*
* Copyright (c) 2021-2024 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("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"}));
});
}

View File

@@ -555,6 +555,8 @@ void CHECK_INCLUDE_DIAGRAM(const clanguml::config::config &config,
#include "t00079/test_case.h" #include "t00079/test_case.h"
#include "t00080/test_case.h" #include "t00080/test_case.h"
#include "t00081/test_case.h" #include "t00081/test_case.h"
#include "t00082/test_case.h"
#include "t00083/test_case.h"
/// ///
/// Sequence diagram tests /// Sequence diagram tests

View File

@@ -240,6 +240,12 @@ test_cases:
- name: t00081 - name: t00081
title: Test case for class members relationships to std types title: Test case for class members relationships to std types
description: 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: Sequence diagrams:
- name: t20001 - name: t20001
title: Basic sequence diagram test case title: Basic sequence diagram test case

View File

@@ -2,6 +2,25 @@ compilation_database_dir: debug
output_directory: output output_directory: output
filter_mode: advanced filter_mode: advanced
diagrams: 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: anyof_test:
type: class type: class
relative_to: ../../../src relative_to: ../../../src
@@ -25,4 +44,44 @@ diagrams:
modules: modules:
- mod1::mod2 - mod1::mod2
namespaces: namespaces:
- ns1::ns2 - 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

View File

@@ -28,12 +28,41 @@
#include "sequence_diagram/model/diagram.h" #include "sequence_diagram/model/diagram.h"
#include <filesystem> #include <filesystem>
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;
using clanguml::common::model::diagram_filter_factory; using clanguml::common::model::diagram_filter_factory;
using clanguml::common::model::namespace_; using clanguml::common::model::namespace_;
using clanguml::common::model::package;
using clanguml::common::model::source_file; using clanguml::common::model::source_file;
using clanguml::config::filter_mode_t; 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") TEST_CASE("Test advanced diagram filter anyof")
{ {
auto cfg = auto cfg =
@@ -42,6 +71,8 @@ TEST_CASE("Test advanced diagram filter anyof")
auto &config = *cfg.diagrams["anyof_test"]; auto &config = *cfg.diagrams["anyof_test"];
clanguml::include_diagram::model::diagram diagram; clanguml::include_diagram::model::diagram diagram;
diagram.set_complete(true);
auto filter_ptr = diagram_filter_factory::create(diagram, config); auto filter_ptr = diagram_filter_factory::create(diagram, config);
diagram_filter &filter = *filter_ptr; diagram_filter &filter = *filter_ptr;
@@ -67,6 +98,7 @@ TEST_CASE("Test advanced diagram filter modules")
auto &config = *cfg.diagrams["modules_test"]; auto &config = *cfg.diagrams["modules_test"];
clanguml::include_diagram::model::diagram diagram; clanguml::include_diagram::model::diagram diagram;
diagram.set_complete(true);
auto filter_ptr = diagram_filter_factory::create(diagram, config); auto filter_ptr = diagram_filter_factory::create(diagram, config);
diagram_filter &filter = *filter_ptr; diagram_filter &filter = *filter_ptr;
@@ -93,6 +125,126 @@ TEST_CASE("Test advanced diagram filter modules")
CHECK_FALSE(filter.should_include(e1)); 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<package>(namespace_{});
ns1->set_name("ns1");
ns1->set_id(eid_t{id++});
diagram.add(namespace_{}, std::move(ns1));
}
{
auto ns1__nsA = std::make_unique<package>(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<class_>(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<class_>(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 /// Main test function
/// ///