xref: /aosp_15_r20/external/pigweed/pw_tokenizer/detokenize_test.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1*61c4878aSAndroid Build Coastguard Worker // Copyright 2020 The Pigweed Authors
2*61c4878aSAndroid Build Coastguard Worker //
3*61c4878aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4*61c4878aSAndroid Build Coastguard Worker // use this file except in compliance with the License. You may obtain a copy of
5*61c4878aSAndroid Build Coastguard Worker // the License at
6*61c4878aSAndroid Build Coastguard Worker //
7*61c4878aSAndroid Build Coastguard Worker //     https://www.apache.org/licenses/LICENSE-2.0
8*61c4878aSAndroid Build Coastguard Worker //
9*61c4878aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*61c4878aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11*61c4878aSAndroid Build Coastguard Worker // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12*61c4878aSAndroid Build Coastguard Worker // License for the specific language governing permissions and limitations under
13*61c4878aSAndroid Build Coastguard Worker // the License.
14*61c4878aSAndroid Build Coastguard Worker 
15*61c4878aSAndroid Build Coastguard Worker #include "pw_tokenizer/detokenize.h"
16*61c4878aSAndroid Build Coastguard Worker 
17*61c4878aSAndroid Build Coastguard Worker #include <string>
18*61c4878aSAndroid Build Coastguard Worker #include <string_view>
19*61c4878aSAndroid Build Coastguard Worker 
20*61c4878aSAndroid Build Coastguard Worker #include "pw_stream/memory_stream.h"
21*61c4878aSAndroid Build Coastguard Worker #include "pw_tokenizer/example_binary_with_tokenized_strings.h"
22*61c4878aSAndroid Build Coastguard Worker #include "pw_unit_test/framework.h"
23*61c4878aSAndroid Build Coastguard Worker 
24*61c4878aSAndroid Build Coastguard Worker namespace pw::tokenizer {
25*61c4878aSAndroid Build Coastguard Worker namespace {
26*61c4878aSAndroid Build Coastguard Worker 
27*61c4878aSAndroid Build Coastguard Worker using namespace std::literals::string_view_literals;
28*61c4878aSAndroid Build Coastguard Worker 
29*61c4878aSAndroid Build Coastguard Worker // Use a shorter name for the error string macro.
30*61c4878aSAndroid Build Coastguard Worker #define ERR PW_TOKENIZER_ARG_DECODING_ERROR
31*61c4878aSAndroid Build Coastguard Worker 
32*61c4878aSAndroid Build Coastguard Worker using Case = std::pair<std::string_view, std::string_view>;
33*61c4878aSAndroid Build Coastguard Worker 
34*61c4878aSAndroid Build Coastguard Worker template <typename... Args>
TestCases(Args...args)35*61c4878aSAndroid Build Coastguard Worker auto TestCases(Args... args) {
36*61c4878aSAndroid Build Coastguard Worker   return std::array<Case, sizeof...(Args)>{args...};
37*61c4878aSAndroid Build Coastguard Worker }
38*61c4878aSAndroid Build Coastguard Worker 
39*61c4878aSAndroid Build Coastguard Worker // Database with the following entries and arbitrary token values:
40*61c4878aSAndroid Build Coastguard Worker // {
41*61c4878aSAndroid Build Coastguard Worker //   0x00000001: "One",
42*61c4878aSAndroid Build Coastguard Worker //   0x00000005: "TWO",
43*61c4878aSAndroid Build Coastguard Worker //   0x000000ff: "333",
44*61c4878aSAndroid Build Coastguard Worker //   0xDDEEEEFF: "One",
45*61c4878aSAndroid Build Coastguard Worker //   0xEEEEEEEE: "$AQAAAA==",  # Nested Base64 token for "One"
46*61c4878aSAndroid Build Coastguard Worker // }
47*61c4878aSAndroid Build Coastguard Worker constexpr char kTestDatabase[] =
48*61c4878aSAndroid Build Coastguard Worker     "TOKENS\0\0"
49*61c4878aSAndroid Build Coastguard Worker     "\x06\x00\x00\x00"  // Number of tokens in this database.
50*61c4878aSAndroid Build Coastguard Worker     "\0\0\0\0"
51*61c4878aSAndroid Build Coastguard Worker     "\x01\x00\x00\x00----"
52*61c4878aSAndroid Build Coastguard Worker     "\x05\x00\x00\x00----"
53*61c4878aSAndroid Build Coastguard Worker     "\xFF\x00\x00\x00----"
54*61c4878aSAndroid Build Coastguard Worker     "\xFF\xEE\xEE\xDD----"
55*61c4878aSAndroid Build Coastguard Worker     "\xEE\xEE\xEE\xEE----"
56*61c4878aSAndroid Build Coastguard Worker     "\x9D\xA7\x97\xF8----"
57*61c4878aSAndroid Build Coastguard Worker     "One\0"
58*61c4878aSAndroid Build Coastguard Worker     "TWO\0"
59*61c4878aSAndroid Build Coastguard Worker     "333\0"
60*61c4878aSAndroid Build Coastguard Worker     "FOUR\0"
61*61c4878aSAndroid Build Coastguard Worker     "$AQAAAA==\0"
62*61c4878aSAndroid Build Coastguard Worker     "■msg♦This is $AQAAAA== message■module♦■file♦file.txt";
63*61c4878aSAndroid Build Coastguard Worker 
64*61c4878aSAndroid Build Coastguard Worker class Detokenize : public ::testing::Test {
65*61c4878aSAndroid Build Coastguard Worker  protected:
Detokenize()66*61c4878aSAndroid Build Coastguard Worker   Detokenize() : detok_(TokenDatabase::Create<kTestDatabase>()) {}
67*61c4878aSAndroid Build Coastguard Worker   Detokenizer detok_;
68*61c4878aSAndroid Build Coastguard Worker };
69*61c4878aSAndroid Build Coastguard Worker 
TEST_F(Detokenize,NoFormatting)70*61c4878aSAndroid Build Coastguard Worker TEST_F(Detokenize, NoFormatting) {
71*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(detok_.Detokenize("\1\0\0\0"sv).BestString(), "One");
72*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(detok_.Detokenize("\5\0\0\0"sv).BestString(), "TWO");
73*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(detok_.Detokenize("\xff\x00\x00\x00"sv).BestString(), "333");
74*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(detok_.Detokenize("\xff\xee\xee\xdd"sv).BestString(), "FOUR");
75*61c4878aSAndroid Build Coastguard Worker }
76*61c4878aSAndroid Build Coastguard Worker 
TEST_F(Detokenize,FromElfSection)77*61c4878aSAndroid Build Coastguard Worker TEST_F(Detokenize, FromElfSection) {
78*61c4878aSAndroid Build Coastguard Worker   // Create a detokenizer from an ELF file with only the pw_tokenizer sections.
79*61c4878aSAndroid Build Coastguard Worker   // See py/detokenize_test.py.
80*61c4878aSAndroid Build Coastguard Worker   // Offset and size of the .pw_tokenizer.entries section in bytes.
81*61c4878aSAndroid Build Coastguard Worker   constexpr uint32_t database_offset_ = 0x00000174;
82*61c4878aSAndroid Build Coastguard Worker   constexpr size_t database_size_ = 0x000004C2;
83*61c4878aSAndroid Build Coastguard Worker 
84*61c4878aSAndroid Build Coastguard Worker   pw::span<const uint8_t> tokenEntries(
85*61c4878aSAndroid Build Coastguard Worker       reinterpret_cast<const uint8_t*>(::test::ns::kElfSection.data() +
86*61c4878aSAndroid Build Coastguard Worker                                        database_offset_),
87*61c4878aSAndroid Build Coastguard Worker       database_size_);
88*61c4878aSAndroid Build Coastguard Worker   pw::Result<Detokenizer> detok_from_elf_ =
89*61c4878aSAndroid Build Coastguard Worker       Detokenizer::FromElfSection(tokenEntries);
90*61c4878aSAndroid Build Coastguard Worker   ASSERT_TRUE(detok_from_elf_.ok());
91*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(detok_from_elf_->Detokenize("\xd6\x8c\x66\x2e").BestString(),
92*61c4878aSAndroid Build Coastguard Worker             "Jello, world!");
93*61c4878aSAndroid Build Coastguard Worker }
94*61c4878aSAndroid Build Coastguard Worker 
TEST_F(Detokenize,FromElfFile)95*61c4878aSAndroid Build Coastguard Worker TEST_F(Detokenize, FromElfFile) {
96*61c4878aSAndroid Build Coastguard Worker   // Create a detokenizer from an ELF file with only the pw_tokenizer sections.
97*61c4878aSAndroid Build Coastguard Worker   // See py/detokenize_test.py.
98*61c4878aSAndroid Build Coastguard Worker   stream::MemoryReader stream(::test::ns::kElfSection);
99*61c4878aSAndroid Build Coastguard Worker 
100*61c4878aSAndroid Build Coastguard Worker   pw::Result<Detokenizer> detok = Detokenizer::FromElfFile(stream);
101*61c4878aSAndroid Build Coastguard Worker   PW_TEST_ASSERT_OK(detok);
102*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(detok->Detokenize("\xd6\x8c\x66\x2e").BestString(),
103*61c4878aSAndroid Build Coastguard Worker             "Jello, world!");
104*61c4878aSAndroid Build Coastguard Worker }
105*61c4878aSAndroid Build Coastguard Worker 
TEST_F(Detokenize,BestString_MissingToken_IsEmpty)106*61c4878aSAndroid Build Coastguard Worker TEST_F(Detokenize, BestString_MissingToken_IsEmpty) {
107*61c4878aSAndroid Build Coastguard Worker   EXPECT_FALSE(detok_.Detokenize("").ok());
108*61c4878aSAndroid Build Coastguard Worker   EXPECT_TRUE(detok_.Detokenize("", 0u).BestString().empty());
109*61c4878aSAndroid Build Coastguard Worker }
110*61c4878aSAndroid Build Coastguard Worker 
TEST_F(Detokenize,BestString_ShorterToken_ZeroExtended)111*61c4878aSAndroid Build Coastguard Worker TEST_F(Detokenize, BestString_ShorterToken_ZeroExtended) {
112*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(detok_.Detokenize("\x42", 1u).token(), 0x42u);
113*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(detok_.Detokenize("\1\0"sv).token(), 0x1u);
114*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(detok_.Detokenize("\1\0\3"sv).token(), 0x030001u);
115*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(detok_.Detokenize("\0\0\0"sv).token(), 0x0u);
116*61c4878aSAndroid Build Coastguard Worker }
117*61c4878aSAndroid Build Coastguard Worker 
TEST_F(Detokenize,BestString_UnknownToken_IsEmpty)118*61c4878aSAndroid Build Coastguard Worker TEST_F(Detokenize, BestString_UnknownToken_IsEmpty) {
119*61c4878aSAndroid Build Coastguard Worker   EXPECT_FALSE(detok_.Detokenize("\0\0\0\0"sv).ok());
120*61c4878aSAndroid Build Coastguard Worker   EXPECT_TRUE(detok_.Detokenize("\0\0\0\0"sv).BestString().empty());
121*61c4878aSAndroid Build Coastguard Worker   EXPECT_TRUE(detok_.Detokenize("\2\0\0\0"sv).BestString().empty());
122*61c4878aSAndroid Build Coastguard Worker   EXPECT_TRUE(detok_.Detokenize("\x10\x32\x54\x76\x99"sv).BestString().empty());
123*61c4878aSAndroid Build Coastguard Worker   EXPECT_TRUE(detok_.Detokenize("\x98\xba\xdc\xfe"sv).BestString().empty());
124*61c4878aSAndroid Build Coastguard Worker }
125*61c4878aSAndroid Build Coastguard Worker 
TEST_F(Detokenize,BestStringWithErrors_MissingToken_ErrorMessage)126*61c4878aSAndroid Build Coastguard Worker TEST_F(Detokenize, BestStringWithErrors_MissingToken_ErrorMessage) {
127*61c4878aSAndroid Build Coastguard Worker   EXPECT_FALSE(detok_.Detokenize("").ok());
128*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(detok_.Detokenize("", 0u).BestStringWithErrors(),
129*61c4878aSAndroid Build Coastguard Worker             ERR("missing token"));
130*61c4878aSAndroid Build Coastguard Worker }
131*61c4878aSAndroid Build Coastguard Worker 
TEST_F(Detokenize,BestStringWithErrors_ShorterTokenMatchesStrings)132*61c4878aSAndroid Build Coastguard Worker TEST_F(Detokenize, BestStringWithErrors_ShorterTokenMatchesStrings) {
133*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(detok_.Detokenize("\1", 1u).BestStringWithErrors(), "One");
134*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(detok_.Detokenize("\1\0"sv).BestStringWithErrors(), "One");
135*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(detok_.Detokenize("\1\0\0"sv).BestStringWithErrors(), "One");
136*61c4878aSAndroid Build Coastguard Worker }
137*61c4878aSAndroid Build Coastguard Worker 
TEST_F(Detokenize,BestStringWithErrors_UnknownToken_ErrorMessage)138*61c4878aSAndroid Build Coastguard Worker TEST_F(Detokenize, BestStringWithErrors_UnknownToken_ErrorMessage) {
139*61c4878aSAndroid Build Coastguard Worker   ASSERT_FALSE(detok_.Detokenize("\0\0\0\0"sv).ok());
140*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(detok_.Detokenize("\0"sv).BestStringWithErrors(),
141*61c4878aSAndroid Build Coastguard Worker             ERR("unknown token 00000000"));
142*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(detok_.Detokenize("\0\0\0"sv).BestStringWithErrors(),
143*61c4878aSAndroid Build Coastguard Worker             ERR("unknown token 00000000"));
144*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(detok_.Detokenize("\0\0\0\0"sv).BestStringWithErrors(),
145*61c4878aSAndroid Build Coastguard Worker             ERR("unknown token 00000000"));
146*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(detok_.Detokenize("\2\0\0\0"sv).BestStringWithErrors(),
147*61c4878aSAndroid Build Coastguard Worker             ERR("unknown token 00000002"));
148*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(detok_.Detokenize("\x10\x32\x54\x76\x99"sv).BestStringWithErrors(),
149*61c4878aSAndroid Build Coastguard Worker             ERR("unknown token 76543210"));
150*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(detok_.Detokenize("\x98\xba\xdc\xfe"sv).BestStringWithErrors(),
151*61c4878aSAndroid Build Coastguard Worker             ERR("unknown token fedcba98"));
152*61c4878aSAndroid Build Coastguard Worker }
153*61c4878aSAndroid Build Coastguard Worker 
154*61c4878aSAndroid Build Coastguard Worker // Base64 versions of the tokens
155*61c4878aSAndroid Build Coastguard Worker #define ONE "$AQAAAA=="
156*61c4878aSAndroid Build Coastguard Worker #define TWO "$BQAAAA=="
157*61c4878aSAndroid Build Coastguard Worker #define THREE "$/wAAAA=="
158*61c4878aSAndroid Build Coastguard Worker #define FOUR "$/+7u3Q=="
159*61c4878aSAndroid Build Coastguard Worker #define NEST_ONE "$7u7u7g=="
160*61c4878aSAndroid Build Coastguard Worker 
TEST_F(Detokenize,Base64_NoArguments)161*61c4878aSAndroid Build Coastguard Worker TEST_F(Detokenize, Base64_NoArguments) {
162*61c4878aSAndroid Build Coastguard Worker   for (auto [data, expected] : TestCases(
163*61c4878aSAndroid Build Coastguard Worker            Case{ONE, "One"},
164*61c4878aSAndroid Build Coastguard Worker            Case{TWO, "TWO"},
165*61c4878aSAndroid Build Coastguard Worker            Case{THREE, "333"},
166*61c4878aSAndroid Build Coastguard Worker            Case{FOUR, "FOUR"},
167*61c4878aSAndroid Build Coastguard Worker            Case{FOUR ONE ONE, "FOUROneOne"},
168*61c4878aSAndroid Build Coastguard Worker            Case{ONE TWO THREE FOUR, "OneTWO333FOUR"},
169*61c4878aSAndroid Build Coastguard Worker            Case{ONE "\r\n" TWO "\r\n" THREE "\r\n" FOUR "\r\n",
170*61c4878aSAndroid Build Coastguard Worker                 "One\r\nTWO\r\n333\r\nFOUR\r\n"},
171*61c4878aSAndroid Build Coastguard Worker            Case{"123" FOUR, "123FOUR"},
172*61c4878aSAndroid Build Coastguard Worker            Case{"123" FOUR ", 56", "123FOUR, 56"},
173*61c4878aSAndroid Build Coastguard Worker            Case{"12" THREE FOUR ", 56", "12333FOUR, 56"},
174*61c4878aSAndroid Build Coastguard Worker            Case{"$0" ONE, "$0One"},
175*61c4878aSAndroid Build Coastguard Worker            Case{"$/+7u3Q=", "$/+7u3Q="},  // incomplete message (missing "=")
176*61c4878aSAndroid Build Coastguard Worker            Case{"$123456==" FOUR, "$123456==FOUR"},
177*61c4878aSAndroid Build Coastguard Worker            Case{NEST_ONE, "One"},
178*61c4878aSAndroid Build Coastguard Worker            Case{NEST_ONE NEST_ONE NEST_ONE, "OneOneOne"},
179*61c4878aSAndroid Build Coastguard Worker            Case{FOUR "$" ONE NEST_ONE "?", "FOUR$OneOne?"})) {
180*61c4878aSAndroid Build Coastguard Worker     EXPECT_EQ(detok_.DetokenizeText(data), expected);
181*61c4878aSAndroid Build Coastguard Worker   }
182*61c4878aSAndroid Build Coastguard Worker }
183*61c4878aSAndroid Build Coastguard Worker 
TEST_F(Detokenize,OptionallyTokenizedData)184*61c4878aSAndroid Build Coastguard Worker TEST_F(Detokenize, OptionallyTokenizedData) {
185*61c4878aSAndroid Build Coastguard Worker   for (auto [data, expected] : TestCases(
186*61c4878aSAndroid Build Coastguard Worker            Case{ONE, "One"},
187*61c4878aSAndroid Build Coastguard Worker            Case{"\1\0\0\0", "One"},
188*61c4878aSAndroid Build Coastguard Worker            Case{TWO, "TWO"},
189*61c4878aSAndroid Build Coastguard Worker            Case{THREE, "333"},
190*61c4878aSAndroid Build Coastguard Worker            Case{FOUR, "FOUR"},
191*61c4878aSAndroid Build Coastguard Worker            Case{FOUR ONE ONE, "FOUROneOne"},
192*61c4878aSAndroid Build Coastguard Worker            Case{ONE TWO THREE FOUR, "OneTWO333FOUR"},
193*61c4878aSAndroid Build Coastguard Worker            Case{ONE "\r\n" TWO "\r\n" THREE "\r\n" FOUR "\r\n",
194*61c4878aSAndroid Build Coastguard Worker                 "One\r\nTWO\r\n333\r\nFOUR\r\n"},
195*61c4878aSAndroid Build Coastguard Worker            Case{"123" FOUR, "123FOUR"},
196*61c4878aSAndroid Build Coastguard Worker            Case{"123" FOUR ", 56", "123FOUR, 56"},
197*61c4878aSAndroid Build Coastguard Worker            Case{"12" THREE FOUR ", 56", "12333FOUR, 56"},
198*61c4878aSAndroid Build Coastguard Worker            Case{"$0" ONE, "$0One"},
199*61c4878aSAndroid Build Coastguard Worker            Case{"$/+7u3Q=", "$/+7u3Q="},  // incomplete message (missing "=")
200*61c4878aSAndroid Build Coastguard Worker            Case{"$123456==" FOUR, "$123456==FOUR"},
201*61c4878aSAndroid Build Coastguard Worker            Case{NEST_ONE, "One"},
202*61c4878aSAndroid Build Coastguard Worker            Case{NEST_ONE NEST_ONE NEST_ONE, "OneOneOne"},
203*61c4878aSAndroid Build Coastguard Worker            Case{FOUR "$" ONE NEST_ONE "?", "FOUR$OneOne?"},
204*61c4878aSAndroid Build Coastguard Worker            Case{"$naeX+A==",
205*61c4878aSAndroid Build Coastguard Worker                 "■msg♦This is One message■module♦■file♦file.txt"})) {
206*61c4878aSAndroid Build Coastguard Worker     EXPECT_EQ(detok_.DecodeOptionallyTokenizedData(as_bytes(span(data))),
207*61c4878aSAndroid Build Coastguard Worker               std::string(expected));
208*61c4878aSAndroid Build Coastguard Worker   }
209*61c4878aSAndroid Build Coastguard Worker }
210*61c4878aSAndroid Build Coastguard Worker 
211*61c4878aSAndroid Build Coastguard Worker constexpr char kDataWithArguments[] =
212*61c4878aSAndroid Build Coastguard Worker     "TOKENS\0\0"
213*61c4878aSAndroid Build Coastguard Worker     "\x09\x00\x00\x00"
214*61c4878aSAndroid Build Coastguard Worker     "\0\0\0\0"
215*61c4878aSAndroid Build Coastguard Worker     "\x00\x00\x00\x00----"
216*61c4878aSAndroid Build Coastguard Worker     "\x0A\x0B\x0C\x0D----"
217*61c4878aSAndroid Build Coastguard Worker     "\x0E\x0F\x00\x01----"
218*61c4878aSAndroid Build Coastguard Worker     "\xAA\xAA\xAA\xAA----"
219*61c4878aSAndroid Build Coastguard Worker     "\xBB\xBB\xBB\xBB----"
220*61c4878aSAndroid Build Coastguard Worker     "\xCC\xCC\xCC\xCC----"
221*61c4878aSAndroid Build Coastguard Worker     "\xDD\xDD\xDD\xDD----"
222*61c4878aSAndroid Build Coastguard Worker     "\xEE\xEE\xEE\xEE----"
223*61c4878aSAndroid Build Coastguard Worker     "\xFF\xFF\xFF\xFF----"
224*61c4878aSAndroid Build Coastguard Worker     "\0"
225*61c4878aSAndroid Build Coastguard Worker     "Use the %s, %s.\0"
226*61c4878aSAndroid Build Coastguard Worker     "Now there are %d of %s!\0"
227*61c4878aSAndroid Build Coastguard Worker     "%c!\0"    // AA
228*61c4878aSAndroid Build Coastguard Worker     "%hhu!\0"  // BB
229*61c4878aSAndroid Build Coastguard Worker     "%hu!\0"   // CC
230*61c4878aSAndroid Build Coastguard Worker     "%u!\0"    // DD
231*61c4878aSAndroid Build Coastguard Worker     "%lu!\0"   // EE
232*61c4878aSAndroid Build Coastguard Worker     "%llu!";   // FF
233*61c4878aSAndroid Build Coastguard Worker 
234*61c4878aSAndroid Build Coastguard Worker constexpr TokenDatabase kWithArgs = TokenDatabase::Create<kDataWithArguments>();
235*61c4878aSAndroid Build Coastguard Worker class DetokenizeWithArgs : public ::testing::Test {
236*61c4878aSAndroid Build Coastguard Worker  protected:
DetokenizeWithArgs()237*61c4878aSAndroid Build Coastguard Worker   DetokenizeWithArgs() : detok_(kWithArgs) {}
238*61c4878aSAndroid Build Coastguard Worker 
239*61c4878aSAndroid Build Coastguard Worker   Detokenizer detok_;
240*61c4878aSAndroid Build Coastguard Worker };
241*61c4878aSAndroid Build Coastguard Worker 
TEST_F(DetokenizeWithArgs,NoMatches)242*61c4878aSAndroid Build Coastguard Worker TEST_F(DetokenizeWithArgs, NoMatches) {
243*61c4878aSAndroid Build Coastguard Worker   EXPECT_TRUE(detok_.Detokenize("\x23\xab\xc9\x87"sv).matches().empty());
244*61c4878aSAndroid Build Coastguard Worker }
245*61c4878aSAndroid Build Coastguard Worker 
TEST_F(DetokenizeWithArgs,SingleMatch)246*61c4878aSAndroid Build Coastguard Worker TEST_F(DetokenizeWithArgs, SingleMatch) {
247*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(detok_.Detokenize("\x00\x00\x00\x00"sv).matches().size(), 1u);
248*61c4878aSAndroid Build Coastguard Worker }
249*61c4878aSAndroid Build Coastguard Worker 
TEST_F(DetokenizeWithArgs,Empty)250*61c4878aSAndroid Build Coastguard Worker TEST_F(DetokenizeWithArgs, Empty) {
251*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(detok_.Detokenize("\x00\x00\x00\x00"sv).BestString(), "");
252*61c4878aSAndroid Build Coastguard Worker }
253*61c4878aSAndroid Build Coastguard Worker 
TEST_F(DetokenizeWithArgs,Successful)254*61c4878aSAndroid Build Coastguard Worker TEST_F(DetokenizeWithArgs, Successful) {
255*61c4878aSAndroid Build Coastguard Worker   // Run through test cases, but don't include cases that use %hhu or %llu since
256*61c4878aSAndroid Build Coastguard Worker   // these are not currently supported in arm-none-eabi-gcc.
257*61c4878aSAndroid Build Coastguard Worker   for (auto [data, expected] : TestCases(
258*61c4878aSAndroid Build Coastguard Worker            Case{"\x0A\x0B\x0C\x0D\5force\4Luke"sv, "Use the force, Luke."},
259*61c4878aSAndroid Build Coastguard Worker            Case{"\x0E\x0F\x00\x01\4\4them"sv, "Now there are 2 of them!"},
260*61c4878aSAndroid Build Coastguard Worker            Case{"\xAA\xAA\xAA\xAA\xfc\x01"sv, "~!"},
261*61c4878aSAndroid Build Coastguard Worker            Case{"\xCC\xCC\xCC\xCC\xfe\xff\x07"sv, "65535!"},
262*61c4878aSAndroid Build Coastguard Worker            Case{"\xDD\xDD\xDD\xDD\xfe\xff\x07"sv, "65535!"},
263*61c4878aSAndroid Build Coastguard Worker            Case{"\xDD\xDD\xDD\xDD\xfe\xff\xff\xff\x1f"sv, "4294967295!"},
264*61c4878aSAndroid Build Coastguard Worker            Case{"\xEE\xEE\xEE\xEE\xfe\xff\x07"sv, "65535!"},
265*61c4878aSAndroid Build Coastguard Worker            Case{"\xEE\xEE\xEE\xEE\xfe\xff\xff\xff\x1f"sv, "4294967295!"})) {
266*61c4878aSAndroid Build Coastguard Worker     EXPECT_EQ(detok_.Detokenize(data).BestString(), expected);
267*61c4878aSAndroid Build Coastguard Worker   }
268*61c4878aSAndroid Build Coastguard Worker }
269*61c4878aSAndroid Build Coastguard Worker 
TEST_F(DetokenizeWithArgs,ExtraDataError)270*61c4878aSAndroid Build Coastguard Worker TEST_F(DetokenizeWithArgs, ExtraDataError) {
271*61c4878aSAndroid Build Coastguard Worker   auto error = detok_.Detokenize("\x00\x00\x00\x00MORE data"sv);
272*61c4878aSAndroid Build Coastguard Worker   EXPECT_FALSE(error.ok());
273*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ("", error.BestString());
274*61c4878aSAndroid Build Coastguard Worker }
275*61c4878aSAndroid Build Coastguard Worker 
TEST_F(DetokenizeWithArgs,MissingArgumentError)276*61c4878aSAndroid Build Coastguard Worker TEST_F(DetokenizeWithArgs, MissingArgumentError) {
277*61c4878aSAndroid Build Coastguard Worker   auto error = detok_.Detokenize("\x0A\x0B\x0C\x0D\5force"sv);
278*61c4878aSAndroid Build Coastguard Worker   EXPECT_FALSE(error.ok());
279*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(error.BestString(), "Use the force, %s.");
280*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(error.BestStringWithErrors(),
281*61c4878aSAndroid Build Coastguard Worker             "Use the force, " ERR("%s MISSING") ".");
282*61c4878aSAndroid Build Coastguard Worker }
283*61c4878aSAndroid Build Coastguard Worker 
TEST_F(DetokenizeWithArgs,DecodingError)284*61c4878aSAndroid Build Coastguard Worker TEST_F(DetokenizeWithArgs, DecodingError) {
285*61c4878aSAndroid Build Coastguard Worker   auto error = detok_.Detokenize("\x0E\x0F\x00\x01\xFF"sv);
286*61c4878aSAndroid Build Coastguard Worker   EXPECT_FALSE(error.ok());
287*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(error.BestString(), "Now there are %d of %s!");
288*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(error.BestStringWithErrors(),
289*61c4878aSAndroid Build Coastguard Worker             "Now there are " ERR("%d ERROR") " of " ERR("%s SKIPPED") "!");
290*61c4878aSAndroid Build Coastguard Worker }
291*61c4878aSAndroid Build Coastguard Worker 
292*61c4878aSAndroid Build Coastguard Worker constexpr char kDataWithCollisions[] =
293*61c4878aSAndroid Build Coastguard Worker     "TOKENS\0\0"
294*61c4878aSAndroid Build Coastguard Worker     "\x0F\x00\x00\x00"
295*61c4878aSAndroid Build Coastguard Worker     "\0\0\0\0"
296*61c4878aSAndroid Build Coastguard Worker     "\x00\x00\x00\x00\xff\xff\xff\xff"  // 1
297*61c4878aSAndroid Build Coastguard Worker     "\x00\x00\x00\x00\x01\x02\x03\x04"  // 2
298*61c4878aSAndroid Build Coastguard Worker     "\x00\x00\x00\x00\xff\xff\xff\xff"  // 3
299*61c4878aSAndroid Build Coastguard Worker     "\x00\x00\x00\x00\xff\xff\xff\xff"  // 4
300*61c4878aSAndroid Build Coastguard Worker     "\x00\x00\x00\x00\xff\xff\xff\xff"  // 5
301*61c4878aSAndroid Build Coastguard Worker     "\x00\x00\x00\x00\xff\xff\xff\xff"  // 6
302*61c4878aSAndroid Build Coastguard Worker     "\x00\x00\x00\x00\xff\xff\xff\xff"  // 7
303*61c4878aSAndroid Build Coastguard Worker     "\xAA\xAA\xAA\xAA\x00\x00\x00\x00"  // 8
304*61c4878aSAndroid Build Coastguard Worker     "\xAA\xAA\xAA\xAA\xff\xff\xff\xff"  // 9
305*61c4878aSAndroid Build Coastguard Worker     "\xBB\xBB\xBB\xBB\xff\xff\xff\xff"  // A
306*61c4878aSAndroid Build Coastguard Worker     "\xBB\xBB\xBB\xBB\xff\xff\xff\xff"  // B
307*61c4878aSAndroid Build Coastguard Worker     "\xCC\xCC\xCC\xCC\xff\xff\xff\xff"  // C
308*61c4878aSAndroid Build Coastguard Worker     "\xCC\xCC\xCC\xCC\xff\xff\xff\xff"  // D
309*61c4878aSAndroid Build Coastguard Worker     "\xDD\xDD\xDD\xDD\xff\xff\xff\xff"  // E
310*61c4878aSAndroid Build Coastguard Worker     "\xDD\xDD\xDD\xDD\xff\xff\xff\xff"  // F
311*61c4878aSAndroid Build Coastguard Worker     // String table
312*61c4878aSAndroid Build Coastguard Worker     "This string is present\0"   // 1
313*61c4878aSAndroid Build Coastguard Worker     "This string is removed\0"   // 2
314*61c4878aSAndroid Build Coastguard Worker     "One arg %d\0"               // 3
315*61c4878aSAndroid Build Coastguard Worker     "One arg %s\0"               // 4
316*61c4878aSAndroid Build Coastguard Worker     "Two args %s %u\0"           // 5
317*61c4878aSAndroid Build Coastguard Worker     "Two args %s %s %% %% %%\0"  // 6
318*61c4878aSAndroid Build Coastguard Worker     "Four args %d %d %d %d\0"    // 7
319*61c4878aSAndroid Build Coastguard Worker     "This one is removed\0"      // 8
320*61c4878aSAndroid Build Coastguard Worker     "This one is present\0"      // 9
321*61c4878aSAndroid Build Coastguard Worker     "Two ints %d %d\0"           // A
322*61c4878aSAndroid Build Coastguard Worker     "Three ints %d %d %d\0"      // B
323*61c4878aSAndroid Build Coastguard Worker     "Three strings %s %s %s\0"   // C
324*61c4878aSAndroid Build Coastguard Worker     "Two strings %s %s\0"        // D
325*61c4878aSAndroid Build Coastguard Worker     "Three %s %s %s\0"           // E
326*61c4878aSAndroid Build Coastguard Worker     "Five %d %d %d %d %s\0";     // F
327*61c4878aSAndroid Build Coastguard Worker 
328*61c4878aSAndroid Build Coastguard Worker constexpr TokenDatabase kWithCollisions =
329*61c4878aSAndroid Build Coastguard Worker     TokenDatabase::Create<kDataWithCollisions>();
330*61c4878aSAndroid Build Coastguard Worker 
331*61c4878aSAndroid Build Coastguard Worker class DetokenizeWithCollisions : public ::testing::Test {
332*61c4878aSAndroid Build Coastguard Worker  protected:
DetokenizeWithCollisions()333*61c4878aSAndroid Build Coastguard Worker   DetokenizeWithCollisions() : detok_(kWithCollisions) {}
334*61c4878aSAndroid Build Coastguard Worker 
335*61c4878aSAndroid Build Coastguard Worker   Detokenizer detok_;
336*61c4878aSAndroid Build Coastguard Worker };
337*61c4878aSAndroid Build Coastguard Worker 
TEST_F(DetokenizeWithCollisions,Collision_AlwaysPreferSuccessfulDecode)338*61c4878aSAndroid Build Coastguard Worker TEST_F(DetokenizeWithCollisions, Collision_AlwaysPreferSuccessfulDecode) {
339*61c4878aSAndroid Build Coastguard Worker   for (auto [data, expected] :
340*61c4878aSAndroid Build Coastguard Worker        TestCases(Case{"\0\0\0\0"sv, "This string is present"},
341*61c4878aSAndroid Build Coastguard Worker                  Case{"\0\0\0\0\x01"sv, "One arg -1"},
342*61c4878aSAndroid Build Coastguard Worker                  Case{"\0\0\0\0\x80"sv, "One arg [...]"},
343*61c4878aSAndroid Build Coastguard Worker                  Case{"\0\0\0\0\4Hey!\x04"sv, "Two args Hey! 2"})) {
344*61c4878aSAndroid Build Coastguard Worker     EXPECT_EQ(detok_.Detokenize(data).BestString(), expected);
345*61c4878aSAndroid Build Coastguard Worker   }
346*61c4878aSAndroid Build Coastguard Worker }
347*61c4878aSAndroid Build Coastguard Worker 
TEST_F(DetokenizeWithCollisions,Collision_PreferDecodingAllBytes)348*61c4878aSAndroid Build Coastguard Worker TEST_F(DetokenizeWithCollisions, Collision_PreferDecodingAllBytes) {
349*61c4878aSAndroid Build Coastguard Worker   for (auto [data, expected] :
350*61c4878aSAndroid Build Coastguard Worker        TestCases(Case{"\0\0\0\0\x80\x80\x80\x80\x00"sv, "Two args [...] 0"},
351*61c4878aSAndroid Build Coastguard Worker                  Case{"\0\0\0\0\x08?"sv, "One arg %s"},
352*61c4878aSAndroid Build Coastguard Worker                  Case{"\0\0\0\0\x01!\x01\x80"sv, "Two args ! \x80 % % %"})) {
353*61c4878aSAndroid Build Coastguard Worker     EXPECT_EQ(detok_.Detokenize(data).BestString(), expected);
354*61c4878aSAndroid Build Coastguard Worker   }
355*61c4878aSAndroid Build Coastguard Worker }
356*61c4878aSAndroid Build Coastguard Worker 
TEST_F(DetokenizeWithCollisions,Collision_PreferFewestDecodingErrors)357*61c4878aSAndroid Build Coastguard Worker TEST_F(DetokenizeWithCollisions, Collision_PreferFewestDecodingErrors) {
358*61c4878aSAndroid Build Coastguard Worker   for (auto [data, expected] :
359*61c4878aSAndroid Build Coastguard Worker        TestCases(Case{"\xBB\xBB\xBB\xBB\x00"sv, "Two ints 0 %d"},
360*61c4878aSAndroid Build Coastguard Worker                  Case{"\xCC\xCC\xCC\xCC\2Yo\5?"sv, "Two strings Yo %s"})) {
361*61c4878aSAndroid Build Coastguard Worker     EXPECT_EQ(detok_.Detokenize(data).BestString(), expected);
362*61c4878aSAndroid Build Coastguard Worker   }
363*61c4878aSAndroid Build Coastguard Worker }
364*61c4878aSAndroid Build Coastguard Worker 
TEST_F(DetokenizeWithCollisions,Collision_PreferMostDecodedArgs)365*61c4878aSAndroid Build Coastguard Worker TEST_F(DetokenizeWithCollisions, Collision_PreferMostDecodedArgs) {
366*61c4878aSAndroid Build Coastguard Worker   auto result = detok_.Detokenize("\xDD\xDD\xDD\xDD\x01\x02\x01\x04\x05"sv);
367*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ((std::string_view)result.matches()[0].value(), "Five -1 1 -1 2 %s");
368*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ((std::string_view)result.matches()[1].value(), "Three \2 \4 %s"sv);
369*61c4878aSAndroid Build Coastguard Worker }
370*61c4878aSAndroid Build Coastguard Worker 
TEST_F(DetokenizeWithCollisions,Collision_PreferMostDecodedArgs_NoPercent)371*61c4878aSAndroid Build Coastguard Worker TEST_F(DetokenizeWithCollisions, Collision_PreferMostDecodedArgs_NoPercent) {
372*61c4878aSAndroid Build Coastguard Worker   // The "Two args %s %s ..." string successfully decodes this, and has more
373*61c4878aSAndroid Build Coastguard Worker   // "arguments", because of %%, but %% doesn't count as as a decoded argument.
374*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(detok_.Detokenize("\0\0\0\0\x01\x00\x01\x02"sv).BestString(),
375*61c4878aSAndroid Build Coastguard Worker             "Four args -1 0 -1 1");
376*61c4878aSAndroid Build Coastguard Worker }
377*61c4878aSAndroid Build Coastguard Worker 
TEST_F(DetokenizeWithCollisions,Collision_PreferStillPresentString)378*61c4878aSAndroid Build Coastguard Worker TEST_F(DetokenizeWithCollisions, Collision_PreferStillPresentString) {
379*61c4878aSAndroid Build Coastguard Worker   for (auto [data, expected] :
380*61c4878aSAndroid Build Coastguard Worker        TestCases(Case{"\x00\x00\x00\x00"sv, "This string is present"},
381*61c4878aSAndroid Build Coastguard Worker                  Case{"\xAA\xAA\xAA\xAA"sv, "This one is present"})) {
382*61c4878aSAndroid Build Coastguard Worker     EXPECT_EQ(detok_.Detokenize(data).BestString(), expected);
383*61c4878aSAndroid Build Coastguard Worker   }
384*61c4878aSAndroid Build Coastguard Worker }
385*61c4878aSAndroid Build Coastguard Worker 
TEST_F(DetokenizeWithCollisions,Collision_TracksAllMatches)386*61c4878aSAndroid Build Coastguard Worker TEST_F(DetokenizeWithCollisions, Collision_TracksAllMatches) {
387*61c4878aSAndroid Build Coastguard Worker   auto result = detok_.Detokenize("\0\0\0\0"sv);
388*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(result.matches().size(), 7u);
389*61c4878aSAndroid Build Coastguard Worker }
390*61c4878aSAndroid Build Coastguard Worker 
391*61c4878aSAndroid Build Coastguard Worker }  // namespace
392*61c4878aSAndroid Build Coastguard Worker }  // namespace pw::tokenizer
393