1*dd3a739dSZachary Turner //===- llvm/unittest/DebugInfo/CodeView/RandomAccessVisitorTest.cpp -------===// 2*dd3a739dSZachary Turner // 3*dd3a739dSZachary Turner // The LLVM Compiler Infrastructure 4*dd3a739dSZachary Turner // 5*dd3a739dSZachary Turner // This file is distributed under the University of Illinois Open Source 6*dd3a739dSZachary Turner // License. See LICENSE.TXT for details. 7*dd3a739dSZachary Turner // 8*dd3a739dSZachary Turner //===----------------------------------------------------------------------===// 9*dd3a739dSZachary Turner 10*dd3a739dSZachary Turner #include "ErrorChecking.h" 11*dd3a739dSZachary Turner 12*dd3a739dSZachary Turner #include "llvm/ADT/SmallBitVector.h" 13*dd3a739dSZachary Turner #include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" 14*dd3a739dSZachary Turner #include "llvm/DebugInfo/CodeView/RandomAccessTypeVisitor.h" 15*dd3a739dSZachary Turner #include "llvm/DebugInfo/CodeView/TypeRecord.h" 16*dd3a739dSZachary Turner #include "llvm/DebugInfo/CodeView/TypeRecordMapping.h" 17*dd3a739dSZachary Turner #include "llvm/DebugInfo/CodeView/TypeSerializer.h" 18*dd3a739dSZachary Turner #include "llvm/DebugInfo/CodeView/TypeServerHandler.h" 19*dd3a739dSZachary Turner #include "llvm/DebugInfo/CodeView/TypeTableBuilder.h" 20*dd3a739dSZachary Turner #include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h" 21*dd3a739dSZachary Turner #include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h" 22*dd3a739dSZachary Turner #include "llvm/DebugInfo/PDB/Native/RawTypes.h" 23*dd3a739dSZachary Turner #include "llvm/Support/Allocator.h" 24*dd3a739dSZachary Turner #include "llvm/Support/BinaryItemStream.h" 25*dd3a739dSZachary Turner #include "llvm/Support/Error.h" 26*dd3a739dSZachary Turner 27*dd3a739dSZachary Turner #include "gtest/gtest.h" 28*dd3a739dSZachary Turner 29*dd3a739dSZachary Turner using namespace llvm; 30*dd3a739dSZachary Turner using namespace llvm::codeview; 31*dd3a739dSZachary Turner using namespace llvm::pdb; 32*dd3a739dSZachary Turner 33*dd3a739dSZachary Turner namespace llvm { 34*dd3a739dSZachary Turner namespace codeview { 35*dd3a739dSZachary Turner inline bool operator==(const ArrayRecord &R1, const ArrayRecord &R2) { 36*dd3a739dSZachary Turner if (R1.ElementType != R2.ElementType) 37*dd3a739dSZachary Turner return false; 38*dd3a739dSZachary Turner if (R1.IndexType != R2.IndexType) 39*dd3a739dSZachary Turner return false; 40*dd3a739dSZachary Turner if (R1.Name != R2.Name) 41*dd3a739dSZachary Turner return false; 42*dd3a739dSZachary Turner if (R1.Size != R2.Size) 43*dd3a739dSZachary Turner return false; 44*dd3a739dSZachary Turner return true; 45*dd3a739dSZachary Turner } 46*dd3a739dSZachary Turner inline bool operator!=(const ArrayRecord &R1, const ArrayRecord &R2) { 47*dd3a739dSZachary Turner return !(R1 == R2); 48*dd3a739dSZachary Turner } 49*dd3a739dSZachary Turner 50*dd3a739dSZachary Turner inline bool operator==(const CVType &R1, const CVType &R2) { 51*dd3a739dSZachary Turner if (R1.Type != R2.Type) 52*dd3a739dSZachary Turner return false; 53*dd3a739dSZachary Turner if (R1.RecordData != R2.RecordData) 54*dd3a739dSZachary Turner return false; 55*dd3a739dSZachary Turner return true; 56*dd3a739dSZachary Turner } 57*dd3a739dSZachary Turner inline bool operator!=(const CVType &R1, const CVType &R2) { 58*dd3a739dSZachary Turner return !(R1 == R2); 59*dd3a739dSZachary Turner } 60*dd3a739dSZachary Turner } 61*dd3a739dSZachary Turner } 62*dd3a739dSZachary Turner 63*dd3a739dSZachary Turner namespace llvm { 64*dd3a739dSZachary Turner template <> struct BinaryItemTraits<CVType> { 65*dd3a739dSZachary Turner static size_t length(const CVType &Item) { return Item.length(); } 66*dd3a739dSZachary Turner static ArrayRef<uint8_t> bytes(const CVType &Item) { return Item.data(); } 67*dd3a739dSZachary Turner }; 68*dd3a739dSZachary Turner } 69*dd3a739dSZachary Turner 70*dd3a739dSZachary Turner namespace { 71*dd3a739dSZachary Turner 72*dd3a739dSZachary Turner class MockCallbacks : public TypeVisitorCallbacks { 73*dd3a739dSZachary Turner public: 74*dd3a739dSZachary Turner virtual Error visitTypeBegin(CVType &CVR, TypeIndex Index) { 75*dd3a739dSZachary Turner Indices.push_back(Index); 76*dd3a739dSZachary Turner return Error::success(); 77*dd3a739dSZachary Turner } 78*dd3a739dSZachary Turner virtual Error visitKnownRecord(CVType &CVR, ArrayRecord &AR) { 79*dd3a739dSZachary Turner VisitedRecords.push_back(AR); 80*dd3a739dSZachary Turner RawRecords.push_back(CVR); 81*dd3a739dSZachary Turner return Error::success(); 82*dd3a739dSZachary Turner } 83*dd3a739dSZachary Turner 84*dd3a739dSZachary Turner uint32_t count() const { 85*dd3a739dSZachary Turner assert(Indices.size() == RawRecords.size()); 86*dd3a739dSZachary Turner assert(Indices.size() == VisitedRecords.size()); 87*dd3a739dSZachary Turner return Indices.size(); 88*dd3a739dSZachary Turner } 89*dd3a739dSZachary Turner std::vector<TypeIndex> Indices; 90*dd3a739dSZachary Turner std::vector<CVType> RawRecords; 91*dd3a739dSZachary Turner std::vector<ArrayRecord> VisitedRecords; 92*dd3a739dSZachary Turner }; 93*dd3a739dSZachary Turner 94*dd3a739dSZachary Turner class RandomAccessVisitorTest : public testing::Test { 95*dd3a739dSZachary Turner public: 96*dd3a739dSZachary Turner RandomAccessVisitorTest() {} 97*dd3a739dSZachary Turner 98*dd3a739dSZachary Turner static void SetUpTestCase() { 99*dd3a739dSZachary Turner GlobalState = llvm::make_unique<GlobalTestState>(); 100*dd3a739dSZachary Turner 101*dd3a739dSZachary Turner TypeTableBuilder Builder(GlobalState->Allocator); 102*dd3a739dSZachary Turner 103*dd3a739dSZachary Turner uint32_t Offset = 0; 104*dd3a739dSZachary Turner for (int I = 0; I < 11; ++I) { 105*dd3a739dSZachary Turner ArrayRecord AR(TypeRecordKind::Array); 106*dd3a739dSZachary Turner AR.ElementType = TypeIndex::Int32(); 107*dd3a739dSZachary Turner AR.IndexType = TypeIndex::UInt32(); 108*dd3a739dSZachary Turner AR.Size = I; 109*dd3a739dSZachary Turner std::string Name; 110*dd3a739dSZachary Turner raw_string_ostream Stream(Name); 111*dd3a739dSZachary Turner Stream << "Array [" << I << "]"; 112*dd3a739dSZachary Turner AR.Name = GlobalState->Strings.save(Stream.str()); 113*dd3a739dSZachary Turner GlobalState->Records.push_back(AR); 114*dd3a739dSZachary Turner GlobalState->Indices.push_back(Builder.writeKnownType(AR)); 115*dd3a739dSZachary Turner 116*dd3a739dSZachary Turner CVType Type(TypeLeafKind::LF_ARRAY, Builder.records().back()); 117*dd3a739dSZachary Turner GlobalState->TypeVector.push_back(Type); 118*dd3a739dSZachary Turner 119*dd3a739dSZachary Turner GlobalState->AllOffsets.push_back( 120*dd3a739dSZachary Turner {GlobalState->Indices.back(), ulittle32_t(Offset)}); 121*dd3a739dSZachary Turner Offset += Type.length(); 122*dd3a739dSZachary Turner } 123*dd3a739dSZachary Turner 124*dd3a739dSZachary Turner GlobalState->ItemStream.setItems(GlobalState->TypeVector); 125*dd3a739dSZachary Turner GlobalState->TypeArray = VarStreamArray<CVType>(GlobalState->ItemStream); 126*dd3a739dSZachary Turner } 127*dd3a739dSZachary Turner 128*dd3a739dSZachary Turner static void TearDownTestCase() { GlobalState.reset(); } 129*dd3a739dSZachary Turner 130*dd3a739dSZachary Turner void SetUp() override { 131*dd3a739dSZachary Turner TestState = llvm::make_unique<PerTestState>(); 132*dd3a739dSZachary Turner 133*dd3a739dSZachary Turner TestState->Pipeline.addCallbackToPipeline(TestState->Deserializer); 134*dd3a739dSZachary Turner TestState->Pipeline.addCallbackToPipeline(TestState->Callbacks); 135*dd3a739dSZachary Turner } 136*dd3a739dSZachary Turner 137*dd3a739dSZachary Turner void TearDown() override { TestState.reset(); } 138*dd3a739dSZachary Turner 139*dd3a739dSZachary Turner protected: 140*dd3a739dSZachary Turner bool ValidateDatabaseRecord(const RandomAccessTypeVisitor &Visitor, 141*dd3a739dSZachary Turner uint32_t Index) { 142*dd3a739dSZachary Turner TypeIndex TI = TypeIndex::fromArrayIndex(Index); 143*dd3a739dSZachary Turner if (!Visitor.database().contains(TI)) 144*dd3a739dSZachary Turner return false; 145*dd3a739dSZachary Turner if (GlobalState->TypeVector[Index] != Visitor.database().getTypeRecord(TI)) 146*dd3a739dSZachary Turner return false; 147*dd3a739dSZachary Turner return true; 148*dd3a739dSZachary Turner } 149*dd3a739dSZachary Turner 150*dd3a739dSZachary Turner bool ValidateVisitedRecord(uint32_t VisitationOrder, 151*dd3a739dSZachary Turner uint32_t GlobalArrayIndex) { 152*dd3a739dSZachary Turner TypeIndex TI = TypeIndex::fromArrayIndex(GlobalArrayIndex); 153*dd3a739dSZachary Turner if (TI != TestState->Callbacks.Indices[VisitationOrder]) 154*dd3a739dSZachary Turner return false; 155*dd3a739dSZachary Turner 156*dd3a739dSZachary Turner if (GlobalState->TypeVector[TI.toArrayIndex()] != 157*dd3a739dSZachary Turner TestState->Callbacks.RawRecords[VisitationOrder]) 158*dd3a739dSZachary Turner return false; 159*dd3a739dSZachary Turner 160*dd3a739dSZachary Turner if (GlobalState->Records[TI.toArrayIndex()] != 161*dd3a739dSZachary Turner TestState->Callbacks.VisitedRecords[VisitationOrder]) 162*dd3a739dSZachary Turner return false; 163*dd3a739dSZachary Turner 164*dd3a739dSZachary Turner return true; 165*dd3a739dSZachary Turner } 166*dd3a739dSZachary Turner 167*dd3a739dSZachary Turner struct GlobalTestState { 168*dd3a739dSZachary Turner GlobalTestState() : Strings(Allocator), ItemStream(llvm::support::little) {} 169*dd3a739dSZachary Turner 170*dd3a739dSZachary Turner BumpPtrAllocator Allocator; 171*dd3a739dSZachary Turner StringSaver Strings; 172*dd3a739dSZachary Turner 173*dd3a739dSZachary Turner std::vector<ArrayRecord> Records; 174*dd3a739dSZachary Turner std::vector<TypeIndex> Indices; 175*dd3a739dSZachary Turner std::vector<TypeIndexOffset> AllOffsets; 176*dd3a739dSZachary Turner std::vector<CVType> TypeVector; 177*dd3a739dSZachary Turner BinaryItemStream<CVType> ItemStream; 178*dd3a739dSZachary Turner VarStreamArray<CVType> TypeArray; 179*dd3a739dSZachary Turner 180*dd3a739dSZachary Turner MutableBinaryByteStream Stream; 181*dd3a739dSZachary Turner }; 182*dd3a739dSZachary Turner 183*dd3a739dSZachary Turner struct PerTestState { 184*dd3a739dSZachary Turner FixedStreamArray<TypeIndexOffset> Offsets; 185*dd3a739dSZachary Turner 186*dd3a739dSZachary Turner TypeVisitorCallbackPipeline Pipeline; 187*dd3a739dSZachary Turner TypeDeserializer Deserializer; 188*dd3a739dSZachary Turner MockCallbacks Callbacks; 189*dd3a739dSZachary Turner }; 190*dd3a739dSZachary Turner 191*dd3a739dSZachary Turner FixedStreamArray<TypeIndexOffset> 192*dd3a739dSZachary Turner createPartialOffsets(MutableBinaryByteStream &Storage, 193*dd3a739dSZachary Turner std::initializer_list<uint32_t> Indices) { 194*dd3a739dSZachary Turner 195*dd3a739dSZachary Turner uint32_t Count = Indices.size(); 196*dd3a739dSZachary Turner uint32_t Size = Count * sizeof(TypeIndexOffset); 197*dd3a739dSZachary Turner uint8_t *Buffer = GlobalState->Allocator.Allocate<uint8_t>(Size); 198*dd3a739dSZachary Turner MutableArrayRef<uint8_t> Bytes(Buffer, Size); 199*dd3a739dSZachary Turner Storage = MutableBinaryByteStream(Bytes, support::little); 200*dd3a739dSZachary Turner BinaryStreamWriter Writer(Storage); 201*dd3a739dSZachary Turner for (const auto I : Indices) 202*dd3a739dSZachary Turner consumeError(Writer.writeObject(GlobalState->AllOffsets[I])); 203*dd3a739dSZachary Turner 204*dd3a739dSZachary Turner BinaryStreamReader Reader(Storage); 205*dd3a739dSZachary Turner FixedStreamArray<TypeIndexOffset> Result; 206*dd3a739dSZachary Turner consumeError(Reader.readArray(Result, Count)); 207*dd3a739dSZachary Turner return Result; 208*dd3a739dSZachary Turner } 209*dd3a739dSZachary Turner 210*dd3a739dSZachary Turner static std::unique_ptr<GlobalTestState> GlobalState; 211*dd3a739dSZachary Turner std::unique_ptr<PerTestState> TestState; 212*dd3a739dSZachary Turner }; 213*dd3a739dSZachary Turner 214*dd3a739dSZachary Turner std::unique_ptr<RandomAccessVisitorTest::GlobalTestState> 215*dd3a739dSZachary Turner RandomAccessVisitorTest::GlobalState; 216*dd3a739dSZachary Turner } 217*dd3a739dSZachary Turner 218*dd3a739dSZachary Turner TEST_F(RandomAccessVisitorTest, MultipleVisits) { 219*dd3a739dSZachary Turner TestState->Offsets = createPartialOffsets(GlobalState->Stream, {0, 8}); 220*dd3a739dSZachary Turner RandomAccessTypeVisitor Visitor(GlobalState->TypeArray, 221*dd3a739dSZachary Turner GlobalState->TypeVector.size(), 222*dd3a739dSZachary Turner TestState->Offsets); 223*dd3a739dSZachary Turner 224*dd3a739dSZachary Turner std::vector<uint32_t> IndicesToVisit = {5, 5, 5}; 225*dd3a739dSZachary Turner 226*dd3a739dSZachary Turner for (uint32_t I : IndicesToVisit) { 227*dd3a739dSZachary Turner TypeIndex TI = TypeIndex::fromArrayIndex(I); 228*dd3a739dSZachary Turner EXPECT_NO_ERROR(Visitor.visitTypeIndex(TI, TestState->Pipeline)); 229*dd3a739dSZachary Turner } 230*dd3a739dSZachary Turner 231*dd3a739dSZachary Turner // [0,8) should be present 232*dd3a739dSZachary Turner EXPECT_EQ(8, Visitor.database().size()); 233*dd3a739dSZachary Turner for (uint32_t I = 0; I < 8; ++I) 234*dd3a739dSZachary Turner EXPECT_TRUE(ValidateDatabaseRecord(Visitor, I)); 235*dd3a739dSZachary Turner 236*dd3a739dSZachary Turner // 5, 5, 5 237*dd3a739dSZachary Turner EXPECT_EQ(3, TestState->Callbacks.count()); 238*dd3a739dSZachary Turner for (auto I : enumerate(IndicesToVisit)) 239*dd3a739dSZachary Turner EXPECT_TRUE(ValidateVisitedRecord(I.index(), I.value())); 240*dd3a739dSZachary Turner } 241*dd3a739dSZachary Turner 242*dd3a739dSZachary Turner TEST_F(RandomAccessVisitorTest, DescendingWithinChunk) { 243*dd3a739dSZachary Turner // Visit multiple items from the same "chunk" in reverse order. In this 244*dd3a739dSZachary Turner // example, it's 7 then 4 then 2. At the end, all records from 0 to 7 should 245*dd3a739dSZachary Turner // be known by the database, but only 2, 4, and 7 should have been visited. 246*dd3a739dSZachary Turner TestState->Offsets = createPartialOffsets(GlobalState->Stream, {0, 8}); 247*dd3a739dSZachary Turner 248*dd3a739dSZachary Turner std::vector<uint32_t> IndicesToVisit = {7, 4, 2}; 249*dd3a739dSZachary Turner 250*dd3a739dSZachary Turner RandomAccessTypeVisitor Visitor(GlobalState->TypeArray, 251*dd3a739dSZachary Turner GlobalState->TypeVector.size(), 252*dd3a739dSZachary Turner TestState->Offsets); 253*dd3a739dSZachary Turner 254*dd3a739dSZachary Turner for (uint32_t I : IndicesToVisit) { 255*dd3a739dSZachary Turner TypeIndex TI = TypeIndex::fromArrayIndex(I); 256*dd3a739dSZachary Turner EXPECT_NO_ERROR(Visitor.visitTypeIndex(TI, TestState->Pipeline)); 257*dd3a739dSZachary Turner } 258*dd3a739dSZachary Turner 259*dd3a739dSZachary Turner // [0, 7] 260*dd3a739dSZachary Turner EXPECT_EQ(8, Visitor.database().size()); 261*dd3a739dSZachary Turner for (uint32_t I = 0; I < 8; ++I) 262*dd3a739dSZachary Turner EXPECT_TRUE(ValidateDatabaseRecord(Visitor, I)); 263*dd3a739dSZachary Turner 264*dd3a739dSZachary Turner // 2, 4, 7 265*dd3a739dSZachary Turner EXPECT_EQ(3, TestState->Callbacks.count()); 266*dd3a739dSZachary Turner for (auto I : enumerate(IndicesToVisit)) 267*dd3a739dSZachary Turner EXPECT_TRUE(ValidateVisitedRecord(I.index(), I.value())); 268*dd3a739dSZachary Turner } 269*dd3a739dSZachary Turner 270*dd3a739dSZachary Turner TEST_F(RandomAccessVisitorTest, AscendingWithinChunk) { 271*dd3a739dSZachary Turner // * Visit multiple items from the same chunk in ascending order, ensuring 272*dd3a739dSZachary Turner // that intermediate items are not visited. In the below example, it's 273*dd3a739dSZachary Turner // 5 -> 6 -> 7 which come from the [4,8) chunk. 274*dd3a739dSZachary Turner TestState->Offsets = createPartialOffsets(GlobalState->Stream, {0, 8}); 275*dd3a739dSZachary Turner 276*dd3a739dSZachary Turner std::vector<uint32_t> IndicesToVisit = {2, 4, 7}; 277*dd3a739dSZachary Turner 278*dd3a739dSZachary Turner RandomAccessTypeVisitor Visitor(GlobalState->TypeArray, 279*dd3a739dSZachary Turner GlobalState->TypeVector.size(), 280*dd3a739dSZachary Turner TestState->Offsets); 281*dd3a739dSZachary Turner 282*dd3a739dSZachary Turner for (uint32_t I : IndicesToVisit) { 283*dd3a739dSZachary Turner TypeIndex TI = TypeIndex::fromArrayIndex(I); 284*dd3a739dSZachary Turner EXPECT_NO_ERROR(Visitor.visitTypeIndex(TI, TestState->Pipeline)); 285*dd3a739dSZachary Turner } 286*dd3a739dSZachary Turner 287*dd3a739dSZachary Turner // [0, 7] 288*dd3a739dSZachary Turner EXPECT_EQ(8, Visitor.database().size()); 289*dd3a739dSZachary Turner for (uint32_t I = 0; I < 8; ++I) 290*dd3a739dSZachary Turner EXPECT_TRUE(ValidateDatabaseRecord(Visitor, I)); 291*dd3a739dSZachary Turner 292*dd3a739dSZachary Turner // 2, 4, 7 293*dd3a739dSZachary Turner EXPECT_EQ(3, TestState->Callbacks.count()); 294*dd3a739dSZachary Turner for (auto &I : enumerate(IndicesToVisit)) 295*dd3a739dSZachary Turner EXPECT_TRUE(ValidateVisitedRecord(I.index(), I.value())); 296*dd3a739dSZachary Turner } 297*dd3a739dSZachary Turner 298*dd3a739dSZachary Turner TEST_F(RandomAccessVisitorTest, StopPrematurelyInChunk) { 299*dd3a739dSZachary Turner // * Don't visit the last item in one chunk, ensuring that visitation stops 300*dd3a739dSZachary Turner // at the record you specify, and the chunk is only partially visited. 301*dd3a739dSZachary Turner // In the below example, this is tested by visiting 0 and 1 but not 2, 302*dd3a739dSZachary Turner // all from the [0,3) chunk. 303*dd3a739dSZachary Turner TestState->Offsets = createPartialOffsets(GlobalState->Stream, {0, 8}); 304*dd3a739dSZachary Turner 305*dd3a739dSZachary Turner std::vector<uint32_t> IndicesToVisit = {0, 1, 2}; 306*dd3a739dSZachary Turner 307*dd3a739dSZachary Turner RandomAccessTypeVisitor Visitor(GlobalState->TypeArray, 308*dd3a739dSZachary Turner GlobalState->TypeVector.size(), 309*dd3a739dSZachary Turner TestState->Offsets); 310*dd3a739dSZachary Turner 311*dd3a739dSZachary Turner for (uint32_t I : IndicesToVisit) { 312*dd3a739dSZachary Turner TypeIndex TI = TypeIndex::fromArrayIndex(I); 313*dd3a739dSZachary Turner EXPECT_NO_ERROR(Visitor.visitTypeIndex(TI, TestState->Pipeline)); 314*dd3a739dSZachary Turner } 315*dd3a739dSZachary Turner 316*dd3a739dSZachary Turner // [0, 8) should be visited. 317*dd3a739dSZachary Turner EXPECT_EQ(8, Visitor.database().size()); 318*dd3a739dSZachary Turner for (uint32_t I = 0; I < 8; ++I) 319*dd3a739dSZachary Turner EXPECT_TRUE(ValidateDatabaseRecord(Visitor, I)); 320*dd3a739dSZachary Turner 321*dd3a739dSZachary Turner // [0, 2] 322*dd3a739dSZachary Turner EXPECT_EQ(3, TestState->Callbacks.count()); 323*dd3a739dSZachary Turner for (auto I : enumerate(IndicesToVisit)) 324*dd3a739dSZachary Turner EXPECT_TRUE(ValidateVisitedRecord(I.index(), I.value())); 325*dd3a739dSZachary Turner } 326*dd3a739dSZachary Turner 327*dd3a739dSZachary Turner TEST_F(RandomAccessVisitorTest, InnerChunk) { 328*dd3a739dSZachary Turner // Test that when a request comes from a chunk in the middle of the partial 329*dd3a739dSZachary Turner // offsets array, that items from surrounding chunks are not visited or 330*dd3a739dSZachary Turner // added to the database. 331*dd3a739dSZachary Turner TestState->Offsets = createPartialOffsets(GlobalState->Stream, {0, 4, 9}); 332*dd3a739dSZachary Turner 333*dd3a739dSZachary Turner std::vector<uint32_t> IndicesToVisit = {5, 7}; 334*dd3a739dSZachary Turner 335*dd3a739dSZachary Turner RandomAccessTypeVisitor Visitor(GlobalState->TypeArray, 336*dd3a739dSZachary Turner GlobalState->TypeVector.size(), 337*dd3a739dSZachary Turner TestState->Offsets); 338*dd3a739dSZachary Turner 339*dd3a739dSZachary Turner for (uint32_t I : IndicesToVisit) { 340*dd3a739dSZachary Turner TypeIndex TI = TypeIndex::fromArrayIndex(I); 341*dd3a739dSZachary Turner EXPECT_NO_ERROR(Visitor.visitTypeIndex(TI, TestState->Pipeline)); 342*dd3a739dSZachary Turner } 343*dd3a739dSZachary Turner 344*dd3a739dSZachary Turner // [4, 9) 345*dd3a739dSZachary Turner EXPECT_EQ(5, Visitor.database().size()); 346*dd3a739dSZachary Turner for (uint32_t I = 4; I < 9; ++I) 347*dd3a739dSZachary Turner EXPECT_TRUE(ValidateDatabaseRecord(Visitor, I)); 348*dd3a739dSZachary Turner 349*dd3a739dSZachary Turner // 5, 7 350*dd3a739dSZachary Turner EXPECT_EQ(2, TestState->Callbacks.count()); 351*dd3a739dSZachary Turner for (auto &I : enumerate(IndicesToVisit)) 352*dd3a739dSZachary Turner EXPECT_TRUE(ValidateVisitedRecord(I.index(), I.value())); 353*dd3a739dSZachary Turner } 354