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

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