1 //===-- FEnvSafeTest.h -----------------------------------------*- C++ -*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===---------------------------------------------------------------------===//
9 #ifndef LLVM_LIBC_TEST_UNITTEST_FPENVSAFE_H
10 #define LLVM_LIBC_TEST_UNITTEST_FPENVSAFE_H
12 #include "hdr/types/fenv_t.h"
13 #include "src/__support/CPP/utility.h"
14 #include "src/__support/macros/config.h"
15 #include "test/UnitTest/Test.h"
17 namespace LIBC_NAMESPACE_DECL
{
20 // This provides a test fixture (or base class for other test fixtures) that
21 // asserts that each test does not leave the FPU state represented by `fenv_t`
22 // (aka `FPState`) perturbed from its initial state.
23 class FEnvSafeTest
: public Test
{
25 void TearDown() override
;
28 // This is an RAII type where `PreserveFEnv preserve{this};` will sample the
29 // `fenv_t` state and restore it when `preserve` goes out of scope.
35 explicit PreserveFEnv(FEnvSafeTest
*self
) : test
{*self
} {
36 test
.get_fenv(before
);
39 // Cause test expectation failures if the current state doesn't match what
40 // was captured in the constructor.
43 // Restore the state captured in the constructor.
44 void restore() { test
.set_fenv(before
); }
46 ~PreserveFEnv() { restore(); }
49 // This is an RAII type where `CheckFEnv check{this};` will sample the
50 // `fenv_t` state and require it be the same when `check` goes out of scope.
51 struct CheckFEnv
: public PreserveFEnv
{
52 using PreserveFEnv::PreserveFEnv
;
54 ~CheckFEnv() { check(); }
57 // This calls callable() and returns its value, but has EXPECT_* failures if
58 // the `fenv_t` state is not preserved by the call.
59 template <typename T
> decltype(auto) check_fenv_preserved(T
&&callable
) {
60 CheckFEnv check
{this};
61 return cpp::forward
<T
>(callable
)();
64 // This calls callable() and returns its value, but saves and restores the
65 // `fenv_t` state around the call.
67 auto with_fenv_preserved(T
&&callable
)
68 -> decltype(cpp::forward
<decltype(callable
)>(callable
)()) {
69 PreserveFEnv preserve
{this};
70 return cpp::forward
<T
>(callable
)();
73 // A test can call these to indicate it will or won't change `fenv_t` state.
74 void will_change_fenv() { should_be_unchanged
= false; }
75 void will_not_change_fenv() { should_be_unchanged
= true; }
77 // This explicitly resets back to the "before" state captured in SetUp().
78 // TearDown() always does this, but should_be_unchanged controls whether
79 // it also causes test failures if a test fails to restore it.
80 void restore_fenv() { check
.restore(); }
83 void get_fenv(fenv_t
&fenv
);
84 void set_fenv(const fenv_t
&fenv
);
85 void expect_fenv_eq(const fenv_t
&before_fenv
, const fenv_t
&after_fenv
);
87 CheckFEnv check
{this};
89 // TODO: Many tests fail if this is true. It needs to be figured out whether
90 // the state should be preserved by each library function under test, and
91 // separately whether each test itself should preserve the state. It
92 // probably isn't important that tests be explicitly written to preserve the
93 // state, as the fixture can (and does) reset it--the next test can rely on
94 // getting "normal" ambient state initially. For library functions that
95 // should preserve the state, that should be checked after each call, not
96 // just after the whole test. So they can use check_fenv_preserved or
97 // with_fenv_preserved as appropriate.
98 bool should_be_unchanged
= false;
101 } // namespace testing
102 } // namespace LIBC_NAMESPACE_DECL
104 #endif // LLVM_LIBC_TEST_UNITTEST_FPENVSAFE_H