diff --git a/src/common/model/diagram.cc b/src/common/model/diagram.cc index 771fac61..74b162a5 100644 --- a/src/common/model/diagram.cc +++ b/src/common/model/diagram.cc @@ -64,7 +64,8 @@ bool diagram::should_include(const element &e) const if (filter_.get() == nullptr) return true; - return filter_->should_include(e); + return filter_->should_include(e) && + filter_->should_include(dynamic_cast(e)); } bool diagram::should_include(const std::string &name) const diff --git a/src/common/model/diagram_filter.cc b/src/common/model/diagram_filter.cc index e52c40d7..7ee323f4 100644 --- a/src/common/model/diagram_filter.cc +++ b/src/common/model/diagram_filter.cc @@ -121,6 +121,12 @@ tvl::value_t filter_visitor::match( return {}; } +tvl::value_t filter_visitor::match( + const diagram & /*d*/, const common::model::source_location & /*f*/) const +{ + return {}; +} + bool filter_visitor::is_inclusive() const { return type_ == filter_t::kInclusive; @@ -413,7 +419,7 @@ paths_filter::paths_filter(filter_t type, const std::filesystem::path &root, } catch (std::filesystem::filesystem_error &e) { LOG_WARN("Cannot add non-existent path {} to paths filter", - path.string()); + absolute_path.string()); continue; } @@ -447,6 +453,31 @@ tvl::value_t paths_filter::match( return false; } +tvl::value_t paths_filter::match( + const diagram & /*d*/, const common::model::source_location &p) const +{ + if (paths_.empty()) { + return {}; + } + + auto sl_path = std::filesystem::path{p.file()}; + + // Matching source paths doesn't make sens if they are not absolute or empty + if (p.file().empty() || sl_path.is_relative()) { + return {}; + } + + for (const auto &path : paths_) { + if (sl_path.root_name().string() == path.root_name().string() && + util::starts_with(sl_path.relative_path(), path.relative_path())) { + + return true; + } + } + + return false; +} + diagram_filter::diagram_filter( const common::model::diagram &d, const config::diagram &c) : diagram_{d} diff --git a/src/common/model/diagram_filter.h b/src/common/model/diagram_filter.h index 24b61955..e3648ce5 100644 --- a/src/common/model/diagram_filter.h +++ b/src/common/model/diagram_filter.h @@ -75,6 +75,9 @@ public: virtual tvl::value_t match( const diagram &d, const common::model::source_file &f) const; + virtual tvl::value_t match( + const diagram &d, const common::model::source_location &f) const; + bool is_inclusive() const; bool is_exclusive() const; @@ -332,6 +335,9 @@ struct paths_filter : public filter_visitor { tvl::value_t match( const diagram &d, const common::model::source_file &r) const override; + tvl::value_t match(const diagram &d, + const common::model::source_location &sl) const override; + private: std::vector paths_; std::filesystem::path root_; diff --git a/src/config/yaml_decoders.cc b/src/config/yaml_decoders.cc index e7d1b55c..69a5addb 100644 --- a/src/config/yaml_decoders.cc +++ b/src/config/yaml_decoders.cc @@ -400,6 +400,7 @@ template <> struct convert { get_option(node, rhs.generate_packages); get_option(node, rhs.relationship_hints); get_option(node, rhs.type_aliases); + get_option(node, rhs.relative_to); get_option(node, rhs.comment_parser); rhs.initialize_relationship_hints(); diff --git a/tests/t00061/.clang-uml b/tests/t00061/.clang-uml new file mode 100644 index 00000000..9643fb5c --- /dev/null +++ b/tests/t00061/.clang-uml @@ -0,0 +1,15 @@ +compilation_database_dir: .. +output_directory: puml +diagrams: + t00061_class: + type: class + relative_to: ../../tests/t00061 + glob: + - t00061.cc + include: + namespaces: + - clanguml::t00061 + paths: + - include/t00061_a.h + using_namespace: + - clanguml::t00061 \ No newline at end of file diff --git a/tests/t00061/include/t00061_a.h b/tests/t00061/include/t00061_a.h new file mode 100644 index 00000000..988b7b9d --- /dev/null +++ b/tests/t00061/include/t00061_a.h @@ -0,0 +1,5 @@ +namespace clanguml { +namespace t00061 { +struct A { }; +} +} \ No newline at end of file diff --git a/tests/t00061/include/t00061_b.h b/tests/t00061/include/t00061_b.h new file mode 100644 index 00000000..f1009c88 --- /dev/null +++ b/tests/t00061/include/t00061_b.h @@ -0,0 +1,5 @@ +namespace clanguml { +namespace t00061 { +struct B { }; +} +} \ No newline at end of file diff --git a/tests/t00061/t00061.cc b/tests/t00061/t00061.cc new file mode 100644 index 00000000..ff043833 --- /dev/null +++ b/tests/t00061/t00061.cc @@ -0,0 +1,11 @@ +#include "include/t00061_a.h" +#include "include/t00061_b.h" + +namespace clanguml { +namespace t00061 { +struct C { + A *a; + B *b; +}; +} +} \ No newline at end of file diff --git a/tests/t00061/test_case.h b/tests/t00061/test_case.h new file mode 100644 index 00000000..38903fc8 --- /dev/null +++ b/tests/t00061/test_case.h @@ -0,0 +1,58 @@ +/** + * tests/t00061/test_case.h + * + * Copyright (c) 2021-2023 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("t00061", "[test-case][class]") +{ + auto [config, db] = load_config("t00061"); + + auto diagram = config.diagrams["t00061_class"]; + + REQUIRE(diagram->name == "t00061_class"); + + auto model = generate_class_diagram(*db, diagram); + + REQUIRE(model->name() == "t00061_class"); + + { + auto puml = generate_class_puml(diagram, *model); + AliasMatcher _A(puml); + + REQUIRE_THAT(puml, StartsWith("@startuml")); + REQUIRE_THAT(puml, EndsWith("@enduml\n")); + + // Check if all classes exist + REQUIRE_THAT(puml, IsClass(_A("A"))); + REQUIRE_THAT(puml, !IsClass(_A("B"))); + REQUIRE_THAT(puml, !IsClass(_A("C"))); + + save_puml( + config.output_directory() + "/" + diagram->name + ".puml", puml); + } + + { + auto j = generate_class_json(diagram, *model); + + using namespace json; + + REQUIRE(IsClass(j, "A")); + REQUIRE(!IsClass(j, "B")); + REQUIRE(!IsClass(j, "C")); + + save_json(config.output_directory() + "/" + diagram->name + ".json", j); + } +} \ No newline at end of file diff --git a/tests/test_cases.cc b/tests/test_cases.cc index 3854f730..b5cc7897 100644 --- a/tests/test_cases.cc +++ b/tests/test_cases.cc @@ -300,6 +300,7 @@ using namespace clanguml::test::matchers; #include "t00059/test_case.h" #endif #include "t00060/test_case.h" +#include "t00061/test_case.h" /// /// Sequence diagram tests diff --git a/util/templates/test_cases/test_case.h b/util/templates/test_cases/test_case.h index 83ed5856..c5f55f42 100644 --- a/util/templates/test_cases/test_case.h +++ b/util/templates/test_cases/test_case.h @@ -28,13 +28,25 @@ TEST_CASE("{{ name }}", "[test-case][{{ type }}]") REQUIRE(model->name() == "{{ name }}_{{ type }}"); - auto puml = generate_{{ type }}_puml(diagram, *model); - AliasMatcher _A(puml); + { + auto puml = generate_{{ type }}_puml(diagram, *model); + AliasMatcher _A(puml); - REQUIRE_THAT(puml, StartsWith("@startuml")); - REQUIRE_THAT(puml, EndsWith("@enduml\n")); + REQUIRE_THAT(puml, StartsWith("@startuml")); + REQUIRE_THAT(puml, EndsWith("@enduml\n")); - {{ examples }} + {{ examples }} + + save_puml( + config.output_directory() + "/" + diagram->name + ".puml", puml); + } + + { + auto j = generate_class_json(diagram, *model); + + using namespace json; + + save_json(config.output_directory() + "/" + diagram->name + ".json", j); + } - save_puml(config.output_directory() + "/" + diagram->name + ".puml", puml); }