Skip to content

Commit f58156c

Browse files
committed
Add support for catch2
This commit allows cookiecutter to support creating Beman libraries that use catch2 as their unit testing library instead of gtest. It adds a new unit_test_library option to the exemplar cookiecutter parameters that can be set to either "gtest" or "catch2". It adds a new exemplar-only CI workflow, catch2_exemplar_test.yml, that smoke tests that the catch2 version of the library works properly. It also bumps infra to include a workaround in use-fetch-content.cmake that allows catch2 to be usable from FetchContent_MakeAvailable without needing to include the workaround in the library's CMake itself. Fixes #311
1 parent 77ea1ca commit f58156c

13 files changed

Lines changed: 135 additions & 14 deletions

File tree

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2+
3+
name: Catch2 Exemplar Test
4+
on:
5+
push:
6+
branches:
7+
- main
8+
pull_request:
9+
10+
jobs:
11+
catch2-exemplar-test:
12+
runs-on: ubuntu-latest
13+
container: ghcr.io/bemanproject/infra-containers-gcc:latest
14+
name: "Catch2 exemplar smoke test"
15+
steps:
16+
- name: Checkout
17+
uses: actions/checkout@v4
18+
- name: Test catch2 exemplar
19+
run: |
20+
cd cookiecutter
21+
source ./check_cookiecutter.sh
22+
cookiecutter_venv_path=$(mktemp --directory --dry-run)
23+
setup_venv "$cookiecutter_venv_path"
24+
stamp "$PWD" "./catch2_exemplar" "interface" "catch2"
25+
cd catch2_exemplar/exemplar
26+
cmake -B build -DCMAKE_PROJECT_TOP_LEVEL_INCLUDES=./infra/cmake/use-fetch-content.cmake -DCMAKE_CXX_STANDARD=20 -DCMAKE_INSTALL_PREFIX=$PWD/dist
27+
cmake --build build
28+
ctest --test-dir build
29+
cmake --install build

.github/workflows/static_exemplar_test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ jobs:
2121
source ./check_cookiecutter.sh
2222
cookiecutter_venv_path=$(mktemp --directory --dry-run)
2323
setup_venv "$cookiecutter_venv_path"
24-
stamp "$PWD" "./static_exemplar" "static"
24+
stamp "$PWD" "./static_exemplar" "static" "gtest"
2525
cd static_exemplar/exemplar
2626
cmake -B build -DCMAKE_PROJECT_TOP_LEVEL_INCLUDES=./infra/cmake/use-fetch-content.cmake -DCMAKE_CXX_STANDARD=20 -DCMAKE_INSTALL_PREFIX=$PWD/dist
2727
cmake --build build

