delta_diff_utils_unittest.cc 31 KB

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