C/C++ API Reference
Loading...
Searching...
No Matches
constexpr.h
Go to the documentation of this file.
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
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
81
101#define PW_CONSTEXPR_TEST(test_suite, test_name, ...) \
102 _PW_IF_CONSTEXPR_TEST(constexpr) \
103 void PwConstexprTest_##test_suite##_##test_name() __VA_ARGS__ \
104 \
105 TEST(test_suite, test_name) { \
106 PwConstexprTest_##test_suite##_##test_name(); \
107 } \
108 \
109 static_assert([] { \
110 _PW_IF_CONSTEXPR_TEST(PwConstexprTest_##test_suite##_##test_name();) \
111 return true; \
112 }())
113
115
116// GoogleTest-style test macros for PW_CONSTEXPR_TEST.
117
118#define PW_TEST_EXPECT_TRUE(expr) _PW_CEXPECT(TRUE, expr)
119#define PW_TEST_EXPECT_FALSE(expr) _PW_CEXPECT(FALSE, expr)
120
121#define PW_TEST_EXPECT_EQ(lhs, rhs) _PW_CEXPECT(EQ, lhs, rhs)
122#define PW_TEST_EXPECT_NE(lhs, rhs) _PW_CEXPECT(NE, lhs, rhs)
123#define PW_TEST_EXPECT_GT(lhs, rhs) _PW_CEXPECT(GT, lhs, rhs)
124#define PW_TEST_EXPECT_GE(lhs, rhs) _PW_CEXPECT(GE, lhs, rhs)
125#define PW_TEST_EXPECT_LT(lhs, rhs) _PW_CEXPECT(LT, lhs, rhs)
126#define PW_TEST_EXPECT_LE(lhs, rhs) _PW_CEXPECT(LE, lhs, rhs)
127
128#define PW_TEST_EXPECT_NEAR(lhs, rhs, error) _PW_CEXPECT(NEAR, lhs, rhs, error)
129#define PW_TEST_EXPECT_FLOAT_EQ(lhs, rhs) _PW_CEXPECT(FLOAT_EQ, lhs, rhs)
130#define PW_TEST_EXPECT_DOUBLE_EQ(lhs, rhs) _PW_CEXPECT(DOUBLE_EQ, lhs, rhs)
131
132#define PW_TEST_EXPECT_STREQ(lhs, rhs) _PW_CEXPECT(STREQ, lhs, rhs)
133#define PW_TEST_EXPECT_STRNE(lhs, rhs) _PW_CEXPECT(STRNE, lhs, rhs)
134
135#define PW_TEST_ASSERT_TRUE(expr) _PW_CASSERT(TRUE, expr)
136#define PW_TEST_ASSERT_FALSE(expr) _PW_CASSERT(FALSE, expr)
137
138#define PW_TEST_ASSERT_EQ(lhs, rhs) _PW_CASSERT(EQ, lhs, rhs)
139#define PW_TEST_ASSERT_NE(lhs, rhs) _PW_CASSERT(NE, lhs, rhs)
140#define PW_TEST_ASSERT_GT(lhs, rhs) _PW_CASSERT(GT, lhs, rhs)
141#define PW_TEST_ASSERT_GE(lhs, rhs) _PW_CASSERT(GE, lhs, rhs)
142#define PW_TEST_ASSERT_LT(lhs, rhs) _PW_CASSERT(LT, lhs, rhs)
143#define PW_TEST_ASSERT_LE(lhs, rhs) _PW_CASSERT(LE, lhs, rhs)
144
145#define PW_TEST_ASSERT_NEAR(lhs, rhs, error) _PW_CASSERT(NEAR, lhs, rhs, error)
146#define PW_TEST_ASSERT_FLOAT_EQ(lhs, rhs) _PW_CASSERT(FLOAT_EQ, lhs, rhs)
147#define PW_TEST_ASSERT_DOUBLE_EQ(lhs, rhs) _PW_CASSERT(DOUBLE_EQ, lhs, rhs)
148
149#define PW_TEST_ASSERT_STREQ(lhs, rhs) _PW_CASSERT(STREQ, lhs, rhs)
150#define PW_TEST_ASSERT_STRNE(lhs, rhs) _PW_CASSERT(STRNE, lhs, rhs)
151
152// Implementation details
153
154#define _PW_CEXPECT(macro, ...) \
155 if (cpp20::is_constant_evaluated()) { \
156 ::pw::unit_test::internal::Constexpr_EXPECT_##macro(__VA_ARGS__); \
157 } else \
158 EXPECT_##macro(__VA_ARGS__)
159
160#define _PW_CASSERT(macro, ...) \
161 if (cpp20::is_constant_evaluated()) { \
162 if (!::pw::unit_test::internal::Constexpr_EXPECT_##macro(__VA_ARGS__)) { \
163 return; \
164 } \
165 } else \
166 ASSERT_##macro(__VA_ARGS__)
167
168// Expands to the provided expression if constexpr tests are enabled.
169// block-submission: disable
170#define _PW_IF_CONSTEXPR_TEST(a) \
171 PW_CONCAT( \
172 _PW_IF_CONSTEXPR_TEST_, \
173 PW_AND(LIB_STDCOMPAT_CONSTEVAL_SUPPORT, \
174 _PW_CONSTEXPR_TEST_ENABLED(SKIP_CONSTEXPR_TESTS_DONT_SUBMIT)))(a)
175
176#define _PW_IF_CONSTEXPR_TEST_0(a)
177#define _PW_IF_CONSTEXPR_TEST_1(a) a
178
179#define _PW_CONSTEXPR_TEST_ENABLED(a) _PW_CONSTEXPR_TEST_ENABLED2(a)
180#define _PW_CONSTEXPR_TEST_ENABLED2(a) _PW_CONSTEXPR_TEST_##a
181
182// Check if the SKIP_CONSTEXPR_TESTS_DONT_SUBMIT macro is defined, which can
183// be used to temporarily disable the constexpr part of tests.
184#define _PW_CONSTEXPR_TEST_SKIP_CONSTEXPR_TESTS_DONT_SUBMIT 1
185#define _PW_CONSTEXPR_TEST_ 0
186#define _PW_CONSTEXPR_TEST_1 0
187#define _PW_CONSTEXPR_TEST_0 \
188 _Do_not_define_SKIP_CONSTEXPR_TESTS_DONT_SUBMIT_to_0_remove_it_instead
189// block-submission: enable
190
191namespace pw::unit_test::internal {
192
193// These undefined, non-constexpr functions are called when a constexpr test
194// assertion fails. They appear in the error message to inform the user.
195bool EXPECT_TRUE_FAILED();
196bool EXPECT_FALSE_FAILED();
197bool EXPECT_EQ_FAILED();
198bool EXPECT_NE_FAILED();
199bool EXPECT_GT_FAILED();
200bool EXPECT_GE_FAILED();
201bool EXPECT_LT_FAILED();
202bool EXPECT_LE_FAILED();
203bool EXPECT_NEAR_FAILED();
204bool EXPECT_STREQ_FAILED();
205bool EXPECT_STRNE_FAILED();
206
207template <typename T>
208constexpr bool Constexpr_EXPECT_TRUE(const T& expr) {
209 return (expr) ? true : EXPECT_TRUE_FAILED();
210}
211
212template <typename T>
213constexpr bool Constexpr_EXPECT_FALSE(const T& expr) {
214 return !(expr) ? true : EXPECT_FALSE_FAILED();
215}
216
217template <typename Lhs, typename Rhs>
218constexpr bool Constexpr_EXPECT_EQ(const Lhs& lhs, const Rhs& rhs) {
219 return (lhs == rhs) ? true : EXPECT_EQ_FAILED();
220}
221
222template <typename Lhs, typename Rhs>
223constexpr bool Constexpr_EXPECT_NE(const Lhs& lhs, const Rhs& rhs) {
224 return (lhs != rhs) ? true : EXPECT_NE_FAILED();
225}
226
227template <typename Lhs, typename Rhs>
228constexpr bool Constexpr_EXPECT_GT(const Lhs& lhs, const Rhs& rhs) {
229 return (lhs > rhs) ? true : EXPECT_GT_FAILED();
230}
231
232template <typename Lhs, typename Rhs>
233constexpr bool Constexpr_EXPECT_GE(const Lhs& lhs, const Rhs& rhs) {
234 return (lhs >= rhs) ? true : EXPECT_GE_FAILED();
235}
236
237template <typename Lhs, typename Rhs>
238constexpr bool Constexpr_EXPECT_LT(const Lhs& lhs, const Rhs& rhs) {
239 return (lhs < rhs) ? true : EXPECT_LT_FAILED();
240}
241
242template <typename Lhs, typename Rhs>
243constexpr bool Constexpr_EXPECT_LE(const Lhs& lhs, const Rhs& rhs) {
244 return (lhs <= rhs) ? true : EXPECT_LE_FAILED();
245}
246
247template <typename T>
248constexpr bool Constexpr_EXPECT_NEAR(T lhs, T rhs, T error) {
249 T diff = lhs - rhs;
250 if (diff < 0) {
251 diff *= -1;
252 }
253 return (diff <= error) ? true : EXPECT_NEAR_FAILED();
254}
255
256constexpr bool Constexpr_EXPECT_FLOAT_EQ(float lhs, float rhs) {
257 // Compare within 4 ULPs, for consistency with GoogleTest's EXPECT_FLOAT_EQ.
258 return Constexpr_EXPECT_NEAR(
259 lhs, rhs, 4 * std::numeric_limits<float>::epsilon());
260}
261
262constexpr bool Constexpr_EXPECT_DOUBLE_EQ(double lhs, double rhs) {
263 // Compare within 4 ULPs, for consistency with GoogleTest's EXPECT_DOUBLE_EQ.
264 return Constexpr_EXPECT_NEAR(
265 lhs, rhs, 4 * std::numeric_limits<double>::epsilon());
266}
267
268[[nodiscard]] constexpr bool StringsAreEqual(const char* lhs, const char* rhs) {
269 if (lhs == nullptr || rhs == nullptr) {
270 return lhs == rhs;
271 }
272
273 while (*lhs == *rhs && *lhs != '\0') {
274 lhs += 1;
275 rhs += 1;
276 }
277
278 return *lhs == '\0' && *rhs == '\0';
279}
280
281constexpr bool Constexpr_EXPECT_STREQ(const char* lhs, const char* rhs) {
282 return StringsAreEqual(lhs, rhs) ? true : EXPECT_STREQ_FAILED();
283}
284
285constexpr bool Constexpr_EXPECT_STRNE(const char* lhs, const char* rhs) {
286 return !StringsAreEqual(lhs, rhs) ? true : EXPECT_STRNE_FAILED();
287}
288
289} // namespace pw::unit_test::internal