105 lines
3.8 KiB
C++
105 lines
3.8 KiB
C++
//===-- FEnvSafeTest.h -----------------------------------------*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===---------------------------------------------------------------------===//
|
|
|
|
#ifndef LLVM_LIBC_TEST_UNITTEST_FPENVSAFE_H
|
|
#define LLVM_LIBC_TEST_UNITTEST_FPENVSAFE_H
|
|
|
|
#include "hdr/types/fenv_t.h"
|
|
#include "src/__support/CPP/utility.h"
|
|
#include "src/__support/macros/config.h"
|
|
#include "test/UnitTest/Test.h"
|
|
|
|
namespace LIBC_NAMESPACE_DECL {
|
|
namespace testing {
|
|
|
|
// This provides a test fixture (or base class for other test fixtures) that
|
|
// asserts that each test does not leave the FPU state represented by `fenv_t`
|
|
// (aka `FPState`) perturbed from its initial state.
|
|
class FEnvSafeTest : public Test {
|
|
public:
|
|
void TearDown() override;
|
|
|
|
protected:
|
|
// This is an RAII type where `PreserveFEnv preserve{this};` will sample the
|
|
// `fenv_t` state and restore it when `preserve` goes out of scope.
|
|
class PreserveFEnv {
|
|
fenv_t before;
|
|
FEnvSafeTest &test;
|
|
|
|
public:
|
|
explicit PreserveFEnv(FEnvSafeTest *self) : test{*self} {
|
|
test.get_fenv(before);
|
|
}
|
|
|
|
// Cause test expectation failures if the current state doesn't match what
|
|
// was captured in the constructor.
|
|
void check();
|
|
|
|
// Restore the state captured in the constructor.
|
|
void restore() { test.set_fenv(before); }
|
|
|
|
~PreserveFEnv() { restore(); }
|
|
};
|
|
|
|
// This is an RAII type where `CheckFEnv check{this};` will sample the
|
|
// `fenv_t` state and require it be the same when `check` goes out of scope.
|
|
struct CheckFEnv : public PreserveFEnv {
|
|
using PreserveFEnv::PreserveFEnv;
|
|
|
|
~CheckFEnv() { check(); }
|
|
};
|
|
|
|
// This calls callable() and returns its value, but has EXPECT_* failures if
|
|
// the `fenv_t` state is not preserved by the call.
|
|
template <typename T> decltype(auto) check_fenv_preserved(T &&callable) {
|
|
CheckFEnv check{this};
|
|
return cpp::forward<T>(callable)();
|
|
}
|
|
|
|
// This calls callable() and returns its value, but saves and restores the
|
|
// `fenv_t` state around the call.
|
|
template <typename T>
|
|
auto with_fenv_preserved(T &&callable)
|
|
-> decltype(cpp::forward<decltype(callable)>(callable)()) {
|
|
PreserveFEnv preserve{this};
|
|
return cpp::forward<T>(callable)();
|
|
}
|
|
|
|
// A test can call these to indicate it will or won't change `fenv_t` state.
|
|
void will_change_fenv() { should_be_unchanged = false; }
|
|
void will_not_change_fenv() { should_be_unchanged = true; }
|
|
|
|
// This explicitly resets back to the "before" state captured in SetUp().
|
|
// TearDown() always does this, but should_be_unchanged controls whether
|
|
// it also causes test failures if a test fails to restore it.
|
|
void restore_fenv() { check.restore(); }
|
|
|
|
private:
|
|
void get_fenv(fenv_t &fenv);
|
|
void set_fenv(const fenv_t &fenv);
|
|
void expect_fenv_eq(const fenv_t &before_fenv, const fenv_t &after_fenv);
|
|
|
|
CheckFEnv check{this};
|
|
|
|
// TODO: Many tests fail if this is true. It needs to be figured out whether
|
|
// the state should be preserved by each library function under test, and
|
|
// separately whether each test itself should preserve the state. It
|
|
// probably isn't important that tests be explicitly written to preserve the
|
|
// state, as the fixture can (and does) reset it--the next test can rely on
|
|
// getting "normal" ambient state initially. For library functions that
|
|
// should preserve the state, that should be checked after each call, not
|
|
// just after the whole test. So they can use check_fenv_preserved or
|
|
// with_fenv_preserved as appropriate.
|
|
bool should_be_unchanged = false;
|
|
};
|
|
|
|
} // namespace testing
|
|
} // namespace LIBC_NAMESPACE_DECL
|
|
|
|
#endif // LLVM_LIBC_TEST_UNITTEST_FPENVSAFE_H
|