cookiecutter/check_cookiecutter.sh

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ function stamp() {
88
local cookiecutter_dir="$1" ; shift
99
local output_dir="$1" ; shift
1010
local library_type="$1" ; shift
11+
local unit_test_library="$1" ; shift
1112
python3 \
1213
-m cookiecutter \
1314
--no-input \
@@ -19,6 +20,7 @@ function stamp() {
1920
description="A Beman Library Exemplar" \
2021
godbolt_link="https://godbolt.org/z/4qEPK87va" \
2122
library_type="$library_type" \
23+
unit_test_library="$unit_test_library" \
2224
_ci_tests_cron="30 15 * * 6" \
2325
_pre_commit_update_cron="0 16 * * 0"
2426
}
@@ -27,9 +29,10 @@ function check_consistency() {
2729
local out_dir_path
2830
out_dir_path=$(mktemp --directory --dry-run)
2931
cd /tmp
30-
stamp "$script_dir" "$out_dir_path" "interface"
32+
stamp "$script_dir" "$out_dir_path" "interface" "gtest"
3133
cp "$script_dir"/../.github/workflows/cookiecutter_test.yml "$out_dir_path"/exemplar/.github/workflows
3234
cp "$script_dir"/../.github/workflows/static_exemplar_test.yml "$out_dir_path"/exemplar/.github/workflows
35+
cp "$script_dir"/../.github/workflows/catch2_exemplar_test.yml "$out_dir_path"/exemplar/.github/workflows
3336
local diff_path
3437
diff_path=$(mktemp)
3538
diff -r "$script_dir/.." "$out_dir_path/exemplar" \

cookiecutter/cookiecutter.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"description": "Short project description.",
66
"godbolt_link": "https://www.example.com",
77
"library_type": ["interface", "static"],
8+
"unit_test_library": ["gtest", "catch2"],
89
"_owner": "bemanproject",
910
"_ci_tests_cron": "",
1011
"_pre_commit_update_cron": "",
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
[beman_submodule]
22
remote=https://github.com/bemanproject/infra.git
3-
commit_hash=63cb577f6484f13ce3349de49ad5ce27e20bf1da
3+
commit_hash=54dcdad8b661a405a6ac06453f0f06da5d30ba5c

cookiecutter/{{cookiecutter.project_name}}/infra/cmake/beman-install-library.cmake

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ include(GNUInstallDirs)
5656
# HEADERS to the default CMAKE install destination.
5757
#
5858
# It also handles the installation of the CMake config package files if
59-
# needed. If the given targets has FILE_SET CXX_MODULE, it will also
59+
# needed. If the given targets has a PUBLIC FILE_SET CXX_MODULE, it will also
6060
# installed to the given DESTINATION
6161
#
6262
# Cache variables:
@@ -72,7 +72,7 @@ include(GNUInstallDirs)
7272
# Caveats
7373
# -------
7474
#
75-
# **Only one `FILE_SET CXX_MODULES` is yet supported to install with this
75+
# **Only one `PUBLIC FILE_SET CXX_MODULES` is yet supported to install with this
7676
# function!**
7777
#
7878
# **Only header files contained in a `PUBLIC FILE_SET TYPE HEADERS` will be
@@ -189,12 +189,12 @@ function(beman_install_library name)
189189
set(_install_header_set_args FILE_SET HEADERS) # Note: empty FILE_SET in this case! CK
190190
endif()
191191

192-
# Detect presence of C++ module file sets, exact one expected!
193-
get_target_property(_module_sets "${_tgt}" CXX_MODULE_SETS)
192+
# Detect presence of PUBLIC C++ module file sets. Note: exact one is expected!
193+
get_target_property(_module_sets "${_tgt}" INTERFACE_CXX_MODULE_SETS)
194194
if(_module_sets)
195195
message(
196196
VERBOSE
197-
"beman-install-library(${name}): '${_tgt}' has CXX_MODULE_SETS=${_module_sets}"
197+
"beman-install-library(${name}): '${_tgt}' has INTERFACE_CXX_MODULE_SETS=${_module_sets}"
198198
)
199199
install(
200200
TARGETS "${_tgt}"

cookiecutter/{{cookiecutter.project_name}}/infra/cmake/use-fetch-content.cmake

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,18 @@ function(BemanExemplar_provideDependency method package_name)
170170
set(INSTALL_GTEST OFF) # Disable GoogleTest installation
171171
FetchContent_MakeAvailable("${BemanExemplar_name}")
172172

173+
# Catch2's CTest integration module isn't on CMAKE_MODULE_PATH
174+
# when brought in via FetchContent. Add it so that
175+
# `include(Catch)` works.
176+
if(BemanExemplar_pkgName STREQUAL "Catch2")
177+
list(
178+
APPEND
179+
CMAKE_MODULE_PATH
180+
"${${BemanExemplar_name}_SOURCE_DIR}/extras"
181+
)
182+
set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" PARENT_SCOPE)
183+
endif()
184+
173185
# Important! <PackageName>_FOUND tells CMake that `find_package` is
174186
# not needed for this package anymore
175187
set("${BemanExemplar_pkgName}_FOUND" TRUE PARENT_SCOPE)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
11
{
22
"dependencies": [
3+
{% if cookiecutter.unit_test_library == "gtest" %}
34
{
45
"name": "googletest",
56
"package_name": "GTest",
67
"git_repository": "https://github.com/google/googletest.git",
78
"git_tag": "6910c9d9165801d8827d628cb72eb7ea9dd538c5"
89
}
10+
{% elif cookiecutter.unit_test_library == "catch2" %}
11+
{
12+
"name": "Catch2",
13+
"package_name": "Catch2",
14+
"git_repository": "https://github.com/catchorg/Catch2.git",
15+
"git_tag": "25319fd3047c6bdcf3c0170e76fa526c77f99ca9"
16+
}
17+
{% endif %}
918
]
1019
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,26 @@
11
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
22

3+
{% if cookiecutter.unit_test_library == "gtest" %}
34
find_package(GTest REQUIRED)
5+
{% elif cookiecutter.unit_test_library == "catch2" %}
6+
find_package(Catch2 3 REQUIRED)
7+
{% endif %}
48

59
add_executable(beman.{{cookiecutter.project_name}}.tests.identity)
610
target_sources(beman.{{cookiecutter.project_name}}.tests.identity PRIVATE identity.test.cpp)
711
target_link_libraries(
812
beman.{{cookiecutter.project_name}}.tests.identity
13+
{% if cookiecutter.unit_test_library == "gtest" %}
914
PRIVATE beman::{{cookiecutter.project_name}} GTest::gtest GTest::gtest_main
15+
{% elif cookiecutter.unit_test_library == "catch2" %}
16+
PRIVATE beman::{{cookiecutter.project_name}} Catch2::Catch2WithMain
17+
{% endif %}
1018
)
1119

20+
{% if cookiecutter.unit_test_library == "gtest" %}
1221
include(GoogleTest)
1322
gtest_discover_tests(beman.{{cookiecutter.project_name}}.tests.identity)
23+
{% elif cookiecutter.unit_test_library == "catch2" %}
24+
include(Catch)
25+
catch_discover_tests(beman.{{cookiecutter.project_name}}.tests.identity)
26+
{% endif %}

cookiecutter/{{cookiecutter.project_name}}/tests/beman/{{cookiecutter.project_name}}/identity.test.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,54 +2,96 @@
22

33
#include <beman/{{cookiecutter.project_name}}/identity.hpp>
44

5+
{% if cookiecutter.unit_test_library == "gtest" %}
56
#include <gtest/gtest.h>
7+
{% elif cookiecutter.unit_test_library == "catch2" %}
8+
#include <catch2/catch_all.hpp>
9+
{% endif %}
610

711
#include <algorithm>
812
#include <functional>
913

1014
namespace exe = beman::{{cookiecutter.project_name}};
1115

16+
{% if cookiecutter.unit_test_library == "gtest" %}
1217
TEST(IdentityTest, call_identity_with_int) {
18+
{% elif cookiecutter.unit_test_library == "catch2" %}
19+
TEST_CASE("can call identity with int", "[{{cookiecutter.project_name}}::call_identity_with_int]") {
20+
{% endif %}
1321
for (int i = -100; i < 100; ++i) {
22+
{% if cookiecutter.unit_test_library == "gtest" %}
1423
EXPECT_EQ(i, exe::identity()(i));
24+
{% elif cookiecutter.unit_test_library == "catch2" %}
25+
CHECK(i == exe::identity()(i));
26+
{% endif %}
1527
}
1628
}
1729

30+
{% if cookiecutter.unit_test_library == "gtest" %}
1831
TEST(IdentityTest, call_identity_with_custom_type) {
32+
{% elif cookiecutter.unit_test_library == "catch2" %}
33+
TEST_CASE("can call identity with custom type", "[{{cookiecutter.project_name}}::call_identity_with_custom_type]") {
34+
{% endif %}
1935
struct S {
2036
int i;
2137
};
2238

2339
for (int i = -100; i < 100; ++i) {
2440
const S s{i};
2541
const S s_id = exe::identity()(s);
42+
{% if cookiecutter.unit_test_library == "gtest" %}
2643
EXPECT_EQ(s.i, s_id.i);
44+
{% elif cookiecutter.unit_test_library == "catch2" %}
45+
CHECK(s.i == s_id.i);
46+
{% endif %}
2747
}
2848
}
2949

50+
{% if cookiecutter.unit_test_library == "gtest" %}
3051
TEST(IdentityTest, compare_std_vs_beman) {
52+
{% elif cookiecutter.unit_test_library == "catch2" %}
53+
TEST_CASE("compare std vs beman", "[{{cookiecutter.project_name}}::compare_std_vs_beman]") {
54+
{% endif %}
3155
// Requires: std::identity support.
3256
#if defined(__cpp_lib_type_identity)
3357
std::identity std_id;
3458
exe::identity beman_id;
3559
for (int i = -100; i < 100; ++i) {
60+
{% if cookiecutter.unit_test_library == "gtest" %}
3661
EXPECT_EQ(std_id(i), beman_id(i));
62+
{% elif cookiecutter.unit_test_library == "catch2" %}
63+
CHECK(std_id(i) == beman_id(i));
64+
{% endif %}
3765
}
3866
#endif
3967
}
4068

69+
{% if cookiecutter.unit_test_library == "gtest" %}
4170
TEST(IdentityTest, check_is_transparent) {
71+
{% elif cookiecutter.unit_test_library == "catch2" %}
72+
TEST_CASE("check is transparent", "[{{cookiecutter.project_name}}::check_is_transparent]") {
73+
{% endif %}
4274
// Requires: transparent operators support.
4375
#if defined(__cpp_lib_transparent_operators)
4476

4577
exe::identity id;
4678

4779
const auto container = {1, 2, 3, 4, 5};
4880
auto it = std::find(std::begin(container), std::end(container), 3);
81+
{% if cookiecutter.unit_test_library == "gtest" %}
4982
EXPECT_EQ(3, *it);
83+
{% elif cookiecutter.unit_test_library == "catch2" %}
84+
CHECK(3 == *it);
85+
{% endif %}
5086
auto it_with_id = std::find(std::begin(container), std::end(container), id(3));
87+
{% if cookiecutter.unit_test_library == "gtest" %}
5188
EXPECT_EQ(3, *it_with_id);
5289

5390
EXPECT_EQ(it, it_with_id);
91+
{% elif cookiecutter.unit_test_library == "catch2" %}
92+
CHECK(3 == *it_with_id);
93+
94+
CHECK(it == it_with_id);
95+
{% endif %}
5496
#endif
5597
}

0 commit comments

Comments
 (0)