From ea6892f754033c3560e263b15b8ff39ad954f836 Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Sun, 17 Dec 2023 20:49:41 +0100 Subject: [PATCH] Added support for class diagram filtering based on C++20 modules (#195) --- CMakeLists.txt | 3 +- Makefile | 25 ++++-- README.md | 1 + .../visitor/translation_unit_visitor.cc | 9 +++ src/common/model/diagram_filter.cc | 39 ++++++++++ src/common/model/diagram_filter.h | 15 ++++ src/common/model/element.h | 15 ++++ .../visitor/translation_unit_visitor.cc | 10 +++ src/common/visitor/translation_unit_visitor.h | 3 + src/config/config.h | 13 ++++ src/config/schema.h | 5 +- src/config/yaml_decoders.cc | 6 ++ src/config/yaml_emitters.cc | 2 + tests/CMakeLists.txt | 45 +++++++++-- tests/t00070/.clang-uml | 12 +++ tests/t00070/src/common.cppm | 11 +++ tests/t00070/src/lib1.cppm | 11 +++ tests/t00070/src/lib2.cppm | 11 +++ tests/t00070/t00070.cc | 14 ++++ tests/t00070/test_case.h | 78 +++++++++++++++++++ tests/test_cases.cc | 3 + 21 files changed, 310 insertions(+), 21 deletions(-) create mode 100644 tests/t00070/.clang-uml create mode 100644 tests/t00070/src/common.cppm create mode 100644 tests/t00070/src/lib1.cppm create mode 100644 tests/t00070/src/lib2.cppm create mode 100644 tests/t00070/t00070.cc create mode 100644 tests/t00070/test_case.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b2bcde24..7721ff83 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.12) +cmake_minimum_required(VERSION 3.16) # # Project name @@ -153,6 +153,7 @@ add_subdirectory(src) # Enable testing via CTest # option(BUILD_TESTS "" ON) +option(ENABLE_CXX_MODULES_TEST_CASES "" OFF) if(BUILD_TESTS) enable_testing() add_subdirectory(tests) diff --git a/Makefile b/Makefile index 9d55dd42..3ab0d8f9 100644 --- a/Makefile +++ b/Makefile @@ -35,6 +35,9 @@ LLVM_CONFIG_PATH ?= CMAKE_PREFIX ?= CMAKE_CXX_FLAGS ?= CMAKE_EXE_LINKER_FLAGS ?= +CMAKE_GENERATOR ?= Unix Makefiles + +ENABLE_CXX_MODULES_TEST_CASES ?= OFF GIT_VERSION ?= $(shell git describe --tags --always --abbrev=7) PKG_VERSION ?= $(shell git describe --tags --always --abbrev=7 | tr - .) @@ -49,6 +52,7 @@ clean: debug/CMakeLists.txt: cmake -S . -B debug \ + -G"$(CMAKE_GENERATOR)" \ -DGIT_VERSION=$(GIT_VERSION) \ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ -DCMAKE_BUILD_TYPE=Debug \ @@ -56,10 +60,12 @@ debug/CMakeLists.txt: -DCMAKE_EXE_LINKER_FLAGS="$(CMAKE_EXE_LINKER_FLAGS)" \ -DLLVM_VERSION=${LLVM_VERSION} \ -DLLVM_CONFIG_PATH=${LLVM_CONFIG_PATH} \ - -DCMAKE_PREFIX=${CMAKE_PREFIX} + -DCMAKE_PREFIX=${CMAKE_PREFIX} \ + -DENABLE_CXX_MODULES_TEST_CASES=$(ENABLE_CXX_MODULES_TEST_CASES) release/CMakeLists.txt: cmake -S . -B release \ + -G"$(CMAKE_GENERATOR)" \ -DGIT_VERSION=$(GIT_VERSION) \ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ -DCMAKE_BUILD_TYPE=Release \ @@ -67,10 +73,12 @@ release/CMakeLists.txt: -DCMAKE_EXE_LINKER_FLAGS="$(CMAKE_EXE_LINKER_FLAGS)" \ -DLLVM_VERSION=${LLVM_VERSION} \ -DLLVM_CONFIG_PATH=${LLVM_CONFIG_PATH} \ - -DCMAKE_PREFIX=${CMAKE_PREFIX} + -DCMAKE_PREFIX=${CMAKE_PREFIX} \ + -DENABLE_CXX_MODULES_TEST_CASES=$(ENABLE_CXX_MODULES_TEST_CASES) debug_tidy/CMakeLists.txt: cmake -S . -B debug_tidy \ + -G"$(CMAKE_GENERATOR)" \ -DGIT_VERSION=$(GIT_VERSION) \ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ -DCMAKE_BUILD_TYPE=Debug \ @@ -79,24 +87,25 @@ debug_tidy/CMakeLists.txt: -DCMAKE_EXE_LINKER_FLAGS="$(CMAKE_EXE_LINKER_FLAGS)" \ -DLLVM_VERSION=${LLVM_VERSION} \ -DLLVM_CONFIG_PATH=${LLVM_CONFIG_PATH} \ - -DCMAKE_PREFIX=${CMAKE_PREFIX} + -DCMAKE_PREFIX=${CMAKE_PREFIX} \ + -DENABLE_CXX_MODULES_TEST_CASES=$(ENABLE_CXX_MODULES_TEST_CASES) debug: debug/CMakeLists.txt echo "Using ${NUMPROC} cores" - make -C debug -j$(NUMPROC) + cmake --build debug -j$(NUMPROC) debug_tidy: debug_tidy/CMakeLists.txt echo "Using ${NUMPROC} cores" - make -C debug_tidy -j$(NUMPROC) + cmake --build debug_tidy -j$(NUMPROC) release: release/CMakeLists.txt - make -C release -j$(NUMPROC) + cmake --build release -j$(NUMPROC) test: debug - CTEST_OUTPUT_ON_FAILURE=1 make -C debug test + CTEST_OUTPUT_ON_FAILURE=1 ctest --test-dir debug test_release: release - CTEST_OUTPUT_ON_FAILURE=1 make -C release test + CTEST_OUTPUT_ON_FAILURE=1 ctest --test-dir release install: release make -C release install DESTDIR=${DESTDIR} diff --git a/README.md b/README.md index 4fcd8feb..93f9359d 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ Main features supported so far include: * Support for plain C99/C11 code (struct, units and their relationships) - [_example_](docs/test_cases/t00057.md) * C++20 concept constraints - [_example_](docs/test_cases/t00059.md) * C++20 coroutines - [_example_](docs/test_cases/t00069.md) + * Diagram content filtering based on C++20 modules - [_example_](docs/test_cases/t00070.md) * **Sequence diagram generation** * Generation of sequence diagram from specific method or function - [_example_](docs/test_cases/t20001.md) * Generation of loop and conditional statements - [_example_](docs/test_cases/t20021.md) diff --git a/src/class_diagram/visitor/translation_unit_visitor.cc b/src/class_diagram/visitor/translation_unit_visitor.cc index 2a19423e..c1fe98df 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.cc +++ b/src/class_diagram/visitor/translation_unit_visitor.cc @@ -159,6 +159,7 @@ bool translation_unit_visitor::VisitEnumDecl(clang::EnumDecl *enm) process_comment(*enm, e); set_source_location(*enm, e); + set_owning_module(*enm, e); if (e.skip()) return true; @@ -265,6 +266,7 @@ bool translation_unit_visitor::VisitTypeAliasTemplateDecl( LOG_DBG("Adding class {} with id {}", name, id); set_source_location(*cls, *template_specialization_ptr); + set_owning_module(*cls, *template_specialization_ptr); add_class(std::move(template_specialization_ptr)); } @@ -677,6 +679,9 @@ bool translation_unit_visitor::VisitCXXRecordDecl(clang::CXXRecordDecl *cls) LOG_DBG( "== getQualifiedNameAsString() = {}", cls->getQualifiedNameAsString()); + if (cls->getOwningModule() != nullptr) + LOG_DBG( + "== getOwningModule()->Name = {}", cls->getOwningModule()->Name); LOG_DBG("== getID() = {}", cls->getID()); LOG_DBG("== isTemplateDecl() = {}", cls->isTemplateDecl()); LOG_DBG("== isTemplated() = {}", cls->isTemplated()); @@ -762,6 +767,7 @@ translation_unit_visitor::create_concept_declaration(clang::ConceptDecl *cpt) process_comment(*cpt, concept_model); set_source_location(*cpt, concept_model); + set_owning_module(*cpt, concept_model); if (concept_model.skip()) return {}; @@ -802,6 +808,7 @@ std::unique_ptr translation_unit_visitor::create_record_declaration( process_comment(*rec, record); set_source_location(*rec, record); + set_owning_module(*rec, record); const auto record_full_name = record_ptr->full_name(false); @@ -841,6 +848,7 @@ std::unique_ptr translation_unit_visitor::create_class_declaration( process_comment(*cls, c); set_source_location(*cls, c); + set_owning_module(*cls, c); if (c.skip()) return {}; @@ -1835,6 +1843,7 @@ translation_unit_visitor::process_template_specialization( process_comment(*cls, template_instantiation); set_source_location(*cls, template_instantiation); + set_owning_module(*cls, template_instantiation); if (template_instantiation.skip()) return {}; diff --git a/src/common/model/diagram_filter.cc b/src/common/model/diagram_filter.cc index da91b003..493b07db 100644 --- a/src/common/model/diagram_filter.cc +++ b/src/common/model/diagram_filter.cc @@ -285,6 +285,39 @@ tvl::value_t namespace_filter::match(const diagram &d, const element &e) const return result; } +modules_filter::modules_filter( + filter_t type, std::vector modules) + : filter_visitor{type} + , modules_{std::move(modules)} +{ +} + +tvl::value_t modules_filter::match(const diagram &d, const element &e) const +{ + if (modules_.empty()) + return {}; + + if (!e.module().has_value()) + return {false}; + + const auto module_toks = util::split(e.module().value(), "."); + + auto result = tvl::any_of(modules_.begin(), modules_.end(), + [&e, &module_toks](const auto &modit) { + if (std::holds_alternative(modit.value())) { + const auto &modit_str = std::get(modit.value()); + const auto modit_toks = util::split(modit_str, "."); + + return e.module() == modit_str || + util::starts_with(module_toks, modit_toks); + } + + return std::get(modit.value()) %= e.module().value(); + }); + + return result; +} + element_filter::element_filter( filter_t type, std::vector elements) : filter_visitor{type} @@ -887,6 +920,9 @@ void diagram_filter::init_filters(const config::diagram &c) add_inclusive_filter(std::make_unique( filter_t::kInclusive, c.include().namespaces)); + add_inclusive_filter(std::make_unique( + filter_t::kInclusive, c.include().modules)); + add_inclusive_filter(std::make_unique( filter_t::kInclusive, c.include().relationships)); @@ -997,6 +1033,9 @@ void diagram_filter::init_filters(const config::diagram &c) add_exclusive_filter(std::make_unique( filter_t::kExclusive, c.exclude().namespaces)); + add_exclusive_filter(std::make_unique( + filter_t::kExclusive, c.exclude().modules)); + add_exclusive_filter(std::make_unique( filter_t::kExclusive, c.root_directory(), c.exclude().paths)); diff --git a/src/common/model/diagram_filter.h b/src/common/model/diagram_filter.h index ea51d01b..9344b27b 100644 --- a/src/common/model/diagram_filter.h +++ b/src/common/model/diagram_filter.h @@ -154,6 +154,21 @@ private: std::vector namespaces_; }; +/** + * Match diagram elements to a set of specified modules or + * module regex patterns. + */ +struct modules_filter : public filter_visitor { + modules_filter(filter_t type, std::vector modules); + + ~modules_filter() override = default; + + tvl::value_t match(const diagram &d, const element &e) const override; + +private: + std::vector modules_; +}; + /** * Match element's name to a set of names or regex patterns. */ diff --git a/src/common/model/element.h b/src/common/model/element.h index 21626a35..d94ce76a 100644 --- a/src/common/model/element.h +++ b/src/common/model/element.h @@ -87,6 +87,20 @@ public: */ const namespace_ &path() const { return ns_; } + /** + * Set elements owning module. + * + * @param module C++20 module. + */ + void set_module(const std::string &module) { module_ = module; } + + /** + * Return elements owning module, if any. + * + * @return C++20 module. + */ + std::optional module() const { return module_; } + /** * Return elements full name. * @@ -120,5 +134,6 @@ public: private: namespace_ ns_; namespace_ using_namespace_; + std::optional module_; }; } // namespace clanguml::common::model diff --git a/src/common/visitor/translation_unit_visitor.cc b/src/common/visitor/translation_unit_visitor.cc index e30deeee..f6741758 100644 --- a/src/common/visitor/translation_unit_visitor.cc +++ b/src/common/visitor/translation_unit_visitor.cc @@ -21,6 +21,8 @@ #include "comment/clang_visitor.h" #include "comment/plain_visitor.h" +#include "clang/Basic/Module.h" + namespace clanguml::common::visitor { translation_unit_visitor::translation_unit_visitor( @@ -161,4 +163,12 @@ void translation_unit_visitor::set_source_location( element.set_location_id(location.getHashValue()); } +void translation_unit_visitor::set_owning_module( + const clang::Decl &decl, clanguml::common::model::element &element) +{ + if (const clang::Module *module = decl.getOwningModule(); + module != nullptr) { + element.set_module(module->Name); + } +} } // namespace clanguml::common::visitor \ No newline at end of file diff --git a/src/common/visitor/translation_unit_visitor.h b/src/common/visitor/translation_unit_visitor.h index 22139863..9021dc95 100644 --- a/src/common/visitor/translation_unit_visitor.h +++ b/src/common/visitor/translation_unit_visitor.h @@ -100,6 +100,9 @@ public: void set_source_location(const clang::SourceLocation &location, clanguml::common::model::source_location &element); + void set_owning_module( + const clang::Decl &decl, clanguml::common::model::element &element); + protected: /** * @brief Process comment directives in comment attached to a declaration diff --git a/src/config/config.h b/src/config/config.h index 5c219910..7b061b78 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -180,6 +180,19 @@ struct filter { */ std::vector namespaces; + /*! @brief Modules filter + * + * Example: + * + * ```yaml + * include + * modules: + * - app.module1 + * - r: ".*internal.*" + * ``` + */ + std::vector modules; + /*! @brief Elements filter * * Example: diff --git a/src/config/schema.h b/src/config/schema.h index 238ce590..21827d67 100644 --- a/src/config/schema.h +++ b/src/config/schema.h @@ -64,10 +64,6 @@ types: regex_t: r: string regex_or_string_t: [string, regex_t] - namespaces_filter_t: - namespaces: [regex_or_string_t] - elements_filter_t: - elements: [regex_or_string_t] element_types_filter_t: !variant - class - enum @@ -121,6 +117,7 @@ types: - context_filter_match_t filter_t: namespaces: !optional [regex_or_string_t] + modules: !optional [regex_or_string_t] elements: !optional [regex_or_string_t] element_types: !optional [element_types_filter_t] relationships: !optional [relationship_filter_t] diff --git a/src/config/yaml_decoders.cc b/src/config/yaml_decoders.cc index 999a3d83..c24686ea 100644 --- a/src/config/yaml_decoders.cc +++ b/src/config/yaml_decoders.cc @@ -475,6 +475,12 @@ template <> struct convert { rhs.namespaces.push_back({ns}); } + if (node["modules"]) { + auto module_list = node["modules"].as(); + for (const auto &ns : module_list) + rhs.modules.push_back({ns}); + } + if (node["relationships"]) rhs.relationships = node["relationships"].as(); diff --git a/src/config/yaml_emitters.cc b/src/config/yaml_emitters.cc index 200e1df4..2bdad164 100644 --- a/src/config/yaml_emitters.cc +++ b/src/config/yaml_emitters.cc @@ -122,6 +122,8 @@ YAML::Emitter &operator<<(YAML::Emitter &out, const filter &f) out << YAML::BeginMap; if (!f.namespaces.empty()) out << YAML::Key << "namespaces" << YAML::Value << f.namespaces; + if (!f.modules.empty()) + out << YAML::Key << "modules" << YAML::Value << f.modules; if (!f.access.empty()) out << YAML::Key << "access" << YAML::Value << f.access; if (!f.context.empty()) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 0fcb00f8..982ca588 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,11 +1,30 @@ file(GLOB_RECURSE TEST_CASE_SOURCES t*/*.cc t*/*.c t*/src/*.c) +file(GLOB_RECURSE TEST_CASE_MODULE_SOURCES t*/src/*.cppm) file(GLOB_RECURSE TEST_CASE_CONFIGS t*/.clang-uml) file(GLOB_RECURSE TEST_CONFIG_YMLS test_config_data/*.yml test_compilation_database_data/*.yml test_compilation_database_data/*.json) set(TEST_CASES_REQUIRING_CXX20 t00056 t00058 t00059 t00065 t00069) +set(TEST_CASES_REQUIRING_CXX20_MODULES t00070) + +if(ENABLE_CXX_MODULES_TEST_CASES) + foreach(CXX20_MOD_TC ${TEST_CASES_REQUIRING_CXX20_MODULES}) + list(APPEND TEST_CASES_REQUIRING_CXX20 ${CXX20_MOD_TC}) + endforeach() + set(CMAKE_CXX_SCAN_FOR_MODULES ON) +else() + foreach(CXX20_MOD_TC ${TEST_CASES_REQUIRING_CXX20_MODULES}) + list(FILTER TEST_CASE_SOURCES + EXCLUDE + REGEX ".*${CXX20_MOD_TC}.*") + list(FILTER TEST_CASE_CONFIGS + EXCLUDE + REGEX ".*${CXX20_MOD_TC}.*") + + endforeach() +endif(ENABLE_CXX_MODULES_TEST_CASES) set(CLANG_UML_TEST_LIBRARIES clang-umllib @@ -19,6 +38,7 @@ endif(MSVC) list(FIND CMAKE_CXX_COMPILE_FEATURES cxx_std_20 SUPPORTS_CXX_STD_20) +# Remove test cases which require C++20 if they are not supported here if(SUPPORTS_CXX_STD_20 EQUAL -1 OR ${LLVM_PACKAGE_VERSION} VERSION_LESS "14.0") set(ENABLE_CXX_STD_20_TEST_CASES 0) @@ -34,7 +54,7 @@ else() set(ENABLE_CXX_STD_20_TEST_CASES 1) endif() -set(TEST_CASES +set(TEST_NAMES test_util test_model test_cases @@ -46,15 +66,24 @@ set(TEST_CASES test_thread_pool_executor test_query_driver_output_extractor) -foreach(TEST_NAME ${TEST_CASES}) - add_executable(${TEST_NAME} - ${TEST_NAME}.cc - $<$:${TEST_CASE_SOURCES}> - catch.h) +foreach(TEST_NAME ${TEST_NAMES}) + add_executable(${TEST_NAME}) + if(TEST_NAME STREQUAL "test_cases") + if(ENABLE_CXX_MODULES_TEST_CASES) + target_sources(${TEST_NAME} PUBLIC FILE_SET CXX_MODULES FILES + ${TEST_CASE_MODULE_SOURCES}) + endif(ENABLE_CXX_MODULES_TEST_CASES) + target_sources(${TEST_NAME} PUBLIC ${TEST_NAME}.cc + ${TEST_CASE_SOURCES} catch.h) + else() + target_sources(${TEST_NAME} PUBLIC ${TEST_NAME}.cc catch.h) + endif(TEST_NAME STREQUAL "test_cases") + target_compile_features(${TEST_NAME} PRIVATE $) target_compile_definitions(${TEST_NAME} PRIVATE - $<$:ENABLE_CXX_STD_20_TEST_CASES>) + $<$:ENABLE_CXX_STD_20_TEST_CASES> + $<$:ENABLE_CXX_MODULES_TEST_CASES>) target_compile_options(${TEST_NAME} PRIVATE $<$: $<$,$>: @@ -89,6 +118,6 @@ foreach(TEST_CONFIG_YML ${TEST_CONFIG_YMLS}) COPYONLY) endforeach() -foreach(TEST_NAME ${TEST_CASES}) +foreach(TEST_NAME ${TEST_NAMES}) add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME}) endforeach() diff --git a/tests/t00070/.clang-uml b/tests/t00070/.clang-uml new file mode 100644 index 00000000..6fa20189 --- /dev/null +++ b/tests/t00070/.clang-uml @@ -0,0 +1,12 @@ +diagrams: + t00070_class: + type: class + glob: + - t00070.cc + include: + modules: + - t00070 + exclude: + modules: + - t00070.lib2 + using_namespace: clanguml::t00070 \ No newline at end of file diff --git a/tests/t00070/src/common.cppm b/tests/t00070/src/common.cppm new file mode 100644 index 00000000..682316c9 --- /dev/null +++ b/tests/t00070/src/common.cppm @@ -0,0 +1,11 @@ +export module t00070; +export import t00070.lib1; +export import t00070.lib2; + +export namespace clanguml::t00070 { +class A { + int get() { return a; } + + int a; +}; +} \ No newline at end of file diff --git a/tests/t00070/src/lib1.cppm b/tests/t00070/src/lib1.cppm new file mode 100644 index 00000000..102c0aa7 --- /dev/null +++ b/tests/t00070/src/lib1.cppm @@ -0,0 +1,11 @@ +export module t00070.lib1; + +export namespace clanguml::t00070 { +class B { }; + +template class BB { + T t; +}; + +enum class BBB { bbb1, bbb2 }; +} \ No newline at end of file diff --git a/tests/t00070/src/lib2.cppm b/tests/t00070/src/lib2.cppm new file mode 100644 index 00000000..1d2a8cac --- /dev/null +++ b/tests/t00070/src/lib2.cppm @@ -0,0 +1,11 @@ +export module t00070.lib2; + +export namespace clanguml::t00070 { +class C { }; + +template class CC { + T t; +}; + +enum class CCC { ccc1, ccc2 }; +} \ No newline at end of file diff --git a/tests/t00070/t00070.cc b/tests/t00070/t00070.cc new file mode 100644 index 00000000..8d1b6420 --- /dev/null +++ b/tests/t00070/t00070.cc @@ -0,0 +1,14 @@ +import t00070.lib1; +import t00070.lib2; + +namespace clanguml { +namespace t00070 { +int tmain() +{ + B b; + C c; + + return 0; +} +} +} \ No newline at end of file diff --git a/tests/t00070/test_case.h b/tests/t00070/test_case.h new file mode 100644 index 00000000..fff287a3 --- /dev/null +++ b/tests/t00070/test_case.h @@ -0,0 +1,78 @@ +/** + * tests/t00070/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("t00070", "[test-case][class]") +{ + auto [config, db] = load_config("t00070"); + + auto diagram = config.diagrams["t00070_class"]; + + REQUIRE(diagram->name == "t00070_class"); + + auto model = generate_class_diagram(*db, diagram); + + REQUIRE(model->name() == "t00070_class"); + + { + auto src = generate_class_puml(diagram, *model); + AliasMatcher _A(src); + + REQUIRE_THAT(src, StartsWith("@startuml")); + REQUIRE_THAT(src, EndsWith("@enduml\n")); + + REQUIRE_THAT(src, !IsClass(_A("A"))); + REQUIRE_THAT(src, IsClass(_A("B"))); + REQUIRE_THAT(src, !IsClass(_A("C"))); + + REQUIRE_THAT(src, IsClassTemplate("BB", "T")); + REQUIRE_THAT(src, !IsClassTemplate("CC", "T")); + + REQUIRE_THAT(src, IsEnum(_A("BBB"))); + REQUIRE_THAT(src, !IsEnum(_A("CCC"))); + + save_puml(config.output_directory(), diagram->name + ".puml", src); + } + + { + auto j = generate_class_json(diagram, *model); + + using namespace json; + + save_json(config.output_directory(), diagram->name + ".json", j); + } + + { + auto src = generate_class_mermaid(diagram, *model); + + mermaid::AliasMatcher _A(src); + using mermaid::IsClass; + using mermaid::IsEnum; + + REQUIRE_THAT(src, !IsClass(_A("A"))); + REQUIRE_THAT(src, IsClass(_A("B"))); + REQUIRE_THAT(src, !IsClass(_A("C"))); + + REQUIRE_THAT(src, IsClass(_A("BB"))); + REQUIRE_THAT(src, !IsClass(_A("CC"))); + + REQUIRE_THAT(src, IsEnum(_A("BBB"))); + REQUIRE_THAT(src, !IsEnum(_A("CCC"))); + + save_mermaid(config.output_directory(), diagram->name + ".mmd", src); + } +} \ No newline at end of file diff --git a/tests/test_cases.cc b/tests/test_cases.cc index 4b7f95f5..ebf1ddb9 100644 --- a/tests/test_cases.cc +++ b/tests/test_cases.cc @@ -407,6 +407,9 @@ using namespace clanguml::test::matchers; #if defined(ENABLE_CXX_STD_20_TEST_CASES) #include "t00069/test_case.h" #endif +#if defined(ENABLE_CXX_MODULES_TEST_CASES) +#include "t00070/test_case.h" +#endif /// /// Sequence diagram tests