193 lines · c
1//===-- ErrnoSetterMatcher.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-exception6//7//===----------------------------------------------------------------------===//8 9#ifndef LLVM_LIBC_TEST_ERRNOSETTERMATCHER_H10#define LLVM_LIBC_TEST_ERRNOSETTERMATCHER_H11 12#include "src/__support/FPUtil/FPBits.h"13#include "src/__support/FPUtil/fpbits_str.h"14#include "src/__support/StringUtil/error_to_string.h"15#include "src/__support/libc_errno.h"16#include "src/__support/macros/config.h"17#include "src/__support/macros/properties/architectures.h"18#include "test/UnitTest/Test.h"19 20namespace LIBC_NAMESPACE_DECL {21namespace testing {22 23namespace internal {24 25enum class CompareAction { EQ = 0, GE, GT, LE, LT, NE };26 27constexpr const char *CompareMessage[] = {28 "equal to", "greater than or equal to",29 "greater than", "less than or equal to",30 "less than", "not equal to"};31 32template <typename T> struct Comparator {33 CompareAction cmp;34 T expected;35 bool compare(T actual) {36 switch (cmp) {37 case CompareAction::EQ:38 return actual == expected;39 case CompareAction::NE:40 return actual != expected;41 case CompareAction::GE:42 return actual >= expected;43 case CompareAction::GT:44 return actual > expected;45 case CompareAction::LE:46 return actual <= expected;47 case CompareAction::LT:48 return actual < expected;49 }50 __builtin_unreachable();51 }52 53 // The NVPTX backend cannot handle circular dependencies on global variables.54 // We provide a constant dummy implementation to prevent this from occurring.55#ifdef LIBC_TARGET_ARCH_IS_NVPTX56 constexpr const char *str() { return ""; }57#else58 const char *str() { return CompareMessage[static_cast<int>(cmp)]; }59#endif60};61 62template <typename T> class ErrnoSetterMatcher : public Matcher<T> {63 Comparator<T> return_cmp;64 Comparator<int> errno_cmp;65 T actual_return;66 int actual_errno;67 68 // Even though this is a errno matcher primarily, it has to cater to platforms69 // which do not have an errno. This predicate checks if errno matching is to70 // be skipped.71 static constexpr bool ignore_errno() {72#ifdef LIBC_TARGET_ARCH_IS_GPU73 return true;74#else75 return false;76#endif77 }78 79public:80 ErrnoSetterMatcher(Comparator<T> rcmp) : return_cmp(rcmp) {}81 ErrnoSetterMatcher(Comparator<T> rcmp, Comparator<int> ecmp)82 : return_cmp(rcmp), errno_cmp(ecmp) {}83 84 ErrnoSetterMatcher<T> with_errno(Comparator<int> ecmp) {85 errno_cmp = ecmp;86 return *this;87 }88 89 void explainError() override {90 if (!return_cmp.compare(actual_return)) {91 if constexpr (cpp::is_floating_point_v<T>) {92 tlog << "Expected return value to be " << return_cmp.str() << ": "93 << str(fputil::FPBits<T>(return_cmp.expected)) << '\n'94 << " But got: "95 << str(fputil::FPBits<T>(actual_return)) << '\n';96 } else {97 tlog << "Expected return value to be " << return_cmp.str() << " "98 << return_cmp.expected << " but got " << actual_return << ".\n";99 }100 }101 102 if constexpr (!ignore_errno()) {103 if (!errno_cmp.compare(actual_errno)) {104 auto expected_str = try_get_errno_name(errno_cmp.expected);105 auto actual_str = try_get_errno_name(actual_errno);106 tlog << "Expected errno to be " << errno_cmp.str() << " "107 << (expected_str ? *expected_str : "<unknown>") << "("108 << errno_cmp.expected << ") but got "109 << (actual_str ? *actual_str : "<unknown>") << "(" << actual_errno110 << ").\n";111 }112 }113 }114 115 bool match(T got) {116 actual_return = got;117 actual_errno = libc_errno;118 libc_errno = 0;119 if constexpr (ignore_errno())120 return return_cmp.compare(actual_return);121 else122 return return_cmp.compare(actual_return) &&123 errno_cmp.compare(actual_errno);124 }125};126 127} // namespace internal128 129namespace ErrnoSetterMatcher {130 131template <typename T> internal::Comparator<T> LT(T val) {132 return internal::Comparator<T>{internal::CompareAction::LT, val};133}134 135template <typename T> internal::Comparator<T> LE(T val) {136 return internal::Comparator<T>{internal::CompareAction::LE, val};137}138 139template <typename T> internal::Comparator<T> GT(T val) {140 return internal::Comparator<T>{internal::CompareAction::GT, val};141}142 143template <typename T> internal::Comparator<T> GE(T val) {144 return internal::Comparator<T>{internal::CompareAction::GE, val};145}146 147template <typename T> internal::Comparator<T> EQ(T val) {148 return internal::Comparator<T>{internal::CompareAction::EQ, val};149}150 151template <typename T> internal::Comparator<T> NE(T val) {152 return internal::Comparator<T>{internal::CompareAction::NE, val};153}154 155template <typename RetT = int>156static internal::ErrnoSetterMatcher<RetT> Succeeds(RetT ExpectedReturn = 0,157 int ExpectedErrno = 0) {158 return internal::ErrnoSetterMatcher<RetT>(EQ(ExpectedReturn),159 EQ(ExpectedErrno));160}161 162template <typename RetT = int>163static internal::ErrnoSetterMatcher<RetT> Fails(int ExpectedErrno,164 RetT ExpectedReturn = -1) {165 return internal::ErrnoSetterMatcher<RetT>(EQ(ExpectedReturn),166 EQ(ExpectedErrno));167}168 169template <typename RetT = int> class ErrnoSetterMatcherBuilder {170public:171 template <typename T> using Cmp = internal::Comparator<T>;172 ErrnoSetterMatcherBuilder(Cmp<RetT> cmp) : return_cmp(cmp) {}173 174 internal::ErrnoSetterMatcher<RetT> with_errno(Cmp<int> cmp) {175 return internal::ErrnoSetterMatcher<RetT>(return_cmp, cmp);176 }177 178private:179 Cmp<RetT> return_cmp;180};181 182template <typename RetT>183static ErrnoSetterMatcherBuilder<RetT> returns(internal::Comparator<RetT> cmp) {184 return ErrnoSetterMatcherBuilder<RetT>(cmp);185}186 187} // namespace ErrnoSetterMatcher188 189} // namespace testing190} // namespace LIBC_NAMESPACE_DECL191 192#endif // LLVM_LIBC_TEST_ERRNOSETTERMATCHER_H193