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