C/C++ API Reference
Loading...
Searching...
No Matches
constexpr.h
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#include <limits>
17
18#include "lib/stdcompat/type_traits.h"
19#include "pw_preprocessor/boolean.h"
20#include "pw_preprocessor/concat.h"
21
23
43#define PW_CONSTEXPR_TEST(test_suite, test_name, ...) \
44 _PW_IF_CONSTEXPR_TEST(constexpr) \
45 void PwConstexprTest_##test_suite##_##test_name() __VA_ARGS__ \
46 \
47 TEST(test_suite, test_name) { \
48 PwConstexprTest_##test_suite##_##test_name(); \
49 } \
50 \
51 static_assert([] { \
52 _PW_IF_CONSTEXPR_TEST(PwConstexprTest_##test_suite##_##test_name();) \
53 return true; \
54 }())
55
56// GoogleTest-style test macros for PW_CONSTEXPR_TEST.
57
58#define PW_TEST_EXPECT_TRUE(expr) _PW_CEXPECT(TRUE, expr)
59#define PW_TEST_EXPECT_FALSE(expr) _PW_CEXPECT(FALSE, expr)
60
61#define PW_TEST_EXPECT_EQ(lhs, rhs) _PW_CEXPECT(EQ, lhs, rhs)
62#define PW_TEST_EXPECT_NE(lhs, rhs) _PW_CEXPECT(NE, lhs, rhs)
63#define PW_TEST_EXPECT_GT(lhs, rhs) _PW_CEXPECT(GT, lhs, rhs)
64#define PW_TEST_EXPECT_GE(lhs, rhs) _PW_CEXPECT(GE, lhs, rhs)
65#define PW_TEST_EXPECT_LT(lhs, rhs) _PW_CEXPECT(LT, lhs, rhs)
66#define PW_TEST_EXPECT_LE(lhs, rhs) _PW_CEXPECT(LE, lhs, rhs)
67
68#define PW_TEST_EXPECT_NEAR(lhs, rhs, error) _PW_CEXPECT(NEAR, lhs, rhs, error)
69#define PW_TEST_EXPECT_FLOAT_EQ(lhs, rhs) _PW_CEXPECT(FLOAT_EQ, lhs, rhs)
70#define PW_TEST_EXPECT_DOUBLE_EQ(lhs, rhs) _PW_CEXPECT(DOUBLE_EQ, lhs, rhs)
71
72#define PW_TEST_EXPECT_STREQ(lhs, rhs) _PW_CEXPECT(STREQ, lhs, rhs)
73#define PW_TEST_EXPECT_STRNE(lhs, rhs) _PW_CEXPECT(STRNE, lhs, rhs)
74
75#define PW_TEST_ASSERT_TRUE(expr) _PW_CASSERT(TRUE, expr)
76#define PW_TEST_ASSERT_FALSE(expr) _PW_CASSERT(FALSE, expr)
77
78#define PW_TEST_ASSERT_EQ(lhs, rhs) _PW_CASSERT(EQ, lhs, rhs)
79#define PW_TEST_ASSERT_NE(lhs, rhs) _PW_CASSERT(NE, lhs, rhs)
80#define PW_TEST_ASSERT_GT(lhs, rhs) _PW_CASSERT(GT, lhs, rhs)
81#define PW_TEST_ASSERT_GE(lhs, rhs) _PW_CASSERT(GE, lhs, rhs)
82#define PW_TEST_ASSERT_LT(lhs, rhs) _PW_CASSERT(LT, lhs, rhs)
83#define PW_TEST_ASSERT_LE(lhs, rhs) _PW_CASSERT(LE, lhs, rhs)
84
85#define PW_TEST_ASSERT_NEAR(lhs, rhs, error) _PW_CASSERT(NEAR, lhs, rhs, error)
86#define PW_TEST_ASSERT_FLOAT_EQ(lhs, rhs) _PW_CASSERT(FLOAT_EQ, lhs, rhs)
87#define PW_TEST_ASSERT_DOUBLE_EQ(lhs, rhs) _PW_CASSERT(DOUBLE_EQ, lhs, rhs)
88
89#define PW_TEST_ASSERT_STREQ(lhs, rhs) _PW_CASSERT(STREQ, lhs, rhs)
90#define PW_TEST_ASSERT_STRNE(lhs, rhs) _PW_CASSERT(STRNE, lhs, rhs)
91
93
94// Implementation details
95
96#define _PW_CEXPECT(macro, ...) \
97 if (cpp20::is_constant_evaluated()) { \
98 ::pw::unit_test::internal::Constexpr_EXPECT_##macro(__VA_ARGS__); \
99 } else \
100 EXPECT_##macro(__VA_ARGS__)
101
102#define _PW_CASSERT(macro, ...) \
103 if (cpp20::is_constant_evaluated()) { \
104 if (!::pw::unit_test::internal::Constexpr_EXPECT_##macro(__VA_ARGS__)) { \
105 return; \
106 } \
107 } else \
108 ASSERT_##macro(__VA_ARGS__)
109
110// Expands to the provided expression if constexpr tests are enabled.
111// block-submission: disable
112#define _PW_IF_CONSTEXPR_TEST(a) \
113 PW_CONCAT( \
114 _PW_IF_CONSTEXPR_TEST_, \
115 PW_AND(LIB_STDCOMPAT_CONSTEVAL_SUPPORT, \
116 _PW_CONSTEXPR_TEST_ENABLED(SKIP_CONSTEXPR_TESTS_DONT_SUBMIT)))(a)
117
118#define _PW_IF_CONSTEXPR_TEST_0(a)
119#define _PW_IF_CONSTEXPR_TEST_1(a) a
120
121#define _PW_CONSTEXPR_TEST_ENABLED(a) _PW_CONSTEXPR_TEST_ENABLED2(a)
122#define _PW_CONSTEXPR_TEST_ENABLED2(a) _PW_CONSTEXPR_TEST_##a
123
124// Check if the SKIP_CONSTEXPR_TESTS_DONT_SUBMIT macro is defined, which can
125// be used to temporarily disable the constexpr part of tests.
126#define _PW_CONSTEXPR_TEST_SKIP_CONSTEXPR_TESTS_DONT_SUBMIT 1
127#define _PW_CONSTEXPR_TEST_ 0
128#define _PW_CONSTEXPR_TEST_1 0
129#define _PW_CONSTEXPR_TEST_0 \
130 _Do_not_define_SKIP_CONSTEXPR_TESTS_DONT_SUBMIT_to_0_remove_it_instead
131// block-submission: enable
132
133namespace pw::unit_test::internal {
134
135// These undefined, non-constexpr functions are called when a constexpr test
136// assertion fails. They appear in the error message to inform the user.
137bool EXPECT_TRUE_FAILED();
138bool EXPECT_FALSE_FAILED();
139bool EXPECT_EQ_FAILED();
140bool EXPECT_NE_FAILED();
141bool EXPECT_GT_FAILED();
142bool EXPECT_GE_FAILED();
143bool EXPECT_LT_FAILED();
144bool EXPECT_LE_FAILED();
145bool EXPECT_NEAR_FAILED();
146bool EXPECT_STREQ_FAILED();
147bool EXPECT_STRNE_FAILED();
148
149template <typename T>
150constexpr bool Constexpr_EXPECT_TRUE(const T& expr) {
151 return (expr) ? true : EXPECT_TRUE_FAILED();
152}
153
154template <typename T>
155constexpr bool Constexpr_EXPECT_FALSE(const T& expr) {
156 return !(expr) ? true : EXPECT_FALSE_FAILED();
157}
158
159template <typename Lhs, typename Rhs>
160constexpr bool Constexpr_EXPECT_EQ(const Lhs& lhs, const Rhs& rhs) {
161 return (lhs == rhs) ? true : EXPECT_EQ_FAILED();
162}
163
164template <typename Lhs, typename Rhs>
165constexpr bool Constexpr_EXPECT_NE(const Lhs& lhs, const Rhs& rhs) {
166 return (lhs != rhs) ? true : EXPECT_NE_FAILED();
167}
168
169template <typename Lhs, typename Rhs>
170constexpr bool Constexpr_EXPECT_GT(const Lhs& lhs, const Rhs& rhs) {
171 return (lhs > rhs) ? true : EXPECT_GT_FAILED();
172}
173
174template <typename Lhs, typename Rhs>
175constexpr bool Constexpr_EXPECT_GE(const Lhs& lhs, const Rhs& rhs) {
176 return (lhs >= rhs) ? true : EXPECT_GE_FAILED();
177}
178
179template <typename Lhs, typename Rhs>
180constexpr bool Constexpr_EXPECT_LT(const Lhs& lhs, const Rhs& rhs) {
181 return (lhs < rhs) ? true : EXPECT_LT_FAILED();
182}
183
184template <typename Lhs, typename Rhs>
185constexpr bool Constexpr_EXPECT_LE(const Lhs& lhs, const Rhs& rhs) {
186 return (lhs <= rhs) ? true : EXPECT_LE_FAILED();
187}
188
189template <typename T>
190constexpr bool Constexpr_EXPECT_NEAR(T lhs, T rhs, T error) {
191 T diff = lhs - rhs;
192 if (diff < 0) {
193 diff *= -1;
194 }
195 return (diff <= error) ? true : EXPECT_NEAR_FAILED();
196}
197
198constexpr bool Constexpr_EXPECT_FLOAT_EQ(float lhs, float rhs) {
199 // Compare within 4 ULPs, for consistency with GoogleTest's EXPECT_FLOAT_EQ.
200 return Constexpr_EXPECT_NEAR(
201 lhs, rhs, 4 * std::numeric_limits<float>::epsilon());
202}
203
204constexpr bool Constexpr_EXPECT_DOUBLE_EQ(double lhs, double rhs) {
205 // Compare within 4 ULPs, for consistency with GoogleTest's EXPECT_DOUBLE_EQ.
206 return Constexpr_EXPECT_NEAR(
207 lhs, rhs, 4 * std::numeric_limits<double>::epsilon());
208}
209
210[[nodiscard]] constexpr bool StringsAreEqual(const char* lhs, const char* rhs) {
211 if (lhs == nullptr || rhs == nullptr) {
212 return lhs == rhs;
213 }
214
215 while (*lhs == *rhs && *lhs != '\0') {
216 lhs += 1;
217 rhs += 1;
218 }
219
220 return *lhs == '\0' && *rhs == '\0';
221}
222
223constexpr bool Constexpr_EXPECT_STREQ(const char* lhs, const char* rhs) {
224 return StringsAreEqual(lhs, rhs) ? true : EXPECT_STREQ_FAILED();
225}
226
227constexpr bool Constexpr_EXPECT_STRNE(const char* lhs, const char* rhs) {
228 return !StringsAreEqual(lhs, rhs) ? true : EXPECT_STRNE_FAILED();
229}
230
231} // namespace pw::unit_test::internal