292 lines · cpp
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-exception6//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 22enum ItemValid { ValidItem, InvalidItem };23enum LinkValid { 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 links36// 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>("cant get next object in (mock) collection",57 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>("cant get prev object in (mock) collection",66 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 FallibleCollectionWalkerWithStructDeref81 : 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 FallibleCollectionWalkerWithFallibleDeref91 : 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>("bad item", 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>("bad item", inconvertibleErrorCode());106 return I;107 }108};109 110TEST(FallibleIteratorTest, BasicSuccess) {111 112 // Check that a basic use-case involing successful iteration over a113 // "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 127TEST(FallibleIteratorTest, BasicFailure) {128 129 // Check that a iteration failure (due to the InvalidLink state on element one130 // 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 145TEST(FallibleIteratorTest, NoRedundantErrorCheckOnEarlyExit) {146 147 // Check that an early return from the loop body does not require a redundant148 // 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 loop162 // body.163}164 165#if LLVM_ENABLE_ABI_BREAKING_CHECKS166TEST(FallibleIteratorTest, RegularLoopExitRequiresErrorCheck) {167 168 // Check that Err must be checked after a normal (i.e. not early) loop exit169 // by failing to check and expecting program death (due to the unchecked170 // 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#endif188 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(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 235TEST(FallibleIteratorTest, CheckStructDerefOperatorSupport) {236 // Check that the fallible_iterator wrapper forwards through to the237 // 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 260TEST(FallibleIteratorTest, CheckDerefToExpectedSupport) {261 262 // Check that the fallible_iterator wrapper forwards value types, in263 // 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} // namespace292