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
16namespace 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.
21class FEnvSafeTest : public Test {
22public:
23 void TearDown() override;
24
25protected:
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:
33 explicit PreserveFEnv(FEnvSafeTest *self) : test{*self} {
34 test.get_fenv(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.
42 void restore() { test.set_fenv(before); }
43
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
52 ~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.
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.
72 void will_change_fenv() { should_be_unchanged = false; }
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.
78 void restore_fenv() { check.restore(); }
79
80private:
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

source code of libc/test/UnitTest/FEnvSafeTest.h