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