delta_diff_utils_unittest.cc revision 55c4f9ba7f5c59e3345f2c1869464433ffa8dc2b
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_generator/delta_diff_utils.h"
18
19#include <algorithm>
20#include <random>
21#include <string>
22#include <vector>
23
24#include <base/files/scoped_file.h>
25#include <base/format_macros.h>
26#include <base/strings/stringprintf.h>
27#include <gtest/gtest.h>
28
29#include "update_engine/common/test_utils.h"
30#include "update_engine/common/utils.h"
31#include "update_engine/payload_generator/delta_diff_generator.h"
32#include "update_engine/payload_generator/extent_ranges.h"
33#include "update_engine/payload_generator/extent_utils.h"
34#include "update_engine/payload_generator/fake_filesystem.h"
35
36using std::string;
37using std::vector;
38
39namespace chromeos_update_engine {
40
41namespace {
42
43// Writes the |data| in the blocks specified by |extents| on the partition
44// |part_path|. The |data| size could be smaller than the size of the blocks
45// passed.
46bool WriteExtents(const string& part_path,
47                  const vector<Extent>& extents,
48                  off_t block_size,
49                  const brillo::Blob& data) {
50  uint64_t offset = 0;
51  base::ScopedFILE fp(fopen(part_path.c_str(), "r+"));
52  TEST_AND_RETURN_FALSE(fp.get());
53
54  for (const Extent& extent : extents) {
55    if (offset >= data.size())
56      break;
57    TEST_AND_RETURN_FALSE(
58        fseek(fp.get(), extent.start_block() * block_size, SEEK_SET) == 0);
59    uint64_t to_write = std::min(extent.num_blocks() * block_size,
60                                 data.size() - offset);
61    TEST_AND_RETURN_FALSE(
62        fwrite(data.data() + offset, 1, to_write, fp.get()) == to_write);
63    offset += extent.num_blocks() * block_size;
64  }
65  return true;
66}
67
68// Create a fake filesystem of the given |size| and initialize the partition
69// holding it in the PartitionConfig |part|.
70void CreatePartition(PartitionConfig* part, const string& pattern,
71                     uint64_t block_size, off_t size) {
72  int fd = -1;
73  ASSERT_TRUE(utils::MakeTempFile(pattern.c_str(), &part->path, &fd));
74  ASSERT_EQ(0, ftruncate(fd, size));
75  ASSERT_EQ(0, close(fd));
76  part->fs_interface.reset(new FakeFilesystem(block_size, size / block_size));
77  part->size = size;
78}
79
80// Writes to the |partition| path blocks such they are all different and they
81// include the tag passed in |tag|, making them also different to any other
82// block on a partition initialized with this function with a different tag.
83// The |block_size| should be a divisor of the partition size.
84// Returns whether the function succeeded writing the partition.
85bool InitializePartitionWithUniqueBlocks(const PartitionConfig& part,
86                                         uint64_t block_size,
87                                         uint64_t tag) {
88  TEST_AND_RETURN_FALSE(part.size % block_size == 0);
89  size_t num_blocks = part.size / block_size;
90  brillo::Blob file_data(part.size);
91  for (size_t i = 0; i < num_blocks; ++i) {
92    string prefix = base::StringPrintf(
93        "block tag 0x%.16" PRIx64 ", block number %16" PRIuS " ", tag, i);
94    brillo::Blob block_data(prefix.begin(), prefix.end());
95    TEST_AND_RETURN_FALSE(prefix.size() <= block_size);
96    block_data.resize(block_size, 'X');
97    std::copy(block_data.begin(), block_data.end(),
98              file_data.begin() + i * block_size);
99  }
100  return test_utils::WriteFileVector(part.path, file_data);
101}
102
103}  // namespace
104
105class DeltaDiffUtilsTest : public ::testing::Test {
106 protected:
107  const uint64_t kDefaultBlockCount = 128;
108
109  void SetUp() override {
110    CreatePartition(&old_part_, "DeltaDiffUtilsTest-old_part-XXXXXX",
111                    block_size_, block_size_ * kDefaultBlockCount);
112    CreatePartition(&new_part_, "DeltaDiffUtilsTest-old_part-XXXXXX",
113                    block_size_, block_size_ * kDefaultBlockCount);
114    ASSERT_TRUE(utils::MakeTempFile("DeltaDiffUtilsTest-blob-XXXXXX",
115                                    &blob_path_,
116                                    &blob_fd_));
117  }
118
119  void TearDown() override {
120    unlink(old_part_.path.c_str());
121    unlink(new_part_.path.c_str());
122    if (blob_fd_ != -1)
123      close(blob_fd_);
124    unlink(blob_path_.c_str());
125  }
126
127  // Helper function to call DeltaMovedAndZeroBlocks() using this class' data
128  // members. This simply avoid repeating all the arguments that never change.
129  bool RunDeltaMovedAndZeroBlocks(ssize_t chunk_blocks,
130                                  bool src_ops_allowed) {
131    BlobFileWriter blob_file(blob_fd_, &blob_size_);
132    return diff_utils::DeltaMovedAndZeroBlocks(
133        &aops_,
134        old_part_.path,
135        new_part_.path,
136        old_part_.size / block_size_,
137        new_part_.size / block_size_,
138        chunk_blocks,
139        src_ops_allowed,
140        &blob_file,
141        &old_visited_blocks_,
142        &new_visited_blocks_);
143  }
144
145  // Old and new temporary partitions used in the tests. These are initialized
146  // with
147  PartitionConfig old_part_{"part"};
148  PartitionConfig new_part_{"part"};
149
150  // The file holding the output blob from the various diff utils functions.
151  string blob_path_;
152  int blob_fd_{-1};
153  off_t blob_size_{0};
154
155  size_t block_size_{kBlockSize};
156
157  // Default input/output arguments used when calling DeltaMovedAndZeroBlocks().
158  vector<AnnotatedOperation> aops_;
159  ExtentRanges old_visited_blocks_;
160  ExtentRanges new_visited_blocks_;
161};
162
163TEST_F(DeltaDiffUtilsTest, MoveSmallTest) {
164  brillo::Blob data_blob(block_size_);
165  test_utils::FillWithData(&data_blob);
166
167  // The old file is on a different block than the new one.
168  vector<Extent> old_extents = { ExtentForRange(11, 1) };
169  vector<Extent> new_extents = { ExtentForRange(1, 1) };
170
171  EXPECT_TRUE(WriteExtents(old_part_.path, old_extents, kBlockSize, data_blob));
172  EXPECT_TRUE(WriteExtents(new_part_.path, new_extents, kBlockSize, data_blob));
173
174  brillo::Blob data;
175  InstallOperation op;
176  EXPECT_TRUE(diff_utils::ReadExtentsToDiff(
177      old_part_.path,
178      new_part_.path,
179      old_extents,
180      new_extents,
181      true,  // bsdiff_allowed
182      true,  // imgdiff_allowed
183      &data,
184      &op,
185      false));  // src_ops_allowed
186  EXPECT_TRUE(data.empty());
187
188  EXPECT_TRUE(op.has_type());
189  EXPECT_EQ(InstallOperation::MOVE, op.type());
190  EXPECT_FALSE(op.has_data_offset());
191  EXPECT_FALSE(op.has_data_length());
192  EXPECT_EQ(1, op.src_extents_size());
193  EXPECT_EQ(kBlockSize, op.src_length());
194  EXPECT_EQ(1, op.dst_extents_size());
195  EXPECT_EQ(kBlockSize, op.dst_length());
196  EXPECT_EQ(BlocksInExtents(op.src_extents()),
197            BlocksInExtents(op.dst_extents()));
198  EXPECT_EQ(1U, BlocksInExtents(op.dst_extents()));
199}
200
201TEST_F(DeltaDiffUtilsTest, MoveWithSameBlock) {
202  // Setup the old/new files so that it has immobile chunks; we make sure to
203  // utilize all sub-cases of such chunks: blocks 21--22 induce a split (src)
204  // and complete removal (dst), whereas blocks 24--25 induce trimming of the
205  // tail (src) and head (dst) of extents. The final block (29) is used for
206  // ensuring we properly account for the number of bytes removed in cases where
207  // the last block is partly filled. The detailed configuration:
208  //
209  // Old:  [ 20     21 22     23     24 25 ] [ 28     29 ]
210  // New:  [ 18 ] [ 21 22 ] [ 20 ] [ 24 25     26 ] [ 29 ]
211  // Same:          ^^ ^^            ^^ ^^            ^^
212  vector<Extent> old_extents = {
213      ExtentForRange(20, 6),
214      ExtentForRange(28, 2) };
215  vector<Extent> new_extents = {
216      ExtentForRange(18, 1),
217      ExtentForRange(21, 2),
218      ExtentForRange(20, 1),
219      ExtentForRange(24, 3),
220      ExtentForRange(29, 1) };
221
222  uint64_t num_blocks = BlocksInExtents(old_extents);
223  EXPECT_EQ(num_blocks, BlocksInExtents(new_extents));
224
225  // The size of the data should match the total number of blocks. Each block
226  // has a different content.
227  brillo::Blob file_data;
228  for (uint64_t i = 0; i < num_blocks; ++i) {
229    file_data.resize(file_data.size() + kBlockSize, 'a' + i);
230  }
231
232  EXPECT_TRUE(WriteExtents(old_part_.path, old_extents, kBlockSize, file_data));
233  EXPECT_TRUE(WriteExtents(new_part_.path, new_extents, kBlockSize, file_data));
234
235  brillo::Blob data;
236  InstallOperation op;
237  EXPECT_TRUE(diff_utils::ReadExtentsToDiff(
238      old_part_.path,
239      new_part_.path,
240      old_extents,
241      new_extents,
242      true,  // bsdiff_allowed
243      true,  // imgdiff_allowed
244      &data,
245      &op,
246      false));  // src_ops_allowed
247
248  EXPECT_TRUE(data.empty());
249
250  EXPECT_TRUE(op.has_type());
251  EXPECT_EQ(InstallOperation::MOVE, op.type());
252  EXPECT_FALSE(op.has_data_offset());
253  EXPECT_FALSE(op.has_data_length());
254
255  // The expected old and new extents that actually moved. See comment above.
256  old_extents = {
257      ExtentForRange(20, 1),
258      ExtentForRange(23, 1),
259      ExtentForRange(28, 1) };
260  new_extents = {
261      ExtentForRange(18, 1),
262      ExtentForRange(20, 1),
263      ExtentForRange(26, 1) };
264  num_blocks = BlocksInExtents(old_extents);
265
266  EXPECT_EQ(num_blocks * kBlockSize, op.src_length());
267  EXPECT_EQ(num_blocks * kBlockSize, op.dst_length());
268
269  EXPECT_EQ(old_extents.size(), static_cast<size_t>(op.src_extents_size()));
270  for (int i = 0; i < op.src_extents_size(); i++) {
271    EXPECT_EQ(old_extents[i].start_block(), op.src_extents(i).start_block())
272        << "i == " << i;
273    EXPECT_EQ(old_extents[i].num_blocks(), op.src_extents(i).num_blocks())
274        << "i == " << i;
275  }
276
277  EXPECT_EQ(new_extents.size(), static_cast<size_t>(op.dst_extents_size()));
278  for (int i = 0; i < op.dst_extents_size(); i++) {
279    EXPECT_EQ(new_extents[i].start_block(), op.dst_extents(i).start_block())
280        << "i == " << i;
281    EXPECT_EQ(new_extents[i].num_blocks(), op.dst_extents(i).num_blocks())
282        << "i == " << i;
283  }
284}
285
286TEST_F(DeltaDiffUtilsTest, BsdiffSmallTest) {
287  // Test a BSDIFF operation from block 1 to block 2.
288  brillo::Blob data_blob(kBlockSize);
289  test_utils::FillWithData(&data_blob);
290
291  // The old file is on a different block than the new one.
292  vector<Extent> old_extents = { ExtentForRange(1, 1) };
293  vector<Extent> new_extents = { ExtentForRange(2, 1) };
294
295  EXPECT_TRUE(WriteExtents(old_part_.path, old_extents, kBlockSize, data_blob));
296  // Modify one byte in the new file.
297  data_blob[0]++;
298  EXPECT_TRUE(WriteExtents(new_part_.path, new_extents, kBlockSize, data_blob));
299
300  brillo::Blob data;
301  InstallOperation op;
302  EXPECT_TRUE(diff_utils::ReadExtentsToDiff(
303      old_part_.path,
304      new_part_.path,
305      old_extents,
306      new_extents,
307      true,  // bsdiff_allowed
308      true,  // imgdiff_allowed
309      &data,
310      &op,
311      false));  // src_ops_allowed
312
313  EXPECT_FALSE(data.empty());
314
315  EXPECT_TRUE(op.has_type());
316  EXPECT_EQ(InstallOperation::BSDIFF, op.type());
317  EXPECT_FALSE(op.has_data_offset());
318  EXPECT_FALSE(op.has_data_length());
319  EXPECT_EQ(1, op.src_extents_size());
320  EXPECT_EQ(kBlockSize, op.src_length());
321  EXPECT_EQ(1, op.dst_extents_size());
322  EXPECT_EQ(kBlockSize, op.dst_length());
323  EXPECT_EQ(BlocksInExtents(op.src_extents()),
324            BlocksInExtents(op.dst_extents()));
325  EXPECT_EQ(1U, BlocksInExtents(op.dst_extents()));
326}
327
328TEST_F(DeltaDiffUtilsTest, BsdiffNotAllowedTest) {
329  // Same setup as the previous test, but this time BSDIFF operations are not
330  // allowed.
331  brillo::Blob data_blob(kBlockSize);
332  test_utils::FillWithData(&data_blob);
333
334  // The old file is on a different block than the new one.
335  vector<Extent> old_extents = { ExtentForRange(1, 1) };
336  vector<Extent> new_extents = { ExtentForRange(2, 1) };
337
338  EXPECT_TRUE(WriteExtents(old_part_.path, old_extents, kBlockSize, data_blob));
339  // Modify one byte in the new file.
340  data_blob[0]++;
341  EXPECT_TRUE(WriteExtents(new_part_.path, new_extents, kBlockSize, data_blob));
342
343  brillo::Blob data;
344  InstallOperation op;
345  EXPECT_TRUE(diff_utils::ReadExtentsToDiff(
346      old_part_.path,
347      new_part_.path,
348      old_extents,
349      new_extents,
350      false,  // bsdiff_allowed
351      false,  // imgdiff_allowed
352      &data,
353      &op,
354      false));  // src_ops_allowed
355
356  EXPECT_FALSE(data.empty());
357
358  // The point of this test is that we don't use BSDIFF the way the above
359  // did. The rest of the details are to be caught in other tests.
360  EXPECT_TRUE(op.has_type());
361  EXPECT_NE(InstallOperation::BSDIFF, op.type());
362}
363
364TEST_F(DeltaDiffUtilsTest, BsdiffNotAllowedMoveTest) {
365  brillo::Blob data_blob(kBlockSize);
366  test_utils::FillWithData(&data_blob);
367
368  // The old file is on a different block than the new one.
369  vector<Extent> old_extents = { ExtentForRange(1, 1) };
370  vector<Extent> new_extents = { ExtentForRange(2, 1) };
371
372  EXPECT_TRUE(WriteExtents(old_part_.path, old_extents, kBlockSize, data_blob));
373  EXPECT_TRUE(WriteExtents(new_part_.path, new_extents, kBlockSize, data_blob));
374
375  brillo::Blob data;
376  InstallOperation op;
377  EXPECT_TRUE(diff_utils::ReadExtentsToDiff(
378      old_part_.path,
379      new_part_.path,
380      old_extents,
381      new_extents,
382      false,  // bsdiff_allowed
383      false,  // imgdiff_allowed
384      &data,
385      &op,
386      false));  // src_ops_allowed
387
388  EXPECT_TRUE(data.empty());
389
390  // The point of this test is that we can still use a MOVE for a file
391  // that is blacklisted.
392  EXPECT_TRUE(op.has_type());
393  EXPECT_EQ(InstallOperation::MOVE, op.type());
394}
395
396TEST_F(DeltaDiffUtilsTest, ReplaceSmallTest) {
397  // The old file is on a different block than the new one.
398  vector<Extent> old_extents = { ExtentForRange(1, 1) };
399  vector<Extent> new_extents = { ExtentForRange(2, 1) };
400
401  // Make a blob that's just 1's that will compress well.
402  brillo::Blob ones(kBlockSize, 1);
403
404  // Make a blob with random data that won't compress well.
405  brillo::Blob random_data;
406  std::mt19937 gen(12345);
407  std::uniform_int_distribution<uint8_t> dis(0, 255);
408  for (uint32_t i = 0; i < kBlockSize; i++) {
409    random_data.push_back(dis(gen));
410  }
411
412  for (int i = 0; i < 2; i++) {
413    brillo::Blob data_to_test = i == 0 ? random_data : ones;
414    // The old_extents will be initialized with 0.
415    EXPECT_TRUE(WriteExtents(new_part_.path, new_extents, kBlockSize,
416                             data_to_test));
417
418    brillo::Blob data;
419    InstallOperation op;
420    EXPECT_TRUE(diff_utils::ReadExtentsToDiff(
421        old_part_.path,
422        new_part_.path,
423        old_extents,
424        new_extents,
425        true,  // bsdiff_allowed
426        true,  // imgdiff_allowed
427        &data,
428        &op,
429        false));  // src_ops_allowed
430    EXPECT_FALSE(data.empty());
431
432    EXPECT_TRUE(op.has_type());
433    const InstallOperation_Type expected_type =
434        (i == 0 ? InstallOperation::REPLACE : InstallOperation::REPLACE_BZ);
435    EXPECT_EQ(expected_type, op.type());
436    EXPECT_FALSE(op.has_data_offset());
437    EXPECT_FALSE(op.has_data_length());
438    EXPECT_EQ(0, op.src_extents_size());
439    EXPECT_FALSE(op.has_src_length());
440    EXPECT_EQ(1, op.dst_extents_size());
441    EXPECT_EQ(data_to_test.size(), op.dst_length());
442    EXPECT_EQ(1U, BlocksInExtents(op.dst_extents()));
443  }
444}
445
446TEST_F(DeltaDiffUtilsTest, SourceCopyTest) {
447  // Makes sure SOURCE_COPY operations are emitted whenever src_ops_allowed
448  // is true. It is the same setup as MoveSmallTest, which checks that
449  // the operation is well-formed.
450  brillo::Blob data_blob(kBlockSize);
451  test_utils::FillWithData(&data_blob);
452
453  // The old file is on a different block than the new one.
454  vector<Extent> old_extents = { ExtentForRange(11, 1) };
455  vector<Extent> new_extents = { ExtentForRange(1, 1) };
456
457  EXPECT_TRUE(WriteExtents(old_part_.path, old_extents, kBlockSize, data_blob));
458  EXPECT_TRUE(WriteExtents(new_part_.path, new_extents, kBlockSize, data_blob));
459
460  brillo::Blob data;
461  InstallOperation op;
462  EXPECT_TRUE(diff_utils::ReadExtentsToDiff(
463      old_part_.path,
464      new_part_.path,
465      old_extents,
466      new_extents,
467      true,  // bsdiff_allowed
468      true,  // imgdiff_allowed
469      &data,
470      &op,
471      true));  // src_ops_allowed
472  EXPECT_TRUE(data.empty());
473
474  EXPECT_TRUE(op.has_type());
475  EXPECT_EQ(InstallOperation::SOURCE_COPY, op.type());
476}
477
478TEST_F(DeltaDiffUtilsTest, SourceBsdiffTest) {
479  // Makes sure SOURCE_BSDIFF operations are emitted whenever src_ops_allowed
480  // is true. It is the same setup as BsdiffSmallTest, which checks
481  // that the operation is well-formed.
482  brillo::Blob data_blob(kBlockSize);
483  test_utils::FillWithData(&data_blob);
484
485  // The old file is on a different block than the new one.
486  vector<Extent> old_extents = { ExtentForRange(1, 1) };
487  vector<Extent> new_extents = { ExtentForRange(2, 1) };
488
489  EXPECT_TRUE(WriteExtents(old_part_.path, old_extents, kBlockSize, data_blob));
490  // Modify one byte in the new file.
491  data_blob[0]++;
492  EXPECT_TRUE(WriteExtents(new_part_.path, new_extents, kBlockSize, data_blob));
493
494  brillo::Blob data;
495  InstallOperation op;
496  EXPECT_TRUE(diff_utils::ReadExtentsToDiff(
497      old_part_.path,
498      new_part_.path,
499      old_extents,
500      new_extents,
501      true,  // bsdiff_allowed
502      true,  // imgdiff_allowed
503      &data,
504      &op,
505      true));  // src_ops_allowed
506
507  EXPECT_FALSE(data.empty());
508  EXPECT_TRUE(op.has_type());
509  EXPECT_EQ(InstallOperation::SOURCE_BSDIFF, op.type());
510}
511
512TEST_F(DeltaDiffUtilsTest, IsNoopOperationTest) {
513  InstallOperation op;
514  op.set_type(InstallOperation::REPLACE_BZ);
515  EXPECT_FALSE(diff_utils::IsNoopOperation(op));
516  op.set_type(InstallOperation::MOVE);
517  EXPECT_TRUE(diff_utils::IsNoopOperation(op));
518  *(op.add_src_extents()) = ExtentForRange(3, 2);
519  *(op.add_dst_extents()) = ExtentForRange(3, 2);
520  EXPECT_TRUE(diff_utils::IsNoopOperation(op));
521  *(op.add_src_extents()) = ExtentForRange(7, 5);
522  *(op.add_dst_extents()) = ExtentForRange(7, 5);
523  EXPECT_TRUE(diff_utils::IsNoopOperation(op));
524  *(op.add_src_extents()) = ExtentForRange(20, 2);
525  *(op.add_dst_extents()) = ExtentForRange(20, 1);
526  *(op.add_dst_extents()) = ExtentForRange(21, 1);
527  EXPECT_TRUE(diff_utils::IsNoopOperation(op));
528  *(op.add_src_extents()) = ExtentForRange(24, 1);
529  *(op.add_dst_extents()) = ExtentForRange(25, 1);
530  EXPECT_FALSE(diff_utils::IsNoopOperation(op));
531}
532
533TEST_F(DeltaDiffUtilsTest, FilterNoopOperations) {
534  AnnotatedOperation aop1;
535  aop1.op.set_type(InstallOperation::REPLACE_BZ);
536  *(aop1.op.add_dst_extents()) = ExtentForRange(3, 2);
537  aop1.name = "aop1";
538
539  AnnotatedOperation aop2 = aop1;
540  aop2.name = "aop2";
541
542  AnnotatedOperation noop;
543  noop.op.set_type(InstallOperation::MOVE);
544  *(noop.op.add_src_extents()) = ExtentForRange(3, 2);
545  *(noop.op.add_dst_extents()) = ExtentForRange(3, 2);
546  noop.name = "noop";
547
548  vector<AnnotatedOperation> ops = {noop, aop1, noop, noop, aop2, noop};
549  diff_utils::FilterNoopOperations(&ops);
550  EXPECT_EQ(2u, ops.size());
551  EXPECT_EQ("aop1", ops[0].name);
552  EXPECT_EQ("aop2", ops[1].name);
553}
554
555// Test the simple case where all the blocks are different and no new blocks are
556// zeroed.
557TEST_F(DeltaDiffUtilsTest, NoZeroedOrUniqueBlocksDetected) {
558  InitializePartitionWithUniqueBlocks(old_part_, block_size_, 5);
559  InitializePartitionWithUniqueBlocks(new_part_, block_size_, 42);
560
561  EXPECT_TRUE(RunDeltaMovedAndZeroBlocks(-1,  // chunk_blocks
562                                         false));  // src_ops_allowed
563
564  EXPECT_EQ(0U, old_visited_blocks_.blocks());
565  EXPECT_EQ(0U, new_visited_blocks_.blocks());
566  EXPECT_EQ(0, blob_size_);
567  EXPECT_TRUE(aops_.empty());
568}
569
570// Test that when the partitions have identical blocks in the same positions no
571// MOVE operation is performed and all the blocks are handled.
572TEST_F(DeltaDiffUtilsTest, IdenticalPartitionsDontMove) {
573  InitializePartitionWithUniqueBlocks(old_part_, block_size_, 42);
574  InitializePartitionWithUniqueBlocks(new_part_, block_size_, 42);
575
576  // Mark some of the blocks as already visited.
577  vector<Extent> already_visited = {ExtentForRange(5, 10),
578                                    ExtentForRange(25, 10)};
579  old_visited_blocks_.AddExtents(already_visited);
580  new_visited_blocks_.AddExtents(already_visited);
581
582  // Most of the blocks rest in the same place, but there's no need for MOVE
583  // operations on those blocks.
584  EXPECT_TRUE(RunDeltaMovedAndZeroBlocks(-1,  // chunk_blocks
585                                         false));  // src_ops_allowed
586
587  EXPECT_EQ(kDefaultBlockCount, old_visited_blocks_.blocks());
588  EXPECT_EQ(kDefaultBlockCount, new_visited_blocks_.blocks());
589  EXPECT_EQ(0, blob_size_);
590  EXPECT_TRUE(aops_.empty());
591}
592
593// Test that when the partitions have identical blocks in the same positions
594// MOVE operation is performed and all the blocks are handled.
595TEST_F(DeltaDiffUtilsTest, IdenticalBlocksAreCopiedFromSource) {
596  // We use a smaller partition for this test.
597  old_part_.size = kBlockSize * 50;
598  new_part_.size = kBlockSize * 50;
599
600  InitializePartitionWithUniqueBlocks(old_part_, block_size_, 42);
601  InitializePartitionWithUniqueBlocks(new_part_, block_size_, 42);
602
603  // Mark some of the blocks as already visited.
604  vector<Extent> already_visited = {ExtentForRange(5, 5),
605                                    ExtentForRange(25, 7)};
606  old_visited_blocks_.AddExtents(already_visited);
607  new_visited_blocks_.AddExtents(already_visited);
608
609  // Override some of the old blocks with different data.
610  vector<Extent> different_blocks = {ExtentForRange(40, 5)};
611  EXPECT_TRUE(WriteExtents(old_part_.path, different_blocks, kBlockSize,
612                           brillo::Blob(5 * kBlockSize, 'a')));
613
614  EXPECT_TRUE(RunDeltaMovedAndZeroBlocks(10,  // chunk_blocks
615                                         true));  // src_ops_allowed
616
617  ExtentRanges expected_ranges;
618  expected_ranges.AddExtent(ExtentForRange(0, 50));
619  expected_ranges.SubtractExtents(different_blocks);
620
621  EXPECT_EQ(expected_ranges.extent_set(), old_visited_blocks_.extent_set());
622  EXPECT_EQ(expected_ranges.extent_set(), new_visited_blocks_.extent_set());
623  EXPECT_EQ(0, blob_size_);
624
625  // We expect all the blocks that we didn't override with |different_blocks|
626  // and that we didn't mark as visited in |already_visited| to match and have a
627  // SOURCE_COPY operation chunked at 10 blocks.
628  vector<Extent> expected_op_extents = {
629      ExtentForRange(0, 5),
630      ExtentForRange(10, 10),
631      ExtentForRange(20, 5),
632      ExtentForRange(32, 8),
633      ExtentForRange(45, 5),
634  };
635
636  EXPECT_EQ(expected_op_extents.size(), aops_.size());
637  for (size_t i = 0; i < aops_.size() && i < expected_op_extents.size(); ++i) {
638    SCOPED_TRACE(base::StringPrintf("Failed on operation number %" PRIuS, i));
639    const AnnotatedOperation& aop = aops_[i];
640    EXPECT_EQ(InstallOperation::SOURCE_COPY, aop.op.type());
641    EXPECT_EQ(1, aop.op.src_extents_size());
642    EXPECT_EQ(expected_op_extents[i], aop.op.src_extents(0));
643    EXPECT_EQ(1, aop.op.dst_extents_size());
644    EXPECT_EQ(expected_op_extents[i], aop.op.dst_extents(0));
645  }
646}
647
648TEST_F(DeltaDiffUtilsTest, IdenticalBlocksAreCopiedInOder) {
649  // We use a smaller partition for this test.
650  old_part_.size = block_size_ * 50;
651  new_part_.size = block_size_ * 50;
652
653  // Create two identical partitions with 5 copies of the same unique "file".
654  brillo::Blob file_data(block_size_ * 10, 'a');
655  for (size_t offset = 0; offset < file_data.size(); offset += block_size_)
656    file_data[offset] = 'a' + offset / block_size_;
657
658  brillo::Blob partition_data(old_part_.size);
659  for (size_t offset = 0; offset < partition_data.size();
660       offset += file_data.size()) {
661    std::copy(file_data.begin(), file_data.end(),
662              partition_data.begin() + offset);
663  }
664  EXPECT_TRUE(test_utils::WriteFileVector(old_part_.path, partition_data));
665  EXPECT_TRUE(test_utils::WriteFileVector(new_part_.path, partition_data));
666
667  EXPECT_TRUE(RunDeltaMovedAndZeroBlocks(-1,  // chunk_blocks
668                                         true));  // src_ops_allowed
669
670  // There should be only one SOURCE_COPY, for the whole partition and the
671  // source extents should cover only the first copy of the source file since
672  // we prefer to re-read files (maybe cached) instead of continue reading the
673  // rest of the partition.
674  EXPECT_EQ(1U, aops_.size());
675  const AnnotatedOperation& aop = aops_[0];
676  EXPECT_EQ(InstallOperation::SOURCE_COPY, aop.op.type());
677  EXPECT_EQ(5, aop.op.src_extents_size());
678  for (int i = 0; i < aop.op.src_extents_size(); ++i) {
679    EXPECT_EQ(ExtentForRange(0, 10), aop.op.src_extents(i));
680  }
681
682  EXPECT_EQ(1, aop.op.dst_extents_size());
683  EXPECT_EQ(ExtentForRange(0, 50), aop.op.dst_extents(0));
684
685  EXPECT_EQ(0, blob_size_);
686}
687
688// Test that all blocks with zeros are handled separately using REPLACE_BZ
689// operations unless they are not moved.
690TEST_F(DeltaDiffUtilsTest, ZeroBlocksUseReplaceBz) {
691  InitializePartitionWithUniqueBlocks(old_part_, block_size_, 42);
692  InitializePartitionWithUniqueBlocks(new_part_, block_size_, 5);
693
694  // We set four ranges of blocks with zeros: a single block, a range that fits
695  // in the chunk size, a range that doesn't and finally a range of zeros that
696  // was also zeros in the old image.
697  vector<Extent> new_zeros = {
698      ExtentForRange(10, 1),
699      ExtentForRange(20, 4),
700      // The last range is split since the old image has zeros in part of it.
701      ExtentForRange(30, 20),
702  };
703  brillo::Blob zeros_data(BlocksInExtents(new_zeros) * block_size_, '\0');
704  EXPECT_TRUE(WriteExtents(new_part_.path, new_zeros, block_size_, zeros_data));
705
706  vector<Extent> old_zeros = vector<Extent>{ExtentForRange(43, 7)};
707  EXPECT_TRUE(WriteExtents(old_part_.path, old_zeros, block_size_, zeros_data));
708
709  EXPECT_TRUE(RunDeltaMovedAndZeroBlocks(5,  // chunk_blocks
710                                         false));  // src_ops_allowed
711
712  // Zeroed blocks from old_visited_blocks_ were copied over, so me actually
713  // use them regardless of the trivial MOVE operation not being emitted.
714  EXPECT_EQ(old_zeros,
715            old_visited_blocks_.GetExtentsForBlockCount(
716                old_visited_blocks_.blocks()));
717
718  // All the new zeroed blocks should be used, part with REPLACE_BZ and part
719  // trivial MOVE operations (not included).
720  EXPECT_EQ(new_zeros,
721            new_visited_blocks_.GetExtentsForBlockCount(
722                new_visited_blocks_.blocks()));
723
724  vector<Extent> expected_op_extents = {
725      ExtentForRange(10, 1),
726      ExtentForRange(20, 4),
727      // This range should be split.
728      ExtentForRange(30, 5),
729      ExtentForRange(35, 5),
730      ExtentForRange(40, 3),
731  };
732
733  EXPECT_EQ(expected_op_extents.size(), aops_.size());
734  for (size_t i = 0; i < aops_.size() && i < expected_op_extents.size(); ++i) {
735    SCOPED_TRACE(base::StringPrintf("Failed on operation number %" PRIuS, i));
736    const AnnotatedOperation& aop = aops_[i];
737    EXPECT_EQ(InstallOperation::REPLACE_BZ, aop.op.type());
738    EXPECT_EQ(0, aop.op.src_extents_size());
739    EXPECT_EQ(1, aop.op.dst_extents_size());
740    EXPECT_EQ(expected_op_extents[i], aop.op.dst_extents(0));
741  }
742  EXPECT_NE(0, blob_size_);
743}
744
745TEST_F(DeltaDiffUtilsTest, ShuffledBlocksAreTracked) {
746  vector<uint64_t> permutation = {0, 1, 5, 6, 7, 2, 3, 4, 9, 10, 11, 12, 8};
747  vector<Extent> perm_extents;
748  for (uint64_t x : permutation)
749    AppendBlockToExtents(&perm_extents, x);
750
751  // We use a smaller partition for this test.
752  old_part_.size = block_size_ * permutation.size();
753  new_part_.size = block_size_ * permutation.size();
754  InitializePartitionWithUniqueBlocks(new_part_, block_size_, 123);
755
756  // We initialize the old_part_ with the blocks from new_part but in the
757  // |permutation| order. Block i in the old_part_ will contain the same data
758  // as block permutation[i] in the new_part_.
759  brillo::Blob new_contents;
760  EXPECT_TRUE(utils::ReadFile(new_part_.path, &new_contents));
761  EXPECT_TRUE(WriteExtents(old_part_.path, perm_extents, block_size_,
762                           new_contents));
763
764  EXPECT_TRUE(RunDeltaMovedAndZeroBlocks(-1,  // chunk_blocks
765                                         true));  // src_ops_allowed
766
767  EXPECT_EQ(permutation.size(), old_visited_blocks_.blocks());
768  EXPECT_EQ(permutation.size(), new_visited_blocks_.blocks());
769
770  // There should be only one SOURCE_COPY, with a complicate list of extents.
771  EXPECT_EQ(1U, aops_.size());
772  const AnnotatedOperation& aop = aops_[0];
773  EXPECT_EQ(InstallOperation::SOURCE_COPY, aop.op.type());
774  vector<Extent> aop_src_extents;
775  ExtentsToVector(aop.op.src_extents(), &aop_src_extents);
776  EXPECT_EQ(perm_extents, aop_src_extents);
777
778  EXPECT_EQ(1, aop.op.dst_extents_size());
779  EXPECT_EQ(ExtentForRange(0, permutation.size()), aop.op.dst_extents(0));
780
781  EXPECT_EQ(0, blob_size_);
782}
783
784}  // namespace chromeos_update_engine
785