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