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