Pigweed
 
Loading...
Searching...
No Matches
constexpr.h File Reference
#include <limits>
#include "lib/stdcompat/type_traits.h"
#include "pw_preprocessor/boolean.h"
#include "pw_preprocessor/concat.h"

Go to the source code of this file.

Namespaces

namespace  pw
 Provides basic helpers for reading and writing UTF-8 encoded strings.
 

Macros

#define PW_CONSTEXPR_TEST(test_suite, test_name, ...)
 
#define PW_TEST_EXPECT_TRUE(expr)   _PW_CEXPECT(TRUE, expr)
 
#define PW_TEST_EXPECT_FALSE(expr)   _PW_CEXPECT(FALSE, expr)
 
#define PW_TEST_EXPECT_EQ(lhs, rhs)   _PW_CEXPECT(EQ, lhs, rhs)
 
#define PW_TEST_EXPECT_NE(lhs, rhs)   _PW_CEXPECT(NE, lhs, rhs)
 
#define PW_TEST_EXPECT_GT(lhs, rhs)   _PW_CEXPECT(GT, lhs, rhs)
 
#define PW_TEST_EXPECT_GE(lhs, rhs)   _PW_CEXPECT(GE, lhs, rhs)
 
#define PW_TEST_EXPECT_LT(lhs, rhs)   _PW_CEXPECT(LT, lhs, rhs)
 
#define PW_TEST_EXPECT_LE(lhs, rhs)   _PW_CEXPECT(LE, lhs, rhs)
 
#define PW_TEST_EXPECT_NEAR(lhs, rhs, error)   _PW_CEXPECT(NEAR, lhs, rhs, error)
 
#define PW_TEST_EXPECT_FLOAT_EQ(lhs, rhs)   _PW_CEXPECT(FLOAT_EQ, lhs, rhs)
 
#define PW_TEST_EXPECT_DOUBLE_EQ(lhs, rhs)   _PW_CEXPECT(DOUBLE_EQ, lhs, rhs)
 
#define PW_TEST_EXPECT_STREQ(lhs, rhs)   _PW_CEXPECT(STREQ, lhs, rhs)
 
#define PW_TEST_EXPECT_STRNE(lhs, rhs)   _PW_CEXPECT(STRNE, lhs, rhs)
 
#define PW_TEST_ASSERT_TRUE(expr)   _PW_CASSERT(TRUE, expr)
 
#define PW_TEST_ASSERT_FALSE(expr)   _PW_CASSERT(FALSE, expr)
 
#define PW_TEST_ASSERT_EQ(lhs, rhs)   _PW_CASSERT(EQ, lhs, rhs)
 
#define PW_TEST_ASSERT_NE(lhs, rhs)   _PW_CASSERT(NE, lhs, rhs)
 
#define PW_TEST_ASSERT_GT(lhs, rhs)   _PW_CASSERT(GT, lhs, rhs)
 
#define PW_TEST_ASSERT_GE(lhs, rhs)   _PW_CASSERT(GE, lhs, rhs)
 
#define PW_TEST_ASSERT_LT(lhs, rhs)   _PW_CASSERT(LT, lhs, rhs)
 
#define PW_TEST_ASSERT_LE(lhs, rhs)   _PW_CASSERT(LE, lhs, rhs)
 
#define PW_TEST_ASSERT_NEAR(lhs, rhs, error)   _PW_CASSERT(NEAR, lhs, rhs, error)
 
#define PW_TEST_ASSERT_FLOAT_EQ(lhs, rhs)   _PW_CASSERT(FLOAT_EQ, lhs, rhs)
 
#define PW_TEST_ASSERT_DOUBLE_EQ(lhs, rhs)   _PW_CASSERT(DOUBLE_EQ, lhs, rhs)
 
#define PW_TEST_ASSERT_STREQ(lhs, rhs)   _PW_CASSERT(STREQ, lhs, rhs)
 
#define PW_TEST_ASSERT_STRNE(lhs, rhs)   _PW_CASSERT(STRNE, lhs, rhs)
 
#define _PW_CEXPECT(macro, ...)
 
#define _PW_CASSERT(macro, ...)
 
#define _PW_IF_CONSTEXPR_TEST(a)
 
#define _PW_IF_CONSTEXPR_TEST_0(a)
 
#define _PW_IF_CONSTEXPR_TEST_1(a)   a
 
#define _PW_CONSTEXPR_TEST_ENABLED(a)   _PW_CONSTEXPR_TEST_ENABLED2(a)
 
