blob: f0f5f7a644fde3475129ed56f622b2eec4330246 [file] [log] [blame]
Andrew de los Reyes80061062010-02-04 14:25:00 -08001// Copyright (c) 2009 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <sys/stat.h>
6#include <sys/types.h>
7#include <unistd.h>
8#include <algorithm>
9#include <string>
10#include <vector>
11#include <gtest/gtest.h>
12#include "update_engine/extent_writer.h"
Andrew de los Reyes09e56d62010-04-23 13:45:53 -070013#include "update_engine/graph_types.h"
Andrew de los Reyes80061062010-02-04 14:25:00 -080014#include "update_engine/test_utils.h"
15#include "update_engine/utils.h"
16
17using std::min;
18using std::string;
19using std::vector;
20
21namespace chromeos_update_engine {
22
23COMPILE_ASSERT(sizeof(off_t) == 8, off_t_not_64_bit);
24
25namespace {
26const char kPathTemplate[] = "./ExtentWriterTest-file.XXXXXX";
27const size_t kBlockSize = 4096;
28}
29
30class ExtentWriterTest : public ::testing::Test {
31 protected:
32 virtual void SetUp() {
33 memcpy(path_, kPathTemplate, sizeof(kPathTemplate));
34 fd_ = mkstemp(path_);
35 ASSERT_GE(fd_, 0);
36 }
37 virtual void TearDown() {
38 close(fd_);
39 unlink(path_);
40 }
41 int fd() { return fd_; }
42 const char* path() { return path_; }
43
44 // Writes data to an extent writer in 'chunk_size' chunks with
45 // the first chunk of size first_chunk_size. It calculates what the
46 // resultant file should look like and ensure that the extent writer
47 // wrote the file correctly.
48 void WriteAlignedExtents(size_t chunk_size, size_t first_chunk_size);
49 void TestZeroPad(bool aligned_size);
50 private:
51 int fd_;
52 char path_[sizeof(kPathTemplate)];
53};
54
55TEST_F(ExtentWriterTest, SimpleTest) {
56 vector<Extent> extents;
57 Extent extent;
58 extent.set_start_block(1);
59 extent.set_num_blocks(1);
60 extents.push_back(extent);
61
62 const string bytes = "1234";
63
64 DirectExtentWriter direct_writer;
65 EXPECT_TRUE(direct_writer.Init(fd(), extents, kBlockSize));
66 EXPECT_TRUE(direct_writer.Write(bytes.data(), bytes.size()));
67 EXPECT_TRUE(direct_writer.End());
68
69 struct stat stbuf;
70 EXPECT_EQ(0, fstat(fd(), &stbuf));
71 EXPECT_EQ(kBlockSize + bytes.size(), stbuf.st_size);
72
73 vector<char> result_file;
74 EXPECT_TRUE(utils::ReadFile(path(), &result_file));
75
76 vector<char> expected_file(kBlockSize);
77 expected_file.insert(expected_file.end(),
78 bytes.data(), bytes.data() + bytes.size());
79 ExpectVectorsEq(expected_file, result_file);
80}
81
82TEST_F(ExtentWriterTest, ZeroLengthTest) {
83 vector<Extent> extents;
84 Extent extent;
85 extent.set_start_block(1);
86 extent.set_num_blocks(1);
87 extents.push_back(extent);
88
89 DirectExtentWriter direct_writer;
90 EXPECT_TRUE(direct_writer.Init(fd(), extents, kBlockSize));
91 EXPECT_TRUE(direct_writer.Write(NULL, 0));
92 EXPECT_TRUE(direct_writer.End());
93}
94
95TEST_F(ExtentWriterTest, OverflowExtentTest) {
96 WriteAlignedExtents(kBlockSize * 3, kBlockSize * 3);
97}
98
99TEST_F(ExtentWriterTest, UnalignedWriteTest) {
100 WriteAlignedExtents(7, 7);
101}
102
103TEST_F(ExtentWriterTest, LargeUnalignedWriteTest) {
104 WriteAlignedExtents(kBlockSize * 2, kBlockSize / 2);
105}
106
107void ExtentWriterTest::WriteAlignedExtents(size_t chunk_size,
108 size_t first_chunk_size) {
109 vector<Extent> extents;
110 Extent extent;
111 extent.set_start_block(1);
112 extent.set_num_blocks(1);
113 extents.push_back(extent);
114 extent.set_start_block(0);
115 extent.set_num_blocks(1);
116 extents.push_back(extent);
117 extent.set_start_block(2);
118 extent.set_num_blocks(1);
119 extents.push_back(extent);
120
121 vector<char> data(kBlockSize * 3);
122 FillWithData(&data);
123
124 DirectExtentWriter direct_writer;
125 EXPECT_TRUE(direct_writer.Init(fd(), extents, kBlockSize));
126
127 size_t bytes_written = 0;
128 while (bytes_written < data.size()) {
129 size_t bytes_to_write = min(data.size() - bytes_written, chunk_size);
130 if (bytes_written == 0) {
131 bytes_to_write = min(data.size() - bytes_written, first_chunk_size);
132 }
133 EXPECT_TRUE(direct_writer.Write(&data[bytes_written], bytes_to_write));
134 bytes_written += bytes_to_write;
135 }
136 EXPECT_TRUE(direct_writer.End());
137
138 struct stat stbuf;
139 EXPECT_EQ(0, fstat(fd(), &stbuf));
140 EXPECT_EQ(data.size(), stbuf.st_size);
141
142 vector<char> result_file;
143 EXPECT_TRUE(utils::ReadFile(path(), &result_file));
144
145 vector<char> expected_file;
146 expected_file.insert(expected_file.end(),
147 data.begin() + kBlockSize,
148 data.begin() + kBlockSize * 2);
149 expected_file.insert(expected_file.end(),
150 data.begin(), data.begin() + kBlockSize);
151 expected_file.insert(expected_file.end(),
152 data.begin() + kBlockSize * 2, data.end());
153 ExpectVectorsEq(expected_file, result_file);
154}
155
156TEST_F(ExtentWriterTest, ZeroPadNullTest) {
157 TestZeroPad(true);
158}
159
160TEST_F(ExtentWriterTest, ZeroPadFillTest) {
161 TestZeroPad(false);
162}
163
164void ExtentWriterTest::TestZeroPad(bool aligned_size) {
165 vector<Extent> extents;
166 Extent extent;
167 extent.set_start_block(1);
168 extent.set_num_blocks(1);
169 extents.push_back(extent);
170 extent.set_start_block(0);
171 extent.set_num_blocks(1);
172 extents.push_back(extent);
173
174 vector<char> data(kBlockSize * 2);
175 FillWithData(&data);
176
177 DirectExtentWriter direct_writer;
178 ZeroPadExtentWriter zero_pad_writer(&direct_writer);
179
180 EXPECT_TRUE(zero_pad_writer.Init(fd(), extents, kBlockSize));
181 size_t bytes_to_write = data.size();
182 const size_t missing_bytes = (aligned_size ? 0 : 9);
183 bytes_to_write -= missing_bytes;
184 lseek64(fd(), kBlockSize - missing_bytes, SEEK_SET);
185 EXPECT_EQ(3, write(fd(), "xxx", 3));
186 ASSERT_TRUE(zero_pad_writer.Write(&data[0], bytes_to_write));
187 EXPECT_TRUE(zero_pad_writer.End());
188
189 struct stat stbuf;
190 EXPECT_EQ(0, fstat(fd(), &stbuf));
191 EXPECT_EQ(data.size(), stbuf.st_size);
192
193 vector<char> result_file;
194 EXPECT_TRUE(utils::ReadFile(path(), &result_file));
195
196 vector<char> expected_file;
197 expected_file.insert(expected_file.end(),
198 data.begin() + kBlockSize,
199 data.begin() + kBlockSize * 2);
200 expected_file.insert(expected_file.end(),
201 data.begin(), data.begin() + kBlockSize);
202 if (missing_bytes) {
203 memset(&expected_file[kBlockSize - missing_bytes], 0, missing_bytes);
204 }
205
206 ExpectVectorsEq(expected_file, result_file);
207}
208
209TEST_F(ExtentWriterTest, SparseFileTest) {
210 vector<Extent> extents;
211 Extent extent;
212 extent.set_start_block(1);
213 extent.set_num_blocks(1);
214 extents.push_back(extent);
215 extent.set_start_block(kSparseHole);
216 extent.set_num_blocks(2);
217 extents.push_back(extent);
218 extent.set_start_block(0);
219 extent.set_num_blocks(1);
220 extents.push_back(extent);
221 const int block_count = 4;
222 const int on_disk_count = 2;
223
224 vector<char> data(17);
225 FillWithData(&data);
226
227 DirectExtentWriter direct_writer;
228 EXPECT_TRUE(direct_writer.Init(fd(), extents, kBlockSize));
229
230 size_t bytes_written = 0;
231 while (bytes_written < (block_count * kBlockSize)) {
232 size_t bytes_to_write = min(block_count * kBlockSize - bytes_written,
233 data.size());
234 EXPECT_TRUE(direct_writer.Write(&data[0], bytes_to_write));
235 bytes_written += bytes_to_write;
236 }
237 EXPECT_TRUE(direct_writer.End());
238
239 // check file size, then data inside
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700240 ASSERT_EQ(2 * kBlockSize, utils::FileSize(path()));
Andrew de los Reyes80061062010-02-04 14:25:00 -0800241
242 vector<char> resultant_data;
243 EXPECT_TRUE(utils::ReadFile(path(), &resultant_data));
244
245 // Create expected data
246 vector<char> expected_data(on_disk_count * kBlockSize);
247 vector<char> big(block_count * kBlockSize);
248 for (vector<char>::size_type i = 0; i < big.size(); i++) {
249 big[i] = data[i % data.size()];
250 }
251 memcpy(&expected_data[kBlockSize], &big[0], kBlockSize);
252 memcpy(&expected_data[0], &big[3 * kBlockSize], kBlockSize);
253 ExpectVectorsEq(expected_data, resultant_data);
254}
255
256} // namespace chromeos_update_engine