//===-- ErrnoSetterMatcher.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_ERRNOSETTERMATCHER_H #define LLVM_LIBC_TEST_ERRNOSETTERMATCHER_H #include "src/__support/FPUtil/FPBits.h" #include "src/__support/FPUtil/fpbits_str.h" #include "src/__support/StringUtil/error_to_string.h" #include "src/__support/macros/config.h" #include "src/__support/macros/properties/architectures.h" #include "src/errno/libc_errno.h" #include "test/UnitTest/Test.h" namespace LIBC_NAMESPACE_DECL { namespace testing { namespace internal { enum class CompareAction { EQ = 0, GE, GT, LE, LT, NE }; constexpr const char *CompareMessage[] = { "equal to", "greater than or equal to", "greater than", "less than or equal to", "less than", "not equal to"}; template struct Comparator { CompareAction cmp; T expected; bool compare(T actual) { switch (cmp) { case CompareAction::EQ: return actual == expected; case CompareAction::NE: return actual != expected; case CompareAction::GE: return actual >= expected; case CompareAction::GT: return actual > expected; case CompareAction::LE: return actual <= expected; case CompareAction::LT: return actual < expected; } __builtin_unreachable(); } // The NVPTX backend cannot handle circular dependencies on global variables. // We provide a constant dummy implementation to prevent this from occurring. #ifdef LIBC_TARGET_ARCH_IS_NVPTX constexpr const char *str() { return ""; } #else const char *str() { return CompareMessage[static_cast(cmp)]; } #endif }; template class ErrnoSetterMatcher : public Matcher { Comparator return_cmp; Comparator errno_cmp; T actual_return; int actual_errno; // Even though this is a errno matcher primarily, it has to cater to platforms // which do not have an errno. This predicate checks if errno matching is to // be skipped. static constexpr bool ignore_errno() { #ifdef LIBC_TARGET_ARCH_IS_GPU return true; #else return false; #endif } public: ErrnoSetterMatcher(Comparator rcmp) : return_cmp(rcmp) {} ErrnoSetterMatcher(Comparator rcmp, Comparator ecmp) : return_cmp(rcmp), errno_cmp(ecmp) {} ErrnoSetterMatcher with_errno(Comparator ecmp) { errno_cmp = ecmp; return *this; } void explainError() override { if (!return_cmp.compare(actual_return)) { if constexpr (cpp::is_floating_point_v) { tlog << "Expected return value to be " << return_cmp.str() << ": " << str(fputil::FPBits(return_cmp.expected)) << '\n' << " But got: " << str(fputil::FPBits(actual_return)) << '\n'; } else { tlog << "Expected return value to be " << return_cmp.str() << " " << return_cmp.expected << " but got " << actual_return << ".\n"; } } if constexpr (!ignore_errno()) { if (!errno_cmp.compare(actual_errno)) { tlog << "Expected errno to be " << errno_cmp.str() << " \"" << get_error_string(errno_cmp.expected) << "\" but got \"" << get_error_string(actual_errno) << "\".\n"; } } } bool match(T got) { actual_return = got; actual_errno = LIBC_NAMESPACE::libc_errno; LIBC_NAMESPACE::libc_errno = 0; if constexpr (ignore_errno()) return return_cmp.compare(actual_return); else return return_cmp.compare(actual_return) && errno_cmp.compare(actual_errno); } }; } // namespace internal namespace ErrnoSetterMatcher { template internal::Comparator LT(T val) { return internal::Comparator{internal::CompareAction::LT, val}; } template internal::Comparator LE(T val) { return internal::Comparator{internal::CompareAction::LE, val}; } template internal::Comparator GT(T val) { return internal::Comparator{internal::CompareAction::GT, val}; } template internal::Comparator GE(T val) { return internal::Comparator{internal::CompareAction::GE, val}; } template internal::Comparator EQ(T val) { return internal::Comparator{internal::CompareAction::EQ, val}; } template internal::Comparator NE(T val) { return internal::Comparator{internal::CompareAction::NE, val}; } template static internal::ErrnoSetterMatcher Succeeds(RetT ExpectedReturn = 0, int ExpectedErrno = 0) { return internal::ErrnoSetterMatcher(EQ(ExpectedReturn), EQ(ExpectedErrno)); } template static internal::ErrnoSetterMatcher Fails(int ExpectedErrno, RetT ExpectedReturn = -1) { return internal::ErrnoSetterMatcher(EQ(ExpectedReturn), EQ(ExpectedErrno)); } template class ErrnoSetterMatcherBuilder { public: template using Cmp = internal::Comparator; ErrnoSetterMatcherBuilder(Cmp cmp) : return_cmp(cmp) {} internal::ErrnoSetterMatcher with_errno(Cmp cmp) { return internal::ErrnoSetterMatcher(return_cmp, cmp); } private: Cmp return_cmp; }; template static ErrnoSetterMatcherBuilder returns(internal::Comparator cmp) { return ErrnoSetterMatcherBuilder(cmp); } } // namespace ErrnoSetterMatcher } // namespace testing } // namespace LIBC_NAMESPACE_DECL #endif // LLVM_LIBC_TEST_ERRNOSETTERMATCHER_H