xref: /aosp_15_r20/external/zucchini/disassembler_ztf_unittest.cc (revision a03ca8b91e029cd15055c20c78c2e087c84792e4)
1*a03ca8b9SKrzysztof Kosiński // Copyright 2018 The Chromium Authors. All rights reserved.
2*a03ca8b9SKrzysztof Kosiński // Use of this source code is governed by a BSD-style license that can be
3*a03ca8b9SKrzysztof Kosiński // found in the LICENSE file.
4*a03ca8b9SKrzysztof Kosiński 
5*a03ca8b9SKrzysztof Kosiński #include "components/zucchini/disassembler_ztf.h"
6*a03ca8b9SKrzysztof Kosiński 
7*a03ca8b9SKrzysztof Kosiński #include <stddef.h>
8*a03ca8b9SKrzysztof Kosiński #include <stdint.h>
9*a03ca8b9SKrzysztof Kosiński 
10*a03ca8b9SKrzysztof Kosiński #include <algorithm>
11*a03ca8b9SKrzysztof Kosiński #include <map>
12*a03ca8b9SKrzysztof Kosiński #include <set>
13*a03ca8b9SKrzysztof Kosiński #include <utility>
14*a03ca8b9SKrzysztof Kosiński #include <vector>
15*a03ca8b9SKrzysztof Kosiński 
16*a03ca8b9SKrzysztof Kosiński #include "base/cxx17_backports.h"
17*a03ca8b9SKrzysztof Kosiński #include "base/strings/string_piece.h"
18*a03ca8b9SKrzysztof Kosiński #include "components/zucchini/buffer_view.h"
19*a03ca8b9SKrzysztof Kosiński #include "components/zucchini/element_detection.h"
20*a03ca8b9SKrzysztof Kosiński #include "testing/gtest/include/gtest/gtest.h"
21*a03ca8b9SKrzysztof Kosiński 
22*a03ca8b9SKrzysztof Kosiński namespace zucchini {
23*a03ca8b9SKrzysztof Kosiński 
24*a03ca8b9SKrzysztof Kosiński namespace {
25*a03ca8b9SKrzysztof Kosiński 
26*a03ca8b9SKrzysztof Kosiński constexpr char kNormalText[] = R"(ZTxt
27*a03ca8b9SKrzysztof Kosiński Hello World!
28*a03ca8b9SKrzysztof Kosiński This is an example of an absolute reference <<1,1>>
29*a03ca8b9SKrzysztof Kosiński And {-01,+05} is an example of a relative ref
30*a03ca8b9SKrzysztof Kosiński txTZ
31*a03ca8b9SKrzysztof Kosiński TRAILING DATA)";
32*a03ca8b9SKrzysztof Kosiński // -1 to exclude null byte.
33*a03ca8b9SKrzysztof Kosiński constexpr size_t kNormalTextExtraBytes = base::size("TRAILING DATA") - 1;
34*a03ca8b9SKrzysztof Kosiński 
35*a03ca8b9SKrzysztof Kosiński constexpr char kOutOfBoundsText[] = R"(ZTxt<1,1>
36*a03ca8b9SKrzysztof Kosiński Hello World!
37*a03ca8b9SKrzysztof Kosiński This is an example of an OOB absolute reference <890,605>
38*a03ca8b9SKrzysztof Kosiński And {-050,+100} is an example of an OOB relative ref.
39*a03ca8b9SKrzysztof Kosiński but [+00,+10] is valid at least. As is (1,5).
40*a03ca8b9SKrzysztof Kosiński <1, 6> and { ,1} aren't nor is {4,5]
41*a03ca8b9SKrzysztof Kosiński {7,6}<1,1><2,3>{+00,+00}{004,100}[+00,+60][+000,-100]<-000,-035>(-00,-00)txTZ
42*a03ca8b9SKrzysztof Kosiński )";
43*a03ca8b9SKrzysztof Kosiński 
44*a03ca8b9SKrzysztof Kosiński // Converts a raw string into data.
StrToData(base::StringPiece s)45*a03ca8b9SKrzysztof Kosiński std::vector<uint8_t> StrToData(base::StringPiece s) {
46*a03ca8b9SKrzysztof Kosiński   return std::vector<uint8_t>(s.begin(), s.end());
47*a03ca8b9SKrzysztof Kosiński }
48*a03ca8b9SKrzysztof Kosiński 
49*a03ca8b9SKrzysztof Kosiński // Compare if |a.location < b.location| as references have unique locations.
50*a03ca8b9SKrzysztof Kosiński struct ReferenceCompare {
operator ()zucchini::__anon5f0ed1f70111::ReferenceCompare51*a03ca8b9SKrzysztof Kosiński   bool operator()(const Reference& a, const Reference& b) const {
52*a03ca8b9SKrzysztof Kosiński     return a.location < b.location;
53*a03ca8b9SKrzysztof Kosiński   }
54*a03ca8b9SKrzysztof Kosiński };
55*a03ca8b9SKrzysztof Kosiński 
56*a03ca8b9SKrzysztof Kosiński using ReferenceKey =
57*a03ca8b9SKrzysztof Kosiński     std::pair<DisassemblerZtf::ReferencePool, DisassemblerZtf::ReferenceType>;
58*a03ca8b9SKrzysztof Kosiński using ReferenceSets =
59*a03ca8b9SKrzysztof Kosiński     std::map<ReferenceKey, std::set<Reference, ReferenceCompare>>;
60*a03ca8b9SKrzysztof Kosiński 
61*a03ca8b9SKrzysztof Kosiński // Write references in |refs_to_write| to |image|. Also validate the
62*a03ca8b9SKrzysztof Kosiński // disassembler parses |image| such that it is of |expected_size|.
WriteReferences(MutableBufferView image,size_t expected_size,const ReferenceSets & refs_to_write)63*a03ca8b9SKrzysztof Kosiński void WriteReferences(MutableBufferView image,
64*a03ca8b9SKrzysztof Kosiński                      size_t expected_size,
65*a03ca8b9SKrzysztof Kosiński                      const ReferenceSets& refs_to_write) {
66*a03ca8b9SKrzysztof Kosiński   EXPECT_TRUE(DisassemblerZtf::QuickDetect(image));
67*a03ca8b9SKrzysztof Kosiński   std::unique_ptr<DisassemblerZtf> dis =
68*a03ca8b9SKrzysztof Kosiński       Disassembler::Make<DisassemblerZtf>(image);
69*a03ca8b9SKrzysztof Kosiński   EXPECT_TRUE(dis);
70*a03ca8b9SKrzysztof Kosiński   EXPECT_EQ(expected_size, dis->size());
71*a03ca8b9SKrzysztof Kosiński   image.shrink(dis->size());
72*a03ca8b9SKrzysztof Kosiński   auto reference_groups = dis->MakeReferenceGroups();
73*a03ca8b9SKrzysztof Kosiński   for (const auto& group : reference_groups) {
74*a03ca8b9SKrzysztof Kosiński     auto writer = group.GetWriter(image, dis.get());
75*a03ca8b9SKrzysztof Kosiński     ReferenceKey key = {
76*a03ca8b9SKrzysztof Kosiński         static_cast<DisassemblerZtf::ReferencePool>(group.pool_tag().value()),
77*a03ca8b9SKrzysztof Kosiński         static_cast<DisassemblerZtf::ReferenceType>(group.type_tag().value())};
78*a03ca8b9SKrzysztof Kosiński     if (!refs_to_write.count(key))
79*a03ca8b9SKrzysztof Kosiński       continue;
80*a03ca8b9SKrzysztof Kosiński     for (const auto& ref : refs_to_write.at(key))
81*a03ca8b9SKrzysztof Kosiński       writer->PutNext(ref);
82*a03ca8b9SKrzysztof Kosiński   }
83*a03ca8b9SKrzysztof Kosiński }
84*a03ca8b9SKrzysztof Kosiński 
85*a03ca8b9SKrzysztof Kosiński // Read references in |refs_to_read| from |image|.  Once found
86*a03ca8b9SKrzysztof Kosiński // the elements are removed from |refs_to_read|. Also validate the
87*a03ca8b9SKrzysztof Kosiński // disassembler parses |image| such that it is of |expected_size|.
ReadReferences(ConstBufferView image,size_t expected_size,ReferenceSets * refs_to_read)88*a03ca8b9SKrzysztof Kosiński void ReadReferences(ConstBufferView image,
89*a03ca8b9SKrzysztof Kosiński                     size_t expected_size,
90*a03ca8b9SKrzysztof Kosiński                     ReferenceSets* refs_to_read) {
91*a03ca8b9SKrzysztof Kosiński   EXPECT_TRUE(DisassemblerZtf::QuickDetect(image));
92*a03ca8b9SKrzysztof Kosiński   std::unique_ptr<DisassemblerZtf> dis =
93*a03ca8b9SKrzysztof Kosiński       Disassembler::Make<DisassemblerZtf>(image);
94*a03ca8b9SKrzysztof Kosiński   EXPECT_TRUE(dis);
95*a03ca8b9SKrzysztof Kosiński   EXPECT_EQ(expected_size, dis->size());
96*a03ca8b9SKrzysztof Kosiński   auto reference_groups = dis->MakeReferenceGroups();
97*a03ca8b9SKrzysztof Kosiński   for (const auto& group : reference_groups) {
98*a03ca8b9SKrzysztof Kosiński     auto reader = group.GetReader(dis.get());
99*a03ca8b9SKrzysztof Kosiński     ReferenceKey key = {
100*a03ca8b9SKrzysztof Kosiński         static_cast<DisassemblerZtf::ReferencePool>(group.pool_tag().value()),
101*a03ca8b9SKrzysztof Kosiński         static_cast<DisassemblerZtf::ReferenceType>(group.type_tag().value())};
102*a03ca8b9SKrzysztof Kosiński     if (!refs_to_read->count(key)) {
103*a03ca8b9SKrzysztof Kosiński       // No elements of this pool/type pair are expected so assert that none are
104*a03ca8b9SKrzysztof Kosiński       // found.
105*a03ca8b9SKrzysztof Kosiński       auto ref = reader->GetNext();
106*a03ca8b9SKrzysztof Kosiński       EXPECT_FALSE(ref.has_value());
107*a03ca8b9SKrzysztof Kosiński       continue;
108*a03ca8b9SKrzysztof Kosiński     }
109*a03ca8b9SKrzysztof Kosiński     // For each reference remove it from the set if it exists, error if
110*a03ca8b9SKrzysztof Kosiński     // unexpected references are found.
111*a03ca8b9SKrzysztof Kosiński     for (auto ref = reader->GetNext(); ref.has_value();
112*a03ca8b9SKrzysztof Kosiński          ref = reader->GetNext()) {
113*a03ca8b9SKrzysztof Kosiński       EXPECT_EQ(1UL, refs_to_read->at(key).erase(ref.value()));
114*a03ca8b9SKrzysztof Kosiński     }
115*a03ca8b9SKrzysztof Kosiński     EXPECT_EQ(0U, refs_to_read->at(key).size());
116*a03ca8b9SKrzysztof Kosiński   }
117*a03ca8b9SKrzysztof Kosiński }
118*a03ca8b9SKrzysztof Kosiński 
TestTranslation(const ZtfTranslator & translator,offset_t expected_location,ztf::LineCol lc)119*a03ca8b9SKrzysztof Kosiński void TestTranslation(const ZtfTranslator& translator,
120*a03ca8b9SKrzysztof Kosiński                      offset_t expected_location,
121*a03ca8b9SKrzysztof Kosiński                      ztf::LineCol lc) {
122*a03ca8b9SKrzysztof Kosiński   // Check the lc is translated to the expected location.
123*a03ca8b9SKrzysztof Kosiński   EXPECT_EQ(expected_location, translator.LineColToOffset(lc));
124*a03ca8b9SKrzysztof Kosiński   auto new_lc = translator.OffsetToLineCol(expected_location);
125*a03ca8b9SKrzysztof Kosiński   if (expected_location == kInvalidOffset) {
126*a03ca8b9SKrzysztof Kosiński     EXPECT_FALSE(translator.IsValid(lc));
127*a03ca8b9SKrzysztof Kosiński     EXPECT_FALSE(new_lc.has_value());
128*a03ca8b9SKrzysztof Kosiński   } else {
129*a03ca8b9SKrzysztof Kosiński     EXPECT_TRUE(translator.IsValid(lc));
130*a03ca8b9SKrzysztof Kosiński     // Check that the reverse is true. |ztf::LineCol{0, 0}| is a sentinel and
131*a03ca8b9SKrzysztof Kosiński     // should never be valid.
132*a03ca8b9SKrzysztof Kosiński     EXPECT_EQ(lc.line, new_lc->line);
133*a03ca8b9SKrzysztof Kosiński     EXPECT_EQ(lc.col, new_lc->col);
134*a03ca8b9SKrzysztof Kosiński   }
135*a03ca8b9SKrzysztof Kosiński }
136*a03ca8b9SKrzysztof Kosiński 
137*a03ca8b9SKrzysztof Kosiński template <typename T>
CountDistinct(const std::vector<T> & v)138*a03ca8b9SKrzysztof Kosiński size_t CountDistinct(const std::vector<T>& v) {
139*a03ca8b9SKrzysztof Kosiński   return std::set<T>(v.begin(), v.end()).size();
140*a03ca8b9SKrzysztof Kosiński }
141*a03ca8b9SKrzysztof Kosiński 
142*a03ca8b9SKrzysztof Kosiński }  // namespace
143*a03ca8b9SKrzysztof Kosiński 
TEST(ZtfTranslatorTest,Translate)144*a03ca8b9SKrzysztof Kosiński TEST(ZtfTranslatorTest, Translate) {
145*a03ca8b9SKrzysztof Kosiński   ztf::dim_t kMaxVal = INT16_MAX;
146*a03ca8b9SKrzysztof Kosiński   ztf::dim_t kMinVal = INT16_MIN;
147*a03ca8b9SKrzysztof Kosiński 
148*a03ca8b9SKrzysztof Kosiński   const std::vector<uint8_t> text(StrToData(kOutOfBoundsText));
149*a03ca8b9SKrzysztof Kosiński   ConstBufferView image(text.data(), text.size());
150*a03ca8b9SKrzysztof Kosiński   ZtfTranslator translator;
151*a03ca8b9SKrzysztof Kosiński   EXPECT_TRUE(translator.Init(image));
152*a03ca8b9SKrzysztof Kosiński 
153*a03ca8b9SKrzysztof Kosiński   // Absolute Translations:
154*a03ca8b9SKrzysztof Kosiński 
155*a03ca8b9SKrzysztof Kosiński   // Check a bunch of invalid locations.
156*a03ca8b9SKrzysztof Kosiński   TestTranslation(translator, kInvalidOffset, ztf::LineCol{50, 60});
157*a03ca8b9SKrzysztof Kosiński   TestTranslation(translator, kInvalidOffset, ztf::LineCol{0, 0});
158*a03ca8b9SKrzysztof Kosiński   TestTranslation(translator, kInvalidOffset, ztf::LineCol{1, 0});
159*a03ca8b9SKrzysztof Kosiński   TestTranslation(translator, kInvalidOffset, ztf::LineCol{0, 1});
160*a03ca8b9SKrzysztof Kosiński   TestTranslation(translator, kInvalidOffset, ztf::LineCol{0, 1});
161*a03ca8b9SKrzysztof Kosiński   TestTranslation(translator, kInvalidOffset, ztf::LineCol{1, -1});
162*a03ca8b9SKrzysztof Kosiński   TestTranslation(translator, kInvalidOffset, ztf::LineCol{-1, 1});
163*a03ca8b9SKrzysztof Kosiński   TestTranslation(translator, kInvalidOffset, ztf::LineCol{-1, -1});
164*a03ca8b9SKrzysztof Kosiński   TestTranslation(translator, kInvalidOffset, ztf::LineCol{1, kMaxVal});
165*a03ca8b9SKrzysztof Kosiński   TestTranslation(translator, kInvalidOffset, ztf::LineCol{kMaxVal, 1});
166*a03ca8b9SKrzysztof Kosiński   TestTranslation(translator, kInvalidOffset, ztf::LineCol{1, kMinVal});
167*a03ca8b9SKrzysztof Kosiński   TestTranslation(translator, kInvalidOffset, ztf::LineCol{kMinVal, 1});
168*a03ca8b9SKrzysztof Kosiński 
169*a03ca8b9SKrzysztof Kosiński   // Check the start of the file.
170*a03ca8b9SKrzysztof Kosiński   TestTranslation(translator, 0, ztf::LineCol{1, 1});
171*a03ca8b9SKrzysztof Kosiński   TestTranslation(translator, 1, ztf::LineCol{1, 2});
172*a03ca8b9SKrzysztof Kosiński 
173*a03ca8b9SKrzysztof Kosiński   // Check the boundary around a newline.
174*a03ca8b9SKrzysztof Kosiński   TestTranslation(translator, 9, ztf::LineCol{1, 10});
175*a03ca8b9SKrzysztof Kosiński   TestTranslation(translator, kInvalidOffset, ztf::LineCol{1, 11});
176*a03ca8b9SKrzysztof Kosiński   TestTranslation(translator, 10, ztf::LineCol{2, 1});
177*a03ca8b9SKrzysztof Kosiński   TestTranslation(translator, kInvalidOffset, ztf::LineCol{2, 0});
178*a03ca8b9SKrzysztof Kosiński 
179*a03ca8b9SKrzysztof Kosiński   // Check the end of the file.
180*a03ca8b9SKrzysztof Kosiński   TestTranslation(translator, kInvalidOffset, ztf::LineCol{8, 1});
181*a03ca8b9SKrzysztof Kosiński   TestTranslation(translator, kInvalidOffset, ztf::LineCol{7, 79});
182*a03ca8b9SKrzysztof Kosiński   // Need to subtract to account for the newline.
183*a03ca8b9SKrzysztof Kosiński   TestTranslation(translator, text.size() - 1, ztf::LineCol{7, 78});
184*a03ca8b9SKrzysztof Kosiński   TestTranslation(translator, text.size() - 2, ztf::LineCol{7, 77});
185*a03ca8b9SKrzysztof Kosiński 
186*a03ca8b9SKrzysztof Kosiński   // Delta Validity
187*a03ca8b9SKrzysztof Kosiński   // - Reminder! 0 -> 1:1
188*a03ca8b9SKrzysztof Kosiński 
189*a03ca8b9SKrzysztof Kosiński   // Common possible edge cases.
190*a03ca8b9SKrzysztof Kosiński   EXPECT_TRUE(translator.IsValid(0, ztf::DeltaLineCol{0, 0}));
191*a03ca8b9SKrzysztof Kosiński   EXPECT_TRUE(translator.IsValid(0, ztf::DeltaLineCol{0, 1}));
192*a03ca8b9SKrzysztof Kosiński   EXPECT_TRUE(translator.IsValid(0, ztf::DeltaLineCol{1, 0}));
193*a03ca8b9SKrzysztof Kosiński   EXPECT_FALSE(translator.IsValid(0, ztf::DeltaLineCol{-1, -1}));
194*a03ca8b9SKrzysztof Kosiński   EXPECT_FALSE(translator.IsValid(0, ztf::DeltaLineCol{-1, 0}));
195*a03ca8b9SKrzysztof Kosiński   EXPECT_FALSE(translator.IsValid(0, ztf::DeltaLineCol{0, -1}));
196*a03ca8b9SKrzysztof Kosiński   EXPECT_FALSE(translator.IsValid(0, ztf::DeltaLineCol{0, -1}));
197*a03ca8b9SKrzysztof Kosiński   EXPECT_FALSE(translator.IsValid(0, ztf::DeltaLineCol{0, kMaxVal}));
198*a03ca8b9SKrzysztof Kosiński   EXPECT_FALSE(translator.IsValid(0, ztf::DeltaLineCol{kMaxVal, 0}));
199*a03ca8b9SKrzysztof Kosiński   EXPECT_FALSE(translator.IsValid(0, ztf::DeltaLineCol{0, kMinVal}));
200*a03ca8b9SKrzysztof Kosiński   EXPECT_FALSE(translator.IsValid(0, ztf::DeltaLineCol{kMinVal, 0}));
201*a03ca8b9SKrzysztof Kosiński   EXPECT_FALSE(translator.IsValid(233, ztf::DeltaLineCol{0, kMaxVal}));
202*a03ca8b9SKrzysztof Kosiński   EXPECT_FALSE(translator.IsValid(233, ztf::DeltaLineCol{kMaxVal, 0}));
203*a03ca8b9SKrzysztof Kosiński   EXPECT_FALSE(translator.IsValid(233, ztf::DeltaLineCol{kMaxVal, kMaxVal}));
204*a03ca8b9SKrzysztof Kosiński 
205*a03ca8b9SKrzysztof Kosiński   // Newline area.
206*a03ca8b9SKrzysztof Kosiński   EXPECT_TRUE(translator.IsValid(0, ztf::DeltaLineCol{0, 9}));
207*a03ca8b9SKrzysztof Kosiński   EXPECT_FALSE(translator.IsValid(0, ztf::DeltaLineCol{0, 10}));
208*a03ca8b9SKrzysztof Kosiński   EXPECT_FALSE(translator.IsValid(9, ztf::DeltaLineCol{0, 1}));
209*a03ca8b9SKrzysztof Kosiński   EXPECT_FALSE(translator.IsValid(9, ztf::DeltaLineCol{-1, 0}));
210*a03ca8b9SKrzysztof Kosiński   EXPECT_FALSE(translator.IsValid(9, ztf::DeltaLineCol{1, -10}));
211*a03ca8b9SKrzysztof Kosiński   EXPECT_TRUE(translator.IsValid(9, ztf::DeltaLineCol{1, -9}));
212*a03ca8b9SKrzysztof Kosiński 
213*a03ca8b9SKrzysztof Kosiński   // End of file.
214*a03ca8b9SKrzysztof Kosiński   EXPECT_FALSE(translator.IsValid(0, ztf::DeltaLineCol{7, 78}));
215*a03ca8b9SKrzysztof Kosiński   EXPECT_FALSE(translator.IsValid(0, ztf::DeltaLineCol{7, 77}));
216*a03ca8b9SKrzysztof Kosiński   EXPECT_FALSE(translator.IsValid(0, ztf::DeltaLineCol{6, 78}));
217*a03ca8b9SKrzysztof Kosiński   EXPECT_TRUE(translator.IsValid(0, ztf::DeltaLineCol{6, 77}));
218*a03ca8b9SKrzysztof Kosiński   EXPECT_FALSE(translator.IsValid(text.size() - 1, ztf::DeltaLineCol{0, 1}));
219*a03ca8b9SKrzysztof Kosiński   EXPECT_FALSE(translator.IsValid(text.size() - 1, ztf::DeltaLineCol{1, 0}));
220*a03ca8b9SKrzysztof Kosiński   EXPECT_TRUE(translator.IsValid(text.size() - 2, ztf::DeltaLineCol{0, 1}));
221*a03ca8b9SKrzysztof Kosiński   EXPECT_FALSE(translator.IsValid(text.size() - 2, ztf::DeltaLineCol{1, 0}));
222*a03ca8b9SKrzysztof Kosiński }
223*a03ca8b9SKrzysztof Kosiński 
224*a03ca8b9SKrzysztof Kosiński // Ensures that ReferenceGroups from DisassemblerZtf::MakeReferenceGroups()
225*a03ca8b9SKrzysztof Kosiński // cover each non-sentinel element in ReferenceType in order, exactly once. Also
226*a03ca8b9SKrzysztof Kosiński // ensures that the ReferenceType elements are grouped by ReferencePool, and
227*a03ca8b9SKrzysztof Kosiński // listed in increasing order.
TEST(DisassemblerZtfTest,ReferenceGroups)228*a03ca8b9SKrzysztof Kosiński TEST(DisassemblerZtfTest, ReferenceGroups) {
229*a03ca8b9SKrzysztof Kosiński   std::vector<uint32_t> pool_list;
230*a03ca8b9SKrzysztof Kosiński   std::vector<uint32_t> type_list;
231*a03ca8b9SKrzysztof Kosiński   DisassemblerZtf dis;
232*a03ca8b9SKrzysztof Kosiński   for (ReferenceGroup group : dis.MakeReferenceGroups()) {
233*a03ca8b9SKrzysztof Kosiński     pool_list.push_back(static_cast<uint32_t>(group.pool_tag().value()));
234*a03ca8b9SKrzysztof Kosiński     type_list.push_back(static_cast<uint32_t>(group.type_tag().value()));
235*a03ca8b9SKrzysztof Kosiński   }
236*a03ca8b9SKrzysztof Kosiński 
237*a03ca8b9SKrzysztof Kosiński   // Check ReferenceByte coverage.
238*a03ca8b9SKrzysztof Kosiński   constexpr size_t kNumTypes = DisassemblerZtf::kNumTypes;
239*a03ca8b9SKrzysztof Kosiński   EXPECT_EQ(kNumTypes, type_list.size());
240*a03ca8b9SKrzysztof Kosiński   EXPECT_EQ(kNumTypes, CountDistinct(type_list));
241*a03ca8b9SKrzysztof Kosiński   EXPECT_TRUE(std::is_sorted(type_list.begin(), type_list.end()));
242*a03ca8b9SKrzysztof Kosiński 
243*a03ca8b9SKrzysztof Kosiński   // Check that ReferenceType elements are grouped by ReferencePool. Note that
244*a03ca8b9SKrzysztof Kosiński   // repeats can occur, and pools can be skipped.
245*a03ca8b9SKrzysztof Kosiński   EXPECT_TRUE(std::is_sorted(pool_list.begin(), pool_list.end()));
246*a03ca8b9SKrzysztof Kosiński }
247*a03ca8b9SKrzysztof Kosiński 
TEST(DisassemblerZtfTest,BadMagic)248*a03ca8b9SKrzysztof Kosiński TEST(DisassemblerZtfTest, BadMagic) {
249*a03ca8b9SKrzysztof Kosiński   // Test a case where there is no header so a disassembler cannot be created.
250*a03ca8b9SKrzysztof Kosiński   {
251*a03ca8b9SKrzysztof Kosiński     const std::vector<uint8_t> text(StrToData("foobarbaz bazbarfoo"));
252*a03ca8b9SKrzysztof Kosiński     ConstBufferView image(text.data(), text.size());
253*a03ca8b9SKrzysztof Kosiński     EXPECT_FALSE(DisassemblerZtf::QuickDetect(image));
254*a03ca8b9SKrzysztof Kosiński     EXPECT_FALSE(Disassembler::Make<DisassemblerZtf>(image));
255*a03ca8b9SKrzysztof Kosiński   }
256*a03ca8b9SKrzysztof Kosiński   // Test a case where there is no footer so a disassembler cannot be created.
257*a03ca8b9SKrzysztof Kosiński   {
258*a03ca8b9SKrzysztof Kosiński     const std::vector<uint8_t> text(StrToData("ZTxtfoobarbaz bazbarfootxTZ"));
259*a03ca8b9SKrzysztof Kosiński     ConstBufferView image(text.data(), text.size());
260*a03ca8b9SKrzysztof Kosiński     EXPECT_TRUE(DisassemblerZtf::QuickDetect(image));
261*a03ca8b9SKrzysztof Kosiński     EXPECT_FALSE(Disassembler::Make<DisassemblerZtf>(image));
262*a03ca8b9SKrzysztof Kosiński   }
263*a03ca8b9SKrzysztof Kosiński   // Test when the header is too short
264*a03ca8b9SKrzysztof Kosiński   {
265*a03ca8b9SKrzysztof Kosiński     const std::vector<uint8_t> text(StrToData("ZTxtxTZ\n"));
266*a03ca8b9SKrzysztof Kosiński     ConstBufferView image(text.data(), text.size());
267*a03ca8b9SKrzysztof Kosiński     EXPECT_FALSE(DisassemblerZtf::QuickDetect(image));
268*a03ca8b9SKrzysztof Kosiński     EXPECT_FALSE(Disassembler::Make<DisassemblerZtf>(image));
269*a03ca8b9SKrzysztof Kosiński   }
270*a03ca8b9SKrzysztof Kosiński }
271*a03ca8b9SKrzysztof Kosiński 
TEST(DisassemblerZtfTest,ZtfSizeBound)272*a03ca8b9SKrzysztof Kosiński TEST(DisassemblerZtfTest, ZtfSizeBound) {
273*a03ca8b9SKrzysztof Kosiński   {
274*a03ca8b9SKrzysztof Kosiński     std::vector<uint8_t> text(StrToData("ZTxt"));
275*a03ca8b9SKrzysztof Kosiński     std::fill_n(std::back_inserter(text), ztf::kMaxDimValue - 2, '\n');
276*a03ca8b9SKrzysztof Kosiński     text.insert(text.end(), {'t', 'x', 'T', 'Z', '\n'});
277*a03ca8b9SKrzysztof Kosiński     ConstBufferView image(text.data(), text.size());
278*a03ca8b9SKrzysztof Kosiński     EXPECT_TRUE(DisassemblerZtf::QuickDetect(image));
279*a03ca8b9SKrzysztof Kosiński     EXPECT_TRUE(Disassembler::Make<DisassemblerZtf>(image));
280*a03ca8b9SKrzysztof Kosiński   }
281*a03ca8b9SKrzysztof Kosiński   {
282*a03ca8b9SKrzysztof Kosiński     std::vector<uint8_t> text(StrToData("ZTxt"));
283*a03ca8b9SKrzysztof Kosiński     std::fill_n(std::back_inserter(text), ztf::kMaxDimValue - 1, '\n');
284*a03ca8b9SKrzysztof Kosiński     text.insert(text.end(), {'t', 'x', 'T', 'Z', '\n'});
285*a03ca8b9SKrzysztof Kosiński     ConstBufferView image(text.data(), text.size());
286*a03ca8b9SKrzysztof Kosiński     EXPECT_TRUE(DisassemblerZtf::QuickDetect(image));
287*a03ca8b9SKrzysztof Kosiński     EXPECT_FALSE(Disassembler::Make<DisassemblerZtf>(image));
288*a03ca8b9SKrzysztof Kosiński   }
289*a03ca8b9SKrzysztof Kosiński }
290*a03ca8b9SKrzysztof Kosiński 
291*a03ca8b9SKrzysztof Kosiński // Try reading from a well formed source.
TEST(DisassemblerZtfTest,NormalRead)292*a03ca8b9SKrzysztof Kosiński TEST(DisassemblerZtfTest, NormalRead) {
293*a03ca8b9SKrzysztof Kosiński   const std::vector<uint8_t> text(StrToData(kNormalText));
294*a03ca8b9SKrzysztof Kosiński   ConstBufferView image(text.data(), text.size());
295*a03ca8b9SKrzysztof Kosiński   ReferenceSets expected_map = {
296*a03ca8b9SKrzysztof Kosiński       {{DisassemblerZtf::kAngles, DisassemblerZtf::kAnglesAbs1},
297*a03ca8b9SKrzysztof Kosiński        {Reference({63, 0})}},
298*a03ca8b9SKrzysztof Kosiński       {{DisassemblerZtf::kBraces, DisassemblerZtf::kBracesRel2},
299*a03ca8b9SKrzysztof Kosiński        {Reference({74, 27})}},
300*a03ca8b9SKrzysztof Kosiński   };
301*a03ca8b9SKrzysztof Kosiński   ReadReferences(image, text.size() - kNormalTextExtraBytes, &expected_map);
302*a03ca8b9SKrzysztof Kosiński }
303*a03ca8b9SKrzysztof Kosiński 
304*a03ca8b9SKrzysztof Kosiński // Try writing to a well formed source and ensure that what is read back
305*a03ca8b9SKrzysztof Kosiński // reflects what was written.
TEST(DisassemblerZtfTest,NormalWrite)306*a03ca8b9SKrzysztof Kosiński TEST(DisassemblerZtfTest, NormalWrite) {
307*a03ca8b9SKrzysztof Kosiński   std::vector<uint8_t> mutable_text(StrToData(kNormalText));
308*a03ca8b9SKrzysztof Kosiński   MutableBufferView image(mutable_text.data(), mutable_text.size());
309*a03ca8b9SKrzysztof Kosiński   ReferenceSets change_map = {
310*a03ca8b9SKrzysztof Kosiński       {{DisassemblerZtf::kParentheses, DisassemblerZtf::kParenthesesAbs1},
311*a03ca8b9SKrzysztof Kosiński        {Reference({63, 71})}},
312*a03ca8b9SKrzysztof Kosiński       {{DisassemblerZtf::kBrackets, DisassemblerZtf::kBracketsRel3},
313*a03ca8b9SKrzysztof Kosiński        {Reference({74, 4})}},
314*a03ca8b9SKrzysztof Kosiński   };
315*a03ca8b9SKrzysztof Kosiński   WriteReferences(image, mutable_text.size() - kNormalTextExtraBytes,
316*a03ca8b9SKrzysztof Kosiński                   change_map);
317*a03ca8b9SKrzysztof Kosiński 
318*a03ca8b9SKrzysztof Kosiński   // As a sanity check see if a disassembler can identify the same references.
319*a03ca8b9SKrzysztof Kosiński   ConstBufferView const_image(image);
320*a03ca8b9SKrzysztof Kosiński   ReadReferences(const_image, mutable_text.size() - kNormalTextExtraBytes,
321*a03ca8b9SKrzysztof Kosiński                  &change_map);
322*a03ca8b9SKrzysztof Kosiński }
323*a03ca8b9SKrzysztof Kosiński 
324*a03ca8b9SKrzysztof Kosiński // Try reading from a source rife with errors.
TEST(DisassemblerZtfTest,ReadOutOfBoundsRefs)325*a03ca8b9SKrzysztof Kosiński TEST(DisassemblerZtfTest, ReadOutOfBoundsRefs) {
326*a03ca8b9SKrzysztof Kosiński   const std::vector<uint8_t> text(StrToData(kOutOfBoundsText));
327*a03ca8b9SKrzysztof Kosiński   ConstBufferView image(text.data(), text.size());
328*a03ca8b9SKrzysztof Kosiński   ReferenceSets expected_map = {
329*a03ca8b9SKrzysztof Kosiński       {{DisassemblerZtf::kAngles, DisassemblerZtf::kAnglesAbs1},
330*a03ca8b9SKrzysztof Kosiński        {Reference({4, 0}), Reference({223, 0}), Reference({228, 12})}},
331*a03ca8b9SKrzysztof Kosiński       {{DisassemblerZtf::kBrackets, DisassemblerZtf::kBracketsRel2},
332*a03ca8b9SKrzysztof Kosiński        {Reference({139, 149})}},
333*a03ca8b9SKrzysztof Kosiński       {{DisassemblerZtf::kBraces, DisassemblerZtf::kBracesAbs1},
334*a03ca8b9SKrzysztof Kosiński        {Reference({218, 223})}},
335*a03ca8b9SKrzysztof Kosiński       {{DisassemblerZtf::kBraces, DisassemblerZtf::kBracesRel2},
336*a03ca8b9SKrzysztof Kosiński        {Reference({233, 233})}},
337*a03ca8b9SKrzysztof Kosiński       {{DisassemblerZtf::kParentheses, DisassemblerZtf::kParenthesesAbs1},
338*a03ca8b9SKrzysztof Kosiński        {Reference({174, 4})}},
339*a03ca8b9SKrzysztof Kosiński   };
340*a03ca8b9SKrzysztof Kosiński   ReadReferences(image, text.size(), &expected_map);
341*a03ca8b9SKrzysztof Kosiński }
342*a03ca8b9SKrzysztof Kosiński 
343*a03ca8b9SKrzysztof Kosiński // Try writing to a source rife with errors (malformed references or ones that
344*a03ca8b9SKrzysztof Kosiński // reference non-existent locations. Some of the values written are also bad. To
345*a03ca8b9SKrzysztof Kosiński // validate check if the expected set of references are read back.
TEST(DisassemblerZtfTest,WriteOutOfBoundsRefs)346*a03ca8b9SKrzysztof Kosiński TEST(DisassemblerZtfTest, WriteOutOfBoundsRefs) {
347*a03ca8b9SKrzysztof Kosiński   // Replace |old_val| (provided for checking) with |new_val| in |set|.
348*a03ca8b9SKrzysztof Kosiński   auto update_set = [](Reference old_ref, Reference new_ref,
349*a03ca8b9SKrzysztof Kosiński                        std::set<Reference, ReferenceCompare>* set) {
350*a03ca8b9SKrzysztof Kosiński     auto it = set->find(old_ref);
351*a03ca8b9SKrzysztof Kosiński     EXPECT_NE(it, set->cend());
352*a03ca8b9SKrzysztof Kosiński     EXPECT_EQ(*it, old_ref);
353*a03ca8b9SKrzysztof Kosiński     set->erase(it);
354*a03ca8b9SKrzysztof Kosiński     set->insert(new_ref);
355*a03ca8b9SKrzysztof Kosiński   };
356*a03ca8b9SKrzysztof Kosiński 
357*a03ca8b9SKrzysztof Kosiński   // Replace |old_val| (provided for checking) with |new_val| in the set which
358*a03ca8b9SKrzysztof Kosiński   // is the value corresponding to |key| in |map|.
359*a03ca8b9SKrzysztof Kosiński   auto update_map =
360*a03ca8b9SKrzysztof Kosiński       [update_set](
361*a03ca8b9SKrzysztof Kosiński           ReferenceKey key, Reference old_ref, Reference new_ref,
362*a03ca8b9SKrzysztof Kosiński           std::map<ReferenceKey, std::set<Reference, ReferenceCompare>>* map) {
363*a03ca8b9SKrzysztof Kosiński         auto it = map->find(key);
364*a03ca8b9SKrzysztof Kosiński         EXPECT_NE(it, map->cend());
365*a03ca8b9SKrzysztof Kosiński         update_set(old_ref, new_ref, &(it->second));
366*a03ca8b9SKrzysztof Kosiński       };
367*a03ca8b9SKrzysztof Kosiński 
368*a03ca8b9SKrzysztof Kosiński   std::vector<uint8_t> mutable_text(StrToData(kOutOfBoundsText));
369*a03ca8b9SKrzysztof Kosiński   MutableBufferView image(mutable_text.data(), mutable_text.size());
370*a03ca8b9SKrzysztof Kosiński   ReferenceSets change_map = {
371*a03ca8b9SKrzysztof Kosiński       {{DisassemblerZtf::kAngles, DisassemblerZtf::kAnglesAbs1},
372*a03ca8b9SKrzysztof Kosiński        {Reference({223, 15}), Reference({228, 13})}},
373*a03ca8b9SKrzysztof Kosiński       {{DisassemblerZtf::kAngles, DisassemblerZtf::kAnglesAbs3},
374*a03ca8b9SKrzysztof Kosiński        {Reference({4, 50})}},  // This should fail to write.
375*a03ca8b9SKrzysztof Kosiński       {{DisassemblerZtf::kBrackets, DisassemblerZtf::kBracketsRel2},
376*a03ca8b9SKrzysztof Kosiński        {Reference({139, static_cast<offset_t>(
377*a03ca8b9SKrzysztof Kosiński                             mutable_text.size())})}},  // This should fail.
378*a03ca8b9SKrzysztof Kosiński       {{DisassemblerZtf::kParentheses, DisassemblerZtf::kParenthesesAbs1},
379*a03ca8b9SKrzysztof Kosiński        {Reference({174, 21})}},  // This should fail.
380*a03ca8b9SKrzysztof Kosiński       {{DisassemblerZtf::kBraces, DisassemblerZtf::kBracesAbs1},
381*a03ca8b9SKrzysztof Kosiński        {Reference({218, 219})}},
382*a03ca8b9SKrzysztof Kosiński       {{DisassemblerZtf::kBraces, DisassemblerZtf::kBracesRel2},
383*a03ca8b9SKrzysztof Kosiński        {Reference({233, 174})}},
384*a03ca8b9SKrzysztof Kosiński   };
385*a03ca8b9SKrzysztof Kosiński   WriteReferences(image, mutable_text.size(), change_map);
386*a03ca8b9SKrzysztof Kosiński 
387*a03ca8b9SKrzysztof Kosiński   // As a sanity check see if a disassembler can identify the same references
388*a03ca8b9SKrzysztof Kosiński   // (excluding the invalid ones).
389*a03ca8b9SKrzysztof Kosiński   change_map.erase(change_map.find(
390*a03ca8b9SKrzysztof Kosiński       {DisassemblerZtf::kAngles, DisassemblerZtf::kAnglesAbs3}));
391*a03ca8b9SKrzysztof Kosiński   change_map.at({DisassemblerZtf::kAngles, DisassemblerZtf::kAnglesAbs1})
392*a03ca8b9SKrzysztof Kosiński       .emplace(Reference{4, 0});
393*a03ca8b9SKrzysztof Kosiński   update_map({DisassemblerZtf::kBrackets, DisassemblerZtf::kBracketsRel2},
394*a03ca8b9SKrzysztof Kosiński              Reference({139, static_cast<offset_t>(mutable_text.size())}),
395*a03ca8b9SKrzysztof Kosiński              Reference({139, 149}), &change_map);
396*a03ca8b9SKrzysztof Kosiński   update_map({DisassemblerZtf::kParentheses, DisassemblerZtf::kParenthesesAbs1},
397*a03ca8b9SKrzysztof Kosiński              Reference({174, 21}), Reference({174, 4}), &change_map);
398*a03ca8b9SKrzysztof Kosiński   ConstBufferView const_image(image);
399*a03ca8b9SKrzysztof Kosiński   ReadReferences(const_image, mutable_text.size(), &change_map);
400*a03ca8b9SKrzysztof Kosiński }
401*a03ca8b9SKrzysztof Kosiński 
402*a03ca8b9SKrzysztof Kosiński }  // namespace zucchini
403