From be2ad5dc9b5a1808a6f5a233ed29a410ca071ae7 Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Wed, 1 Mar 2023 18:37:31 +0100 Subject: [PATCH] Added test case for C++20 concepts with variadic params --- CHANGELOG.md | 1 + README.md | 1 + tests/CMakeLists.txt | 2 +- tests/t00058/.clang-uml | 15 +++++++++ tests/t00058/t00058.cc | 44 ++++++++++++++++++++++++ tests/t00058/test_case.h | 69 ++++++++++++++++++++++++++++++++++++++ tests/test_cases.cc | 4 ++- tests/test_cases.yaml | 5 ++- util/generate_test_case.py | 10 ++++-- 9 files changed, 146 insertions(+), 5 deletions(-) create mode 100644 tests/t00058/.clang-uml create mode 100644 tests/t00058/t00058.cc create mode 100644 tests/t00058/test_case.h diff --git a/CHANGELOG.md b/CHANGELOG.md index e9300d77..1731e69e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # CHANGELOG + * Added initial support for C++20 concept rendering (#96) * Added support for plain C11 translation units (#97) * Added 'row' and 'column' layout hints for aligning elements (#90) * Added 'together' layout hint for grouping elements (#43) diff --git a/README.md b/README.md index da7caf82..d19c1935 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ Main features supported so far include: * Optional package generation from namespaces * Interactive links to online code to classes, methods and class fields in SVG diagrams * Support for plain C99/C11 code (struct and units relationships) + * C++20 concept constraints * **Sequence diagram generation** * Generation of sequence diagram from specific method or function * Generation of loop and conditional statements diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 224fc65b..821a3622 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -3,7 +3,7 @@ file(GLOB_RECURSE TEST_CASE_SOURCES t*/*.cc t*/*.c t*/src/*.c) file(GLOB_RECURSE TEST_CASE_CONFIGS t*/.clang-uml) file(GLOB_RECURSE TEST_CONFIG_YMLS test_config_data/*.yml) -set(TEST_CASES_REQUIRING_CXX20 t00056) +set(TEST_CASES_REQUIRING_CXX20 t00056 t00058) set(CLANG_UML_TEST_LIBRARIES clang-umllib diff --git a/tests/t00058/.clang-uml b/tests/t00058/.clang-uml new file mode 100644 index 00000000..dfc6afb4 --- /dev/null +++ b/tests/t00058/.clang-uml @@ -0,0 +1,15 @@ +compilation_database_dir: .. +output_directory: puml +diagrams: + t00058_class: + type: class + glob: + - ../../tests/t00058/t00058.cc + include: + namespaces: + - clanguml::t00058 + using_namespace: + - clanguml::t00058 + plantuml: + after: + - '{{ alias("same_as_first_type") }} ..> {{ alias("first_type") }}' \ No newline at end of file diff --git a/tests/t00058/t00058.cc b/tests/t00058/t00058.cc new file mode 100644 index 00000000..e0ca24ee --- /dev/null +++ b/tests/t00058/t00058.cc @@ -0,0 +1,44 @@ +#include +#include +#include + +// Based on a blog post: +// https://andreasfertig.blog/2020/08/cpp20-concepts-testing-constrained-functions/ + +namespace clanguml { +namespace t00058 { + +template struct first_type { + using type = T; +}; + +template +using first_type_t = typename first_type::type; + +// TODO: Dependency of this concept on first_type<> template does not currently +// work due to the fact that I don't know how to extract that information +// from clang::DependentNameType to which first_type_t<> resolves to... +template +concept same_as_first_type = std::is_same_v, + std::remove_cvref_t>>; + +template + requires same_as_first_type +struct A { + std::vector a; +}; + +template + requires same_as_first_type +struct B { + std::vector b; + P bb; +}; + +struct R { + A aa; + B> bb; +}; + +} +} \ No newline at end of file diff --git a/tests/t00058/test_case.h b/tests/t00058/test_case.h new file mode 100644 index 00000000..bcfea0f3 --- /dev/null +++ b/tests/t00058/test_case.h @@ -0,0 +1,69 @@ +/** + * tests/t00058/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("t00058", "[test-case][class]") +{ + auto [config, db] = load_config("t00058"); + + auto diagram = config.diagrams["t00058_class"]; + + REQUIRE(diagram->name == "t00058_class"); + + auto model = generate_class_diagram(*db, diagram); + + REQUIRE(model->name() == "t00058_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, IsClassTemplate("A", "int,int,double,std::string")); + REQUIRE_THAT( + puml, IsClassTemplate("B", "int,std::string,int,double,A")); + + REQUIRE_THAT(puml, IsConcept(_A("same_as_first_type"))); + + REQUIRE_THAT(puml, + IsConstraint(_A("A"), _A("same_as_first_type"), + "T,Args...")); + + REQUIRE_THAT(puml, + IsConstraint(_A("B"), _A("same_as_first_type"), + "T,Args...")); + + REQUIRE_THAT(puml, + IsAggregation(_A("R"), _A("A"), "+aa")); + REQUIRE_THAT(puml, + IsAggregation( + _A("R"), _A("B>"), "+bb")); + + REQUIRE_THAT(puml, + IsInstantiation( + _A("A"), _A("A"))); + REQUIRE_THAT(puml, + IsInstantiation(_A("B"), + _A("B>"))); + + REQUIRE_THAT(puml, + IsDependency( + _A("same_as_first_type"), _A("first_type"))); + + save_puml(config.output_directory() + "/" + diagram->name + ".puml", puml); +} \ No newline at end of file diff --git a/tests/test_cases.cc b/tests/test_cases.cc index 8412df53..06cc0c11 100644 --- a/tests/test_cases.cc +++ b/tests/test_cases.cc @@ -259,7 +259,9 @@ using namespace clanguml::test::matchers; #include "t00056/test_case.h" #endif #include "t00057/test_case.h" - +#if defined(ENABLE_CXX_STD_20_TEST_CASES) +#include "t00058/test_case.h" +#endif /// /// Sequence diagram tests /// diff --git a/tests/test_cases.yaml b/tests/test_cases.yaml index 662c1bdf..0c912406 100644 --- a/tests/test_cases.yaml +++ b/tests/test_cases.yaml @@ -163,11 +163,14 @@ test_cases: title: Test case for `row` and `column` layout hints description: - name: t00056 - title: Basic C++ concepts test case + title: Basic C++20 concepts test case description: - name: t00057 title: Test case C99/C11 translation units with structs and unions description: + - name: t00058 + title: Test case for concepts with variadic parameters and type aliases + description: Sequence diagrams: - name: t20001 title: Basic sequence diagram test case diff --git a/util/generate_test_case.py b/util/generate_test_case.py index 44b99c53..f6c4653b 100755 --- a/util/generate_test_case.py +++ b/util/generate_test_case.py @@ -31,6 +31,12 @@ CLASS_DIAGRAM_TEST_CASE_EXAMPLES = """ // Check if class templates exist //REQUIRE_THAT(puml, IsClassTemplate("A", "T,P,CMP,int N")); + // Check concepts + //REQUIRE_THAT(puml, IsConcept(_A("AConcept"))); + //REQUIRE_THAT(puml, + // IsConceptRequirement( + // _A("AConcept"), "sizeof (T) > sizeof (P)")); + // Check if all enums exist //REQUIRE_THAT(puml, IsEnum(_A("Lights"))); @@ -49,8 +55,8 @@ CLASS_DIAGRAM_TEST_CASE_EXAMPLES = """ // Check if all relationships exist //REQUIRE_THAT(puml, IsAssociation(_A("D"), _A("A"), "-as")); //REQUIRE_THAT(puml, IsDependency(_A("R"), _A("B"))); - //REQUIRE_THAT(puml, IsAggregation(_A("R"), _A("D"))); - //REQUIRE_THAT(puml, IsComposition(_A("R"), _A("D"))); + //REQUIRE_THAT(puml, IsAggregation(_A("R"), _A("D"), "-ag")); + //REQUIRE_THAT(puml, IsComposition(_A("R"), _A("D"), "-ac")); //REQUIRE_THAT(puml, IsInstantiation(_A("ABCD::F"), _A("F"))); """