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