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
18using namespace llvm;
19
20namespace {
21
22using ItemValid = enum { ValidItem, InvalidItem };
23using LinkValid = enum { ValidLink, InvalidLink };
24
25class Item {
26public:
27 Item(ItemValid V) : V(V) {}
28 bool isValid() const { return V == ValidItem; }
29
30private:
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.
39using FallibleCollection = std::vector<std::pair<Item, LinkValid>>;
40
41class FallibleCollectionWalker {
42public:
43 FallibleCollectionWalker(FallibleCollection &C, unsigned Idx)
44 : C(C), Idx(Idx) {}
45
46 Item &operator*() { return C[Idx].first; }
47
48 const Item &operator*() const { return C[Idx].first; }
49
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>(Args: "cant get next object in (mock) collection",
57 Args: inconvertibleErrorCode());
58 }
59
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>(Args: "cant get prev object in (mock) collection",
66 Args: inconvertibleErrorCode());
67 }
68
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
75private:
76 FallibleCollection &C;
77 unsigned Idx;
78};
79
80class FallibleCollectionWalkerWithStructDeref
81 : public FallibleCollectionWalker {
82public:
83 using FallibleCollectionWalker::FallibleCollectionWalker;
84
85 Item *operator->() { return &this->operator*(); }
86
87 const Item *operator->() const { return &this->operator*(); }
88};
89
90class FallibleCollectionWalkerWithFallibleDeref
91 : public FallibleCollectionWalker {
92public:
93 using FallibleCollectionWalker::FallibleCollectionWalker;
94
95 Expected<Item &> operator*() {
96 auto &I = FallibleCollectionWalker::operator*();
97 if (!I.isValid())
98 return make_error<StringError>(Args: "bad item", Args: inconvertibleErrorCode());
99 return I;
100 }
101
102 Expected<const Item &> operator*() const {
103 const auto &I = FallibleCollectionWalker::operator*();
104 if (!I.isValid())
105 return make_error<StringError>(Args: "bad item", Args: inconvertibleErrorCode());
106 return I;
107 }
108};
109
110TEST(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>(I: begin, E: end, Err))
123 EXPECT_TRUE(Elem.isValid());
124 cantFail(Err: std::move(Err));
125}
126
127TEST(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>(I: begin, E: end, Err))
140 EXPECT_TRUE(Elem.isValid());
141
142 EXPECT_THAT_ERROR(std::move(Err), Failed()) << "Expected failure value";
143}
144
145TEST(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>(I: begin, E: 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
166TEST(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
189TEST(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(I: 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(I: 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(I: 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(I: 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
235TEST(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(I: begin, Err);
248 EXPECT_TRUE(I->isValid());
249 cantFail(Err: std::move(Err));
250 }
251
252 {
253 Error Err = Error::success();
254 const auto I = make_fallible_itr(I: begin, Err);
255 EXPECT_TRUE(I->isValid());
256 cantFail(Err: std::move(Err));
257 }
258}
259
260TEST(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(I: begin, Err);
274 auto E = make_fallible_end(E: 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(Err: std::move(Err));
289}
290
291} // namespace
292

source code of llvm/unittests/ADT/FallibleIteratorTest.cpp