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 "src/__support/macros/config.h" 15 #include "test/UnitTest/Test.h" 16 17 namespace LIBC_NAMESPACE_DECL { 18 namespace testing { 19 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 { 24 public: 25 void TearDown() override; 26 27 protected: 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. 30 class PreserveFEnv { 31 fenv_t before; 32 FEnvSafeTest &test; 33 34 public: PreserveFEnv(FEnvSafeTest * self)35 explicit PreserveFEnv(FEnvSafeTest *self) : test{*self} { 36 test.get_fenv(before); 37 } 38 39 // Cause test expectation failures if the current state doesn't match what 40 // was captured in the constructor. 41 void check(); 42 43 // Restore the state captured in the constructor. restore()44 void restore() { test.set_fenv(before); } 45 ~PreserveFEnv()46 ~PreserveFEnv() { restore(); } 47 }; 48 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; 53 ~CheckFEnvCheckFEnv54 ~CheckFEnv() { check(); } 55 }; 56 57 // This calls callable() and returns its value, but has EXPECT_* failures if 58 // the `fenv_t` state is not preserved by the call. decltype(auto)59 template <typename T> decltype(auto) check_fenv_preserved(T &&callable) { 60 CheckFEnv check{this}; 61 return cpp::forward<T>(callable)(); 62 } 63 64 // This calls callable() and returns its value, but saves and restores the 65 // `fenv_t` state around the call. 66 template <typename T> 67 auto with_fenv_preserved(T &&callable) 68 -> decltype(cpp::forward<decltype(callable)>(callable)()) { 69 PreserveFEnv preserve{this}; 70 return cpp::forward<T>(callable)(); 71 } 72 73 // A test can call these to indicate it will or won't change `fenv_t` state. will_change_fenv()74 void will_change_fenv() { should_be_unchanged = false; } will_not_change_fenv()75 void will_not_change_fenv() { should_be_unchanged = true; } 76 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. restore_fenv()80 void restore_fenv() { check.restore(); } 81 82 private: 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); 86 87 CheckFEnv check{this}; 88 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; 99 }; 100 101 } // namespace testing 102 } // namespace LIBC_NAMESPACE_DECL 103 104 #endif // LLVM_LIBC_TEST_UNITTEST_FPENVSAFE_H 105