blob: 510d1ed3e77a829353bf212cf85977111d820c3d [file] [log] [blame]
Don Garrettf4b28742012-03-27 20:48:06 -07001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
adlr@google.com3defe6a2009-12-04 20:57:17 +00002// 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/types.h>
6#include <sys/stat.h>
7#include <fcntl.h>
8#include <unistd.h>
Thieu Le5c7d9752010-12-15 16:09:28 -08009
Gilad Arnoldfa404502014-01-01 23:36:12 -080010#include <map>
Andrew de los Reyes4fe15d02009-12-10 19:01:36 -080011#include <set>
Andrew de los Reyesef017552010-10-06 17:57:52 -070012#include <sstream>
adlr@google.com3defe6a2009-12-04 20:57:17 +000013#include <string>
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070014#include <utility>
adlr@google.com3defe6a2009-12-04 20:57:17 +000015#include <vector>
Thieu Le5c7d9752010-12-15 16:09:28 -080016
17#include <base/logging.h>
18#include <base/string_util.h>
adlr@google.com3defe6a2009-12-04 20:57:17 +000019#include <gtest/gtest.h>
Thieu Le5c7d9752010-12-15 16:09:28 -080020
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070021#include "update_engine/cycle_breaker.h"
adlr@google.com3defe6a2009-12-04 20:57:17 +000022#include "update_engine/delta_diff_generator.h"
Andrew de los Reyes09e56d62010-04-23 13:45:53 -070023#include "update_engine/delta_performer.h"
Gilad Arnoldfa404502014-01-01 23:36:12 -080024#include "update_engine/extent_mapper.h"
Andrew de los Reyesef017552010-10-06 17:57:52 -070025#include "update_engine/extent_ranges.h"
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070026#include "update_engine/graph_types.h"
27#include "update_engine/graph_utils.h"
adlr@google.com3defe6a2009-12-04 20:57:17 +000028#include "update_engine/subprocess.h"
29#include "update_engine/test_utils.h"
Andrew de los Reyesef017552010-10-06 17:57:52 -070030#include "update_engine/topological_sort.h"
adlr@google.com3defe6a2009-12-04 20:57:17 +000031#include "update_engine/utils.h"
32
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070033using std::make_pair;
34using std::set;
35using std::string;
Andrew de los Reyesef017552010-10-06 17:57:52 -070036using std::stringstream;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070037using std::vector;
38
adlr@google.com3defe6a2009-12-04 20:57:17 +000039namespace chromeos_update_engine {
40
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070041typedef DeltaDiffGenerator::Block Block;
42
Gilad Arnoldfa404502014-01-01 23:36:12 -080043typedef bool (*GetExtentsWithChunk)(const std::string&, off_t, off_t,
44 std::vector<Extent>*);
45extern GetExtentsWithChunk get_extents_with_chunk_func;
46
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070047namespace {
Andrew de los Reyes09e56d62010-04-23 13:45:53 -070048int64_t BlocksInExtents(
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070049 const google::protobuf::RepeatedPtrField<Extent>& extents) {
Andrew de los Reyes09e56d62010-04-23 13:45:53 -070050 int64_t ret = 0;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070051 for (int i = 0; i < extents.size(); i++) {
52 ret += extents.Get(i).num_blocks();
53 }
54 return ret;
55}
56} // namespace {}
57
58class DeltaDiffGeneratorTest : public ::testing::Test {
59 protected:
Gilad Arnold99dcdc82013-07-16 03:00:20 -070060 const string& old_path() { return old_path_; }
61 const string& new_path() { return new_path_; }
62
63 virtual void SetUp() {
64 ASSERT_TRUE(utils::MakeTempFile("DeltaDiffGeneratorTest-old_path-XXXXXX",
65 &old_path_, NULL));
66 ASSERT_TRUE(utils::MakeTempFile("DeltaDiffGeneratorTest-new_path-XXXXXX",
67 &new_path_, NULL));
68 }
69
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070070 virtual void TearDown() {
71 unlink(old_path().c_str());
72 unlink(new_path().c_str());
73 }
Gilad Arnold99dcdc82013-07-16 03:00:20 -070074
75 private:
76 string old_path_;
77 string new_path_;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070078};
79
80TEST_F(DeltaDiffGeneratorTest, RunAsRootMoveSmallTest) {
81 EXPECT_TRUE(utils::WriteFile(old_path().c_str(),
82 reinterpret_cast<const char*>(kRandomString),
83 sizeof(kRandomString)));
84 EXPECT_TRUE(utils::WriteFile(new_path().c_str(),
85 reinterpret_cast<const char*>(kRandomString),
86 sizeof(kRandomString)));
87 vector<char> data;
88 DeltaArchiveManifest_InstallOperation op;
89 EXPECT_TRUE(DeltaDiffGenerator::ReadFileToDiff(old_path(),
90 new_path(),
Darin Petkov8e447e02013-04-16 16:23:50 +020091 0, // chunk_offset
92 -1, // chunk_size
Don Garrett36e60772012-03-29 10:31:20 -070093 true, // bsdiff_allowed
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070094 &data,
Darin Petkov68c10d12010-10-14 09:24:37 -070095 &op,
96 true));
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070097 EXPECT_TRUE(data.empty());
98
99 EXPECT_TRUE(op.has_type());
100 EXPECT_EQ(DeltaArchiveManifest_InstallOperation_Type_MOVE, op.type());
101 EXPECT_FALSE(op.has_data_offset());
102 EXPECT_FALSE(op.has_data_length());
103 EXPECT_EQ(1, op.src_extents_size());
104 EXPECT_EQ(sizeof(kRandomString), op.src_length());
105 EXPECT_EQ(1, op.dst_extents_size());
106 EXPECT_EQ(sizeof(kRandomString), op.dst_length());
107 EXPECT_EQ(BlocksInExtents(op.src_extents()),
108 BlocksInExtents(op.dst_extents()));
109 EXPECT_EQ(1, BlocksInExtents(op.dst_extents()));
110}
111
Gilad Arnoldfa404502014-01-01 23:36:12 -0800112std::map<string, vector<Extent>*> fake_file_extents;
113
114bool FakeGetExtents(const string& path, off_t chunk_offset, off_t chunk_size,
115 vector<Extent>* out) {
116 if (fake_file_extents.count(path) == 1) {
117 *out = *fake_file_extents[path];
118 return true;
119 } else {
120 return false;
121 }
122}
123
124uint64_t AddExtent(uint64_t start_block, uint64_t num_blocks,
125 vector<Extent>* extents) {
126 Extent e;
127 e.set_start_block(start_block);
128 e.set_num_blocks(num_blocks);
129 extents->push_back(e);
130 return num_blocks;
131}
132
133TEST_F(DeltaDiffGeneratorTest, RunAsRootMoveWithSameBlock) {
134 // Mock out the extent gathering function.
135 GetExtentsWithChunk orig_get_extents_with_chunk_func =
136 get_extents_with_chunk_func;
137 get_extents_with_chunk_func = FakeGetExtents;
138
139 // Setup the old/new files so that it has immobile chunks; we make sure to
140 // utilize all sub-cases of such chunks: blocks 21--22 induce a split (src)
141 // and complete removal (dst), whereas blocks 24--25 induce trimming of the
142 // tail (src) and head (dst) of extents. The detailed configuration:
143 //
144 // Old: [ 20 21 22 23 24 25 ] [ 28 ]
145 // New: [ 18 ] [ 21 22 ] [ 20 ] [ 24 25 26 ]
146 // Same: ^^ ^^ ^^ ^^
147 vector<Extent> old_extents;
148 uint64_t num_extents = 0;
149 num_extents += AddExtent(20, 6, &old_extents);
150 num_extents += AddExtent(28, 1, &old_extents);
151 fake_file_extents[old_path()] = &old_extents;
152 vector<Extent> new_extents;
153 AddExtent(18, 1, &new_extents);
154 AddExtent(21, 2, &new_extents);
155 AddExtent(20, 1, &new_extents);
156 AddExtent(24, 3, &new_extents);
157 fake_file_extents[new_path()] = &new_extents;
158
159 // The size of the data should match the total number of blocks.
160 const size_t file_len = 5 * 4096;
161 const string random_string(reinterpret_cast<const char*>(kRandomString),
162 sizeof(kRandomString));
163 string random_data;
164 while (random_data.size() < file_len)
165 random_data += random_string;
166 if (random_data.size() > file_len)
167 random_data.erase(file_len);
168 EXPECT_TRUE(utils::WriteFile(old_path().c_str(),
169 random_data.c_str(), file_len));
170 EXPECT_TRUE(utils::WriteFile(new_path().c_str(),
171 random_data.c_str(), file_len));
172
173 vector<char> data;
174 DeltaArchiveManifest_InstallOperation op;
175 EXPECT_TRUE(DeltaDiffGenerator::ReadFileToDiff(old_path(),
176 new_path(),
177 0, // chunk_offset
178 -1, // chunk_size
179 true, // bsdiff_allowed
180 &data,
181 &op,
182 true));
183
184 // Adjust the old/new extents to remove duplicates.
185 old_extents[0].set_num_blocks(1);
186 Extent e;
187 e.set_start_block(23);
188 e.set_num_blocks(1);
189 old_extents.insert(old_extents.begin() + 1, e);
190 new_extents.erase(new_extents.begin() + 1);
191 new_extents[2].set_start_block(26);
192 new_extents[2].set_num_blocks(1);
193 num_extents -= 4;
194
195 EXPECT_TRUE(data.empty());
196
197 EXPECT_TRUE(op.has_type());
198 EXPECT_EQ(DeltaArchiveManifest_InstallOperation_Type_MOVE, op.type());
199 EXPECT_FALSE(op.has_data_offset());
200 EXPECT_FALSE(op.has_data_length());
201
202 EXPECT_EQ(file_len, op.src_length());
203 EXPECT_EQ(num_extents, BlocksInExtents(op.src_extents()));
204 EXPECT_EQ(old_extents.size(), op.src_extents_size());
205 for (int i = 0; i < op.src_extents_size(); i++) {
206 EXPECT_EQ(old_extents[i].start_block(), op.src_extents(i).start_block())
207 << "i == " << i;
208 EXPECT_EQ(old_extents[i].num_blocks(), op.src_extents(i).num_blocks())
209 << "i == " << i;
210 }
211
212 EXPECT_EQ(file_len, op.dst_length());
213 EXPECT_EQ(num_extents, BlocksInExtents(op.dst_extents()));
214 EXPECT_EQ(new_extents.size(), op.dst_extents_size());
215 for (int i = 0; i < op.dst_extents_size(); i++) {
216 EXPECT_EQ(new_extents[i].start_block(), op.dst_extents(i).start_block())
217 << "i == " << i;
218 EXPECT_EQ(new_extents[i].num_blocks(), op.dst_extents(i).num_blocks())
219 << "i == " << i;
220 }
221
222 // Clean up fake extents and restore the module's extent gathering logic.
223 fake_file_extents.clear();
224 get_extents_with_chunk_func = orig_get_extents_with_chunk_func;
225}
226
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700227TEST_F(DeltaDiffGeneratorTest, RunAsRootBsdiffSmallTest) {
228 EXPECT_TRUE(utils::WriteFile(old_path().c_str(),
229 reinterpret_cast<const char*>(kRandomString),
230 sizeof(kRandomString) - 1));
231 EXPECT_TRUE(utils::WriteFile(new_path().c_str(),
232 reinterpret_cast<const char*>(kRandomString),
233 sizeof(kRandomString)));
234 vector<char> data;
235 DeltaArchiveManifest_InstallOperation op;
236 EXPECT_TRUE(DeltaDiffGenerator::ReadFileToDiff(old_path(),
237 new_path(),
Darin Petkov8e447e02013-04-16 16:23:50 +0200238 0, // chunk_offset
239 -1, // chunk_size
Don Garrett36e60772012-03-29 10:31:20 -0700240 true, // bsdiff_allowed
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700241 &data,
Darin Petkov68c10d12010-10-14 09:24:37 -0700242 &op,
243 true));
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700244 EXPECT_FALSE(data.empty());
245
246 EXPECT_TRUE(op.has_type());
247 EXPECT_EQ(DeltaArchiveManifest_InstallOperation_Type_BSDIFF, op.type());
248 EXPECT_FALSE(op.has_data_offset());
249 EXPECT_FALSE(op.has_data_length());
250 EXPECT_EQ(1, op.src_extents_size());
251 EXPECT_EQ(sizeof(kRandomString) - 1, op.src_length());
252 EXPECT_EQ(1, op.dst_extents_size());
253 EXPECT_EQ(sizeof(kRandomString), op.dst_length());
254 EXPECT_EQ(BlocksInExtents(op.src_extents()),
255 BlocksInExtents(op.dst_extents()));
256 EXPECT_EQ(1, BlocksInExtents(op.dst_extents()));
257}
258
Don Garrett36e60772012-03-29 10:31:20 -0700259TEST_F(DeltaDiffGeneratorTest, RunAsRootBsdiffNotAllowedTest) {
Don Garrettf4b28742012-03-27 20:48:06 -0700260 EXPECT_TRUE(utils::WriteFile(old_path().c_str(),
261 reinterpret_cast<const char*>(kRandomString),
262 sizeof(kRandomString) - 1));
263 EXPECT_TRUE(utils::WriteFile(new_path().c_str(),
264 reinterpret_cast<const char*>(kRandomString),
265 sizeof(kRandomString)));
266 vector<char> data;
267 DeltaArchiveManifest_InstallOperation op;
268
Don Garrettf4b28742012-03-27 20:48:06 -0700269 EXPECT_TRUE(DeltaDiffGenerator::ReadFileToDiff(old_path(),
270 new_path(),
Darin Petkov8e447e02013-04-16 16:23:50 +0200271 0, // chunk_offset
272 -1, // chunk_size
Don Garrett36e60772012-03-29 10:31:20 -0700273 false, // bsdiff_allowed
Don Garrettf4b28742012-03-27 20:48:06 -0700274 &data,
275 &op,
276 true));
277 EXPECT_FALSE(data.empty());
278
279 // The point of this test is that we don't use BSDIFF the way the above
280 // did. The rest of the details are to be caught in other tests.
281 EXPECT_TRUE(op.has_type());
282 EXPECT_NE(DeltaArchiveManifest_InstallOperation_Type_BSDIFF, op.type());
283}
284
Don Garrett36e60772012-03-29 10:31:20 -0700285TEST_F(DeltaDiffGeneratorTest, RunAsRootBsdiffNotAllowedMoveTest) {
Don Garrettf4b28742012-03-27 20:48:06 -0700286 EXPECT_TRUE(utils::WriteFile(old_path().c_str(),
287 reinterpret_cast<const char*>(kRandomString),
288 sizeof(kRandomString)));
289 EXPECT_TRUE(utils::WriteFile(new_path().c_str(),
290 reinterpret_cast<const char*>(kRandomString),
291 sizeof(kRandomString)));
292 vector<char> data;
293 DeltaArchiveManifest_InstallOperation op;
294
Don Garrettf4b28742012-03-27 20:48:06 -0700295 EXPECT_TRUE(DeltaDiffGenerator::ReadFileToDiff(old_path(),
296 new_path(),
Darin Petkov8e447e02013-04-16 16:23:50 +0200297 0, // chunk_offset
298 -1, // chunk_size
Don Garrett36e60772012-03-29 10:31:20 -0700299 false, // bsdiff_allowed
Don Garrettf4b28742012-03-27 20:48:06 -0700300 &data,
301 &op,
302 true));
303 EXPECT_TRUE(data.empty());
304
305 // The point of this test is that we can still use a MOVE for a file
306 // that is blacklisted.
307 EXPECT_TRUE(op.has_type());
308 EXPECT_EQ(DeltaArchiveManifest_InstallOperation_Type_MOVE, op.type());
309}
310
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700311TEST_F(DeltaDiffGeneratorTest, RunAsRootReplaceSmallTest) {
312 vector<char> new_data;
313 for (int i = 0; i < 2; i++) {
314 new_data.insert(new_data.end(),
315 kRandomString,
316 kRandomString + sizeof(kRandomString));
317 EXPECT_TRUE(utils::WriteFile(new_path().c_str(),
318 &new_data[0],
319 new_data.size()));
320 vector<char> data;
321 DeltaArchiveManifest_InstallOperation op;
322 EXPECT_TRUE(DeltaDiffGenerator::ReadFileToDiff(old_path(),
323 new_path(),
Darin Petkov8e447e02013-04-16 16:23:50 +0200324 0, // chunk_offset
325 -1, // chunk_size
Don Garrett36e60772012-03-29 10:31:20 -0700326 true, // bsdiff_allowed
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700327 &data,
Darin Petkov68c10d12010-10-14 09:24:37 -0700328 &op,
329 true));
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700330 EXPECT_FALSE(data.empty());
331
332 EXPECT_TRUE(op.has_type());
333 const DeltaArchiveManifest_InstallOperation_Type expected_type =
334 (i == 0 ? DeltaArchiveManifest_InstallOperation_Type_REPLACE :
335 DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ);
336 EXPECT_EQ(expected_type, op.type());
337 EXPECT_FALSE(op.has_data_offset());
338 EXPECT_FALSE(op.has_data_length());
339 EXPECT_EQ(0, op.src_extents_size());
340 EXPECT_FALSE(op.has_src_length());
341 EXPECT_EQ(1, op.dst_extents_size());
342 EXPECT_EQ(new_data.size(), op.dst_length());
343 EXPECT_EQ(1, BlocksInExtents(op.dst_extents()));
344 }
345}
346
Darin Petkov68c10d12010-10-14 09:24:37 -0700347TEST_F(DeltaDiffGeneratorTest, RunAsRootBsdiffNoGatherExtentsSmallTest) {
348 EXPECT_TRUE(utils::WriteFile(old_path().c_str(),
349 reinterpret_cast<const char*>(kRandomString),
350 sizeof(kRandomString) - 1));
351 EXPECT_TRUE(utils::WriteFile(new_path().c_str(),
352 reinterpret_cast<const char*>(kRandomString),
353 sizeof(kRandomString)));
354 vector<char> data;
355 DeltaArchiveManifest_InstallOperation op;
356 EXPECT_TRUE(DeltaDiffGenerator::ReadFileToDiff(old_path(),
357 new_path(),
Darin Petkov8e447e02013-04-16 16:23:50 +0200358 0, // chunk_offset
359 -1, // chunk_size
Don Garrett36e60772012-03-29 10:31:20 -0700360 true, // bsdiff_allowed
Darin Petkov68c10d12010-10-14 09:24:37 -0700361 &data,
362 &op,
363 false));
364 EXPECT_FALSE(data.empty());
365
366 EXPECT_TRUE(op.has_type());
367 EXPECT_EQ(DeltaArchiveManifest_InstallOperation_Type_BSDIFF, op.type());
368 EXPECT_FALSE(op.has_data_offset());
369 EXPECT_FALSE(op.has_data_length());
370 EXPECT_EQ(1, op.src_extents_size());
371 EXPECT_EQ(0, op.src_extents().Get(0).start_block());
372 EXPECT_EQ(1, op.src_extents().Get(0).num_blocks());
373 EXPECT_EQ(sizeof(kRandomString) - 1, op.src_length());
374 EXPECT_EQ(1, op.dst_extents_size());
375 EXPECT_EQ(0, op.dst_extents().Get(0).start_block());
376 EXPECT_EQ(1, op.dst_extents().Get(0).num_blocks());
377 EXPECT_EQ(sizeof(kRandomString), op.dst_length());
378}
379
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700380namespace {
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700381void AppendExtent(vector<Extent>* vect, uint64_t start, uint64_t length) {
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700382 vect->resize(vect->size() + 1);
383 vect->back().set_start_block(start);
384 vect->back().set_num_blocks(length);
385}
386void OpAppendExtent(DeltaArchiveManifest_InstallOperation* op,
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700387 uint64_t start,
388 uint64_t length) {
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700389 Extent* extent = op->add_src_extents();
390 extent->set_start_block(start);
391 extent->set_num_blocks(length);
392}
393}
394
395TEST_F(DeltaDiffGeneratorTest, SubstituteBlocksTest) {
396 vector<Extent> remove_blocks;
397 AppendExtent(&remove_blocks, 3, 3);
398 AppendExtent(&remove_blocks, 7, 1);
399 vector<Extent> replace_blocks;
400 AppendExtent(&replace_blocks, 10, 2);
401 AppendExtent(&replace_blocks, 13, 2);
Andrew de los Reyesef017552010-10-06 17:57:52 -0700402 Vertex vertex;
403 DeltaArchiveManifest_InstallOperation& op = vertex.op;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700404 OpAppendExtent(&op, 4, 3);
405 OpAppendExtent(&op, kSparseHole, 4); // Sparse hole in file
406 OpAppendExtent(&op, 3, 1);
407 OpAppendExtent(&op, 7, 3);
Darin Petkov68c10d12010-10-14 09:24:37 -0700408
Andrew de los Reyesef017552010-10-06 17:57:52 -0700409 DeltaDiffGenerator::SubstituteBlocks(&vertex, remove_blocks, replace_blocks);
Darin Petkov68c10d12010-10-14 09:24:37 -0700410
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700411 EXPECT_EQ(7, op.src_extents_size());
412 EXPECT_EQ(11, op.src_extents(0).start_block());
413 EXPECT_EQ(1, op.src_extents(0).num_blocks());
414 EXPECT_EQ(13, op.src_extents(1).start_block());
415 EXPECT_EQ(1, op.src_extents(1).num_blocks());
416 EXPECT_EQ(6, op.src_extents(2).start_block());
417 EXPECT_EQ(1, op.src_extents(2).num_blocks());
418 EXPECT_EQ(kSparseHole, op.src_extents(3).start_block());
419 EXPECT_EQ(4, op.src_extents(3).num_blocks());
420 EXPECT_EQ(10, op.src_extents(4).start_block());
421 EXPECT_EQ(1, op.src_extents(4).num_blocks());
422 EXPECT_EQ(14, op.src_extents(5).start_block());
423 EXPECT_EQ(1, op.src_extents(5).num_blocks());
424 EXPECT_EQ(8, op.src_extents(6).start_block());
425 EXPECT_EQ(2, op.src_extents(6).num_blocks());
426}
427
428TEST_F(DeltaDiffGeneratorTest, CutEdgesTest) {
429 Graph graph;
430 vector<Block> blocks(9);
Darin Petkov68c10d12010-10-14 09:24:37 -0700431
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700432 // Create nodes in graph
433 {
434 graph.resize(graph.size() + 1);
435 graph.back().op.set_type(DeltaArchiveManifest_InstallOperation_Type_MOVE);
436 // Reads from blocks 3, 5, 7
437 vector<Extent> extents;
438 graph_utils::AppendBlockToExtents(&extents, 3);
439 graph_utils::AppendBlockToExtents(&extents, 5);
440 graph_utils::AppendBlockToExtents(&extents, 7);
441 DeltaDiffGenerator::StoreExtents(extents,
442 graph.back().op.mutable_src_extents());
443 blocks[3].reader = graph.size() - 1;
444 blocks[5].reader = graph.size() - 1;
445 blocks[7].reader = graph.size() - 1;
Darin Petkov68c10d12010-10-14 09:24:37 -0700446
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700447 // Writes to blocks 1, 2, 4
448 extents.clear();
449 graph_utils::AppendBlockToExtents(&extents, 1);
450 graph_utils::AppendBlockToExtents(&extents, 2);
451 graph_utils::AppendBlockToExtents(&extents, 4);
452 DeltaDiffGenerator::StoreExtents(extents,
453 graph.back().op.mutable_dst_extents());
454 blocks[1].writer = graph.size() - 1;
455 blocks[2].writer = graph.size() - 1;
456 blocks[4].writer = graph.size() - 1;
457 }
458 {
459 graph.resize(graph.size() + 1);
460 graph.back().op.set_type(DeltaArchiveManifest_InstallOperation_Type_MOVE);
461 // Reads from blocks 1, 2, 4
462 vector<Extent> extents;
463 graph_utils::AppendBlockToExtents(&extents, 1);
464 graph_utils::AppendBlockToExtents(&extents, 2);
465 graph_utils::AppendBlockToExtents(&extents, 4);
466 DeltaDiffGenerator::StoreExtents(extents,
467 graph.back().op.mutable_src_extents());
468 blocks[1].reader = graph.size() - 1;
469 blocks[2].reader = graph.size() - 1;
470 blocks[4].reader = graph.size() - 1;
Darin Petkov68c10d12010-10-14 09:24:37 -0700471
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700472 // Writes to blocks 3, 5, 6
473 extents.clear();
474 graph_utils::AppendBlockToExtents(&extents, 3);
475 graph_utils::AppendBlockToExtents(&extents, 5);
476 graph_utils::AppendBlockToExtents(&extents, 6);
477 DeltaDiffGenerator::StoreExtents(extents,
478 graph.back().op.mutable_dst_extents());
479 blocks[3].writer = graph.size() - 1;
480 blocks[5].writer = graph.size() - 1;
481 blocks[6].writer = graph.size() - 1;
482 }
Darin Petkov68c10d12010-10-14 09:24:37 -0700483
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700484 // Create edges
485 DeltaDiffGenerator::CreateEdges(&graph, blocks);
Darin Petkov68c10d12010-10-14 09:24:37 -0700486
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700487 // Find cycles
488 CycleBreaker cycle_breaker;
489 set<Edge> cut_edges;
490 cycle_breaker.BreakCycles(graph, &cut_edges);
491
492 EXPECT_EQ(1, cut_edges.size());
493 EXPECT_TRUE(cut_edges.end() != cut_edges.find(make_pair<Vertex::Index>(1,
494 0)));
495
Andrew de los Reyesef017552010-10-06 17:57:52 -0700496 vector<CutEdgeVertexes> cuts;
497 EXPECT_TRUE(DeltaDiffGenerator::CutEdges(&graph, cut_edges, &cuts));
Darin Petkov68c10d12010-10-14 09:24:37 -0700498
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700499 EXPECT_EQ(3, graph.size());
Darin Petkov68c10d12010-10-14 09:24:37 -0700500
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700501 // Check new node in graph:
502 EXPECT_EQ(DeltaArchiveManifest_InstallOperation_Type_MOVE,
503 graph.back().op.type());
504 EXPECT_EQ(2, graph.back().op.src_extents_size());
Andrew de los Reyesef017552010-10-06 17:57:52 -0700505 EXPECT_EQ(1, graph.back().op.dst_extents_size());
506 EXPECT_EQ(kTempBlockStart, graph.back().op.dst_extents(0).start_block());
507 EXPECT_EQ(2, graph.back().op.dst_extents(0).num_blocks());
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700508 EXPECT_TRUE(graph.back().out_edges.empty());
Darin Petkov68c10d12010-10-14 09:24:37 -0700509
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700510 // Check that old node reads from new blocks
Andrew de los Reyesef017552010-10-06 17:57:52 -0700511 EXPECT_EQ(2, graph[0].op.src_extents_size());
512 EXPECT_EQ(kTempBlockStart, graph[0].op.src_extents(0).start_block());
513 EXPECT_EQ(2, graph[0].op.src_extents(0).num_blocks());
514 EXPECT_EQ(7, graph[0].op.src_extents(1).start_block());
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700515 EXPECT_EQ(1, graph[0].op.src_extents(1).num_blocks());
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700516
517 // And that the old dst extents haven't changed
518 EXPECT_EQ(2, graph[0].op.dst_extents_size());
519 EXPECT_EQ(1, graph[0].op.dst_extents(0).start_block());
520 EXPECT_EQ(2, graph[0].op.dst_extents(0).num_blocks());
521 EXPECT_EQ(4, graph[0].op.dst_extents(1).start_block());
522 EXPECT_EQ(1, graph[0].op.dst_extents(1).num_blocks());
Andrew de los Reyesd12784c2010-07-26 13:55:14 -0700523
524 // Ensure it only depends on the next node and the new temp node
525 EXPECT_EQ(2, graph[0].out_edges.size());
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700526 EXPECT_TRUE(graph[0].out_edges.end() != graph[0].out_edges.find(1));
Andrew de los Reyesd12784c2010-07-26 13:55:14 -0700527 EXPECT_TRUE(graph[0].out_edges.end() != graph[0].out_edges.find(graph.size() -
528 1));
529
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700530 // Check second node has unchanged extents
531 EXPECT_EQ(2, graph[1].op.src_extents_size());
532 EXPECT_EQ(1, graph[1].op.src_extents(0).start_block());
533 EXPECT_EQ(2, graph[1].op.src_extents(0).num_blocks());
534 EXPECT_EQ(4, graph[1].op.src_extents(1).start_block());
535 EXPECT_EQ(1, graph[1].op.src_extents(1).num_blocks());
536
537 EXPECT_EQ(2, graph[1].op.dst_extents_size());
538 EXPECT_EQ(3, graph[1].op.dst_extents(0).start_block());
539 EXPECT_EQ(1, graph[1].op.dst_extents(0).num_blocks());
540 EXPECT_EQ(5, graph[1].op.dst_extents(1).start_block());
541 EXPECT_EQ(2, graph[1].op.dst_extents(1).num_blocks());
Darin Petkov68c10d12010-10-14 09:24:37 -0700542
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700543 // Ensure it only depends on the next node
544 EXPECT_EQ(1, graph[1].out_edges.size());
545 EXPECT_TRUE(graph[1].out_edges.end() != graph[1].out_edges.find(2));
546}
547
548TEST_F(DeltaDiffGeneratorTest, ReorderBlobsTest) {
549 string orig_blobs;
550 EXPECT_TRUE(
551 utils::MakeTempFile("ReorderBlobsTest.orig.XXXXXX", &orig_blobs, NULL));
552
553 string orig_data = "abcd";
554 EXPECT_TRUE(
555 utils::WriteFile(orig_blobs.c_str(), orig_data.data(), orig_data.size()));
556
557 string new_blobs;
558 EXPECT_TRUE(
559 utils::MakeTempFile("ReorderBlobsTest.new.XXXXXX", &new_blobs, NULL));
Darin Petkov68c10d12010-10-14 09:24:37 -0700560
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700561 DeltaArchiveManifest manifest;
562 DeltaArchiveManifest_InstallOperation* op =
563 manifest.add_install_operations();
564 op->set_data_offset(1);
565 op->set_data_length(3);
566 op = manifest.add_install_operations();
567 op->set_data_offset(0);
568 op->set_data_length(1);
Darin Petkov68c10d12010-10-14 09:24:37 -0700569
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700570 EXPECT_TRUE(DeltaDiffGenerator::ReorderDataBlobs(&manifest,
571 orig_blobs,
572 new_blobs));
Darin Petkov68c10d12010-10-14 09:24:37 -0700573
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700574 string new_data;
Gilad Arnold19a45f02012-07-19 12:36:10 -0700575 EXPECT_TRUE(utils::ReadFile(new_blobs, &new_data));
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700576 EXPECT_EQ("bcda", new_data);
577 EXPECT_EQ(2, manifest.install_operations_size());
578 EXPECT_EQ(0, manifest.install_operations(0).data_offset());
579 EXPECT_EQ(3, manifest.install_operations(0).data_length());
580 EXPECT_EQ(3, manifest.install_operations(1).data_offset());
581 EXPECT_EQ(1, manifest.install_operations(1).data_length());
Darin Petkov68c10d12010-10-14 09:24:37 -0700582
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700583 unlink(orig_blobs.c_str());
584 unlink(new_blobs.c_str());
585}
adlr@google.com3defe6a2009-12-04 20:57:17 +0000586
Andrew de los Reyesef017552010-10-06 17:57:52 -0700587TEST_F(DeltaDiffGeneratorTest, MoveFullOpsToBackTest) {
588 Graph graph(4);
589 graph[0].file_name = "A";
590 graph[0].op.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE);
591 graph[1].file_name = "B";
592 graph[1].op.set_type(DeltaArchiveManifest_InstallOperation_Type_BSDIFF);
593 graph[2].file_name = "C";
594 graph[2].op.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ);
595 graph[3].file_name = "D";
596 graph[3].op.set_type(DeltaArchiveManifest_InstallOperation_Type_MOVE);
597
598 vector<Vertex::Index> vect(graph.size());
599
600 for (vector<Vertex::Index>::size_type i = 0; i < vect.size(); ++i) {
601 vect[i] = i;
602 }
603 DeltaDiffGenerator::MoveFullOpsToBack(&graph, &vect);
604 EXPECT_EQ(vect.size(), graph.size());
605 EXPECT_EQ(graph[vect[0]].file_name, "B");
606 EXPECT_EQ(graph[vect[1]].file_name, "D");
607 EXPECT_EQ(graph[vect[2]].file_name, "A");
608 EXPECT_EQ(graph[vect[3]].file_name, "C");
609}
610
611namespace {
612
613#define OP_BSDIFF DeltaArchiveManifest_InstallOperation_Type_BSDIFF
614#define OP_MOVE DeltaArchiveManifest_InstallOperation_Type_MOVE
615#define OP_REPLACE DeltaArchiveManifest_InstallOperation_Type_REPLACE
616#define OP_REPLACE_BZ DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ
617
618void GenVertex(Vertex* out,
619 const vector<Extent>& src_extents,
620 const vector<Extent>& dst_extents,
621 const string& path,
622 DeltaArchiveManifest_InstallOperation_Type type) {
623 out->op.set_type(type);
624 out->file_name = path;
625 DeltaDiffGenerator::StoreExtents(src_extents, out->op.mutable_src_extents());
626 DeltaDiffGenerator::StoreExtents(dst_extents, out->op.mutable_dst_extents());
627}
628
629vector<Extent> VectOfExt(uint64_t start_block, uint64_t num_blocks) {
630 return vector<Extent>(1, ExtentForRange(start_block, num_blocks));
631}
632
633EdgeProperties EdgeWithReadDep(const vector<Extent>& extents) {
634 EdgeProperties ret;
635 ret.extents = extents;
636 return ret;
637}
638
639EdgeProperties EdgeWithWriteDep(const vector<Extent>& extents) {
640 EdgeProperties ret;
641 ret.write_extents = extents;
642 return ret;
643}
644
645template<typename T>
646void DumpVect(const vector<T>& vect) {
647 std::stringstream ss(stringstream::out);
648 for (typename vector<T>::const_iterator it = vect.begin(), e = vect.end();
649 it != e; ++it) {
650 ss << *it << ", ";
651 }
652 LOG(INFO) << "{" << ss.str() << "}";
653}
654
655} // namespace {}
656
657TEST_F(DeltaDiffGeneratorTest, RunAsRootAssignTempBlocksTest) {
658 Graph graph(9);
659 const vector<Extent> empt; // empty
660 const string kFilename = "/foo";
Darin Petkov68c10d12010-10-14 09:24:37 -0700661
Andrew de los Reyesef017552010-10-06 17:57:52 -0700662 // Some scratch space:
663 GenVertex(&graph[0], empt, VectOfExt(200, 1), "", OP_REPLACE);
664 GenVertex(&graph[1], empt, VectOfExt(210, 10), "", OP_REPLACE);
665 GenVertex(&graph[2], empt, VectOfExt(220, 1), "", OP_REPLACE);
666
667 // A cycle that requires 10 blocks to break:
668 GenVertex(&graph[3], VectOfExt(10, 11), VectOfExt(0, 9), "", OP_BSDIFF);
669 graph[3].out_edges[4] = EdgeWithReadDep(VectOfExt(0, 9));
670 GenVertex(&graph[4], VectOfExt(0, 9), VectOfExt(10, 11), "", OP_BSDIFF);
671 graph[4].out_edges[3] = EdgeWithReadDep(VectOfExt(10, 11));
672
673 // A cycle that requires 9 blocks to break:
674 GenVertex(&graph[5], VectOfExt(40, 11), VectOfExt(30, 10), "", OP_BSDIFF);
675 graph[5].out_edges[6] = EdgeWithReadDep(VectOfExt(30, 10));
676 GenVertex(&graph[6], VectOfExt(30, 10), VectOfExt(40, 11), "", OP_BSDIFF);
677 graph[6].out_edges[5] = EdgeWithReadDep(VectOfExt(40, 11));
678
679 // A cycle that requires 40 blocks to break (which is too many):
680 GenVertex(&graph[7],
681 VectOfExt(120, 50),
682 VectOfExt(60, 40),
683 "",
684 OP_BSDIFF);
685 graph[7].out_edges[8] = EdgeWithReadDep(VectOfExt(60, 40));
686 GenVertex(&graph[8],
687 VectOfExt(60, 40),
688 VectOfExt(120, 50),
689 kFilename,
690 OP_BSDIFF);
691 graph[8].out_edges[7] = EdgeWithReadDep(VectOfExt(120, 50));
Darin Petkov68c10d12010-10-14 09:24:37 -0700692
Andrew de los Reyesef017552010-10-06 17:57:52 -0700693 graph_utils::DumpGraph(graph);
Darin Petkov68c10d12010-10-14 09:24:37 -0700694
Andrew de los Reyesef017552010-10-06 17:57:52 -0700695 vector<Vertex::Index> final_order;
696
697
698 // Prepare the filesystem with the minimum required for this to work
699 string temp_dir;
700 EXPECT_TRUE(utils::MakeTempDirectory("/tmp/AssignTempBlocksTest.XXXXXX",
701 &temp_dir));
702 ScopedDirRemover temp_dir_remover(temp_dir);
Darin Petkov68c10d12010-10-14 09:24:37 -0700703
Andrew de los Reyesef017552010-10-06 17:57:52 -0700704 const size_t kBlockSize = 4096;
705 vector<char> temp_data(kBlockSize * 50);
706 FillWithData(&temp_data);
707 EXPECT_TRUE(WriteFileVector(temp_dir + kFilename, temp_data));
708 ScopedPathUnlinker filename_unlinker(temp_dir + kFilename);
Darin Petkov68c10d12010-10-14 09:24:37 -0700709
Andrew de los Reyesef017552010-10-06 17:57:52 -0700710 int fd;
711 EXPECT_TRUE(utils::MakeTempFile("/tmp/AssignTempBlocksTestData.XXXXXX",
712 NULL,
713 &fd));
714 ScopedFdCloser fd_closer(&fd);
715 off_t data_file_size = 0;
716
717
718 EXPECT_TRUE(DeltaDiffGenerator::ConvertGraphToDag(&graph,
719 temp_dir,
720 fd,
721 &data_file_size,
Andrew de los Reyes927179d2010-12-02 11:26:48 -0800722 &final_order,
723 Vertex::kInvalidIndex));
Andrew de los Reyesef017552010-10-06 17:57:52 -0700724
Darin Petkov68c10d12010-10-14 09:24:37 -0700725
Andrew de los Reyesef017552010-10-06 17:57:52 -0700726 Graph expected_graph(12);
727 GenVertex(&expected_graph[0], empt, VectOfExt(200, 1), "", OP_REPLACE);
728 GenVertex(&expected_graph[1], empt, VectOfExt(210, 10), "", OP_REPLACE);
729 GenVertex(&expected_graph[2], empt, VectOfExt(220, 1), "", OP_REPLACE);
730 GenVertex(&expected_graph[3],
731 VectOfExt(10, 11),
732 VectOfExt(0, 9),
733 "",
734 OP_BSDIFF);
735 expected_graph[3].out_edges[9] = EdgeWithReadDep(VectOfExt(0, 9));
736 GenVertex(&expected_graph[4],
737 VectOfExt(60, 9),
738 VectOfExt(10, 11),
739 "",
740 OP_BSDIFF);
741 expected_graph[4].out_edges[3] = EdgeWithReadDep(VectOfExt(10, 11));
742 expected_graph[4].out_edges[9] = EdgeWithWriteDep(VectOfExt(60, 9));
743 GenVertex(&expected_graph[5],
744 VectOfExt(40, 11),
745 VectOfExt(30, 10),
746 "",
747 OP_BSDIFF);
748 expected_graph[5].out_edges[10] = EdgeWithReadDep(VectOfExt(30, 10));
749
750 GenVertex(&expected_graph[6],
751 VectOfExt(60, 10),
752 VectOfExt(40, 11),
753 "",
754 OP_BSDIFF);
755 expected_graph[6].out_edges[5] = EdgeWithReadDep(VectOfExt(40, 11));
756 expected_graph[6].out_edges[10] = EdgeWithWriteDep(VectOfExt(60, 10));
Darin Petkov68c10d12010-10-14 09:24:37 -0700757
Andrew de los Reyesef017552010-10-06 17:57:52 -0700758 GenVertex(&expected_graph[7],
759 VectOfExt(120, 50),
760 VectOfExt(60, 40),
761 "",
762 OP_BSDIFF);
763 expected_graph[7].out_edges[6] = EdgeWithReadDep(VectOfExt(60, 10));
764
765 GenVertex(&expected_graph[8], empt, VectOfExt(0, 50), "/foo", OP_REPLACE_BZ);
766 expected_graph[8].out_edges[7] = EdgeWithReadDep(VectOfExt(120, 50));
767
768 GenVertex(&expected_graph[9],
769 VectOfExt(0, 9),
770 VectOfExt(60, 9),
771 "",
772 OP_MOVE);
773
774 GenVertex(&expected_graph[10],
775 VectOfExt(30, 10),
776 VectOfExt(60, 10),
777 "",
778 OP_MOVE);
779 expected_graph[10].out_edges[4] = EdgeWithReadDep(VectOfExt(60, 9));
Darin Petkov68c10d12010-10-14 09:24:37 -0700780
Andrew de los Reyesef017552010-10-06 17:57:52 -0700781 EXPECT_EQ(12, graph.size());
782 EXPECT_FALSE(graph.back().valid);
783 for (Graph::size_type i = 0; i < graph.size() - 1; i++) {
784 EXPECT_TRUE(graph[i].out_edges == expected_graph[i].out_edges);
785 if (i == 8) {
786 // special case
787 } else {
788 // EXPECT_TRUE(graph[i] == expected_graph[i]) << "i = " << i;
789 }
790 }
791}
792
Darin Petkov94817cb2013-05-08 14:33:24 +0200793// Test that sparse holes are not used as scratch. More specifically, test that
794// if all full operations write to sparse holes and there's no extra scratch
795// space, delta operations that need scratch are converted to full. See
796// crbug.com/238440.
797TEST_F(DeltaDiffGeneratorTest, RunAsRootNoSparseAsTempTest) {
798 Graph graph(3);
799 const vector<Extent> kEmpty;
800 const string kFilename = "/foo";
801
802 // Make sure this sparse hole is not used as scratch.
803 GenVertex(&graph[0], kEmpty, VectOfExt(kSparseHole, 1), "", OP_REPLACE);
804
805 // Create a single-block cycle.
806 GenVertex(&graph[1], VectOfExt(0, 1), VectOfExt(1, 1), "", OP_BSDIFF);
807 graph[1].out_edges[2] = EdgeWithReadDep(VectOfExt(1, 1));
808 GenVertex(&graph[2], VectOfExt(1, 1), VectOfExt(0, 1), kFilename, OP_BSDIFF);
809 graph[2].out_edges[1] = EdgeWithReadDep(VectOfExt(0, 1));
810
811 graph_utils::DumpGraph(graph);
812
813 vector<Vertex::Index> final_order;
814
815 // Prepare the filesystem with the minimum required for this to work.
816 string temp_dir;
817 EXPECT_TRUE(utils::MakeTempDirectory("/tmp/NoSparseAsTempTest.XXXXXX",
818 &temp_dir));
819 ScopedDirRemover temp_dir_remover(temp_dir);
820
821 const size_t kBlockSize = 4096;
822 vector<char> temp_data(kBlockSize);
823 FillWithData(&temp_data);
824 EXPECT_TRUE(WriteFileVector(temp_dir + kFilename, temp_data));
825 ScopedPathUnlinker filename_unlinker(temp_dir + kFilename);
826
827 int fd = -1;
828 EXPECT_TRUE(utils::MakeTempFile("/tmp/NoSparseAsTempTestData.XXXXXX",
829 NULL,
830 &fd));
831 ScopedFdCloser fd_closer(&fd);
832 off_t data_file_size = 0;
833
834 EXPECT_TRUE(DeltaDiffGenerator::ConvertGraphToDag(&graph,
835 temp_dir,
836 fd,
837 &data_file_size,
838 &final_order,
839 Vertex::kInvalidIndex));
840
841 ASSERT_EQ(4, graph.size());
842
843 // The second BSDIFF operation must have been converted to a full operation
844 // (due to insufficient scratch space).
845 EXPECT_TRUE(graph[2].op.type() == OP_REPLACE ||
846 graph[2].op.type() == OP_REPLACE_BZ);
847
848 // The temporary node created for breaking the cycle must have been marked as
849 // invalid.
850 EXPECT_FALSE(graph[3].valid);
851}
852
853// Test that sparse holes are not used as scratch. More specifically, test that
854// if scratch space comes only from full operations writing to real blocks as
855// well as sparse holes, only the real blocks are utilized. See
856// crbug.com/238440.
857TEST_F(DeltaDiffGeneratorTest, NoSparseAsTempTest) {
858 Graph graph;
859 vector<Block> blocks(4);
860
861 // Create nodes in |graph|.
862 {
863 graph.resize(graph.size() + 1);
864 graph.back().op.set_type(
865 DeltaArchiveManifest_InstallOperation_Type_REPLACE);
866
867 // Write to a sparse hole -- basically a no-op to ensure sparse holes are
868 // not used as scratch.
869 vector<Extent> extents;
870 graph_utils::AppendBlockToExtents(&extents, kSparseHole);
871 DeltaDiffGenerator::StoreExtents(extents,
872 graph.back().op.mutable_dst_extents());
873 }
874 {
875 graph.resize(graph.size() + 1);
876 graph.back().op.set_type(OP_REPLACE);
877
878 // Scratch space: write to block 0 with sparse holes around.
879 vector<Extent> extents;
880 graph_utils::AppendBlockToExtents(&extents, kSparseHole);
881 graph_utils::AppendBlockToExtents(&extents, 0);
882 graph_utils::AppendBlockToExtents(&extents, kSparseHole);
883 DeltaDiffGenerator::StoreExtents(extents,
884 graph.back().op.mutable_dst_extents());
885 blocks[0].writer = graph.size() - 1;
886 }
887 {
888 graph.resize(graph.size() + 1);
889 graph.back().op.set_type(OP_REPLACE);
890
891 // Write to a sparse hole.
892 vector<Extent> extents;
893 graph_utils::AppendBlockToExtents(&extents, kSparseHole);
894 DeltaDiffGenerator::StoreExtents(extents,
895 graph.back().op.mutable_dst_extents());
896 }
897 // Create a two-node cycle between (2, sparse, sparse) and (1, sparse, 3).
898 {
899 graph.resize(graph.size() + 1);
900 graph.back().op.set_type(OP_MOVE);
901 // Read from (2, sparse, sparse).
902 vector<Extent> extents;
903 graph_utils::AppendBlockToExtents(&extents, 2);
904 graph_utils::AppendBlockToExtents(&extents, kSparseHole);
905 graph_utils::AppendBlockToExtents(&extents, kSparseHole);
906 DeltaDiffGenerator::StoreExtents(extents,
907 graph.back().op.mutable_src_extents());
908 blocks[2].reader = graph.size() - 1;
909
910 // Write to (1, sparse, 3).
911 extents.clear();
912 graph_utils::AppendBlockToExtents(&extents, 1);
913 graph_utils::AppendBlockToExtents(&extents, kSparseHole);
914 graph_utils::AppendBlockToExtents(&extents, 3);
915 DeltaDiffGenerator::StoreExtents(extents,
916 graph.back().op.mutable_dst_extents());
917 blocks[1].writer = graph.size() - 1;
918 blocks[3].writer = graph.size() - 1;
919 }
920 {
921 graph.resize(graph.size() + 1);
922 graph.back().op.set_type(OP_MOVE);
923 // Read from (1, sparse, 3).
924 vector<Extent> extents;
925 graph_utils::AppendBlockToExtents(&extents, 1);
926 graph_utils::AppendBlockToExtents(&extents, kSparseHole);
927 graph_utils::AppendBlockToExtents(&extents, 3);
928 DeltaDiffGenerator::StoreExtents(extents,
929 graph.back().op.mutable_src_extents());
930 blocks[1].reader = graph.size() - 1;
931 blocks[3].reader = graph.size() - 1;
932
933 // Write to (2, sparse, sparse).
934 extents.clear();
935 graph_utils::AppendBlockToExtents(&extents, 2);
936 graph_utils::AppendBlockToExtents(&extents, kSparseHole);
937 graph_utils::AppendBlockToExtents(&extents, kSparseHole);
938 DeltaDiffGenerator::StoreExtents(extents,
939 graph.back().op.mutable_dst_extents());
940 blocks[2].writer = graph.size() - 1;
941 }
942
943 graph_utils::DumpGraph(graph);
944
945 // Create edges
946 DeltaDiffGenerator::CreateEdges(&graph, blocks);
947
948 graph_utils::DumpGraph(graph);
949
950 vector<Vertex::Index> final_order;
951 off_t data_file_size = 0;
952 EXPECT_TRUE(DeltaDiffGenerator::ConvertGraphToDag(&graph,
953 "/non/existent/dir",
954 -1,
955 &data_file_size,
956 &final_order,
957 Vertex::kInvalidIndex));
958
959 // Check for a single temporary node writing to scratch.
960 ASSERT_EQ(6, graph.size());
961 EXPECT_EQ(DeltaArchiveManifest_InstallOperation_Type_MOVE,
962 graph[5].op.type());
963 EXPECT_EQ(1, graph[5].op.src_extents_size());
964 ASSERT_EQ(1, graph[5].op.dst_extents_size());
965 EXPECT_EQ(0, graph[5].op.dst_extents(0).start_block());
966 EXPECT_EQ(1, graph[5].op.dst_extents(0).num_blocks());
967
968 // Make sure the cycle nodes still read from and write to sparse holes.
969 for (int i = 3; i < 5; i++) {
970 ASSERT_GE(graph[i].op.src_extents_size(), 2);
971 EXPECT_EQ(kSparseHole, graph[i].op.src_extents(1).start_block());
972 ASSERT_GE(graph[i].op.dst_extents_size(), 2);
973 EXPECT_EQ(kSparseHole, graph[i].op.dst_extents(1).start_block());
974 }
975}
976
Darin Petkov9fa7ec52010-10-18 11:45:23 -0700977TEST_F(DeltaDiffGeneratorTest, IsNoopOperationTest) {
978 DeltaArchiveManifest_InstallOperation op;
979 op.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ);
980 EXPECT_FALSE(DeltaDiffGenerator::IsNoopOperation(op));
981 op.set_type(DeltaArchiveManifest_InstallOperation_Type_MOVE);
982 EXPECT_TRUE(DeltaDiffGenerator::IsNoopOperation(op));
983 *(op.add_src_extents()) = ExtentForRange(3, 2);
984 *(op.add_dst_extents()) = ExtentForRange(3, 2);
985 EXPECT_TRUE(DeltaDiffGenerator::IsNoopOperation(op));
986 *(op.add_src_extents()) = ExtentForRange(7, 5);
987 *(op.add_dst_extents()) = ExtentForRange(7, 5);
988 EXPECT_TRUE(DeltaDiffGenerator::IsNoopOperation(op));
989 *(op.add_src_extents()) = ExtentForRange(20, 2);
990 *(op.add_dst_extents()) = ExtentForRange(20, 1);
991 *(op.add_dst_extents()) = ExtentForRange(21, 1);
992 EXPECT_TRUE(DeltaDiffGenerator::IsNoopOperation(op));
993 *(op.add_src_extents()) = ExtentForRange(kSparseHole, 2);
994 *(op.add_src_extents()) = ExtentForRange(kSparseHole, 1);
995 *(op.add_dst_extents()) = ExtentForRange(kSparseHole, 3);
996 EXPECT_TRUE(DeltaDiffGenerator::IsNoopOperation(op));
997 *(op.add_src_extents()) = ExtentForRange(24, 1);
998 *(op.add_dst_extents()) = ExtentForRange(25, 1);
999 EXPECT_FALSE(DeltaDiffGenerator::IsNoopOperation(op));
1000}
1001
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001002TEST_F(DeltaDiffGeneratorTest, RunAsRootAssignTempBlocksReuseTest) {
1003 // AssignTempBlocks(Graph* graph,
1004 // const string& new_root,
1005 // int data_fd,
1006 // off_t* data_file_size,
1007 // vector<Vertex::Index>* op_indexes,
1008 // vector<vector<Vertex::Index>::size_type>* reverse_op_indexes,
1009 // const vector<CutEdgeVertexes>& cuts
1010 Graph graph(9);
1011
1012 const vector<Extent> empt;
1013 uint64_t tmp = kTempBlockStart;
1014 const string kFilename = "/foo";
1015
1016 vector<CutEdgeVertexes> cuts;
1017 cuts.resize(3);
1018
1019 // Simple broken loop:
1020 GenVertex(&graph[0], VectOfExt(0, 1), VectOfExt(1, 1), "", OP_MOVE);
1021 GenVertex(&graph[1], VectOfExt(tmp, 1), VectOfExt(0, 1), "", OP_MOVE);
1022 GenVertex(&graph[2], VectOfExt(1, 1), VectOfExt(tmp, 1), "", OP_MOVE);
1023 // Corresponding edges:
1024 graph[0].out_edges[2] = EdgeWithReadDep(VectOfExt(1, 1));
1025 graph[1].out_edges[2] = EdgeWithWriteDep(VectOfExt(tmp, 1));
1026 graph[1].out_edges[0] = EdgeWithReadDep(VectOfExt(0, 1));
1027 // Store the cut:
1028 cuts[0].old_dst = 1;
1029 cuts[0].old_src = 0;
1030 cuts[0].new_vertex = 2;
1031 cuts[0].tmp_extents = VectOfExt(tmp, 1);
1032 tmp++;
1033
1034 // Slightly more complex pair of loops:
1035 GenVertex(&graph[3], VectOfExt(4, 2), VectOfExt(2, 2), "", OP_MOVE);
1036 GenVertex(&graph[4], VectOfExt(6, 1), VectOfExt(7, 1), "", OP_MOVE);
1037 GenVertex(&graph[5], VectOfExt(tmp, 3), VectOfExt(4, 3), kFilename, OP_MOVE);
1038 GenVertex(&graph[6], VectOfExt(2, 2), VectOfExt(tmp, 2), "", OP_MOVE);
1039 GenVertex(&graph[7], VectOfExt(7, 1), VectOfExt(tmp + 2, 1), "", OP_MOVE);
1040 // Corresponding edges:
1041 graph[3].out_edges[6] = EdgeWithReadDep(VectOfExt(2, 2));
1042 graph[4].out_edges[7] = EdgeWithReadDep(VectOfExt(7, 1));
1043 graph[5].out_edges[6] = EdgeWithWriteDep(VectOfExt(tmp, 2));
1044 graph[5].out_edges[7] = EdgeWithWriteDep(VectOfExt(tmp + 2, 1));
1045 graph[5].out_edges[3] = EdgeWithReadDep(VectOfExt(4, 2));
1046 graph[5].out_edges[4] = EdgeWithReadDep(VectOfExt(6, 1));
1047 // Store the cuts:
1048 cuts[1].old_dst = 5;
1049 cuts[1].old_src = 3;
1050 cuts[1].new_vertex = 6;
1051 cuts[1].tmp_extents = VectOfExt(tmp, 2);
1052 cuts[2].old_dst = 5;
1053 cuts[2].old_src = 4;
1054 cuts[2].new_vertex = 7;
1055 cuts[2].tmp_extents = VectOfExt(tmp + 2, 1);
1056
1057 // Supplier of temp block:
1058 GenVertex(&graph[8], empt, VectOfExt(8, 1), "", OP_REPLACE);
1059
1060 // Specify the final order:
1061 vector<Vertex::Index> op_indexes;
1062 op_indexes.push_back(2);
1063 op_indexes.push_back(0);
1064 op_indexes.push_back(1);
1065 op_indexes.push_back(6);
1066 op_indexes.push_back(3);
1067 op_indexes.push_back(7);
1068 op_indexes.push_back(4);
1069 op_indexes.push_back(5);
1070 op_indexes.push_back(8);
1071
1072 vector<vector<Vertex::Index>::size_type> reverse_op_indexes;
1073 DeltaDiffGenerator::GenerateReverseTopoOrderMap(op_indexes,
1074 &reverse_op_indexes);
1075
1076 // Prepare the filesystem with the minimum required for this to work
1077 string temp_dir;
1078 EXPECT_TRUE(utils::MakeTempDirectory("/tmp/AssignTempBlocksReuseTest.XXXXXX",
1079 &temp_dir));
1080 ScopedDirRemover temp_dir_remover(temp_dir);
1081
1082 const size_t kBlockSize = 4096;
1083 vector<char> temp_data(kBlockSize * 3);
1084 FillWithData(&temp_data);
1085 EXPECT_TRUE(WriteFileVector(temp_dir + kFilename, temp_data));
1086 ScopedPathUnlinker filename_unlinker(temp_dir + kFilename);
1087
1088 int fd;
1089 EXPECT_TRUE(utils::MakeTempFile("/tmp/AssignTempBlocksReuseTest.XXXXXX",
1090 NULL,
1091 &fd));
1092 ScopedFdCloser fd_closer(&fd);
1093 off_t data_file_size = 0;
Thieu Le5c7d9752010-12-15 16:09:28 -08001094
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001095 EXPECT_TRUE(DeltaDiffGenerator::AssignTempBlocks(&graph,
1096 temp_dir,
1097 fd,
1098 &data_file_size,
1099 &op_indexes,
1100 &reverse_op_indexes,
1101 cuts));
1102 EXPECT_FALSE(graph[6].valid);
1103 EXPECT_FALSE(graph[7].valid);
1104 EXPECT_EQ(1, graph[1].op.src_extents_size());
1105 EXPECT_EQ(2, graph[1].op.src_extents(0).start_block());
1106 EXPECT_EQ(1, graph[1].op.src_extents(0).num_blocks());
1107 EXPECT_EQ(OP_REPLACE_BZ, graph[5].op.type());
1108}
1109
Andrew de los Reyes927179d2010-12-02 11:26:48 -08001110TEST_F(DeltaDiffGeneratorTest, CreateScratchNodeTest) {
1111 Vertex vertex;
1112 DeltaDiffGenerator::CreateScratchNode(12, 34, &vertex);
1113 EXPECT_EQ(DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ,
1114 vertex.op.type());
1115 EXPECT_EQ(0, vertex.op.data_offset());
1116 EXPECT_EQ(0, vertex.op.data_length());
1117 EXPECT_EQ(1, vertex.op.dst_extents_size());
1118 EXPECT_EQ(12, vertex.op.dst_extents(0).start_block());
1119 EXPECT_EQ(34, vertex.op.dst_extents(0).num_blocks());
1120}
1121
adlr@google.com3defe6a2009-12-04 20:57:17 +00001122} // namespace chromeos_update_engine