1dd3a739dSZachary Turner //===- llvm/unittest/DebugInfo/CodeView/RandomAccessVisitorTest.cpp -------===//
2dd3a739dSZachary Turner //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6dd3a739dSZachary Turner //
7dd3a739dSZachary Turner //===----------------------------------------------------------------------===//
8dd3a739dSZachary Turner 
9ca6dbf14SZachary Turner #include "llvm/DebugInfo/CodeView/AppendingTypeTableBuilder.h"
10dd3a739dSZachary Turner #include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
11526f4f2aSZachary Turner #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
12dd3a739dSZachary Turner #include "llvm/DebugInfo/CodeView/TypeRecord.h"
13dd3a739dSZachary Turner #include "llvm/DebugInfo/CodeView/TypeRecordMapping.h"
14dd3a739dSZachary Turner #include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
15dd3a739dSZachary Turner #include "llvm/DebugInfo/PDB/Native/RawTypes.h"
16dd3a739dSZachary Turner #include "llvm/Support/Allocator.h"
17632ba9fcSReid Kleckner #include "llvm/Support/BinaryByteStream.h"
18dd3a739dSZachary Turner #include "llvm/Support/BinaryItemStream.h"
19dd3a739dSZachary Turner #include "llvm/Support/Error.h"
20cb30e705SZachary Turner #include "llvm/Testing/Support/Error.h"
21dd3a739dSZachary Turner 
22dd3a739dSZachary Turner #include "gtest/gtest.h"
23dd3a739dSZachary Turner 
24dd3a739dSZachary Turner using namespace llvm;
25dd3a739dSZachary Turner using namespace llvm::codeview;
26dd3a739dSZachary Turner using namespace llvm::pdb;
27dd3a739dSZachary Turner 
28dd3a739dSZachary Turner namespace llvm {
29dd3a739dSZachary Turner namespace codeview {
operator ==(const ArrayRecord & R1,const ArrayRecord & R2)30dd3a739dSZachary Turner inline bool operator==(const ArrayRecord &R1, const ArrayRecord &R2) {
31dd3a739dSZachary Turner   if (R1.ElementType != R2.ElementType)
32dd3a739dSZachary Turner     return false;
33dd3a739dSZachary Turner   if (R1.IndexType != R2.IndexType)
34dd3a739dSZachary Turner     return false;
35dd3a739dSZachary Turner   if (R1.Name != R2.Name)
36dd3a739dSZachary Turner     return false;
37dd3a739dSZachary Turner   if (R1.Size != R2.Size)
38dd3a739dSZachary Turner     return false;
39dd3a739dSZachary Turner   return true;
40dd3a739dSZachary Turner }
operator !=(const ArrayRecord & R1,const ArrayRecord & R2)41dd3a739dSZachary Turner inline bool operator!=(const ArrayRecord &R1, const ArrayRecord &R2) {
42dd3a739dSZachary Turner   return !(R1 == R2);
43dd3a739dSZachary Turner }
44dd3a739dSZachary Turner 
operator ==(const CVType & R1,const CVType & R2)45dd3a739dSZachary Turner inline bool operator==(const CVType &R1, const CVType &R2) {
46dd3a739dSZachary Turner   if (R1.RecordData != R2.RecordData)
47dd3a739dSZachary Turner     return false;
48dd3a739dSZachary Turner   return true;
49dd3a739dSZachary Turner }
operator !=(const CVType & R1,const CVType & R2)50dd3a739dSZachary Turner inline bool operator!=(const CVType &R1, const CVType &R2) {
51dd3a739dSZachary Turner   return !(R1 == R2);
52dd3a739dSZachary Turner }
53dd3a739dSZachary Turner }
54dd3a739dSZachary Turner }
55dd3a739dSZachary Turner 
56dd3a739dSZachary Turner namespace llvm {
57dd3a739dSZachary Turner template <> struct BinaryItemTraits<CVType> {
lengthllvm::BinaryItemTraits58dd3a739dSZachary Turner   static size_t length(const CVType &Item) { return Item.length(); }
bytesllvm::BinaryItemTraits59dd3a739dSZachary Turner   static ArrayRef<uint8_t> bytes(const CVType &Item) { return Item.data(); }
60dd3a739dSZachary Turner };
61dd3a739dSZachary Turner }
62dd3a739dSZachary Turner 
63dd3a739dSZachary Turner namespace {
64dd3a739dSZachary Turner 
65dd3a739dSZachary Turner class MockCallbacks : public TypeVisitorCallbacks {
66dd3a739dSZachary Turner public:
visitTypeBegin(CVType & CVR,TypeIndex Index)67*31eb8349SLogan Smith   Error visitTypeBegin(CVType &CVR, TypeIndex Index) override {
68dd3a739dSZachary Turner     Indices.push_back(Index);
69dd3a739dSZachary Turner     return Error::success();
70dd3a739dSZachary Turner   }
visitKnownRecord(CVType & CVR,ArrayRecord & AR)71*31eb8349SLogan Smith   Error visitKnownRecord(CVType &CVR, ArrayRecord &AR) override {
72dd3a739dSZachary Turner     VisitedRecords.push_back(AR);
73dd3a739dSZachary Turner     RawRecords.push_back(CVR);
74dd3a739dSZachary Turner     return Error::success();
75dd3a739dSZachary Turner   }
76dd3a739dSZachary Turner 
count() const77dd3a739dSZachary Turner   uint32_t count() const {
78dd3a739dSZachary Turner     assert(Indices.size() == RawRecords.size());
79dd3a739dSZachary Turner     assert(Indices.size() == VisitedRecords.size());
80dd3a739dSZachary Turner     return Indices.size();
81dd3a739dSZachary Turner   }
82dd3a739dSZachary Turner   std::vector<TypeIndex> Indices;
83dd3a739dSZachary Turner   std::vector<CVType> RawRecords;
84dd3a739dSZachary Turner   std::vector<ArrayRecord> VisitedRecords;
85dd3a739dSZachary Turner };
86dd3a739dSZachary Turner 
87dd3a739dSZachary Turner class RandomAccessVisitorTest : public testing::Test {
88dd3a739dSZachary Turner public:
RandomAccessVisitorTest()89dd3a739dSZachary Turner   RandomAccessVisitorTest() {}
90dd3a739dSZachary Turner 
SetUpTestCase()91dd3a739dSZachary Turner   static void SetUpTestCase() {
920eaee545SJonas Devlieghere     GlobalState = std::make_unique<GlobalTestState>();
93dd3a739dSZachary Turner 
94ca6dbf14SZachary Turner     AppendingTypeTableBuilder Builder(GlobalState->Allocator);
95dd3a739dSZachary Turner 
96dd3a739dSZachary Turner     uint32_t Offset = 0;
97dd3a739dSZachary Turner     for (int I = 0; I < 11; ++I) {
98dd3a739dSZachary Turner       ArrayRecord AR(TypeRecordKind::Array);
99dd3a739dSZachary Turner       AR.ElementType = TypeIndex::Int32();
100dd3a739dSZachary Turner       AR.IndexType = TypeIndex::UInt32();
101dd3a739dSZachary Turner       AR.Size = I;
102dd3a739dSZachary Turner       std::string Name;
103dd3a739dSZachary Turner       raw_string_ostream Stream(Name);
104dd3a739dSZachary Turner       Stream << "Array [" << I << "]";
105dd3a739dSZachary Turner       AR.Name = GlobalState->Strings.save(Stream.str());
106dd3a739dSZachary Turner       GlobalState->Records.push_back(AR);
1076900de1dSZachary Turner       GlobalState->Indices.push_back(Builder.writeLeafType(AR));
108dd3a739dSZachary Turner 
109e10d0041SReid Kleckner       CVType Type(Builder.records().back());
110dd3a739dSZachary Turner       GlobalState->TypeVector.push_back(Type);
111dd3a739dSZachary Turner 
112dd3a739dSZachary Turner       GlobalState->AllOffsets.push_back(
113dd3a739dSZachary Turner           {GlobalState->Indices.back(), ulittle32_t(Offset)});
114dd3a739dSZachary Turner       Offset += Type.length();
115dd3a739dSZachary Turner     }
116dd3a739dSZachary Turner 
117dd3a739dSZachary Turner     GlobalState->ItemStream.setItems(GlobalState->TypeVector);
118dd3a739dSZachary Turner     GlobalState->TypeArray = VarStreamArray<CVType>(GlobalState->ItemStream);
119dd3a739dSZachary Turner   }
120dd3a739dSZachary Turner 
TearDownTestCase()121dd3a739dSZachary Turner   static void TearDownTestCase() { GlobalState.reset(); }
122dd3a739dSZachary Turner 
SetUp()123dd3a739dSZachary Turner   void SetUp() override {
1240eaee545SJonas Devlieghere     TestState = std::make_unique<PerTestState>();
125dd3a739dSZachary Turner   }
126dd3a739dSZachary Turner 
TearDown()127dd3a739dSZachary Turner   void TearDown() override { TestState.reset(); }
128dd3a739dSZachary Turner 
129dd3a739dSZachary Turner protected:
ValidateDatabaseRecord(LazyRandomTypeCollection & Types,uint32_t Index)130526f4f2aSZachary Turner   bool ValidateDatabaseRecord(LazyRandomTypeCollection &Types, uint32_t Index) {
131dd3a739dSZachary Turner     TypeIndex TI = TypeIndex::fromArrayIndex(Index);
132526f4f2aSZachary Turner     if (!Types.contains(TI))
133dd3a739dSZachary Turner       return false;
134526f4f2aSZachary Turner     if (GlobalState->TypeVector[Index] != Types.getType(TI))
135dd3a739dSZachary Turner       return false;
136dd3a739dSZachary Turner     return true;
137dd3a739dSZachary Turner   }
138dd3a739dSZachary Turner 
ValidateVisitedRecord(uint32_t VisitationOrder,uint32_t GlobalArrayIndex)139dd3a739dSZachary Turner   bool ValidateVisitedRecord(uint32_t VisitationOrder,
140dd3a739dSZachary Turner                              uint32_t GlobalArrayIndex) {
141dd3a739dSZachary Turner     TypeIndex TI = TypeIndex::fromArrayIndex(GlobalArrayIndex);
142dd3a739dSZachary Turner     if (TI != TestState->Callbacks.Indices[VisitationOrder])
143dd3a739dSZachary Turner       return false;
144dd3a739dSZachary Turner 
145dd3a739dSZachary Turner     if (GlobalState->TypeVector[TI.toArrayIndex()] !=
146dd3a739dSZachary Turner         TestState->Callbacks.RawRecords[VisitationOrder])
147dd3a739dSZachary Turner       return false;
148dd3a739dSZachary Turner 
149dd3a739dSZachary Turner     if (GlobalState->Records[TI.toArrayIndex()] !=
150dd3a739dSZachary Turner         TestState->Callbacks.VisitedRecords[VisitationOrder])
151dd3a739dSZachary Turner       return false;
152dd3a739dSZachary Turner 
153dd3a739dSZachary Turner     return true;
154dd3a739dSZachary Turner   }
155dd3a739dSZachary Turner 
156dd3a739dSZachary Turner   struct GlobalTestState {
GlobalTestState__anon4c709ac60111::RandomAccessVisitorTest::GlobalTestState157dd3a739dSZachary Turner     GlobalTestState() : Strings(Allocator), ItemStream(llvm::support::little) {}
158dd3a739dSZachary Turner 
159dd3a739dSZachary Turner     BumpPtrAllocator Allocator;
160dd3a739dSZachary Turner     StringSaver Strings;
161dd3a739dSZachary Turner 
162dd3a739dSZachary Turner     std::vector<ArrayRecord> Records;
163dd3a739dSZachary Turner     std::vector<TypeIndex> Indices;
164dd3a739dSZachary Turner     std::vector<TypeIndexOffset> AllOffsets;
165dd3a739dSZachary Turner     std::vector<CVType> TypeVector;
166dd3a739dSZachary Turner     BinaryItemStream<CVType> ItemStream;
167dd3a739dSZachary Turner     VarStreamArray<CVType> TypeArray;
168dd3a739dSZachary Turner 
169dd3a739dSZachary Turner     MutableBinaryByteStream Stream;
170dd3a739dSZachary Turner   };
171dd3a739dSZachary Turner 
172dd3a739dSZachary Turner   struct PerTestState {
173dd3a739dSZachary Turner     FixedStreamArray<TypeIndexOffset> Offsets;
174dd3a739dSZachary Turner 
175dd3a739dSZachary Turner     MockCallbacks Callbacks;
176dd3a739dSZachary Turner   };
177dd3a739dSZachary Turner 
178dd3a739dSZachary Turner   FixedStreamArray<TypeIndexOffset>
createPartialOffsets(MutableBinaryByteStream & Storage,std::initializer_list<uint32_t> Indices)179dd3a739dSZachary Turner   createPartialOffsets(MutableBinaryByteStream &Storage,
180dd3a739dSZachary Turner                        std::initializer_list<uint32_t> Indices) {
181dd3a739dSZachary Turner 
182dd3a739dSZachary Turner     uint32_t Count = Indices.size();
183dd3a739dSZachary Turner     uint32_t Size = Count * sizeof(TypeIndexOffset);
184dd3a739dSZachary Turner     uint8_t *Buffer = GlobalState->Allocator.Allocate<uint8_t>(Size);
185dd3a739dSZachary Turner     MutableArrayRef<uint8_t> Bytes(Buffer, Size);
186dd3a739dSZachary Turner     Storage = MutableBinaryByteStream(Bytes, support::little);
187dd3a739dSZachary Turner     BinaryStreamWriter Writer(Storage);
188dd3a739dSZachary Turner     for (const auto I : Indices)
189dd3a739dSZachary Turner       consumeError(Writer.writeObject(GlobalState->AllOffsets[I]));
190dd3a739dSZachary Turner 
191dd3a739dSZachary Turner     BinaryStreamReader Reader(Storage);
192dd3a739dSZachary Turner     FixedStreamArray<TypeIndexOffset> Result;
193dd3a739dSZachary Turner     consumeError(Reader.readArray(Result, Count));
194dd3a739dSZachary Turner     return Result;
195dd3a739dSZachary Turner   }
196dd3a739dSZachary Turner 
197dd3a739dSZachary Turner   static std::unique_ptr<GlobalTestState> GlobalState;
198dd3a739dSZachary Turner   std::unique_ptr<PerTestState> TestState;
199dd3a739dSZachary Turner };
200dd3a739dSZachary Turner 
201dd3a739dSZachary Turner std::unique_ptr<RandomAccessVisitorTest::GlobalTestState>
202dd3a739dSZachary Turner     RandomAccessVisitorTest::GlobalState;
203dd3a739dSZachary Turner }
204dd3a739dSZachary Turner 
TEST_F(RandomAccessVisitorTest,MultipleVisits)205dd3a739dSZachary Turner TEST_F(RandomAccessVisitorTest, MultipleVisits) {
206dd3a739dSZachary Turner   TestState->Offsets = createPartialOffsets(GlobalState->Stream, {0, 8});
207526f4f2aSZachary Turner   LazyRandomTypeCollection Types(GlobalState->TypeArray,
208dd3a739dSZachary Turner                                  GlobalState->TypeVector.size(),
209dd3a739dSZachary Turner                                  TestState->Offsets);
210dd3a739dSZachary Turner 
211dd3a739dSZachary Turner   std::vector<uint32_t> IndicesToVisit = {5, 5, 5};
212dd3a739dSZachary Turner 
213dd3a739dSZachary Turner   for (uint32_t I : IndicesToVisit) {
214dd3a739dSZachary Turner     TypeIndex TI = TypeIndex::fromArrayIndex(I);
215526f4f2aSZachary Turner     CVType T = Types.getType(TI);
216cb30e705SZachary Turner     EXPECT_THAT_ERROR(codeview::visitTypeRecord(T, TI, TestState->Callbacks),
217cb30e705SZachary Turner                       Succeeded());
218dd3a739dSZachary Turner   }
219dd3a739dSZachary Turner 
220dd3a739dSZachary Turner   // [0,8) should be present
221526f4f2aSZachary Turner   EXPECT_EQ(8u, Types.size());
222dd3a739dSZachary Turner   for (uint32_t I = 0; I < 8; ++I)
223526f4f2aSZachary Turner     EXPECT_TRUE(ValidateDatabaseRecord(Types, I));
224dd3a739dSZachary Turner 
225dd3a739dSZachary Turner   // 5, 5, 5
226bce6d327SJustin Bogner   EXPECT_EQ(3u, TestState->Callbacks.count());
227dd3a739dSZachary Turner   for (auto I : enumerate(IndicesToVisit))
228dd3a739dSZachary Turner     EXPECT_TRUE(ValidateVisitedRecord(I.index(), I.value()));
229dd3a739dSZachary Turner }
230dd3a739dSZachary Turner 
TEST_F(RandomAccessVisitorTest,DescendingWithinChunk)231dd3a739dSZachary Turner TEST_F(RandomAccessVisitorTest, DescendingWithinChunk) {
232dd3a739dSZachary Turner   // Visit multiple items from the same "chunk" in reverse order.  In this
233dd3a739dSZachary Turner   // example, it's 7 then 4 then 2.  At the end, all records from 0 to 7 should
234dd3a739dSZachary Turner   // be known by the database, but only 2, 4, and 7 should have been visited.
235dd3a739dSZachary Turner   TestState->Offsets = createPartialOffsets(GlobalState->Stream, {0, 8});
236dd3a739dSZachary Turner 
237dd3a739dSZachary Turner   std::vector<uint32_t> IndicesToVisit = {7, 4, 2};
238dd3a739dSZachary Turner 
239526f4f2aSZachary Turner   LazyRandomTypeCollection Types(GlobalState->TypeArray,
240dd3a739dSZachary Turner                                  GlobalState->TypeVector.size(),
241dd3a739dSZachary Turner                                  TestState->Offsets);
242dd3a739dSZachary Turner   for (uint32_t I : IndicesToVisit) {
243dd3a739dSZachary Turner     TypeIndex TI = TypeIndex::fromArrayIndex(I);
244526f4f2aSZachary Turner     CVType T = Types.getType(TI);
245cb30e705SZachary Turner     EXPECT_THAT_ERROR(codeview::visitTypeRecord(T, TI, TestState->Callbacks),
246cb30e705SZachary Turner                       Succeeded());
247dd3a739dSZachary Turner   }
248dd3a739dSZachary Turner 
249dd3a739dSZachary Turner   // [0, 7]
250526f4f2aSZachary Turner   EXPECT_EQ(8u, Types.size());
251dd3a739dSZachary Turner   for (uint32_t I = 0; I < 8; ++I)
252526f4f2aSZachary Turner     EXPECT_TRUE(ValidateDatabaseRecord(Types, I));
253dd3a739dSZachary Turner 
254dd3a739dSZachary Turner   // 2, 4, 7
255bce6d327SJustin Bogner   EXPECT_EQ(3u, TestState->Callbacks.count());
256dd3a739dSZachary Turner   for (auto I : enumerate(IndicesToVisit))
257dd3a739dSZachary Turner     EXPECT_TRUE(ValidateVisitedRecord(I.index(), I.value()));
258dd3a739dSZachary Turner }
259dd3a739dSZachary Turner 
TEST_F(RandomAccessVisitorTest,AscendingWithinChunk)260dd3a739dSZachary Turner TEST_F(RandomAccessVisitorTest, AscendingWithinChunk) {
261dd3a739dSZachary Turner   // * Visit multiple items from the same chunk in ascending order, ensuring
262dd3a739dSZachary Turner   //   that intermediate items are not visited.  In the below example, it's
263dd3a739dSZachary Turner   //   5 -> 6 -> 7 which come from the [4,8) chunk.
264dd3a739dSZachary Turner   TestState->Offsets = createPartialOffsets(GlobalState->Stream, {0, 8});
265dd3a739dSZachary Turner 
266dd3a739dSZachary Turner   std::vector<uint32_t> IndicesToVisit = {2, 4, 7};
267dd3a739dSZachary Turner 
268526f4f2aSZachary Turner   LazyRandomTypeCollection Types(GlobalState->TypeArray,
269dd3a739dSZachary Turner                                  GlobalState->TypeVector.size(),
270dd3a739dSZachary Turner                                  TestState->Offsets);
271dd3a739dSZachary Turner   for (uint32_t I : IndicesToVisit) {
272dd3a739dSZachary Turner     TypeIndex TI = TypeIndex::fromArrayIndex(I);
273526f4f2aSZachary Turner     CVType T = Types.getType(TI);
274cb30e705SZachary Turner     EXPECT_THAT_ERROR(codeview::visitTypeRecord(T, TI, TestState->Callbacks),
275cb30e705SZachary Turner                       Succeeded());
276dd3a739dSZachary Turner   }
277dd3a739dSZachary Turner 
278dd3a739dSZachary Turner   // [0, 7]
279526f4f2aSZachary Turner   EXPECT_EQ(8u, Types.size());
280dd3a739dSZachary Turner   for (uint32_t I = 0; I < 8; ++I)
281526f4f2aSZachary Turner     EXPECT_TRUE(ValidateDatabaseRecord(Types, I));
282dd3a739dSZachary Turner 
283dd3a739dSZachary Turner   // 2, 4, 7
284bce6d327SJustin Bogner   EXPECT_EQ(3u, TestState->Callbacks.count());
285dd3a739dSZachary Turner   for (auto &I : enumerate(IndicesToVisit))
286dd3a739dSZachary Turner     EXPECT_TRUE(ValidateVisitedRecord(I.index(), I.value()));
287dd3a739dSZachary Turner }
288dd3a739dSZachary Turner 
TEST_F(RandomAccessVisitorTest,StopPrematurelyInChunk)289dd3a739dSZachary Turner TEST_F(RandomAccessVisitorTest, StopPrematurelyInChunk) {
290dd3a739dSZachary Turner   // * Don't visit the last item in one chunk, ensuring that visitation stops
291dd3a739dSZachary Turner   //   at the record you specify, and the chunk is only partially visited.
292dd3a739dSZachary Turner   //   In the below example, this is tested by visiting 0 and 1 but not 2,
293dd3a739dSZachary Turner   //   all from the [0,3) chunk.
294dd3a739dSZachary Turner   TestState->Offsets = createPartialOffsets(GlobalState->Stream, {0, 8});
295dd3a739dSZachary Turner 
296dd3a739dSZachary Turner   std::vector<uint32_t> IndicesToVisit = {0, 1, 2};
297dd3a739dSZachary Turner 
298526f4f2aSZachary Turner   LazyRandomTypeCollection Types(GlobalState->TypeArray,
299dd3a739dSZachary Turner                                  GlobalState->TypeVector.size(),
300dd3a739dSZachary Turner                                  TestState->Offsets);
301dd3a739dSZachary Turner 
302dd3a739dSZachary Turner   for (uint32_t I : IndicesToVisit) {
303dd3a739dSZachary Turner     TypeIndex TI = TypeIndex::fromArrayIndex(I);
304526f4f2aSZachary Turner     CVType T = Types.getType(TI);
305cb30e705SZachary Turner     EXPECT_THAT_ERROR(codeview::visitTypeRecord(T, TI, TestState->Callbacks),
306cb30e705SZachary Turner                       Succeeded());
307dd3a739dSZachary Turner   }
308dd3a739dSZachary Turner 
309dd3a739dSZachary Turner   // [0, 8) should be visited.
310526f4f2aSZachary Turner   EXPECT_EQ(8u, Types.size());
311dd3a739dSZachary Turner   for (uint32_t I = 0; I < 8; ++I)
312526f4f2aSZachary Turner     EXPECT_TRUE(ValidateDatabaseRecord(Types, I));
313dd3a739dSZachary Turner 
314dd3a739dSZachary Turner   // [0, 2]
315bce6d327SJustin Bogner   EXPECT_EQ(3u, TestState->Callbacks.count());
316dd3a739dSZachary Turner   for (auto I : enumerate(IndicesToVisit))
317dd3a739dSZachary Turner     EXPECT_TRUE(ValidateVisitedRecord(I.index(), I.value()));
318dd3a739dSZachary Turner }
319dd3a739dSZachary Turner 
TEST_F(RandomAccessVisitorTest,InnerChunk)320dd3a739dSZachary Turner TEST_F(RandomAccessVisitorTest, InnerChunk) {
321dd3a739dSZachary Turner   // Test that when a request comes from a chunk in the middle of the partial
322dd3a739dSZachary Turner   // offsets array, that items from surrounding chunks are not visited or
323dd3a739dSZachary Turner   // added to the database.
324dd3a739dSZachary Turner   TestState->Offsets = createPartialOffsets(GlobalState->Stream, {0, 4, 9});
325dd3a739dSZachary Turner 
326dd3a739dSZachary Turner   std::vector<uint32_t> IndicesToVisit = {5, 7};
327dd3a739dSZachary Turner 
328526f4f2aSZachary Turner   LazyRandomTypeCollection Types(GlobalState->TypeArray,
329dd3a739dSZachary Turner                                  GlobalState->TypeVector.size(),
330dd3a739dSZachary Turner                                  TestState->Offsets);
331dd3a739dSZachary Turner 
332dd3a739dSZachary Turner   for (uint32_t I : IndicesToVisit) {
333dd3a739dSZachary Turner     TypeIndex TI = TypeIndex::fromArrayIndex(I);
334526f4f2aSZachary Turner     CVType T = Types.getType(TI);
335cb30e705SZachary Turner     EXPECT_THAT_ERROR(codeview::visitTypeRecord(T, TI, TestState->Callbacks),
336cb30e705SZachary Turner                       Succeeded());
337dd3a739dSZachary Turner   }
338dd3a739dSZachary Turner 
339dd3a739dSZachary Turner   // [4, 9)
340526f4f2aSZachary Turner   EXPECT_EQ(5u, Types.size());
341dd3a739dSZachary Turner   for (uint32_t I = 4; I < 9; ++I)
342526f4f2aSZachary Turner     EXPECT_TRUE(ValidateDatabaseRecord(Types, I));
343dd3a739dSZachary Turner 
344dd3a739dSZachary Turner   // 5, 7
345bce6d327SJustin Bogner   EXPECT_EQ(2u, TestState->Callbacks.count());
346dd3a739dSZachary Turner   for (auto &I : enumerate(IndicesToVisit))
347dd3a739dSZachary Turner     EXPECT_TRUE(ValidateVisitedRecord(I.index(), I.value()));
348dd3a739dSZachary Turner }
349ad859bd4SZachary Turner 
TEST_F(RandomAccessVisitorTest,CrossChunkName)350ad859bd4SZachary Turner TEST_F(RandomAccessVisitorTest, CrossChunkName) {
351ca6dbf14SZachary Turner   AppendingTypeTableBuilder Builder(GlobalState->Allocator);
352ad859bd4SZachary Turner 
353ad859bd4SZachary Turner   // TypeIndex 0
354ad859bd4SZachary Turner   ClassRecord Class(TypeRecordKind::Class);
355ad859bd4SZachary Turner   Class.Name = "FooClass";
356ad859bd4SZachary Turner   Class.Options = ClassOptions::None;
357ad859bd4SZachary Turner   Class.MemberCount = 0;
358a56e4ee3SZachary Turner   Class.Size = 4U;
359ad859bd4SZachary Turner   Class.DerivationList = TypeIndex::fromArrayIndex(0);
360ad859bd4SZachary Turner   Class.FieldList = TypeIndex::fromArrayIndex(0);
361ad859bd4SZachary Turner   Class.VTableShape = TypeIndex::fromArrayIndex(0);
3626900de1dSZachary Turner   TypeIndex IndexZero = Builder.writeLeafType(Class);
363ad859bd4SZachary Turner 
364ad859bd4SZachary Turner   // TypeIndex 1 refers to type index 0.
365ad859bd4SZachary Turner   ModifierRecord Modifier(TypeRecordKind::Modifier);
366ad859bd4SZachary Turner   Modifier.ModifiedType = TypeIndex::fromArrayIndex(0);
367ad859bd4SZachary Turner   Modifier.Modifiers = ModifierOptions::Const;
3686900de1dSZachary Turner   TypeIndex IndexOne = Builder.writeLeafType(Modifier);
369ad859bd4SZachary Turner 
370ad859bd4SZachary Turner   // set up a type stream that refers to the above two serialized records.
371e10d0041SReid Kleckner   std::vector<CVType> TypeArray = {
372e10d0041SReid Kleckner       {Builder.records()[0]},
373e10d0041SReid Kleckner       {Builder.records()[1]},
374e10d0041SReid Kleckner   };
375ad859bd4SZachary Turner   BinaryItemStream<CVType> ItemStream(llvm::support::little);
376ad859bd4SZachary Turner   ItemStream.setItems(TypeArray);
377ad859bd4SZachary Turner   VarStreamArray<CVType> TypeStream(ItemStream);
378ad859bd4SZachary Turner 
379ad859bd4SZachary Turner   // Figure out the byte offset of the second item.
380ad859bd4SZachary Turner   auto ItemOneIter = TypeStream.begin();
381ad859bd4SZachary Turner   ++ItemOneIter;
382ad859bd4SZachary Turner 
383ad859bd4SZachary Turner   // Set up a partial offsets buffer that contains the first and second items
384ad859bd4SZachary Turner   // in separate chunks.
385ad859bd4SZachary Turner   std::vector<TypeIndexOffset> TIO;
386ad859bd4SZachary Turner   TIO.push_back({IndexZero, ulittle32_t(0u)});
387ad859bd4SZachary Turner   TIO.push_back({IndexOne, ulittle32_t(ItemOneIter.offset())});
388ad859bd4SZachary Turner   ArrayRef<uint8_t> Buffer(reinterpret_cast<const uint8_t *>(TIO.data()),
389ad859bd4SZachary Turner                            TIO.size() * sizeof(TypeIndexOffset));
390ad859bd4SZachary Turner 
391ad859bd4SZachary Turner   BinaryStreamReader Reader(Buffer, llvm::support::little);
392ad859bd4SZachary Turner   FixedStreamArray<TypeIndexOffset> PartialOffsets;
393ad859bd4SZachary Turner   ASSERT_THAT_ERROR(Reader.readArray(PartialOffsets, 2), Succeeded());
394ad859bd4SZachary Turner 
395ad859bd4SZachary Turner   LazyRandomTypeCollection Types(TypeStream, 2, PartialOffsets);
396ad859bd4SZachary Turner 
397ad859bd4SZachary Turner   StringRef Name = Types.getTypeName(IndexOne);
398ad859bd4SZachary Turner   EXPECT_EQ("const FooClass", Name);
399ad859bd4SZachary Turner }
400