#define _PW_CONSTEXPR_TEST_ENABLED2(a)   _PW_CONSTEXPR_TEST_##a
 
#define _PW_CONSTEXPR_TEST_SKIP_CONSTEXPR_TESTS_DONT_SUBMIT   1
 
#define _PW_CONSTEXPR_TEST_   0
 
#define _PW_CONSTEXPR_TEST_1   0
 
#define _PW_CONSTEXPR_TEST_0    _Do_not_define_SKIP_CONSTEXPR_TESTS_DONT_SUBMIT_to_0_remove_it_instead
 

Functions

bool pw::unit_test::internal::EXPECT_TRUE_FAILED ()
 
bool pw::unit_test::internal::EXPECT_FALSE_FAILED ()
 
bool pw::unit_test::internal::EXPECT_EQ_FAILED ()
 
bool pw::unit_test::internal::EXPECT_NE_FAILED ()
 
bool pw::unit_test::internal::EXPECT_GT_FAILED ()
 
bool pw::unit_test::internal::EXPECT_GE_FAILED ()
 
bool pw::unit_test::internal::EXPECT_LT_FAILED ()
 
bool pw::unit_test::internal::EXPECT_LE_FAILED ()
 
bool pw::unit_test::internal::EXPECT_NEAR_FAILED ()
 
bool pw::unit_test::internal::EXPECT_STREQ_FAILED ()
 
bool pw::unit_test::internal::EXPECT_STRNE_FAILED ()
 
template<typename T >
constexpr bool pw::unit_test::internal::Constexpr_EXPECT_TRUE (const T &expr)
 
template<typename T >
constexpr bool pw::unit_test::internal::Constexpr_EXPECT_FALSE (const T &expr)
 
template<typename Lhs , typename Rhs >
constexpr bool pw::unit_test::internal::Constexpr_EXPECT_EQ (const Lhs &lhs, const Rhs &rhs)
 
template<typename Lhs , typename Rhs >
constexpr bool pw::unit_test::internal::Constexpr_EXPECT_NE (const Lhs &lhs, const Rhs &rhs)
 
template<typename Lhs , typename Rhs >
constexpr bool pw::unit_test::internal::Constexpr_EXPECT_GT (const Lhs &lhs, const Rhs &rhs)
 
template<typename Lhs , typename Rhs >
constexpr bool pw::unit_test::internal::Constexpr_EXPECT_GE (const Lhs &lhs, const Rhs &rhs)
 
template<typename Lhs , typename Rhs >
constexpr bool pw::unit_test::internal::Constexpr_EXPECT_LT (const Lhs &lhs, const Rhs &rhs)
 
template<typename Lhs , typename Rhs >
constexpr bool pw::unit_test::internal::Constexpr_EXPECT_LE (const Lhs &lhs, const Rhs &rhs)
 
template<typename T >
constexpr bool pw::unit_test::internal::Constexpr_EXPECT_NEAR (T lhs, T rhs, T error)
 
constexpr bool pw::unit_test::internal::Constexpr_EXPECT_FLOAT_EQ (float lhs, float rhs)
 
constexpr bool pw::unit_test::internal::Constexpr_EXPECT_DOUBLE_EQ (double lhs, double rhs)
 
constexpr bool pw::unit_test::internal::StringsAreEqual (const char *lhs, const char *rhs)
 
constexpr bool pw::unit_test::internal::Constexpr_EXPECT_STREQ (const char *lhs, const char *rhs)
 
constexpr bool pw::unit_test::internal::Constexpr_EXPECT_STRNE (const char *lhs, const char *rhs)
 

Detailed Description

The

embed:rst:inline :c:macro:`PW_CONSTEXPR_TEST` 

macro defines a test that is executed both at compile time in a static_assert and as a regular GoogleTest-style TEST(). This offers the advantages of compile-time testing in a structured, familiar API, without sacrificing anything from GoogleTest-style tests. The framework uses the standard GoogleTest macros at run time, and is compatible with GoogleTest or Pigweed's pw_unit_test:light framework.

