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