Arene Base
Fundamental Utilities For Safety Critical C++
Loading...
Searching...
No Matches
constexpr_test.hpp
Go to the documentation of this file.
1// Copyright 2026, Toyota Motor Corporation
2//
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4
5#ifndef INCLUDE_GUARD_ARENE_BASE_ARENE_BASE_TESTING_GTEST_WITH_GTEST_CONSTEXPR_TEST_HPP_
6#define INCLUDE_GUARD_ARENE_BASE_ARENE_BASE_TESTING_GTEST_WITH_GTEST_CONSTEXPR_TEST_HPP_
7
8// IWYU pragma: private, include "arene/base/testing/gtest.hpp"
9// IWYU pragma: friend "arene/base/testing/gtest/.*"
10
11#include <gtest/gtest.h>
12
13// parasoft-begin-suppress AUTOSAR-A16_2_2-a-2 "Arene Base aggregate headers permitted by A16-2-2 Permit #1"
14#include "arene/base/compiler_support/preprocessor.hpp"
15#include "arene/base/stdlib_choice/integral_constant.hpp"
16// parasoft-end-suppress AUTOSAR-A16_2_2-a-2
17
18namespace arene {
19namespace base {
20namespace testing {
21namespace internal {
22
23/// @brief dummy function declaration to allow unqualified lookup
24///
25template <class>
26void test_impl() = delete;
27
28/// @brief GoogleTest test type used invoke tests at compile-time
29/// @tparam TestTag tag associated with a specific test implementation function
30///
31template <class TestTag>
32class constexpr_test : public ::testing::Test {
33 public:
34 /// @brief test implementation function
35 ///
36 /// Invokes the associated test implementation function twice: once at
37 /// compile-time and again at runtime.
38 ///
39 auto TestBody() -> void override {
40 static_assert((test_impl(TestTag{}), true), "test body not invocable in a constant expression");
41 test_impl(TestTag{});
42 }
43};
44
45/// @brief GoogleTest test type used to conditionally invoke tests at compile-time
46/// @tparam TestTag tag associated with a specific test implementation function
47/// @tparam RunInConstexpr when @c true the test body is also evaluated at compile-time
48///
49template <class TestTag, bool RunInConstexpr>
51 public:
52 auto TestBody() -> void override {
53 /* The test_impl() function must be called at runtime before the compile-time dispatch,
54 * otherwise gcc8 will incorrectly think test_impl() is not invocable in a constant expression. */
55 test_impl<void>(TestTag{});
56 static_cast<void>(std::integral_constant<bool, (test_impl<void>(TestTag{}), true)>{});
57 }
58};
59
60/// @brief Specialization for the @c false case: the test body runs at runtime only.
61///
62template <class TestTag>
63class conditionally_constexpr_test<TestTag, false> : public ::testing::Test {
64 public:
65 auto TestBody() -> void override { test_impl<void>(TestTag{}); }
66};
67
68} // namespace internal
69} // namespace testing
70} // namespace base
71} // namespace arene
72
73// AUTOSAR exceptions:
74// A16-0-1 The pre-processor shall only be used for unconditional and
75// conditional file inclusion and include guards, and using the following
76// directives: (1) #ifndef, (2) #ifdef, (3) #if, (4) #if defined, (5) #elif, (6)
77// #else, (7) #define, (8) #endif, (9) #include.
78
79// parasoft-begin-suppress AUTOSAR-A16_0_1-d "Function-like macro permitted by A16-0-1 Permit #1"
80
81// parasoft-begin-suppress AUTOSAR-M16_3_1-a "Token pasting '##' is used to
82// provide a unique type for use with GoogleTest, a macro based library"
83
84// NOLINTBEGIN(cppcoreguidelines-macro-usage)
85/// @brief defines the name of a type associated with a testcase
86/// @param testsuite The GoogleTest testsuite name
87/// @param testcase The GoogleTest testcase name
88///
89#define TESTCASE_TYPE_NAME(testsuite, testcase) testsuite##_##testcase
90
91/// @brief defines the name of a tag associated with a testcase
92/// @param testsuite The GoogleTest testsuite name
93/// @param testcase The GoogleTest testcase name
94///
95#define TESTCASE_TAG_NAME(testsuite, testcase) testsuite##_##testcase##_tag
96
97// parasoft-end-suppress AUTOSAR-M16_3_1-a
98// parasoft-end-suppress AUTOSAR-A16_0_1-d
99
100// parasoft-begin-suppress AUTOSAR-A16_0_1-d "Function-like macro permitted by
101// A16-0-1 Permit #1"
102// This function macro expands '__FILE__' and '__LINE__'.
103// This function macro indirectly uses the '##' pre-processor operators to
104// manipulate tokens, which is necessary to generate unique identifiers for
105// GoogleTest.
106
107// parasoft-begin-suppress AUTOSAR-M16_0_6-a "'testsuite' and 'testcase' cannot
108// be parenthesized without breaking macro"
109
110/// @brief defines a test case that is invoked at compile-time
111/// @param testsuite The GoogleTest testsuite name
112/// @param testcase The GoogleTest testcase name
113///
114/// Performs custom test registration for testsuite @c testsuite and testcase
115/// @testcase that invokes a @c constexpr test implementation function. The
116/// implementation function is invoked twice: once at compile-time to ensure it
117/// is in-fact compile-time invocable, and again at runtime in order to generate
118/// test coverage.
119///
120#define CONSTEXPR_TEST(testsuite, testcase)
121 class TESTCASE_TYPE_NAME(testsuite, testcase);
122
123 class TESTCASE_TAG_NAME(testsuite, testcase) {
124 static ::testing::TestInfo const* const test_info_;
125 };
126
127 /* NOLINTNEXTLINE(fuchsia-statically-constructed-objects) */
128 ::testing::TestInfo const* const TESTCASE_TAG_NAME(testsuite, testcase)::test_info_ = ::testing::RegisterTest(
129 ARENE_STRINGIZE(testsuite),
130 ARENE_STRINGIZE(testcase),
131 nullptr,
132 nullptr,
133 __FILE__,
134 __LINE__,
135 [] {
136 return static_cast<::testing::Test*>(
137 new ::arene::base::testing::internal::constexpr_test<TESTCASE_TAG_NAME(testsuite, testcase)>{}
138 );
139 }
140 );
141
142 constexpr void test_impl(TESTCASE_TAG_NAME(testsuite, testcase))
143
144// parasoft-end-suppress AUTOSAR-M16_0_6-a
145// parasoft-end-suppress AUTOSAR-A16_0_1-d
146
147// parasoft-begin-suppress AUTOSAR-A16_0_1-d "Function-like macro permitted by
148// A16-0-1 Permit #1"
149// This function macro indirectly uses the '##' pre-processor operators to
150// manipulate tokens, which is necessary to generate unique identifiers for
151// GoogleTest.
152
153// parasoft-begin-suppress AUTOSAR-M16_0_6-a "'testsuite' and 'testcase' cannot
154// be parenthesized without breaking macro"
155
156/// @brief defines a test case that is conditionally invoked at compile-time
157/// @param testsuite The GoogleTest testsuite name
158/// @param testcase The GoogleTest testcase name
159/// @param Condition A condition convertible to @c bool
160///
161/// Defines a @c TEST that conditionally invokes a @c constexpr test implementation
162/// function. The implementation function is invoked at least once at runtime, and
163/// possibly a second time at compile-time, depending on the @c Condition parameter.
164///
165/// ~~~{.cpp}
166/// CONDITIONALLY_CONSTEXPR_TEST(
167/// TestSuite,
168/// TestCase,
169/// ARENE_HAS_BUILTIN(__some_builtin))
170/// {
171/// ...
172/// }
173/// ~~~
174///
175#define CONDITIONALLY_CONSTEXPR_TEST(testsuite, testcase, ...)
176 class TESTCASE_TYPE_NAME(testsuite, testcase);
177
178 class TESTCASE_TAG_NAME(testsuite, testcase) {
179 static ::testing::TestInfo const* const test_info_;
180 };
181
182 /* NOLINTNEXTLINE(fuchsia-statically-constructed-objects) */
183 ::testing::TestInfo const* const TESTCASE_TAG_NAME(testsuite, testcase)::test_info_ = ::testing::RegisterTest(
184 ARENE_STRINGIZE(testsuite),
185 ARENE_STRINGIZE(testcase),
186 nullptr,
187 nullptr,
188 __FILE__,
189 __LINE__,
190 [] {
191 return static_cast<::testing::Test*>(
192 new ::arene::base::testing::internal::
193 conditionally_constexpr_test<TESTCASE_TAG_NAME(testsuite, testcase), (__VA_ARGS__)>{}
194 );
195 }
196 );
197
198 /* test_impl is a template so it is only analyzed at instantiation, which happens only from the true-branch \
199 * specialization of conditionally_constexpr_test. This avoids -Winvalid-constexpr on the false branch when \
200 * the user body calls non-constexpr functions. */
201 template <class>
202 constexpr void test_impl(TESTCASE_TAG_NAME(testsuite, testcase))
203
204// parasoft-end-suppress AUTOSAR-M16_0_6-a
205// parasoft-end-suppress AUTOSAR-A16_0_1-d
206
207// parasoft-begin-suppress AUTOSAR-A16_0_1-d "Function-like macro permitted by
208// A16-0-1 Permit #1"
209// This function macro indirectly uses the '##' pre-processor operators to
210// manipulate tokens, which is necessary to generate unique identifiers for
211// GoogleTest.
212
213// parasoft-begin-suppress AUTOSAR-M16_0_6-a "'testsuite' and 'testcase' cannot
214// be parenthesized without breaking macro"
215
216/// @brief defines a typed test case that is invoked at compile-time
217/// @param testsuite The GoogleTest testsuite name
218/// @param testcase The GoogleTest testcase name
219///
220/// Defines a @c TYPED_TEST that invokes a @c constexpr test implementation
221/// function. The implementation function is invoked twice: once at compile-time
222/// to ensure it is in-fact compile-time invocable, and again at runtime in
223/// order to generate test coverage.
224///
225/// A test defined with this macro may not use @c *this or other non- @c
226/// constexpr members from the test fixture.
227///
228#define CONSTEXPR_TYPED_TEST(testsuite, testcase)
229 class TESTCASE_TYPE_NAME(testsuite, testcase);
230
231 class TESTCASE_TAG_NAME(testsuite, testcase) {};
232
233 TYPED_TEST(testsuite, testcase) {
234 using ::arene::base::testing::internal::test_impl;
235 using tag_type = TESTCASE_TAG_NAME(testsuite, testcase);
236 static_assert((test_impl<TypeParam>(tag_type{}), true), "test body not invocable in a constant expression");
237 test_impl<TypeParam>(tag_type{});
238 }
239
240 template <class TypeParam>
241 constexpr void test_impl(TESTCASE_TAG_NAME(testsuite, testcase))
242
243// parasoft-end-suppress AUTOSAR-M16_0_6-a
244// parasoft-end-suppress AUTOSAR-A16_0_1-d
245
246// parasoft-begin-suppress AUTOSAR-A16_0_1-d "Function-like macro permitted by
247// A16-0-1 Permit #1"
248// This function macro indirectly uses the '##' pre-processor operators to
249// manipulate tokens, which is necessary to generate unique identifiers for
250// GoogleTest.
251
252// parasoft-begin-suppress AUTOSAR-M16_0_6-a "'testsuite' and 'testcase' cannot
253// be parenthesized without breaking macro"
254
255/// @brief defines a typed test case that is conditionally invoked at compile-time
256/// @param testsuite The GoogleTest testsuite name
257/// @param testcase The GoogleTest testcase name
258/// @param Condition A condition convertible to @c bool which depends on @c TypeParam
259///
260/// Defines a @c TYPED_TEST that conditionally invokes a @c constexpr test implementation
261/// function. The implementation function is invoked at least once at runtime, and possibly a second time at
262/// compile-time, depending on the @c CheckIfShouldRunInConstexpr parameter.
263///
264/// The @c CheckIfShouldRunInConstexpr parameter can be a type_trait depending on the @c TypeParam of the @c TYPED_TEST.
265/// ~~~{.cpp}
266/// CONDITIONALLY_CONSTEXPR_TYPED_TEST(
267/// TestSuite,
268/// TestCase,
269/// my_metafunction<TypeParam>{})
270/// {
271/// ...
272/// }
273/// ~~~
274///
275/// A test defined with this macro may not use @c *this or other non- @c
276/// constexpr members from the test fixture.
277///
278#define CONDITIONALLY_CONSTEXPR_TYPED_TEST(testsuite, testcase, ...)
279 class TESTCASE_TYPE_NAME(testsuite, testcase);
280
281 class TESTCASE_TAG_NAME(testsuite, testcase) {};
282
283 template <class TypeParam>
284 constexpr void
285 maybe_do_test_in_constexpr(TESTCASE_TAG_NAME(testsuite, testcase) tag, std::integral_constant<bool, true>) {
286 using ::arene::base::testing::internal::test_impl;
287 /* do the test, we don't use @c static_assert due to some gcc8 issues */
288 static_cast<void>(std::integral_constant<bool, (test_impl<TypeParam>(tag), true)>{});
289 }
290
291 template <class TypeParam>
292 constexpr void
293 maybe_do_test_in_constexpr(TESTCASE_TAG_NAME(testsuite, testcase), std::integral_constant<bool, false>) {}
294
295 TYPED_TEST(testsuite, testcase) {
296 using ::arene::base::testing::internal::test_impl;
297 using tag_type = TESTCASE_TAG_NAME(testsuite, testcase);
298 /* The test_impl() function must be called at runtime before dispatching to maybe_do_test_in_constexpr(), \
299 * otherwise gcc8 will incorrectly think test_impl() is not invocable in a constant expression. */
300 test_impl<TypeParam>(tag_type{});
301 maybe_do_test_in_constexpr<TypeParam>(tag_type{}, std::integral_constant<bool, (__VA_ARGS__)>{});
302 }
303
304 template <class TypeParam>
305 constexpr void test_impl(TESTCASE_TAG_NAME(testsuite, testcase))
306
307/// @brief defines a typed test case that is invoked at compile time which is conditionally run based on the third
308/// parameter
309/// @param testsuite The GoogleTest testsuite name
310/// @param testcase The GoogleTest testcase name
311/// @param Condition A condition convertible to @c bool which depends on @c TypeParam
312///
313/// Defines a @c TYPED_TEST that invokes a @c constexpr test implementation
314/// function. The implementation function is invoked twice: once at compile-time
315/// to ensure it is in-fact compile-time invocable, and again at runtime in
316/// order to generate test coverage. If the @c Condition is false, then the implementation function is never
317/// instantiated.
318///
319/// The @c Condition parameter can be a type_trait depending on the @c TypeParam of the @c TYPED_TEST.
320/// ~~~{.cpp}
321/// CONDITIONAL_CONSTEXPR_TYPED_TEST(
322/// TestSuite,
323/// TestCase,
324/// my_metafunction<TypeParam>{})
325/// {
326/// ...
327/// }
328/// ~~~
329///
330/// A test defined with this macro may not use @c *this or other non- @c
331/// constexpr members from the test fixture.
332///
333#define CONDITIONAL_CONSTEXPR_TYPED_TEST(testsuite, testcase, ...)
334 class TESTCASE_TYPE_NAME(testsuite, testcase);
335
336 class TESTCASE_TAG_NAME(testsuite, testcase) {};
337
338 template <class TypeParam>
339 constexpr void maybe_do_test(TESTCASE_TAG_NAME(testsuite, testcase) tag, std::integral_constant<bool, true>) {
340 using ::arene::base::testing::internal::test_impl;
341
342 /* do the test, we don't use @c static_assert due to some gcc8 issues */
343 static_cast<void>(std::integral_constant<bool, (test_impl<TypeParam>(tag), true)>{});
344 test_impl<TypeParam>(tag);
345 }
346
347 template <class TypeParam>
348 constexpr void maybe_do_test(TESTCASE_TAG_NAME(testsuite, testcase), std::integral_constant<bool, false>) {
349 GTEST_SKIP() << "Disabled by condition: " << ARENE_STRINGIZE(__VA_ARGS__);
350 }
351
352 TYPED_TEST(testsuite, testcase) {
353 using tag_type = TESTCASE_TAG_NAME(testsuite, testcase);
354 maybe_do_test<TypeParam>(tag_type{}, std::integral_constant<bool, (__VA_ARGS__)>{});
355 }
356
357 template <class TypeParam>
358 constexpr void test_impl(TESTCASE_TAG_NAME(testsuite, testcase))
359
360// parasoft-end-suppress AUTOSAR-M16_0_6-a
361// parasoft-end-suppress AUTOSAR-A16_0_1-d
362
363// parasoft-begin-suppress AUTOSAR-A16_0_1-d "Defines function-like macros matching the style of GoogleTest assertion
364// macros"
365
366/// @brief asserts that the argument is @c true
367///
368/// Replacement assertion macro for use in @c CONSTEXPR_TEST and @c
369/// CONSTEXPR_TYPED_TEST. No GoogleTest assertion macro can be used at
370/// compile-time due to all implementations invoking functionality that cannot
371/// be used at compile-time (e.g. malloc).
372///
373#define CONSTEXPR_ASSERT(...)
374 if (not(__VA_ARGS__)) {
375 FAIL();
376 }
377
378/// @brief asserts that the argument is @c false
379///
380/// Replacement assertion macro for use in @c CONSTEXPR_TEST and @c
381/// CONSTEXPR_TYPED_TEST. No GoogleTest assertion macro can be used at
382/// compile-time due to all implementations invoking functionality that cannot
383/// be used at compile-time (e.g. malloc).
384///
385#define CONSTEXPR_ASSERT_FALSE(...) CONSTEXPR_ASSERT(!(__VA_ARGS__))
386
387/// @brief asserts that the arguments are equal
388///
389/// Replacement assertion macro for use in @c CONSTEXPR_TEST and @c
390/// CONSTEXPR_TYPED_TEST. No GoogleTest assertion macro can be used at
391/// compile-time due to all implementations invoking functionality that cannot
392/// be used at compile-time (e.g. malloc).
393///
394#define CONSTEXPR_ASSERT_EQ(lhs, rhs) CONSTEXPR_ASSERT(((lhs) == (rhs)))
395
396// parasoft-end-suppress AUTOSAR-A16_0_1-d
397
398// NOLINTEND(cppcoreguidelines-macro-usage)
399
400#endif // INCLUDE_GUARD_ARENE_BASE_ARENE_BASE_TESTING_GTEST_WITH_GTEST_CONSTEXPR_TEST_HPP_
auto TestBody() -> void override
Definition constexpr_test.hpp:65
GoogleTest test type used to conditionally invoke tests at compile-time.
Definition constexpr_test.hpp:50
auto TestBody() -> void override
Definition constexpr_test.hpp:52
GoogleTest test type used invoke tests at compile-time.
Definition constexpr_test.hpp:32
auto TestBody() -> void override
test implementation function
Definition constexpr_test.hpp:39
Definition constexpr_test.hpp:21
void test_impl()=delete
dummy function declaration to allow unqualified lookup
Definition customization.hpp:36
Definition array_exceptions_disabled.cpp:11
Copyright 2026, Toyota Motor Corporation.
Definition array_exceptions_disabled.cpp:10
#define TESTCASE_TAG_NAME(testsuite, testcase)
defines the name of a tag associated with a testcase
Definition constexpr_test.hpp:95
#define TESTCASE_TYPE_NAME(testsuite, testcase)
defines the name of a type associated with a testcase
Definition constexpr_test.hpp:89
#define CONSTEXPR_ASSERT(...)
asserts that the argument is true
Definition constexpr_test.hpp:370