To create a constexpr test:

  • Include "pw_unit_test/constexpr.h" alongside the test framework ("pw_unit_test/framework.h" or "gtest/gtest.h").
  • Use the macro
    embed:rst:inline :c:macro:`PW_CONSTEXPR_TEST` 
    instead of TEST. Note that the function body passed as the third argument to the macro.
  • Use the familiar GoogleTest macros, but with a PW_TEST_ prefix. For example:
    • EXPECT_TRUE
      embed:rst:inline :c:macro:`PW_TEST_EXPECT_TRUE` 
    • EXPECT_EQ
      embed:rst:inline :c:macro:`PW_TEST_EXPECT_EQ` 
    • ASSERT_STREQ
      embed:rst:inline :c:macro:`PW_TEST_ASSERT_STREQ` 
    • etc.

The result is a familiar-looking unit test that executes both at compile time and run time.

embed:rst:leading-asterisk
 
*  .. literalinclude:: constexpr_test.cc
*     :language: cpp
*     :start-after: [pw_unit_test-constexpr]
*     :end-before: [pw_unit_test-constexpr]
* 
*  

Why should I run tests at compile time?

  • Cross compile and execute tests without having to flash them to a device.
  • Ensure constexpr functions can actually be evaluated at compile time. For example, function templates may be marked as constexpr, even if they do not support constant evaluation when instantiated.
  • Catch undefined behavior, out-of-bounds access, and other issues during compilation on any platform, without needing to run sanitizers.

If compile-time testing is so great, why execute the tests are run time at all?

  • Code may run differently at compile time and execution, particularly when std::is_constant_evaluated or if consteval are used.
  • Error messages are much better at runtime.
    embed:rst:inline :c:macro:`PW_CONSTEXPR_TEST` 
    makes it simple to temporarily disable compile-time tests and see the rich GoogleTest-like output (see
    embed:rst:inline :c:macro:`SKIP_CONSTEXPR_TESTS_DONT_SUBMIT` 
    ).
  • Tools like code coverage only work for code that is executed normally.
embed:rst:inline :c:macro:`PW_CONSTEXPR_TEST` 

uses stdcompat's cpp20::is_constant_evaluated(). If the compiler does not support is_constant_evaluated, only the regular GoogleTest version will run. Note that compiler support is independent of the C++ standard in use.

Macro Definition Documentation

◆ _PW_CASSERT

#define _PW_CASSERT (   macro,
  ... 
)
Value:
if (cpp20::is_constant_evaluated()) { \
if (!::pw::unit_test::internal::Constexpr_EXPECT_##macro(__VA_ARGS__)) { \
return; \
} \
} else \
ASSERT_##macro(__VA_ARGS__)

◆ _PW_CEXPECT

#define _PW_CEXPECT (   macro,
  ... 
)
Value:
if (cpp20::is_constant_evaluated()) { \
::pw::unit_test::internal::Constexpr_EXPECT_##macro(__VA_ARGS__); \
} else \
EXPECT_##macro(__VA_ARGS__)

◆ _PW_IF_CONSTEXPR_TEST

#define _PW_IF_CONSTEXPR_TEST (   a)
Value:
PW_CONCAT( \
_PW_IF_CONSTEXPR_TEST_, \
PW_AND(LIB_STDCOMPAT_CONSTEVAL_SUPPORT, \
_PW_CONSTEXPR_TEST_ENABLED(SKIP_CONSTEXPR_TESTS_DONT_SUBMIT)))(a)

◆ PW_CONSTEXPR_TEST

#define PW_CONSTEXPR_TEST (   test_suite,
  test_name,
  ... 
)
Value:
_PW_IF_CONSTEXPR_TEST(constexpr) \
void PwConstexprTest_##test_suite##_##test_name() __VA_ARGS__ \
\
TEST(test_suite, test_name) { \
PwConstexprTest_##test_suite##_##test_name(); \
} \
\
static_assert([] { \
_PW_IF_CONSTEXPR_TEST(PwConstexprTest_##test_suite##_##test_name();) \
return true; \
}())

Defines a test that is executed both at compile time in a static_assert and as a regular GoogleTest-style TEST().

PW_CONSTEXPR_TEST works similarly to the GoogleTest TEST() macro, but has some differences.

  • All tested code must be constexpr.
  • Requires the PW_TEST_* prefixed versions of GoogleTest's EXPECT_* and ASSERT_* macros.
  • The function body is a macro argument. This has two implications:
    • The function body cannot contain preprocessor directives, such as #define. If these are needed, move them to a separate function that is called from the test.
    • The PW_CONSTEXPR_TEST macro must be terminated with ); after the test body argument.
Parameters
test_suiteGoogleTest test suite name
test_nameGoogleTest test name
...test function body surrounded by { }.