| // Copyright 2019 The Crashpad Authors. All rights reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "util/stream/base94_output_stream.h" |
| |
| #include <string.h> |
| |
| #include <algorithm> |
| #include <sstream> |
| |
| #include "base/macros.h" |
| #include "base/rand_util.h" |
| #include "base/stl_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "gtest/gtest.h" |
| #include "util/stream/test_output_stream.h" |
| |
| namespace crashpad { |
| namespace test { |
| namespace { |
| |
| constexpr size_t kLongDataLength = 4096 * 10; |
| |
| std::string DumpInput(const uint8_t* input, size_t size) { |
| std::stringstream s; |
| size_t index = 0; |
| size_t byte_count = 0; |
| while (index < size) { |
| s << "0x" << std::hex << static_cast<int>(*(input + index++)) << ","; |
| if (byte_count++ > 1024) { |
| s << "\n"; |
| byte_count = 0; |
| } |
| } |
| return s.str(); |
| } |
| |
| class Base94OutputStreamTest : public testing::Test { |
| public: |
| Base94OutputStreamTest() {} |
| |
| protected: |
| void SetUp() override { |
| auto output_stream = std::make_unique<TestOutputStream>(); |
| encode_test_output_stream_ = output_stream.get(); |
| encoder_ = std::make_unique<Base94OutputStream>( |
| Base94OutputStream::Mode::kEncode, std::move(output_stream)); |
| output_stream = std::make_unique<TestOutputStream>(); |
| decode_test_output_stream_ = output_stream.get(); |
| decoder_ = std::make_unique<Base94OutputStream>( |
| Base94OutputStream::Mode::kDecode, std::move(output_stream)); |
| output_stream = std::make_unique<TestOutputStream>(); |
| round_trip_test_output_stream_ = output_stream.get(); |
| round_trip_ = std::make_unique<Base94OutputStream>( |
| Base94OutputStream::Mode::kEncode, |
| std::make_unique<Base94OutputStream>(Base94OutputStream::Mode::kDecode, |
| std::move(output_stream))); |
| } |
| |
| const uint8_t* BuildDeterministicInput(size_t size) { |
| deterministic_input_ = std::make_unique<uint8_t[]>(size); |
| uint8_t* deterministic_input_base = deterministic_input_.get(); |
| while (size-- > 0) |
| deterministic_input_base[size] = static_cast<uint8_t>(size); |
| return deterministic_input_base; |
| } |
| |
| const uint8_t* BuildRandomInput(size_t size) { |
| input_ = std::make_unique<uint8_t[]>(size); |
| base::RandBytes(&input_[0], size); |
| return input_.get(); |
| } |
| |
| Base94OutputStream* round_trip() const { return round_trip_.get(); } |
| const TestOutputStream& round_trip_test_output_stream() const { |
| return *round_trip_test_output_stream_; |
| } |
| |
| static void VerifyEncoding(const TestOutputStream& out, |
| const std::string& expected) { |
| EXPECT_EQ(out.all_data().size(), expected.size()); |
| EXPECT_EQ(memcmp(out.all_data().data(), expected.data(), expected.size()), |
| 0); |
| } |
| |
| static void VerifyDecoding(const TestOutputStream& out, |
| const std::vector<uint8_t>& expected) { |
| EXPECT_EQ(out.all_data().size(), expected.size()); |
| EXPECT_EQ(memcmp(out.all_data().data(), expected.data(), expected.size()), |
| 0); |
| } |
| |
| void RunTest(const std::string& text, const std::vector<uint8_t>& binary) { |
| EXPECT_TRUE(encoder_->Write(binary.data(), binary.size())); |
| EXPECT_TRUE(encoder_->Flush()); |
| VerifyEncoding(*encode_test_output_stream_, text); |
| EXPECT_TRUE(decoder_->Write(reinterpret_cast<const uint8_t*>(text.data()), |
| text.size())); |
| EXPECT_TRUE(decoder_->Flush()); |
| VerifyDecoding(*decode_test_output_stream_, binary); |
| } |
| |
| void VerifyRoundTrip(const std::vector<uint8_t>& expected) { |
| TestOutputStream* out = round_trip_test_output_stream_; |
| EXPECT_EQ(out->all_data().size(), expected.size()); |
| EXPECT_EQ(memcmp(out->all_data().data(), expected.data(), expected.size()), |
| 0); |
| } |
| |
| private: |
| std::unique_ptr<Base94OutputStream> encoder_; |
| std::unique_ptr<Base94OutputStream> decoder_; |
| std::unique_ptr<Base94OutputStream> round_trip_; |
| TestOutputStream* encode_test_output_stream_; |
| TestOutputStream* decode_test_output_stream_; |
| TestOutputStream* round_trip_test_output_stream_; |
| std::unique_ptr<uint8_t[]> input_; |
| std::unique_ptr<uint8_t[]> deterministic_input_; |
| |
| DISALLOW_COPY_AND_ASSIGN(Base94OutputStreamTest); |
| }; |
| |
| TEST_F(Base94OutputStreamTest, Encoding) { |
| std::vector<uint8_t> binary = {0x0}; |
| std::string text("!"); |
| RunTest(text, binary); |
| } |
| |
| TEST_F(Base94OutputStreamTest, Encoding1) { |
| std::vector<uint8_t> binary = {0x0, 0x0}; |
| std::string text("!!!"); |
| RunTest(text, binary); |
| } |
| |
| TEST_F(Base94OutputStreamTest, Encoding2) { |
| std::vector<uint8_t> binary = {0x0, 0x0, 0x0}; |
| std::string text("!!!!"); |
| RunTest(text, binary); |
| } |
| |
| TEST_F(Base94OutputStreamTest, Encoding3) { |
| std::vector<uint8_t> binary = {0x0, 0x0, 0x0, 0x0}; |
| std::string text("!!!!!"); |
| RunTest(text, binary); |
| } |
| |
| TEST_F(Base94OutputStreamTest, Encoding4) { |
| std::vector<uint8_t> binary = {0x0, 0x0, 0x0, 0x0, 0x0}; |
| std::string text("!!!!!!"); |
| RunTest(text, binary); |
| } |
| |
| TEST_F(Base94OutputStreamTest, Encoding10) { |
| std::vector<uint8_t> binary = {0xFF}; |
| std::string text("d#"); |
| RunTest(text, binary); |
| } |
| |
| TEST_F(Base94OutputStreamTest, Encoding11) { |
| std::vector<uint8_t> binary = {0xFF, 0xFF}; |
| std::string text(".x("); |
| RunTest(text, binary); |
| } |
| |
| TEST_F(Base94OutputStreamTest, Encoding12) { |
| std::vector<uint8_t> binary = {0xFF, 0xFF, 0xFF}; |
| std::string text(".xj6"); |
| RunTest(text, binary); |
| } |
| |
| TEST_F(Base94OutputStreamTest, Encoding13) { |
| std::vector<uint8_t> binary = {0xFF, 0xFF, 0xFF, 0xFF}; |
| std::string text(".x.x`"); |
| RunTest(text, binary); |
| } |
| |
| TEST_F(Base94OutputStreamTest, Encoding14) { |
| std::vector<uint8_t> binary = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; |
| std::string text(".x.x.x\""); |
| RunTest(text, binary); |
| } |
| |
| TEST_F(Base94OutputStreamTest, Printable94) { |
| std::vector<uint8_t> binary = { |
| 0x5e, 0x0, 0x47, 0xa0, 0x1d, 0x60, 0xa, 0xab, 0x41, 0x41, 0xa4, 0x9, |
| 0x64, 0x71, 0x32, 0xc, 0x47, 0xf9, 0x20, 0x22, 0xa3, 0x44, 0xa0, 0x84, |
| 0x15, 0xe0, 0xf2, 0x61, 0xfc, 0x4c, 0xb7, 0xe1, 0x39, 0x9b, 0x47, 0xff, |
| 0x64, 0x21, 0x5c, 0x74, 0x91, 0xec, 0x52, 0x75, 0xa2, 0x51, 0x93, 0x4a, |
| 0x5e, 0x45, 0x2d, 0xd8, 0xf5, 0xc0, 0xdc, 0x58, 0x33, 0x63, 0x69, 0x8b, |
| 0x4d, 0xbd, 0x25, 0x39, 0x54, 0x77, 0xf0, 0xcc, 0x5e, 0xf1, 0x23, 0x81, |
| 0x6, 0x21, 0x71, 0x28, 0x28, 0x2}; |
| std::string text( |
| "!\"#$%&'()*+,-./" |
| "0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`" |
| "abcdefghijklmnopqrstuvwxyz{|}~!"); |
| RunTest(text, binary); |
| } |
| |
| TEST_F(Base94OutputStreamTest, WriteLongDataMultipleTimes) { |
| const uint8_t* input = BuildRandomInput(kLongDataLength); |
| SCOPED_TRACE(base::StringPrintf("Input: %s", |
| DumpInput(input, kLongDataLength).c_str())); |
| // Call Write() a random number of times. |
| size_t index = 0; |
| while (index < kLongDataLength) { |
| size_t write_length = |
| std::min(static_cast<size_t>(base::RandInt(0, 4096 * 2)), |
| kLongDataLength - index); |
| SCOPED_TRACE( |
| base::StringPrintf("index %zu, write_length %zu", index, write_length)); |
| EXPECT_TRUE(round_trip()->Write(input + index, write_length)); |
| index += write_length; |
| } |
| EXPECT_TRUE(round_trip()->Flush()); |
| EXPECT_EQ(round_trip_test_output_stream().all_data().size(), kLongDataLength); |
| EXPECT_EQ(memcmp(round_trip_test_output_stream().all_data().data(), |
| input, |
| kLongDataLength), |
| 0); |
| } |
| |
| TEST_F(Base94OutputStreamTest, WriteDeterministicLongDataMultipleTimes) { |
| const uint8_t* input = BuildDeterministicInput(kLongDataLength); |
| |
| static constexpr size_t kWriteLengths[] = { |
| 4, 96, 40, kLongDataLength - 4 - 96 - 40}; |
| |
| size_t offset = 0; |
| for (size_t index = 0; index < base::size(kWriteLengths); ++index) { |
| const size_t write_length = kWriteLengths[index]; |
| SCOPED_TRACE(base::StringPrintf( |
| "offset %zu, write_length %zu", offset, write_length)); |
| EXPECT_TRUE(round_trip()->Write(input + offset, write_length)); |
| offset += write_length; |
| } |
| EXPECT_TRUE(round_trip()->Flush()); |
| EXPECT_EQ(round_trip_test_output_stream().all_data().size(), kLongDataLength); |
| EXPECT_EQ(memcmp(round_trip_test_output_stream().all_data().data(), |
| input, |
| kLongDataLength), |
| 0); |
| } |
| |
| TEST_F(Base94OutputStreamTest, NoWriteOrFlush) { |
| EXPECT_EQ(round_trip_test_output_stream().write_count(), 0u); |
| EXPECT_EQ(round_trip_test_output_stream().flush_count(), 0u); |
| EXPECT_TRUE(round_trip_test_output_stream().all_data().empty()); |
| } |
| |
| TEST_F(Base94OutputStreamTest, FlushWithoutWrite) { |
| EXPECT_TRUE(round_trip()->Flush()); |
| EXPECT_EQ(round_trip_test_output_stream().write_count(), 0u); |
| EXPECT_EQ(round_trip_test_output_stream().flush_count(), 1u); |
| EXPECT_TRUE(round_trip_test_output_stream().all_data().empty()); |
| } |
| |
| TEST_F(Base94OutputStreamTest, WriteEmptyData) { |
| std::vector<uint8_t> empty_data; |
| EXPECT_TRUE(round_trip()->Write( |
| static_cast<const uint8_t*>(empty_data.data()), empty_data.size())); |
| EXPECT_TRUE(round_trip()->Flush()); |
| EXPECT_TRUE(round_trip()->Flush()); |
| EXPECT_EQ(round_trip_test_output_stream().write_count(), 0u); |
| EXPECT_EQ(round_trip_test_output_stream().flush_count(), 2u); |
| EXPECT_TRUE(round_trip_test_output_stream().all_data().empty()); |
| } |
| |
| TEST_F(Base94OutputStreamTest, Process7bitsInFinishDecoding) { |
| std::vector<uint8_t> input = { |
| 0xff, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; |
| EXPECT_TRUE(round_trip()->Write(static_cast<const uint8_t*>(input.data()), |
| input.size())); |
| EXPECT_TRUE(round_trip()->Flush()); |
| VerifyRoundTrip(input); |
| } |
| |
| } // namespace |
| } // namespace test |
| } // namespace crashpad |