1 // Copyright 2024 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 #pragma once
15
16 // block-submission: disable
17 /// @file pw_unit_test/constexpr.h
18 ///
19 /// The @c_macro{PW_CONSTEXPR_TEST} macro defines a test that is executed both
20 /// at compile time in a `static_assert` and as a regular GoogleTest-style
21 /// `TEST()`. This offers the advantages of compile-time testing in a
22 /// structured, familiar API, without sacrificing anything from GoogleTest-style
23 /// tests. The framework uses the standard GoogleTest macros at run time, and is
24 /// compatible with GoogleTest or Pigweed's `pw_unit_test:light` framework.
25 ///
26 /// To create a `constexpr` test:
27 /// - Include `"pw_unit_test/constexpr.h"` alongside the test framework
28 /// (`"pw_unit_test/framework.h"` or `"gtest/gtest.h"`).
29 /// - Use the macro @c_macro{PW_CONSTEXPR_TEST} instead of `TEST`. Note that the
30 /// function body passed as the third argument to the macro.
31 /// - Use the familiar GoogleTest macros, but with a `PW_TEST_` prefix. For
32 /// example:
33 /// - `EXPECT_TRUE` → @c_macro{PW_TEST_EXPECT_TRUE}
34 /// - `EXPECT_EQ` → @c_macro{PW_TEST_EXPECT_EQ}
35 /// - `ASSERT_STREQ` → @c_macro{PW_TEST_ASSERT_STREQ}
36 /// - etc.
37 ///
38 /// The result is a familiar-looking unit test that executes both at compile
39 /// time and run time.
40 ///
41 /// @rst
42 /// .. literalinclude:: constexpr_test.cc
43 /// :language: cpp
44 /// :start-after: [pw_unit_test-constexpr]
45 /// :end-before: [pw_unit_test-constexpr]
46 ///
47 /// @endrst
48 ///
49 /// **Why should I run tests at compile time?**
50 ///
51 /// - Cross compile and execute tests without having to flash them to a device.
52 /// - Ensure `constexpr` functions can actually be evaluated at compile time.
53 /// For example, function templates may be marked as `constexpr`, even if they
54 /// do not support constant evaluation when instantiated.
55 /// - Catch undefined behavior, out-of-bounds access, and other issues during
56 /// compilation on any platform, without needing to run sanitizers.
57 ///
58 /// **If compile-time testing is so great, why execute the tests are run time at
59 /// all?**
60 ///
61 /// - Code may run differently at compile time and execution, particularly when
62 /// `std::is_constant_evaluated` or `if consteval` are used.
63 /// - Error messages are much better at runtime. @c_macro{PW_CONSTEXPR_TEST}
64 /// makes it simple to temporarily disable compile-time tests and see the rich
65 /// GoogleTest-like output (see @c_macro{SKIP_CONSTEXPR_TESTS_DONT_SUBMIT}).
66 /// - Tools like code coverage only work for code that is executed normally.
67 ///
68 /// @c_macro{PW_CONSTEXPR_TEST} uses `stdcompat`'s
69 /// `cpp20::is_constant_evaluated()`. If the compiler does not support
70 /// `is_constant_evaluated`, only the regular GoogleTest version will run. Note
71 /// that compiler support is independent of the C++ standard in use.
72 // block-submission: enable
73
74 #include <limits>
75
76 #include "lib/stdcompat/type_traits.h"
77 #include "pw_preprocessor/boolean.h"
78 #include "pw_preprocessor/concat.h"
79
80 /// Defines a test that is executed both at compile time in a `static_assert`
81 /// and as a regular GoogleTest-style `TEST()`.
82 ///
83 /// `PW_CONSTEXPR_TEST` works similarly to the GoogleTest `TEST()` macro, but
84 /// has some differences.
85 ///
86 /// - All tested code must be `constexpr`.
87 /// - Requires the `PW_TEST_*` prefixed versions of GoogleTest's
88 /// `EXPECT_*` and `ASSERT_*` macros.
89 /// - The function body is a macro argument. This has two implications:
90 /// - The function body cannot contain preprocessor directives, such as
91 /// `#define`. If these are needed, move them to a separate function that is
92 /// called from the test.
93 /// - The `PW_CONSTEXPR_TEST` macro must be terminated with `);` after the
94 /// test body argument.
95 ///
96 /// @param test_suite GoogleTest test suite name
97 /// @param test_name GoogleTest test name
98 /// @param ... test function body surrounded by `{` `}`.
99 #define PW_CONSTEXPR_TEST(test_suite, test_name, ...) \
100 _PW_IF_CONSTEXPR_TEST(constexpr) \
101 void PwConstexprTest_##test_suite##_##test_name() __VA_ARGS__ \
102 \
103 TEST(test_suite, test_name) { \
104 PwConstexprTest_##test_suite##_##test_name(); \
105 } \
106 \
107 static_assert([] { \
108 _PW_IF_CONSTEXPR_TEST(PwConstexprTest_##test_suite##_##test_name();) \
109 return true; \
110 }())
111
112 // GoogleTest-style test macros for PW_CONSTEXPR_TEST.
113
114 #define PW_TEST_EXPECT_TRUE(expr) _PW_CEXPECT(TRUE, expr)
115 #define PW_TEST_EXPECT_FALSE(expr) _PW_CEXPECT(FALSE, expr)
116
117 #define PW_TEST_EXPECT_EQ(lhs, rhs) _PW_CEXPECT(EQ, lhs, rhs)
118 #define PW_TEST_EXPECT_NE(lhs, rhs) _PW_CEXPECT(NE, lhs, rhs)
119 #define PW_TEST_EXPECT_GT(lhs, rhs) _PW_CEXPECT(GT, lhs, rhs)
120 #define PW_TEST_EXPECT_GE(lhs, rhs) _PW_CEXPECT(GE, lhs, rhs)
121 #define PW_TEST_EXPECT_LT(lhs, rhs) _PW_CEXPECT(LT, lhs, rhs)
122 #define PW_TEST_EXPECT_LE(lhs, rhs) _PW_CEXPECT(LE, lhs, rhs)
123
124 #define PW_TEST_EXPECT_NEAR(lhs, rhs, error) _PW_CEXPECT(NEAR, lhs, rhs, error)
125 #define PW_TEST_EXPECT_FLOAT_EQ(lhs, rhs) _PW_CEXPECT(FLOAT_EQ, lhs, rhs)
126 #define PW_TEST_EXPECT_DOUBLE_EQ(lhs, rhs) _PW_CEXPECT(DOUBLE_EQ, lhs, rhs)
127
128 #define PW_TEST_EXPECT_STREQ(lhs, rhs) _PW_CEXPECT(STREQ, lhs, rhs)
129 #define PW_TEST_EXPECT_STRNE(lhs, rhs) _PW_CEXPECT(STRNE, lhs, rhs)
130
131 #define PW_TEST_ASSERT_TRUE(expr) _PW_CASSERT(TRUE, expr)
132 #define PW_TEST_ASSERT_FALSE(expr) _PW_CASSERT(FALSE, expr)
133
134 #define PW_TEST_ASSERT_EQ(lhs, rhs) _PW_CASSERT(EQ, lhs, rhs)
135 #define PW_TEST_ASSERT_NE(lhs, rhs) _PW_CASSERT(NE, lhs, rhs)
136 #define PW_TEST_ASSERT_GT(lhs, rhs) _PW_CASSERT(GT, lhs, rhs)
137 #define PW_TEST_ASSERT_GE(lhs, rhs) _PW_CASSERT(GE, lhs, rhs)
138 #define PW_TEST_ASSERT_LT(lhs, rhs) _PW_CASSERT(LT, lhs, rhs)
139 #define PW_TEST_ASSERT_LE(lhs, rhs) _PW_CASSERT(LE, lhs, rhs)
140
141 #define PW_TEST_ASSERT_NEAR(lhs, rhs, error) _PW_CASSERT(NEAR, lhs, rhs, error)
142 #define PW_TEST_ASSERT_FLOAT_EQ(lhs, rhs) _PW_CASSERT(FLOAT_EQ, lhs, rhs)
143 #define PW_TEST_ASSERT_DOUBLE_EQ(lhs, rhs) _PW_CASSERT(DOUBLE_EQ, lhs, rhs)
144
145 #define PW_TEST_ASSERT_STREQ(lhs, rhs) _PW_CASSERT(STREQ, lhs, rhs)
146 #define PW_TEST_ASSERT_STRNE(lhs, rhs) _PW_CASSERT(STRNE, lhs, rhs)
147
148 // Implementation details
149
150 #define _PW_CEXPECT(macro, ...) \
151 if (cpp20::is_constant_evaluated()) { \
152 ::pw::unit_test::internal::Constexpr_EXPECT_##macro(__VA_ARGS__); \
153 } else \
154 EXPECT_##macro(__VA_ARGS__)
155
156 #define _PW_CASSERT(macro, ...) \
157 if (cpp20::is_constant_evaluated()) { \
158 if (!::pw::unit_test::internal::Constexpr_EXPECT_##macro(__VA_ARGS__)) { \
159 return; \
160 } \
161 } else \
162 ASSERT_##macro(__VA_ARGS__)
163
164 // Expands to the provided expression if constexpr tests are enabled.
165 // block-submission: disable
166 #define _PW_IF_CONSTEXPR_TEST(a) \
167 PW_CONCAT( \
168 _PW_IF_CONSTEXPR_TEST_, \
169 PW_AND(LIB_STDCOMPAT_CONSTEVAL_SUPPORT, \
170 _PW_CONSTEXPR_TEST_ENABLED(SKIP_CONSTEXPR_TESTS_DONT_SUBMIT)))(a)
171
172 #define _PW_IF_CONSTEXPR_TEST_0(a)
173 #define _PW_IF_CONSTEXPR_TEST_1(a) a
174
175 #define _PW_CONSTEXPR_TEST_ENABLED(a) _PW_CONSTEXPR_TEST_ENABLED2(a)
176 #define _PW_CONSTEXPR_TEST_ENABLED2(a) _PW_CONSTEXPR_TEST_##a
177
178 // Check if the SKIP_CONSTEXPR_TESTS_DONT_SUBMIT macro is defined, which can
179 // be used to temporarily disable the constexpr part of tests.
180 #define _PW_CONSTEXPR_TEST_SKIP_CONSTEXPR_TESTS_DONT_SUBMIT 1
181 #define _PW_CONSTEXPR_TEST_ 0
182 #define _PW_CONSTEXPR_TEST_1 0
183 #define _PW_CONSTEXPR_TEST_0 \
184 _Do_not_define_SKIP_CONSTEXPR_TESTS_DONT_SUBMIT_to_0_remove_it_instead
185 // block-submission: enable
186
187 namespace pw::unit_test::internal {
188
189 // These undefined, non-constexpr functions are called when a constexpr test
190 // assertion fails. They appear in the error message to inform the user.
191 bool EXPECT_TRUE_FAILED();
192 bool EXPECT_FALSE_FAILED();
193 bool EXPECT_EQ_FAILED();
194 bool EXPECT_NE_FAILED();
195 bool EXPECT_GT_FAILED();
196 bool EXPECT_GE_FAILED();
197 bool EXPECT_LT_FAILED();
198 bool EXPECT_LE_FAILED();
199 bool EXPECT_NEAR_FAILED();
200 bool EXPECT_STREQ_FAILED();
201 bool EXPECT_STRNE_FAILED();
202
203 template <typename T>
Constexpr_EXPECT_TRUE(const T & expr)204 constexpr bool Constexpr_EXPECT_TRUE(const T& expr) {
205 return (expr) ? true : EXPECT_TRUE_FAILED();
206 }
207
208 template <typename T>
Constexpr_EXPECT_FALSE(const T & expr)209 constexpr bool Constexpr_EXPECT_FALSE(const T& expr) {
210 return !(expr) ? true : EXPECT_FALSE_FAILED();
211 }
212
213 template <typename Lhs, typename Rhs>
Constexpr_EXPECT_EQ(const Lhs & lhs,const Rhs & rhs)214 constexpr bool Constexpr_EXPECT_EQ(const Lhs& lhs, const Rhs& rhs) {
215 return (lhs == rhs) ? true : EXPECT_EQ_FAILED();
216 }
217
218 template <typename Lhs, typename Rhs>
Constexpr_EXPECT_NE(const Lhs & lhs,const Rhs & rhs)219 constexpr bool Constexpr_EXPECT_NE(const Lhs& lhs, const Rhs& rhs) {
220 return (lhs != rhs) ? true : EXPECT_NE_FAILED();
221 }
222
223 template <typename Lhs, typename Rhs>
Constexpr_EXPECT_GT(const Lhs & lhs,const Rhs & rhs)224 constexpr bool Constexpr_EXPECT_GT(const Lhs& lhs, const Rhs& rhs) {
225 return (lhs > rhs) ? true : EXPECT_GT_FAILED();
226 }
227
228 template <typename Lhs, typename Rhs>
Constexpr_EXPECT_GE(const Lhs & lhs,const Rhs & rhs)229 constexpr bool Constexpr_EXPECT_GE(const Lhs& lhs, const Rhs& rhs) {
230 return (lhs >= rhs) ? true : EXPECT_GE_FAILED();
231 }
232
233 template <typename Lhs, typename Rhs>
Constexpr_EXPECT_LT(const Lhs & lhs,const Rhs & rhs)234 constexpr bool Constexpr_EXPECT_LT(const Lhs& lhs, const Rhs& rhs) {
235 return (lhs < rhs) ? true : EXPECT_LT_FAILED();
236 }
237
238 template <typename Lhs, typename Rhs>
Constexpr_EXPECT_LE(const Lhs & lhs,const Rhs & rhs)239 constexpr bool Constexpr_EXPECT_LE(const Lhs& lhs, const Rhs& rhs) {
240 return (lhs <= rhs) ? true : EXPECT_LE_FAILED();
241 }
242
243 template <typename T>
Constexpr_EXPECT_NEAR(T lhs,T rhs,T error)244 constexpr bool Constexpr_EXPECT_NEAR(T lhs, T rhs, T error) {
245 T diff = lhs - rhs;
246 if (diff < 0) {
247 diff *= -1;
248 }
249 return (diff <= error) ? true : EXPECT_NEAR_FAILED();
250 }
251
Constexpr_EXPECT_FLOAT_EQ(float lhs,float rhs)252 constexpr bool Constexpr_EXPECT_FLOAT_EQ(float lhs, float rhs) {
253 // Compare within 4 ULPs, for consistency with GoogleTest's EXPECT_FLOAT_EQ.
254 return Constexpr_EXPECT_NEAR(
255 lhs, rhs, 4 * std::numeric_limits<float>::epsilon());
256 }
257
Constexpr_EXPECT_DOUBLE_EQ(double lhs,double rhs)258 constexpr bool Constexpr_EXPECT_DOUBLE_EQ(double lhs, double rhs) {
259 // Compare within 4 ULPs, for consistency with GoogleTest's EXPECT_DOUBLE_EQ.
260 return Constexpr_EXPECT_NEAR(
261 lhs, rhs, 4 * std::numeric_limits<double>::epsilon());
262 }
263
StringsAreEqual(const char * lhs,const char * rhs)264 [[nodiscard]] constexpr bool StringsAreEqual(const char* lhs, const char* rhs) {
265 if (lhs == nullptr || rhs == nullptr) {
266 return lhs == rhs;
267 }
268
269 while (*lhs == *rhs && *lhs != '\0') {
270 lhs += 1;
271 rhs += 1;
272 }
273
274 return *lhs == '\0' && *rhs == '\0';
275 }
276
Constexpr_EXPECT_STREQ(const char * lhs,const char * rhs)277 constexpr bool Constexpr_EXPECT_STREQ(const char* lhs, const char* rhs) {
278 return StringsAreEqual(lhs, rhs) ? true : EXPECT_STREQ_FAILED();
279 }
280
Constexpr_EXPECT_STRNE(const char * lhs,const char * rhs)281 constexpr bool Constexpr_EXPECT_STRNE(const char* lhs, const char* rhs) {
282 return !StringsAreEqual(lhs, rhs) ? true : EXPECT_STRNE_FAILED();
283 }
284
285 } // namespace pw::unit_test::internal
286