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