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 
36 using std::string;
37 using std::vector;
38 
39 namespace chromeos_update_engine {
40 
41 namespace {
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.
WriteExtents(const string & part_path,const vector<Extent> & extents,off_t block_size,const brillo::Blob & data)46 bool 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 =
60         std::min(static_cast<uint64_t>(extent.num_blocks()) * block_size,
61                  static_cast<uint64_t>(data.size()) - offset);
62     TEST_AND_RETURN_FALSE(fwrite(data.data() + offset, 1, to_write, fp.get()) ==
63                           to_write);
64     offset += extent.num_blocks() * block_size;
65   }
66   return true;
67 }
68 
69 // Create a fake filesystem of the given |size| and initialize the partition
70 // holding it in the PartitionConfig |part|.
CreatePartition(PartitionConfig * part,ScopedTempFile * part_file,uint64_t block_size,off_t size)71 void CreatePartition(PartitionConfig* part,
72                      ScopedTempFile* part_file,
73                      uint64_t block_size,
74                      off_t size) {
75   part->path = part_file->path();
76   ASSERT_EQ(0, ftruncate(part_file->fd(), size));
77   part_file->CloseFd();
78   part->fs_interface.reset(new FakeFilesystem(block_size, size / block_size));
79   part->size = size;
80 }
81 
82 // Writes to the |partition| path blocks such they are all different and they
83 // include the tag passed in |tag|, making them also different to any other
84 // block on a partition initialized with this function with a different tag.
85 // The |block_size| should be a divisor of the partition size.
86 // Returns whether the function succeeded writing the partition.
InitializePartitionWithUniqueBlocks(const PartitionConfig & part,uint64_t block_size,uint64_t tag)87 bool InitializePartitionWithUniqueBlocks(const PartitionConfig& part,
88                                          uint64_t block_size,
89                                          uint64_t tag) {
90   TEST_AND_RETURN_FALSE(part.size % block_size == 0);
91   size_t num_blocks = part.size / block_size;
92   brillo::Blob file_data(part.size);
93   for (size_t i = 0; i < num_blocks; ++i) {
94     string prefix = base::StringPrintf(
95         "block tag 0x%.16" PRIx64 ", block number %16" PRIuS " ", tag, i);
96     brillo::Blob block_data(prefix.begin(), prefix.end());
97     TEST_AND_RETURN_FALSE(prefix.size() <= block_size);
98     block_data.resize(block_size, 'X');
99     std::copy(block_data.begin(),
100               block_data.end(),
101               file_data.begin() + i * block_size);
102   }
103   return test_utils::WriteFileVector(part.path, file_data);
104 }
105 
106 }  // namespace
107 
108 class DeltaDiffUtilsTest : public ::testing::Test {
109  protected:
110   const uint64_t kDefaultBlockCount = 128;
111 
SetUp()112   void SetUp() override {
113     CreatePartition(&old_part_,
114                     &old_part_file_,
115                     block_size_,
116                     block_size_ * kDefaultBlockCount);
117     CreatePartition(&new_part_,
118                     &new_part_file_,
119                     block_size_,
120                     block_size_ * kDefaultBlockCount);
121   }
122 
123   // Helper function to call DeltaMovedAndZeroBlocks() using this class' data
124   // members. This simply avoids repeating all the arguments that never change.
RunDeltaMovedAndZeroBlocks(ssize_t chunk_blocks,uint32_t minor_version)125   bool RunDeltaMovedAndZeroBlocks(ssize_t chunk_blocks,
126                                   uint32_t minor_version) {
127     BlobFileWriter blob_file(tmp_blob_file_.fd(), &blob_size_);
128     PayloadVersion version(kBrilloMajorPayloadVersion, minor_version);
129     ExtentRanges old_zero_blocks;
130     return diff_utils::DeltaMovedAndZeroBlocks(&aops_,
131                                                old_part_.path,
132                                                new_part_.path,
133                                                old_part_.size / block_size_,
134                                                new_part_.size / block_size_,
135                                                chunk_blocks,
136                                                version,
137                                                &blob_file,
138                                                &old_visited_blocks_,
139                                                &new_visited_blocks_,
140                                                &old_zero_blocks);
141   }
142 
143   // Old and new temporary partitions used in the tests. These are initialized
144   // with
145   PartitionConfig old_part_{"part"};
146   PartitionConfig new_part_{"part"};
147   ScopedTempFile old_part_file_{"DeltaDiffUtilsTest-old_part-XXXXXX", true};
148   ScopedTempFile new_part_file_{"DeltaDiffUtilsTest-new_part-XXXXXX", true};
149 
150   // The file holding the output blob from the various diff utils functions.
151   ScopedTempFile tmp_blob_file_{"DeltaDiffUtilsTest-blob-XXXXXX", true};
152   off_t blob_size_{0};
153 
154   size_t block_size_{kBlockSize};
155 
156   // Default input/output arguments used when calling DeltaMovedAndZeroBlocks().
157   vector<AnnotatedOperation> aops_;
158   ExtentRanges old_visited_blocks_;
159   ExtentRanges new_visited_blocks_;
160 };
161 
TEST_F(DeltaDiffUtilsTest,SkipVerityExtentsTest)162 TEST_F(DeltaDiffUtilsTest, SkipVerityExtentsTest) {
163   new_part_.verity.hash_tree_extent = ExtentForRange(20, 30);
164   new_part_.verity.fec_extent = ExtentForRange(40, 50);
165 
166   BlobFileWriter blob_file(tmp_blob_file_.fd(), &blob_size_);
167   EXPECT_TRUE(diff_utils::DeltaReadPartition(
168       &aops_,
169       old_part_,
170       new_part_,
171       -1,
172       -1,
173       PayloadVersion(kMaxSupportedMajorPayloadVersion,
174                      kVerityMinorPayloadVersion),
175       &blob_file));
176   for (const auto& aop : aops_) {
177     new_visited_blocks_.AddRepeatedExtents(aop.op.dst_extents());
178   }
179   for (const auto& extent : new_visited_blocks_.extent_set()) {
180     EXPECT_FALSE(ExtentRanges::ExtentsOverlap(
181         extent, new_part_.verity.hash_tree_extent));
182     EXPECT_FALSE(
183         ExtentRanges::ExtentsOverlap(extent, new_part_.verity.fec_extent));
184   }
185 }
186 
TEST_F(DeltaDiffUtilsTest,ReplaceSmallTest)187 TEST_F(DeltaDiffUtilsTest, ReplaceSmallTest) {
188   // The old file is on a different block than the new one.
189   vector<Extent> old_extents = {ExtentForRange(1, 1)};
190   vector<Extent> new_extents = {ExtentForRange(2, 1)};
191 
192   // Make a blob that's just 1's that will compress well.
193   brillo::Blob ones(kBlockSize, 1);
194 
195   // Make a blob with random data that won't compress well.
196   brillo::Blob random_data;
197   std::mt19937 gen(12345);
198   std::uniform_int_distribution<uint8_t> dis(0, 255);
199   for (uint32_t i = 0; i < kBlockSize; i++) {
200     random_data.push_back(dis(gen));
201   }
202 
203   for (int i = 0; i < 2; i++) {
204     brillo::Blob data_to_test = i == 0 ? random_data : ones;
205     // The old_extents will be initialized with 0.
206     EXPECT_TRUE(
207         WriteExtents(new_part_.path, new_extents, kBlockSize, data_to_test));
208 
209     brillo::Blob data;
210     InstallOperation op;
211     EXPECT_TRUE(diff_utils::ReadExtentsToDiff(
212         old_part_.path,
213         new_part_.path,
214         old_extents,
215         new_extents,
216         {},  // old_deflates
217         {},  // new_deflates
218         PayloadVersion(kBrilloMajorPayloadVersion, kSourceMinorPayloadVersion),
219         &data,
220         &op));
221     EXPECT_FALSE(data.empty());
222 
223     EXPECT_TRUE(op.has_type());
224     const InstallOperation::Type expected_type =
225         (i == 0 ? InstallOperation::REPLACE : InstallOperation::REPLACE_BZ);
226     EXPECT_EQ(expected_type, op.type());
227     EXPECT_FALSE(op.has_data_offset());
228     EXPECT_FALSE(op.has_data_length());
229     EXPECT_EQ(0, op.src_extents_size());
230     EXPECT_FALSE(op.has_src_length());
231     EXPECT_EQ(1, op.dst_extents_size());
232     EXPECT_FALSE(op.has_dst_length());
233     EXPECT_EQ(1U, utils::BlocksInExtents(op.dst_extents()));
234   }
235 }
236 
TEST_F(DeltaDiffUtilsTest,SourceCopyTest)237 TEST_F(DeltaDiffUtilsTest, SourceCopyTest) {
238   // Makes sure SOURCE_COPY operations are emitted whenever src_ops_allowed
239   // is true. It is the same setup as MoveSmallTest, which checks that
240   // the operation is well-formed.
241   brillo::Blob data_blob(kBlockSize);
242   test_utils::FillWithData(&data_blob);
243 
244   // The old file is on a different block than the new one.
245   vector<Extent> old_extents = {ExtentForRange(11, 1)};
246   vector<Extent> new_extents = {ExtentForRange(1, 1)};
247 
248   EXPECT_TRUE(WriteExtents(old_part_.path, old_extents, kBlockSize, data_blob));
249   EXPECT_TRUE(WriteExtents(new_part_.path, new_extents, kBlockSize, data_blob));
250 
251   brillo::Blob data;
252   InstallOperation op;
253   EXPECT_TRUE(diff_utils::ReadExtentsToDiff(
254       old_part_.path,
255       new_part_.path,
256       old_extents,
257       new_extents,
258       {},  // old_deflates
259       {},  // new_deflates
260       PayloadVersion(kBrilloMajorPayloadVersion, kSourceMinorPayloadVersion),
261       &data,
262       &op));
263   EXPECT_TRUE(data.empty());
264 
265   EXPECT_TRUE(op.has_type());
266   EXPECT_EQ(InstallOperation::SOURCE_COPY, op.type());
267 }
268 
TEST_F(DeltaDiffUtilsTest,SourceBsdiffTest)269 TEST_F(DeltaDiffUtilsTest, SourceBsdiffTest) {
270   // Makes sure SOURCE_BSDIFF operations are emitted whenever src_ops_allowed
271   // is true. It is the same setup as BsdiffSmallTest, which checks
272   // that the operation is well-formed.
273   brillo::Blob data_blob(kBlockSize);
274   test_utils::FillWithData(&data_blob);
275 
276   // The old file is on a different block than the new one.
277   vector<Extent> old_extents = {ExtentForRange(1, 1)};
278   vector<Extent> new_extents = {ExtentForRange(2, 1)};
279 
280   EXPECT_TRUE(WriteExtents(old_part_.path, old_extents, kBlockSize, data_blob));
281   // Modify one byte in the new file.
282   data_blob[0]++;
283   EXPECT_TRUE(WriteExtents(new_part_.path, new_extents, kBlockSize, data_blob));
284 
285   brillo::Blob data;
286   InstallOperation op;
287   EXPECT_TRUE(diff_utils::ReadExtentsToDiff(
288       old_part_.path,
289       new_part_.path,
290       old_extents,
291       new_extents,
292       {},  // old_deflates
293       {},  // new_deflates
294       PayloadVersion(kBrilloMajorPayloadVersion, kSourceMinorPayloadVersion),
295       &data,
296       &op));
297 
298   EXPECT_FALSE(data.empty());
299   EXPECT_TRUE(op.has_type());
300   EXPECT_EQ(InstallOperation::SOURCE_BSDIFF, op.type());
301 }
302 
TEST_F(DeltaDiffUtilsTest,PreferReplaceTest)303 TEST_F(DeltaDiffUtilsTest, PreferReplaceTest) {
304   brillo::Blob data_blob(kBlockSize);
305   vector<Extent> extents = {ExtentForRange(1, 1)};
306 
307   // Write something in the first 50 bytes so that REPLACE_BZ will be slightly
308   // larger than BROTLI_BSDIFF.
309   std::iota(data_blob.begin(), data_blob.begin() + 50, 0);
310   EXPECT_TRUE(WriteExtents(old_part_.path, extents, kBlockSize, data_blob));
311   // Shift the first 50 bytes in the new file by one.
312   std::iota(data_blob.begin(), data_blob.begin() + 50, 1);
313   EXPECT_TRUE(WriteExtents(new_part_.path, extents, kBlockSize, data_blob));
314 
315   brillo::Blob data;
316   InstallOperation op;
317   EXPECT_TRUE(diff_utils::ReadExtentsToDiff(
318       old_part_.path,
319       new_part_.path,
320       extents,
321       extents,
322       {},  // old_deflates
323       {},  // new_deflates
324       PayloadVersion(kMaxSupportedMajorPayloadVersion,
325                      kMaxSupportedMinorPayloadVersion),
326       &data,
327       &op));
328 
329   EXPECT_FALSE(data.empty());
330   EXPECT_TRUE(op.has_type());
331   EXPECT_EQ(InstallOperation::REPLACE_BZ, op.type());
332 }
333 
334 // Test the simple case where all the blocks are different and no new blocks are
335 // zeroed.
TEST_F(DeltaDiffUtilsTest,NoZeroedOrUniqueBlocksDetected)336 TEST_F(DeltaDiffUtilsTest, NoZeroedOrUniqueBlocksDetected) {
337   InitializePartitionWithUniqueBlocks(old_part_, block_size_, 5);
338   InitializePartitionWithUniqueBlocks(new_part_, block_size_, 42);
339 
340   EXPECT_TRUE(RunDeltaMovedAndZeroBlocks(-1,  // chunk_blocks
341                                          kSourceMinorPayloadVersion));
342 
343   EXPECT_EQ(0U, old_visited_blocks_.blocks());
344   EXPECT_EQ(0U, new_visited_blocks_.blocks());
345   EXPECT_EQ(0, blob_size_);
346   EXPECT_TRUE(aops_.empty());
347 }
348 
349 // Test that when the partitions have identical blocks in the same positions
350 // MOVE operation is performed and all the blocks are handled.
TEST_F(DeltaDiffUtilsTest,IdenticalBlocksAreCopiedFromSource)351 TEST_F(DeltaDiffUtilsTest, IdenticalBlocksAreCopiedFromSource) {
352   // We use a smaller partition for this test.
353   old_part_.size = kBlockSize * 50;
354   new_part_.size = kBlockSize * 50;
355 
356   InitializePartitionWithUniqueBlocks(old_part_, block_size_, 42);
357   InitializePartitionWithUniqueBlocks(new_part_, block_size_, 42);
358 
359   // Mark some of the blocks as already visited.
360   vector<Extent> already_visited = {ExtentForRange(5, 5),
361                                     ExtentForRange(25, 7)};
362   old_visited_blocks_.AddExtents(already_visited);
363   new_visited_blocks_.AddExtents(already_visited);
364 
365   // Override some of the old blocks with different data.
366   vector<Extent> different_blocks = {ExtentForRange(40, 5)};
367   EXPECT_TRUE(WriteExtents(old_part_.path,
368                            different_blocks,
369                            kBlockSize,
370                            brillo::Blob(5 * kBlockSize, 'a')));
371 
372   EXPECT_TRUE(RunDeltaMovedAndZeroBlocks(10,  // chunk_blocks
373                                          kSourceMinorPayloadVersion));
374 
375   ExtentRanges expected_ranges;
376   expected_ranges.AddExtent(ExtentForRange(0, 50));
377   expected_ranges.SubtractExtents(different_blocks);
378 
379   EXPECT_EQ(expected_ranges.extent_set(), old_visited_blocks_.extent_set());
380   EXPECT_EQ(expected_ranges.extent_set(), new_visited_blocks_.extent_set());
381   EXPECT_EQ(0, blob_size_);
382 
383   // We expect all the blocks that we didn't override with |different_blocks|
384   // and that we didn't mark as visited in |already_visited| to match and have a
385   // SOURCE_COPY operation chunked at 10 blocks.
386   vector<Extent> expected_op_extents = {
387       ExtentForRange(0, 5),
388       ExtentForRange(10, 10),
389       ExtentForRange(20, 5),
390       ExtentForRange(32, 8),
391       ExtentForRange(45, 5),
392   };
393 
394   EXPECT_EQ(expected_op_extents.size(), aops_.size());
395   for (size_t i = 0; i < aops_.size() && i < expected_op_extents.size(); ++i) {
396     SCOPED_TRACE(base::StringPrintf("Failed on operation number %" PRIuS, i));
397     const AnnotatedOperation& aop = aops_[i];
398     EXPECT_EQ(InstallOperation::SOURCE_COPY, aop.op.type());
399     EXPECT_EQ(1, aop.op.src_extents_size());
400     EXPECT_EQ(expected_op_extents[i], aop.op.src_extents(0));
401     EXPECT_EQ(1, aop.op.dst_extents_size());
402     EXPECT_EQ(expected_op_extents[i], aop.op.dst_extents(0));
403   }
404 }
405 
TEST_F(DeltaDiffUtilsTest,IdenticalBlocksAreCopiedInOder)406 TEST_F(DeltaDiffUtilsTest, IdenticalBlocksAreCopiedInOder) {
407   // We use a smaller partition for this test.
408   old_part_.size = block_size_ * 50;
409   new_part_.size = block_size_ * 50;
410 
411   // Create two identical partitions with 5 copies of the same unique "file".
412   brillo::Blob file_data(block_size_ * 10, 'a');
413   for (size_t offset = 0; offset < file_data.size(); offset += block_size_)
414     file_data[offset] = 'a' + offset / block_size_;
415 
416   brillo::Blob partition_data(old_part_.size);
417   for (size_t offset = 0; offset < partition_data.size();
418        offset += file_data.size()) {
419     std::copy(
420         file_data.begin(), file_data.end(), partition_data.begin() + offset);
421   }
422   EXPECT_TRUE(test_utils::WriteFileVector(old_part_.path, partition_data));
423   EXPECT_TRUE(test_utils::WriteFileVector(new_part_.path, partition_data));
424 
425   EXPECT_TRUE(RunDeltaMovedAndZeroBlocks(-1,  // chunk_blocks
426                                          kSourceMinorPayloadVersion));
427 
428   // There should be only one SOURCE_COPY, for the whole partition and the
429   // source extents should cover only the first copy of the source file since
430   // we prefer to re-read files (maybe cached) instead of continue reading the
431   // rest of the partition.
432   EXPECT_EQ(1U, aops_.size());
433   const AnnotatedOperation& aop = aops_[0];
434   EXPECT_EQ(InstallOperation::SOURCE_COPY, aop.op.type());
435   EXPECT_EQ(5, aop.op.src_extents_size());
436   for (int i = 0; i < aop.op.src_extents_size(); ++i) {
437     EXPECT_EQ(ExtentForRange(0, 10), aop.op.src_extents(i));
438   }
439 
440   EXPECT_EQ(1, aop.op.dst_extents_size());
441   EXPECT_EQ(ExtentForRange(0, 50), aop.op.dst_extents(0));
442 
443   EXPECT_EQ(0, blob_size_);
444 }
445 
446 // Test that all blocks with zeros are handled separately using REPLACE_BZ
447 // operations unless they are not moved.
TEST_F(DeltaDiffUtilsTest,ZeroBlocksUseReplaceBz)448 TEST_F(DeltaDiffUtilsTest, ZeroBlocksUseReplaceBz) {
449   InitializePartitionWithUniqueBlocks(old_part_, block_size_, 42);
450   InitializePartitionWithUniqueBlocks(new_part_, block_size_, 5);
451 
452   // We set four ranges of blocks with zeros: a single block, a range that fits
453   // in the chunk size, a range that doesn't and finally a range of zeros that
454   // was also zeros in the old image.
455   vector<Extent> new_zeros = {
456       ExtentForRange(10, 1),
457       ExtentForRange(20, 4),
458       // The last range is split since the old image has zeros in part of it.
459       ExtentForRange(30, 20),
460   };
461   brillo::Blob zeros_data(utils::BlocksInExtents(new_zeros) * block_size_,
462                           '\0');
463   EXPECT_TRUE(WriteExtents(new_part_.path, new_zeros, block_size_, zeros_data));
464 
465   vector<Extent> old_zeros = vector<Extent>{ExtentForRange(43, 7)};
466   EXPECT_TRUE(WriteExtents(old_part_.path, old_zeros, block_size_, zeros_data));
467 
468   EXPECT_TRUE(RunDeltaMovedAndZeroBlocks(5,  // chunk_blocks
469                                          kSourceMinorPayloadVersion));
470 
471   // Zeroed blocks from |old_visited_blocks_| were copied over.
472   EXPECT_EQ(old_zeros,
473             old_visited_blocks_.GetExtentsForBlockCount(
474                 old_visited_blocks_.blocks()));
475 
476   // All the new zeroed blocks should be used with REPLACE_BZ.
477   EXPECT_EQ(new_zeros,
478             new_visited_blocks_.GetExtentsForBlockCount(
479                 new_visited_blocks_.blocks()));
480 
481   vector<Extent> expected_op_extents = {
482       ExtentForRange(10, 1),
483       ExtentForRange(20, 4),
484       // This range should be split.
485       ExtentForRange(30, 5),
486       ExtentForRange(35, 5),
487       ExtentForRange(40, 5),
488       ExtentForRange(45, 5),
489   };
490 
491   EXPECT_EQ(expected_op_extents.size(), aops_.size());
492   for (size_t i = 0; i < aops_.size() && i < expected_op_extents.size(); ++i) {
493     SCOPED_TRACE(base::StringPrintf("Failed on operation number %" PRIuS, i));
494     const AnnotatedOperation& aop = aops_[i];
495     EXPECT_EQ(InstallOperation::REPLACE_BZ, aop.op.type());
496     EXPECT_EQ(0, aop.op.src_extents_size());
497     EXPECT_EQ(1, aop.op.dst_extents_size());
498     EXPECT_EQ(expected_op_extents[i], aop.op.dst_extents(0));
499   }
500   EXPECT_NE(0, blob_size_);
501 }
502 
TEST_F(DeltaDiffUtilsTest,ShuffledBlocksAreTracked)503 TEST_F(DeltaDiffUtilsTest, ShuffledBlocksAreTracked) {
504   vector<uint64_t> permutation = {0, 1, 5, 6, 7, 2, 3, 4, 9, 10, 11, 12, 8};
505   vector<Extent> perm_extents;
506   for (uint64_t x : permutation)
507     AppendBlockToExtents(&perm_extents, x);
508 
509   // We use a smaller partition for this test.
510   old_part_.size = block_size_ * permutation.size();
511   new_part_.size = block_size_ * permutation.size();
512   InitializePartitionWithUniqueBlocks(new_part_, block_size_, 123);
513 
514   // We initialize the old_part_ with the blocks from new_part but in the
515   // |permutation| order. Block i in the old_part_ will contain the same data
516   // as block permutation[i] in the new_part_.
517   brillo::Blob new_contents;
518   EXPECT_TRUE(utils::ReadFile(new_part_.path, &new_contents));
519   EXPECT_TRUE(
520       WriteExtents(old_part_.path, perm_extents, block_size_, new_contents));
521 
522   EXPECT_TRUE(RunDeltaMovedAndZeroBlocks(-1,  // chunk_blocks
523                                          kSourceMinorPayloadVersion));
524 
525   EXPECT_EQ(permutation.size(), old_visited_blocks_.blocks());
526   EXPECT_EQ(permutation.size(), new_visited_blocks_.blocks());
527 
528   // There should be only one SOURCE_COPY, with a complicate list of extents.
529   EXPECT_EQ(1U, aops_.size());
530   const AnnotatedOperation& aop = aops_[0];
531   EXPECT_EQ(InstallOperation::SOURCE_COPY, aop.op.type());
532   vector<Extent> aop_src_extents;
533   ExtentsToVector(aop.op.src_extents(), &aop_src_extents);
534   EXPECT_EQ(perm_extents, aop_src_extents);
535 
536   EXPECT_EQ(1, aop.op.dst_extents_size());
537   EXPECT_EQ(ExtentForRange(0, permutation.size()), aop.op.dst_extents(0));
538 
539   EXPECT_EQ(0, blob_size_);
540 }
541 
TEST_F(DeltaDiffUtilsTest,IsExtFilesystemTest)542 TEST_F(DeltaDiffUtilsTest, IsExtFilesystemTest) {
543   EXPECT_TRUE(diff_utils::IsExtFilesystem(
544       test_utils::GetBuildArtifactsPath("gen/disk_ext2_1k.img")));
545   EXPECT_TRUE(diff_utils::IsExtFilesystem(
546       test_utils::GetBuildArtifactsPath("gen/disk_ext2_4k.img")));
547 }
548 
TEST_F(DeltaDiffUtilsTest,GetOldFileEmptyTest)549 TEST_F(DeltaDiffUtilsTest, GetOldFileEmptyTest) {
550   EXPECT_TRUE(diff_utils::GetOldFile({}, "filename").name.empty());
551 }
552 
TEST_F(DeltaDiffUtilsTest,GetOldFileTest)553 TEST_F(DeltaDiffUtilsTest, GetOldFileTest) {
554   std::map<string, FilesystemInterface::File> old_files_map;
555   auto file_list = {
556       "filename",
557       "filename.zip",
558       "version1.1",
559       "version2.0",
560       "version",
561       "update_engine",
562       "delta_generator",
563   };
564   for (const auto& name : file_list) {
565     FilesystemInterface::File file;
566     file.name = name;
567     old_files_map.emplace(name, file);
568   }
569 
570   // Always return exact match if possible.
571   for (const auto& name : file_list)
572     EXPECT_EQ(diff_utils::GetOldFile(old_files_map, name).name, name);
573 
574   EXPECT_EQ(diff_utils::GetOldFile(old_files_map, "file_name").name,
575             "filename");
576   EXPECT_EQ(diff_utils::GetOldFile(old_files_map, "filename_new.zip").name,
577             "filename.zip");
578   EXPECT_EQ(diff_utils::GetOldFile(old_files_map, "version1.2").name,
579             "version1.1");
580   EXPECT_EQ(diff_utils::GetOldFile(old_files_map, "version3.0").name,
581             "version2.0");
582   EXPECT_EQ(diff_utils::GetOldFile(old_files_map, "_version").name, "version");
583   EXPECT_EQ(
584       diff_utils::GetOldFile(old_files_map, "update_engine_unittest").name,
585       "update_engine");
586   EXPECT_EQ(diff_utils::GetOldFile(old_files_map, "bin/delta_generator").name,
587             "delta_generator");
588   // Check file name with minimum size.
589   EXPECT_EQ(diff_utils::GetOldFile(old_files_map, "a").name, "filename");
590 }
591 
592 }  // namespace chromeos_update_engine
593