1 //===- unittests/ADT/FallibleIteratorTest.cpp - fallible_iterator.h tests -===//
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 #include "llvm/ADT/fallible_iterator.h"
10 #include "llvm/Testing/Support/Error.h"
11
12 #include "gtest/gtest-spi.h"
13 #include "gtest/gtest.h"
14
15 #include <utility>
16 #include <vector>
17
18 using namespace llvm;
19
20 namespace {
21
22 using ItemValid = enum { ValidItem, InvalidItem };
23 using LinkValid = enum { ValidLink, InvalidLink };
24
25 class Item {
26 public:
Item(ItemValid V)27 Item(ItemValid V) : V(V) {}
isValid() const28 bool isValid() const { return V == ValidItem; }
29
30 private:
31 ItemValid V;
32 };
33
34 // A utility to mock "bad collections". It supports both invalid items,
35 // where the dereference operator may return an Error, and bad links
36 // where the inc/dec operations may return an Error.
37 // Each element of the mock collection contains a pair of a (possibly broken)
38 // item and link.
39 using FallibleCollection = std::vector<std::pair<Item, LinkValid>>;
40
41 class FallibleCollectionWalker {
42 public:
FallibleCollectionWalker(FallibleCollection & C,unsigned Idx)43 FallibleCollectionWalker(FallibleCollection &C, unsigned Idx)
44 : C(C), Idx(Idx) {}
45
operator *()46 Item &operator*() { return C[Idx].first; }
47
operator *() const48 const Item &operator*() const { return C[Idx].first; }
49
inc()50 Error inc() {
51 assert(Idx != C.size() && "Walking off end of (mock) collection");
52 if (C[Idx].second == ValidLink) {
53 ++Idx;
54 return Error::success();
55 }
56 return make_error<StringError>("cant get next object in (mock) collection",
57 inconvertibleErrorCode());
58 }
59
dec()60 Error dec() {
61 assert(Idx != 0 && "Walking off start of (mock) collection");
62 --Idx;
63 if (C[Idx].second == ValidLink)
64 return Error::success();
65 return make_error<StringError>("cant get prev object in (mock) collection",
66 inconvertibleErrorCode());
67 }
68
operator ==(const FallibleCollectionWalker & LHS,const FallibleCollectionWalker & RHS)69 friend bool operator==(const FallibleCollectionWalker &LHS,
70 const FallibleCollectionWalker &RHS) {
71 assert(&LHS.C == &RHS.C && "Comparing iterators across collectionss.");
72 return LHS.Idx == RHS.Idx;
73 }
74
75 private:
76 FallibleCollection &C;
77 unsigned Idx;
78 };
79
80 class FallibleCollectionWalkerWithStructDeref
81 : public FallibleCollectionWalker {
82 public:
83 using FallibleCollectionWalker::FallibleCollectionWalker;
84
operator ->()85 Item *operator->() { return &this->operator*(); }
86
operator ->() const87 const Item *operator->() const { return &this->operator*(); }
88 };
89
90 class FallibleCollectionWalkerWithFallibleDeref
91 : public FallibleCollectionWalker {
92 public:
93 using FallibleCollectionWalker::FallibleCollectionWalker;
94
operator *()95 Expected<Item &> operator*() {
96 auto &I = FallibleCollectionWalker::operator*();
97 if (!I.isValid())
98 return make_error<StringError>("bad item", inconvertibleErrorCode());
99 return I;
100 }
101
operator *() const102 Expected<const Item &> operator*() const {
103 const auto &I = FallibleCollectionWalker::operator*();
104 if (!I.isValid())
105 return make_error<StringError>("bad item", inconvertibleErrorCode());
106 return I;
107 }
108 };
109
TEST(FallibleIteratorTest,BasicSuccess)110 TEST(FallibleIteratorTest, BasicSuccess) {
111
112 // Check that a basic use-case involing successful iteration over a
113 // "FallibleCollection" works.
114
115 FallibleCollection C({{ValidItem, ValidLink}, {ValidItem, ValidLink}});
116
117 FallibleCollectionWalker begin(C, 0);
118 FallibleCollectionWalker end(C, 2);
119
120 Error Err = Error::success();
121 for (auto &Elem :
122 make_fallible_range<FallibleCollectionWalker>(begin, end, Err))
123 EXPECT_TRUE(Elem.isValid());
124 cantFail(std::move(Err));
125 }
126
TEST(FallibleIteratorTest,BasicFailure)127 TEST(FallibleIteratorTest, BasicFailure) {
128
129 // Check that a iteration failure (due to the InvalidLink state on element one
130 // of the fallible collection) breaks out of the loop and raises an Error.
131
132 FallibleCollection C({{ValidItem, ValidLink}, {ValidItem, InvalidLink}});
133
134 FallibleCollectionWalker begin(C, 0);
135 FallibleCollectionWalker end(C, 2);
136
137 Error Err = Error::success();
138 for (auto &Elem :
139 make_fallible_range<FallibleCollectionWalker>(begin, end, Err))
140 EXPECT_TRUE(Elem.isValid());
141
142 EXPECT_THAT_ERROR(std::move(Err), Failed()) << "Expected failure value";
143 }
144
TEST(FallibleIteratorTest,NoRedundantErrorCheckOnEarlyExit)145 TEST(FallibleIteratorTest, NoRedundantErrorCheckOnEarlyExit) {
146
147 // Check that an early return from the loop body does not require a redundant
148 // check of Err.
149
150 FallibleCollection C({{ValidItem, ValidLink}, {ValidItem, ValidLink}});
151
152 FallibleCollectionWalker begin(C, 0);
153 FallibleCollectionWalker end(C, 2);
154
155 Error Err = Error::success();
156 for (auto &Elem :
157 make_fallible_range<FallibleCollectionWalker>(begin, end, Err)) {
158 (void)Elem;
159 return;
160 }
161 // Err not checked, but should be ok because we exit from the loop
162 // body.
163 }
164
165 #if LLVM_ENABLE_ABI_BREAKING_CHECKS
TEST(FallibleIteratorTest,RegularLoopExitRequiresErrorCheck)166 TEST(FallibleIteratorTest, RegularLoopExitRequiresErrorCheck) {
167
168 // Check that Err must be checked after a normal (i.e. not early) loop exit
169 // by failing to check and expecting program death (due to the unchecked
170 // error).
171
172 EXPECT_DEATH(
173 {
174 FallibleCollection C({{ValidItem, ValidLink}, {ValidItem, ValidLink}});
175
176 FallibleCollectionWalker begin(C, 0);
177 FallibleCollectionWalker end(C, 2);
178
179 Error Err = Error::success();
180 for (auto &Elem :
181 make_fallible_range<FallibleCollectionWalker>(begin, end, Err))
182 (void)Elem;
183 },
184 "Program aborted due to an unhandled Error:")
185 << "Normal (i.e. not early) loop exit should require an error check";
186 }
187 #endif
188
TEST(FallibleIteratorTest,RawIncrementAndDecrementBehavior)189 TEST(FallibleIteratorTest, RawIncrementAndDecrementBehavior) {
190
191 // Check the exact behavior of increment / decrement.
192
193 FallibleCollection C({{ValidItem, ValidLink},
194 {ValidItem, InvalidLink},
195 {ValidItem, ValidLink},
196 {ValidItem, InvalidLink}});
197
198 {
199 // One increment from begin succeeds.
200 Error Err = Error::success();
201 auto I = make_fallible_itr(FallibleCollectionWalker(C, 0), Err);
202 ++I;
203 EXPECT_THAT_ERROR(std::move(Err), Succeeded());
204 }
205
206 {
207 // Two increments from begin fail.
208 Error Err = Error::success();
209 auto I = make_fallible_itr(FallibleCollectionWalker(C, 0), Err);
210 ++I;
211 EXPECT_THAT_ERROR(std::move(Err), Succeeded());
212 ++I;
213 EXPECT_THAT_ERROR(std::move(Err), Failed()) << "Expected failure value";
214 }
215
216 {
217 // One decement from element three succeeds.
218 Error Err = Error::success();
219 auto I = make_fallible_itr(FallibleCollectionWalker(C, 3), Err);
220 --I;
221 EXPECT_THAT_ERROR(std::move(Err), Succeeded());
222 }
223
224 {
225 // One decement from element three succeeds.
226 Error Err = Error::success();
227 auto I = make_fallible_itr(FallibleCollectionWalker(C, 3), Err);
228 --I;
229 EXPECT_THAT_ERROR(std::move(Err), Succeeded());
230 --I;
231 EXPECT_THAT_ERROR(std::move(Err), Failed());
232 }
233 }
234
TEST(FallibleIteratorTest,CheckStructDerefOperatorSupport)235 TEST(FallibleIteratorTest, CheckStructDerefOperatorSupport) {
236 // Check that the fallible_iterator wrapper forwards through to the
237 // underlying iterator's structure dereference operator if present.
238
239 FallibleCollection C({{ValidItem, ValidLink},
240 {ValidItem, ValidLink},
241 {InvalidItem, InvalidLink}});
242
243 FallibleCollectionWalkerWithStructDeref begin(C, 0);
244
245 {
246 Error Err = Error::success();
247 auto I = make_fallible_itr(begin, Err);
248 EXPECT_TRUE(I->isValid());
249 cantFail(std::move(Err));
250 }
251
252 {
253 Error Err = Error::success();
254 const auto I = make_fallible_itr(begin, Err);
255 EXPECT_TRUE(I->isValid());
256 cantFail(std::move(Err));
257 }
258 }
259
TEST(FallibleIteratorTest,CheckDerefToExpectedSupport)260 TEST(FallibleIteratorTest, CheckDerefToExpectedSupport) {
261
262 // Check that the fallible_iterator wrapper forwards value types, in
263 // particular llvm::Expected, correctly.
264
265 FallibleCollection C({{ValidItem, ValidLink},
266 {InvalidItem, ValidLink},
267 {ValidItem, ValidLink}});
268
269 FallibleCollectionWalkerWithFallibleDeref begin(C, 0);
270 FallibleCollectionWalkerWithFallibleDeref end(C, 3);
271
272 Error Err = Error::success();
273 auto I = make_fallible_itr(begin, Err);
274 auto E = make_fallible_end(end);
275
276 Expected<Item> V1 = *I;
277 EXPECT_THAT_ERROR(V1.takeError(), Succeeded());
278 ++I;
279 EXPECT_NE(I, E); // Implicitly check error.
280 Expected<Item> V2 = *I;
281 EXPECT_THAT_ERROR(V2.takeError(), Failed());
282 ++I;
283 EXPECT_NE(I, E); // Implicitly check error.
284 Expected<Item> V3 = *I;
285 EXPECT_THAT_ERROR(V3.takeError(), Succeeded());
286 ++I;
287 EXPECT_EQ(I, E);
288 cantFail(std::move(Err));
289 }
290
291 } // namespace
292