• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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