1 // Copyright 2019 The Crashpad Authors. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "util/stream/base94_output_stream.h"
16 
17 #include <string.h>
18 
19 #include <algorithm>
20 #include <sstream>
21 
22 #include "base/macros.h"
23 #include "base/rand_util.h"
24 #include "base/stl_util.h"
25 #include "base/strings/stringprintf.h"
26 #include "gtest/gtest.h"
27 #include "util/stream/test_output_stream.h"
28 
29 namespace crashpad {
30 namespace test {
31 namespace {
32 
33 constexpr size_t kLongDataLength = 4096 * 10;
34 
DumpInput(const uint8_t * input,size_t size)35 std::string DumpInput(const uint8_t* input, size_t size) {
36   std::stringstream s;
37   size_t index = 0;
38   size_t byte_count = 0;
39   while (index < size) {
40     s << "0x" << std::hex << static_cast<int>(*(input + index++)) << ",";
41     if (byte_count++ > 1024) {
42       s << "\n";
43       byte_count = 0;
44     }
45   }
46   return s.str();
47 }
48 
49 class Base94OutputStreamTest : public testing::Test {
50  public:
Base94OutputStreamTest()51   Base94OutputStreamTest() {}
52 
53  protected:
SetUp()54   void SetUp() override {
55     auto output_stream = std::make_unique<TestOutputStream>();
56     encode_test_output_stream_ = output_stream.get();
57     encoder_ = std::make_unique<Base94OutputStream>(
58         Base94OutputStream::Mode::kEncode, std::move(output_stream));
59     output_stream = std::make_unique<TestOutputStream>();
60     decode_test_output_stream_ = output_stream.get();
61     decoder_ = std::make_unique<Base94OutputStream>(
62         Base94OutputStream::Mode::kDecode, std::move(output_stream));
63     output_stream = std::make_unique<TestOutputStream>();
64     round_trip_test_output_stream_ = output_stream.get();
65     round_trip_ = std::make_unique<Base94OutputStream>(
66         Base94OutputStream::Mode::kEncode,
67         std::make_unique<Base94OutputStream>(Base94OutputStream::Mode::kDecode,
68                                              std::move(output_stream)));
69   }
70 
BuildDeterministicInput(size_t size)71   const uint8_t* BuildDeterministicInput(size_t size) {
72     deterministic_input_ = std::make_unique<uint8_t[]>(size);
73     uint8_t* deterministic_input_base = deterministic_input_.get();
74     while (size-- > 0)
75       deterministic_input_base[size] = static_cast<uint8_t>(size);
76     return deterministic_input_base;
77   }
78 
BuildRandomInput(size_t size)79   const uint8_t* BuildRandomInput(size_t size) {
80     input_ = std::make_unique<uint8_t[]>(size);
81     base::RandBytes(&input_[0], size);
82     return input_.get();
83   }
84 
round_trip() const85   Base94OutputStream* round_trip() const { return round_trip_.get(); }
round_trip_test_output_stream() const86   const TestOutputStream& round_trip_test_output_stream() const {
87     return *round_trip_test_output_stream_;
88   }
89 
VerifyEncoding(const TestOutputStream & out,const std::string & expected)90   static void VerifyEncoding(const TestOutputStream& out,
91                              const std::string& expected) {
92     EXPECT_EQ(out.all_data().size(), expected.size());
93     EXPECT_EQ(memcmp(out.all_data().data(), expected.data(), expected.size()),
94               0);
95   }
96 
VerifyDecoding(const TestOutputStream & out,const std::vector<uint8_t> & expected)97   static void VerifyDecoding(const TestOutputStream& out,
98                              const std::vector<uint8_t>& expected) {
99     EXPECT_EQ(out.all_data().size(), expected.size());
100     EXPECT_EQ(memcmp(out.all_data().data(), expected.data(), expected.size()),
101               0);
102   }
103 
RunTest(const std::string & text,const std::vector<uint8_t> & binary)104   void RunTest(const std::string& text, const std::vector<uint8_t>& binary) {
105     EXPECT_TRUE(encoder_->Write(binary.data(), binary.size()));
106     EXPECT_TRUE(encoder_->Flush());
107     VerifyEncoding(*encode_test_output_stream_, text);
108     EXPECT_TRUE(decoder_->Write(reinterpret_cast<const uint8_t*>(text.data()),
109                                 text.size()));
110     EXPECT_TRUE(decoder_->Flush());
111     VerifyDecoding(*decode_test_output_stream_, binary);
112   }
113 
VerifyRoundTrip(const std::vector<uint8_t> & expected)114   void VerifyRoundTrip(const std::vector<uint8_t>& expected) {
115     TestOutputStream* out = round_trip_test_output_stream_;
116     EXPECT_EQ(out->all_data().size(), expected.size());
117     EXPECT_EQ(memcmp(out->all_data().data(), expected.data(), expected.size()),
118               0);
119   }
120 
121  private:
122   std::unique_ptr<Base94OutputStream> encoder_;
123   std::unique_ptr<Base94OutputStream> decoder_;
124   std::unique_ptr<Base94OutputStream> round_trip_;
125   TestOutputStream* encode_test_output_stream_;
126   TestOutputStream* decode_test_output_stream_;
127   TestOutputStream* round_trip_test_output_stream_;
128   std::unique_ptr<uint8_t[]> input_;
129   std::unique_ptr<uint8_t[]> deterministic_input_;
130 
131   DISALLOW_COPY_AND_ASSIGN(Base94OutputStreamTest);
132 };
133 
TEST_F(Base94OutputStreamTest,Encoding)134 TEST_F(Base94OutputStreamTest, Encoding) {
135   std::vector<uint8_t> binary = {0x0};
136   std::string text("!");
137   RunTest(text, binary);
138 }
139 
TEST_F(Base94OutputStreamTest,Encoding1)140 TEST_F(Base94OutputStreamTest, Encoding1) {
141   std::vector<uint8_t> binary = {0x0, 0x0};
142   std::string text("!!!");
143   RunTest(text, binary);
144 }
145 
TEST_F(Base94OutputStreamTest,Encoding2)146 TEST_F(Base94OutputStreamTest, Encoding2) {
147   std::vector<uint8_t> binary = {0x0, 0x0, 0x0};
148   std::string text("!!!!");
149   RunTest(text, binary);
150 }
151 
TEST_F(Base94OutputStreamTest,Encoding3)152 TEST_F(Base94OutputStreamTest, Encoding3) {
153   std::vector<uint8_t> binary = {0x0, 0x0, 0x0, 0x0};
154   std::string text("!!!!!");
155   RunTest(text, binary);
156 }
157 
TEST_F(Base94OutputStreamTest,Encoding4)158 TEST_F(Base94OutputStreamTest, Encoding4) {
159   std::vector<uint8_t> binary = {0x0, 0x0, 0x0, 0x0, 0x0};
160   std::string text("!!!!!!");
161   RunTest(text, binary);
162 }
163 
TEST_F(Base94OutputStreamTest,Encoding10)164 TEST_F(Base94OutputStreamTest, Encoding10) {
165   std::vector<uint8_t> binary = {0xFF};
166   std::string text("d#");
167   RunTest(text, binary);
168 }
169 
TEST_F(Base94OutputStreamTest,Encoding11)170 TEST_F(Base94OutputStreamTest, Encoding11) {
171   std::vector<uint8_t> binary = {0xFF, 0xFF};
172   std::string text(".x(");
173   RunTest(text, binary);
174 }
175 
TEST_F(Base94OutputStreamTest,Encoding12)176 TEST_F(Base94OutputStreamTest, Encoding12) {
177   std::vector<uint8_t> binary = {0xFF, 0xFF, 0xFF};
178   std::string text(".xj6");
179   RunTest(text, binary);
180 }
181 
TEST_F(Base94OutputStreamTest,Encoding13)182 TEST_F(Base94OutputStreamTest, Encoding13) {
183   std::vector<uint8_t> binary = {0xFF, 0xFF, 0xFF, 0xFF};
184   std::string text(".x.x`");
185   RunTest(text, binary);
186 }
187 
TEST_F(Base94OutputStreamTest,Encoding14)188 TEST_F(Base94OutputStreamTest, Encoding14) {
189   std::vector<uint8_t> binary = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
190   std::string text(".x.x.x\"");
191   RunTest(text, binary);
192 }
193 
TEST_F(Base94OutputStreamTest,Printable94)194 TEST_F(Base94OutputStreamTest, Printable94) {
195   std::vector<uint8_t> binary = {
196       0x5e, 0x0,  0x47, 0xa0, 0x1d, 0x60, 0xa,  0xab, 0x41, 0x41, 0xa4, 0x9,
197       0x64, 0x71, 0x32, 0xc,  0x47, 0xf9, 0x20, 0x22, 0xa3, 0x44, 0xa0, 0x84,
198       0x15, 0xe0, 0xf2, 0x61, 0xfc, 0x4c, 0xb7, 0xe1, 0x39, 0x9b, 0x47, 0xff,
199       0x64, 0x21, 0x5c, 0x74, 0x91, 0xec, 0x52, 0x75, 0xa2, 0x51, 0x93, 0x4a,
200       0x5e, 0x45, 0x2d, 0xd8, 0xf5, 0xc0, 0xdc, 0x58, 0x33, 0x63, 0x69, 0x8b,
201       0x4d, 0xbd, 0x25, 0x39, 0x54, 0x77, 0xf0, 0xcc, 0x5e, 0xf1, 0x23, 0x81,
202       0x6,  0x21, 0x71, 0x28, 0x28, 0x2};
203   std::string text(
204       "!\"#$%&'()*+,-./"
205       "0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`"
206       "abcdefghijklmnopqrstuvwxyz{|}~!");
207   RunTest(text, binary);
208 }
209 
TEST_F(Base94OutputStreamTest,WriteLongDataMultipleTimes)210 TEST_F(Base94OutputStreamTest, WriteLongDataMultipleTimes) {
211   const uint8_t* input = BuildRandomInput(kLongDataLength);
212   SCOPED_TRACE(base::StringPrintf("Input: %s",
213                                   DumpInput(input, kLongDataLength).c_str()));
214   // Call Write() a random number of times.
215   size_t index = 0;
216   while (index < kLongDataLength) {
217     size_t write_length =
218         std::min(static_cast<size_t>(base::RandInt(0, 4096 * 2)),
219                  kLongDataLength - index);
220     SCOPED_TRACE(
221         base::StringPrintf("index %zu, write_length %zu", index, write_length));
222     EXPECT_TRUE(round_trip()->Write(input + index, write_length));
223     index += write_length;
224   }
225   EXPECT_TRUE(round_trip()->Flush());
226   EXPECT_EQ(round_trip_test_output_stream().all_data().size(), kLongDataLength);
227   EXPECT_EQ(memcmp(round_trip_test_output_stream().all_data().data(),
228                    input,
229                    kLongDataLength),
230             0);
231 }
232 
TEST_F(Base94OutputStreamTest,WriteDeterministicLongDataMultipleTimes)233 TEST_F(Base94OutputStreamTest, WriteDeterministicLongDataMultipleTimes) {
234   const uint8_t* input = BuildDeterministicInput(kLongDataLength);
235 
236   static constexpr size_t kWriteLengths[] = {
237       4, 96, 40, kLongDataLength - 4 - 96 - 40};
238 
239   size_t offset = 0;
240   for (size_t index = 0; index < base::size(kWriteLengths); ++index) {
241     const size_t write_length = kWriteLengths[index];
242     SCOPED_TRACE(base::StringPrintf(
243         "offset %zu, write_length %zu", offset, write_length));
244     EXPECT_TRUE(round_trip()->Write(input + offset, write_length));
245     offset += write_length;
246   }
247   EXPECT_TRUE(round_trip()->Flush());
248   EXPECT_EQ(round_trip_test_output_stream().all_data().size(), kLongDataLength);
249   EXPECT_EQ(memcmp(round_trip_test_output_stream().all_data().data(),
250                    input,
251                    kLongDataLength),
252             0);
253 }
254 
TEST_F(Base94OutputStreamTest,NoWriteOrFlush)255 TEST_F(Base94OutputStreamTest, NoWriteOrFlush) {
256   EXPECT_EQ(round_trip_test_output_stream().write_count(), 0u);
257   EXPECT_EQ(round_trip_test_output_stream().flush_count(), 0u);
258   EXPECT_TRUE(round_trip_test_output_stream().all_data().empty());
259 }
260 
TEST_F(Base94OutputStreamTest,FlushWithoutWrite)261 TEST_F(Base94OutputStreamTest, FlushWithoutWrite) {
262   EXPECT_TRUE(round_trip()->Flush());
263   EXPECT_EQ(round_trip_test_output_stream().write_count(), 0u);
264   EXPECT_EQ(round_trip_test_output_stream().flush_count(), 1u);
265   EXPECT_TRUE(round_trip_test_output_stream().all_data().empty());
266 }
267 
TEST_F(Base94OutputStreamTest,WriteEmptyData)268 TEST_F(Base94OutputStreamTest, WriteEmptyData) {
269   std::vector<uint8_t> empty_data;
270   EXPECT_TRUE(round_trip()->Write(
271       static_cast<const uint8_t*>(empty_data.data()), empty_data.size()));
272   EXPECT_TRUE(round_trip()->Flush());
273   EXPECT_TRUE(round_trip()->Flush());
274   EXPECT_EQ(round_trip_test_output_stream().write_count(), 0u);
275   EXPECT_EQ(round_trip_test_output_stream().flush_count(), 2u);
276   EXPECT_TRUE(round_trip_test_output_stream().all_data().empty());
277 }
278 
TEST_F(Base94OutputStreamTest,Process7bitsInFinishDecoding)279 TEST_F(Base94OutputStreamTest, Process7bitsInFinishDecoding) {
280   std::vector<uint8_t> input = {
281       0xff, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
282   EXPECT_TRUE(round_trip()->Write(static_cast<const uint8_t*>(input.data()),
283                                   input.size()));
284   EXPECT_TRUE(round_trip()->Flush());
285   VerifyRoundTrip(input);
286 }
287 
288 }  // namespace
289 }  // namespace test
290 }  // namespace crashpad
291