1//
2// Copyright (C) 2015 The Android Open Source Project
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8//      http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15//
16
17#include "update_engine/payload_consumer/xz_extent_writer.h"
18
19#include <fcntl.h>
20#include <sys/stat.h>
21#include <sys/types.h>
22#include <unistd.h>
23
24#include <algorithm>
25#include <string>
26#include <vector>
27
28#include <brillo/make_unique_ptr.h>
29#include <gtest/gtest.h>
30
31#include "update_engine/common/test_utils.h"
32#include "update_engine/common/utils.h"
33#include "update_engine/payload_consumer/fake_extent_writer.h"
34
35using std::string;
36using std::vector;
37
38namespace chromeos_update_engine {
39
40namespace {
41
42const char kSampleData[] = "Redundaaaaaaaaaaaaaant\n";
43
44// Compressed data with CRC-32 check, generated with:
45// echo "Redundaaaaaaaaaaaaaant" | xz -9 --check=crc32 |
46// hexdump -v -e '"    " 12/1 "0x%02x, " "\n"'
47const uint8_t kCompressedDataCRC32[] = {
48    0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00, 0x00, 0x01, 0x69, 0x22, 0xde, 0x36,
49    0x02, 0x00, 0x21, 0x01, 0x1c, 0x00, 0x00, 0x00, 0x10, 0xcf, 0x58, 0xcc,
50    0xe0, 0x00, 0x16, 0x00, 0x10, 0x5d, 0x00, 0x29, 0x19, 0x48, 0x87, 0x88,
51    0xec, 0x49, 0x88, 0x73, 0x8b, 0x5d, 0xa6, 0x46, 0xb4, 0x00, 0x00, 0x00,
52    0x68, 0xfc, 0x7b, 0x25, 0x00, 0x01, 0x28, 0x17, 0x46, 0x9e, 0x08, 0xfe,
53    0x90, 0x42, 0x99, 0x0d, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x59, 0x5a,
54};
55
56// Compressed data without checksum, generated with:
57// echo "Redundaaaaaaaaaaaaaant" | xz -9 --check=none |
58// hexdump -v -e '"    " 12/1 "0x%02x, " "\n"'
59const uint8_t kCompressedDataNoCheck[] = {
60    0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00, 0x00, 0x00, 0xff, 0x12, 0xd9, 0x41,
61    0x02, 0x00, 0x21, 0x01, 0x1c, 0x00, 0x00, 0x00, 0x10, 0xcf, 0x58, 0xcc,
62    0xe0, 0x00, 0x16, 0x00, 0x10, 0x5d, 0x00, 0x29, 0x19, 0x48, 0x87, 0x88,
63    0xec, 0x49, 0x88, 0x73, 0x8b, 0x5d, 0xa6, 0x46, 0xb4, 0x00, 0x00, 0x00,
64    0x00, 0x01, 0x24, 0x17, 0x4a, 0xd1, 0xbd, 0x52, 0x06, 0x72, 0x9e, 0x7a,
65    0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x5a,
66};
67
68// Highly redundant data bigger than the internal buffer, generated with:
69// dd if=/dev/zero bs=30K count=1 | tr '\0' 'a' | xz -9 --check=crc32 |
70// hexdump -v -e '"    " 12/1 "0x%02x, " "\n"'
71const uint8_t kCompressed30KiBofA[] = {
72    0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00, 0x00, 0x01, 0x69, 0x22, 0xde, 0x36,
73    0x02, 0x00, 0x21, 0x01, 0x1c, 0x00, 0x00, 0x00, 0x10, 0xcf, 0x58, 0xcc,
74    0xe0, 0x77, 0xff, 0x00, 0x41, 0x5d, 0x00, 0x30, 0xef, 0xfb, 0xbf, 0xfe,
75    0xa3, 0xb1, 0x5e, 0xe5, 0xf8, 0x3f, 0xb2, 0xaa, 0x26, 0x55, 0xf8, 0x68,
76    0x70, 0x41, 0x70, 0x15, 0x0f, 0x8d, 0xfd, 0x1e, 0x4c, 0x1b, 0x8a, 0x42,
77    0xb7, 0x19, 0xf4, 0x69, 0x18, 0x71, 0xae, 0x66, 0x23, 0x8a, 0x8a, 0x4d,
78    0x2f, 0xa3, 0x0d, 0xd9, 0x7f, 0xa6, 0xe3, 0x8c, 0x23, 0x11, 0x53, 0xe0,
79    0x59, 0x18, 0xc5, 0x75, 0x8a, 0xe2, 0x76, 0x4c, 0xee, 0x30, 0x00, 0x00,
80    0x00, 0x00, 0x00, 0x00, 0xf9, 0x47, 0xb5, 0xee, 0x00, 0x01, 0x59, 0x80,
81    0xf0, 0x01, 0x00, 0x00, 0xe0, 0x41, 0x96, 0xde, 0x3e, 0x30, 0x0d, 0x8b,
82    0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x59, 0x5a,
83};
84
85}  // namespace
86
87class XzExtentWriterTest : public ::testing::Test {
88 protected:
89  void SetUp() override {
90    fake_extent_writer_ = new FakeExtentWriter();
91    xz_writer_.reset(
92        new XzExtentWriter(brillo::make_unique_ptr(fake_extent_writer_)));
93  }
94
95  void WriteAll(const brillo::Blob& compressed) {
96    EXPECT_TRUE(xz_writer_->Init(fd_, {}, 1024));
97    EXPECT_TRUE(xz_writer_->Write(compressed.data(), compressed.size()));
98    EXPECT_TRUE(xz_writer_->End());
99
100    EXPECT_TRUE(fake_extent_writer_->InitCalled());
101    EXPECT_TRUE(fake_extent_writer_->EndCalled());
102  }
103
104  // Owned by |xz_writer_|. This object is invalidated after |xz_writer_| is
105  // deleted.
106  FakeExtentWriter* fake_extent_writer_{nullptr};
107  std::unique_ptr<XzExtentWriter> xz_writer_;
108
109  const brillo::Blob sample_data_{
110      std::begin(kSampleData),
111      std::begin(kSampleData) + strlen(kSampleData)};
112  FileDescriptorPtr fd_;
113};
114
115TEST_F(XzExtentWriterTest, CreateAndDestroy) {
116  // Test that no Init() or End() called doesn't crash the program.
117  EXPECT_FALSE(fake_extent_writer_->InitCalled());
118  EXPECT_FALSE(fake_extent_writer_->EndCalled());
119}
120
121TEST_F(XzExtentWriterTest, CompressedSampleData) {
122  WriteAll(brillo::Blob(std::begin(kCompressedDataNoCheck),
123                          std::end(kCompressedDataNoCheck)));
124  EXPECT_EQ(sample_data_, fake_extent_writer_->WrittenData());
125}
126
127TEST_F(XzExtentWriterTest, CompressedSampleDataWithCrc) {
128  WriteAll(brillo::Blob(std::begin(kCompressedDataCRC32),
129                          std::end(kCompressedDataCRC32)));
130  EXPECT_EQ(sample_data_, fake_extent_writer_->WrittenData());
131}
132
133TEST_F(XzExtentWriterTest, CompressedDataBiggerThanTheBuffer) {
134  // Test that even if the output data is bigger than the internal buffer, all
135  // the data is written.
136  WriteAll(brillo::Blob(std::begin(kCompressed30KiBofA),
137                          std::end(kCompressed30KiBofA)));
138  brillo::Blob expected_data(30 * 1024, 'a');
139  EXPECT_EQ(expected_data, fake_extent_writer_->WrittenData());
140}
141
142TEST_F(XzExtentWriterTest, GarbageDataRejected) {
143  EXPECT_TRUE(xz_writer_->Init(fd_, {}, 1024));
144  // The sample_data_ is an uncompressed string.
145  EXPECT_FALSE(xz_writer_->Write(sample_data_.data(), sample_data_.size()));
146  EXPECT_TRUE(xz_writer_->End());
147
148  EXPECT_TRUE(fake_extent_writer_->EndCalled());
149}
150
151TEST_F(XzExtentWriterTest, PartialDataIsKept) {
152  brillo::Blob compressed(std::begin(kCompressed30KiBofA),
153                            std::end(kCompressed30KiBofA));
154  EXPECT_TRUE(xz_writer_->Init(fd_, {}, 1024));
155  for (uint8_t byte : compressed) {
156    EXPECT_TRUE(xz_writer_->Write(&byte, 1));
157  }
158  EXPECT_TRUE(xz_writer_->End());
159
160  // The sample_data_ is an uncompressed string.
161  brillo::Blob expected_data(30 * 1024, 'a');
162  EXPECT_EQ(expected_data, fake_extent_writer_->WrittenData());
163}
164
165}  // namespace chromeos_update_engine
166