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