1 //===- unittests/ADT/FallibleIteratorTest.cpp - fallible_iterator.h tests -===//
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
7 //===----------------------------------------------------------------------===//
9 #include "llvm/ADT/fallible_iterator.h"
10 #include "llvm/Testing/Support/Error.h"
12 #include "gtest/gtest-spi.h"
13 #include "gtest/gtest.h"
22 using ItemValid
= enum { ValidItem
, InvalidItem
};
23 using LinkValid
= enum { ValidLink
, InvalidLink
};
27 Item(ItemValid V
) : V(V
) {}
28 bool isValid() const { return V
== ValidItem
; }
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)
39 using FallibleCollection
= std::vector
<std::pair
<Item
, LinkValid
>>;
41 class FallibleCollectionWalker
{
43 FallibleCollectionWalker(FallibleCollection
&C
, unsigned Idx
)
46 Item
&operator*() { return C
[Idx
].first
; }
48 const Item
&operator*() const { return C
[Idx
].first
; }
51 assert(Idx
!= C
.size() && "Walking off end of (mock) collection");
52 if (C
[Idx
].second
== ValidLink
) {
54 return Error::success();
56 return make_error
<StringError
>("cant get next object in (mock) collection",
57 inconvertibleErrorCode());
61 assert(Idx
!= 0 && "Walking off start of (mock) collection");
63 if (C
[Idx
].second
== ValidLink
)
64 return Error::success();
65 return make_error
<StringError
>("cant get prev object in (mock) collection",
66 inconvertibleErrorCode());
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
;
76 FallibleCollection
&C
;
80 class FallibleCollectionWalkerWithStructDeref
81 : public FallibleCollectionWalker
{
83 using FallibleCollectionWalker::FallibleCollectionWalker
;
85 Item
*operator->() { return &this->operator*(); }
87 const Item
*operator->() const { return &this->operator*(); }
90 class FallibleCollectionWalkerWithFallibleDeref
91 : public FallibleCollectionWalker
{
93 using FallibleCollectionWalker::FallibleCollectionWalker
;
95 Expected
<Item
&> operator*() {
96 auto &I
= FallibleCollectionWalker::operator*();
98 return make_error
<StringError
>("bad item", inconvertibleErrorCode());
102 Expected
<const Item
&> operator*() const {
103 const auto &I
= FallibleCollectionWalker::operator*();
105 return make_error
<StringError
>("bad item", inconvertibleErrorCode());
110 TEST(FallibleIteratorTest
, BasicSuccess
) {
112 // Check that a basic use-case involing successful iteration over a
113 // "FallibleCollection" works.
115 FallibleCollection
C({{ValidItem
, ValidLink
}, {ValidItem
, ValidLink
}});
117 FallibleCollectionWalker
begin(C
, 0);
118 FallibleCollectionWalker
end(C
, 2);
120 Error Err
= Error::success();
122 make_fallible_range
<FallibleCollectionWalker
>(begin
, end
, Err
))
123 EXPECT_TRUE(Elem
.isValid());
124 cantFail(std::move(Err
));
127 TEST(FallibleIteratorTest
, BasicFailure
) {
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.
132 FallibleCollection
C({{ValidItem
, ValidLink
}, {ValidItem
, InvalidLink
}});
134 FallibleCollectionWalker
begin(C
, 0);
135 FallibleCollectionWalker
end(C
, 2);
137 Error Err
= Error::success();
139 make_fallible_range
<FallibleCollectionWalker
>(begin
, end
, Err
))
140 EXPECT_TRUE(Elem
.isValid());
142 EXPECT_THAT_ERROR(std::move(Err
), Failed()) << "Expected failure value";
145 TEST(FallibleIteratorTest
, NoRedundantErrorCheckOnEarlyExit
) {
147 // Check that an early return from the loop body does not require a redundant
150 FallibleCollection
C({{ValidItem
, ValidLink
}, {ValidItem
, ValidLink
}});
152 FallibleCollectionWalker
begin(C
, 0);
153 FallibleCollectionWalker
end(C
, 2);
155 Error Err
= Error::success();
157 make_fallible_range
<FallibleCollectionWalker
>(begin
, end
, Err
)) {
161 // Err not checked, but should be ok because we exit from the loop
165 #if LLVM_ENABLE_ABI_BREAKING_CHECKS
166 TEST(FallibleIteratorTest
, RegularLoopExitRequiresErrorCheck
) {
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
174 FallibleCollection
C({{ValidItem
, ValidLink
}, {ValidItem
, ValidLink
}});
176 FallibleCollectionWalker
begin(C
, 0);
177 FallibleCollectionWalker
end(C
, 2);
179 Error Err
= Error::success();
181 make_fallible_range
<FallibleCollectionWalker
>(begin
, end
, Err
))
184 "Program aborted due to an unhandled Error:")
185 << "Normal (i.e. not early) loop exit should require an error check";
189 TEST(FallibleIteratorTest
, RawIncrementAndDecrementBehavior
) {
191 // Check the exact behavior of increment / decrement.
193 FallibleCollection
C({{ValidItem
, ValidLink
},
194 {ValidItem
, InvalidLink
},
195 {ValidItem
, ValidLink
},
196 {ValidItem
, InvalidLink
}});
199 // One increment from begin succeeds.
200 Error Err
= Error::success();
201 auto I
= make_fallible_itr(FallibleCollectionWalker(C
, 0), Err
);
203 EXPECT_THAT_ERROR(std::move(Err
), Succeeded());
207 // Two increments from begin fail.
208 Error Err
= Error::success();
209 auto I
= make_fallible_itr(FallibleCollectionWalker(C
, 0), Err
);
211 EXPECT_THAT_ERROR(std::move(Err
), Succeeded());
213 EXPECT_THAT_ERROR(std::move(Err
), Failed()) << "Expected failure value";
217 // One decement from element three succeeds.
218 Error Err
= Error::success();
219 auto I
= make_fallible_itr(FallibleCollectionWalker(C
, 3), Err
);
221 EXPECT_THAT_ERROR(std::move(Err
), Succeeded());
225 // One decement from element three succeeds.
226 Error Err
= Error::success();
227 auto I
= make_fallible_itr(FallibleCollectionWalker(C
, 3), Err
);
229 EXPECT_THAT_ERROR(std::move(Err
), Succeeded());
231 EXPECT_THAT_ERROR(std::move(Err
), Failed());
235 TEST(FallibleIteratorTest
, CheckStructDerefOperatorSupport
) {
236 // Check that the fallible_iterator wrapper forwards through to the
237 // underlying iterator's structure dereference operator if present.
239 FallibleCollection
C({{ValidItem
, ValidLink
},
240 {ValidItem
, ValidLink
},
241 {InvalidItem
, InvalidLink
}});
243 FallibleCollectionWalkerWithStructDeref
begin(C
, 0);
246 Error Err
= Error::success();
247 auto I
= make_fallible_itr(begin
, Err
);
248 EXPECT_TRUE(I
->isValid());
249 cantFail(std::move(Err
));
253 Error Err
= Error::success();
254 const auto I
= make_fallible_itr(begin
, Err
);
255 EXPECT_TRUE(I
->isValid());
256 cantFail(std::move(Err
));
260 TEST(FallibleIteratorTest
, CheckDerefToExpectedSupport
) {
262 // Check that the fallible_iterator wrapper forwards value types, in
263 // particular llvm::Expected, correctly.
265 FallibleCollection
C({{ValidItem
, ValidLink
},
266 {InvalidItem
, ValidLink
},
267 {ValidItem
, ValidLink
}});
269 FallibleCollectionWalkerWithFallibleDeref
begin(C
, 0);
270 FallibleCollectionWalkerWithFallibleDeref
end(C
, 3);
272 Error Err
= Error::success();
273 auto I
= make_fallible_itr(begin
, Err
);
274 auto E
= make_fallible_end(end
);
276 Expected
<Item
> V1
= *I
;
277 EXPECT_THAT_ERROR(V1
.takeError(), Succeeded());
279 EXPECT_NE(I
, E
); // Implicitly check error.
280 Expected
<Item
> V2
= *I
;
281 EXPECT_THAT_ERROR(V2
.takeError(), Failed());
283 EXPECT_NE(I
, E
); // Implicitly check error.
284 Expected
<Item
> V3
= *I
;
285 EXPECT_THAT_ERROR(V3
.takeError(), Succeeded());
288 cantFail(std::move(Err
));