adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 1 | // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include <sys/types.h> |
| 6 | #include <sys/stat.h> |
| 7 | #include <fcntl.h> |
| 8 | #include <unistd.h> |
Andrew de los Reyes | 4fe15d0 | 2009-12-10 19:01:36 -0800 | [diff] [blame] | 9 | #include <set> |
adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 10 | #include <string> |
Andrew de los Reyes | b10320d | 2010-03-31 16:44:44 -0700 | [diff] [blame] | 11 | #include <utility> |
adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 12 | #include <vector> |
adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 13 | #include <gtest/gtest.h> |
Chris Masone | 790e62e | 2010-08-12 10:41:18 -0700 | [diff] [blame] | 14 | #include "base/logging.h" |
Andrew de los Reyes | b10320d | 2010-03-31 16:44:44 -0700 | [diff] [blame] | 15 | #include "update_engine/cycle_breaker.h" |
adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 16 | #include "update_engine/delta_diff_generator.h" |
Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 17 | #include "update_engine/delta_performer.h" |
Andrew de los Reyes | b10320d | 2010-03-31 16:44:44 -0700 | [diff] [blame] | 18 | #include "update_engine/graph_types.h" |
| 19 | #include "update_engine/graph_utils.h" |
adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 20 | #include "update_engine/subprocess.h" |
| 21 | #include "update_engine/test_utils.h" |
| 22 | #include "update_engine/utils.h" |
| 23 | |
Andrew de los Reyes | b10320d | 2010-03-31 16:44:44 -0700 | [diff] [blame] | 24 | using std::make_pair; |
| 25 | using std::set; |
| 26 | using std::string; |
| 27 | using std::vector; |
| 28 | |
adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 29 | namespace chromeos_update_engine { |
| 30 | |
Andrew de los Reyes | b10320d | 2010-03-31 16:44:44 -0700 | [diff] [blame] | 31 | typedef DeltaDiffGenerator::Block Block; |
| 32 | |
| 33 | namespace { |
Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 34 | int64_t BlocksInExtents( |
Andrew de los Reyes | b10320d | 2010-03-31 16:44:44 -0700 | [diff] [blame] | 35 | const google::protobuf::RepeatedPtrField<Extent>& extents) { |
Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 36 | int64_t ret = 0; |
Andrew de los Reyes | b10320d | 2010-03-31 16:44:44 -0700 | [diff] [blame] | 37 | for (int i = 0; i < extents.size(); i++) { |
| 38 | ret += extents.Get(i).num_blocks(); |
| 39 | } |
| 40 | return ret; |
| 41 | } |
| 42 | } // namespace {} |
| 43 | |
| 44 | class DeltaDiffGeneratorTest : public ::testing::Test { |
| 45 | protected: |
| 46 | const string old_path() { return "DeltaDiffGeneratorTest-old_path"; } |
| 47 | const string new_path() { return "DeltaDiffGeneratorTest-new_path"; } |
| 48 | virtual void TearDown() { |
| 49 | unlink(old_path().c_str()); |
| 50 | unlink(new_path().c_str()); |
| 51 | } |
| 52 | }; |
| 53 | |
| 54 | TEST_F(DeltaDiffGeneratorTest, RunAsRootMoveSmallTest) { |
| 55 | EXPECT_TRUE(utils::WriteFile(old_path().c_str(), |
| 56 | reinterpret_cast<const char*>(kRandomString), |
| 57 | sizeof(kRandomString))); |
| 58 | EXPECT_TRUE(utils::WriteFile(new_path().c_str(), |
| 59 | reinterpret_cast<const char*>(kRandomString), |
| 60 | sizeof(kRandomString))); |
| 61 | vector<char> data; |
| 62 | DeltaArchiveManifest_InstallOperation op; |
| 63 | EXPECT_TRUE(DeltaDiffGenerator::ReadFileToDiff(old_path(), |
| 64 | new_path(), |
| 65 | &data, |
| 66 | &op)); |
| 67 | EXPECT_TRUE(data.empty()); |
| 68 | |
| 69 | EXPECT_TRUE(op.has_type()); |
| 70 | EXPECT_EQ(DeltaArchiveManifest_InstallOperation_Type_MOVE, op.type()); |
| 71 | EXPECT_FALSE(op.has_data_offset()); |
| 72 | EXPECT_FALSE(op.has_data_length()); |
| 73 | EXPECT_EQ(1, op.src_extents_size()); |
| 74 | EXPECT_EQ(sizeof(kRandomString), op.src_length()); |
| 75 | EXPECT_EQ(1, op.dst_extents_size()); |
| 76 | EXPECT_EQ(sizeof(kRandomString), op.dst_length()); |
| 77 | EXPECT_EQ(BlocksInExtents(op.src_extents()), |
| 78 | BlocksInExtents(op.dst_extents())); |
| 79 | EXPECT_EQ(1, BlocksInExtents(op.dst_extents())); |
| 80 | } |
| 81 | |
| 82 | TEST_F(DeltaDiffGeneratorTest, RunAsRootBsdiffSmallTest) { |
| 83 | EXPECT_TRUE(utils::WriteFile(old_path().c_str(), |
| 84 | reinterpret_cast<const char*>(kRandomString), |
| 85 | sizeof(kRandomString) - 1)); |
| 86 | EXPECT_TRUE(utils::WriteFile(new_path().c_str(), |
| 87 | reinterpret_cast<const char*>(kRandomString), |
| 88 | sizeof(kRandomString))); |
| 89 | vector<char> data; |
| 90 | DeltaArchiveManifest_InstallOperation op; |
| 91 | EXPECT_TRUE(DeltaDiffGenerator::ReadFileToDiff(old_path(), |
| 92 | new_path(), |
| 93 | &data, |
| 94 | &op)); |
| 95 | EXPECT_FALSE(data.empty()); |
| 96 | |
| 97 | EXPECT_TRUE(op.has_type()); |
| 98 | EXPECT_EQ(DeltaArchiveManifest_InstallOperation_Type_BSDIFF, op.type()); |
| 99 | EXPECT_FALSE(op.has_data_offset()); |
| 100 | EXPECT_FALSE(op.has_data_length()); |
| 101 | EXPECT_EQ(1, op.src_extents_size()); |
| 102 | EXPECT_EQ(sizeof(kRandomString) - 1, op.src_length()); |
| 103 | EXPECT_EQ(1, op.dst_extents_size()); |
| 104 | EXPECT_EQ(sizeof(kRandomString), op.dst_length()); |
| 105 | EXPECT_EQ(BlocksInExtents(op.src_extents()), |
| 106 | BlocksInExtents(op.dst_extents())); |
| 107 | EXPECT_EQ(1, BlocksInExtents(op.dst_extents())); |
| 108 | } |
| 109 | |
| 110 | TEST_F(DeltaDiffGeneratorTest, RunAsRootReplaceSmallTest) { |
| 111 | vector<char> new_data; |
| 112 | for (int i = 0; i < 2; i++) { |
| 113 | new_data.insert(new_data.end(), |
| 114 | kRandomString, |
| 115 | kRandomString + sizeof(kRandomString)); |
| 116 | EXPECT_TRUE(utils::WriteFile(new_path().c_str(), |
| 117 | &new_data[0], |
| 118 | new_data.size())); |
| 119 | vector<char> data; |
| 120 | DeltaArchiveManifest_InstallOperation op; |
| 121 | EXPECT_TRUE(DeltaDiffGenerator::ReadFileToDiff(old_path(), |
| 122 | new_path(), |
| 123 | &data, |
| 124 | &op)); |
| 125 | EXPECT_FALSE(data.empty()); |
| 126 | |
| 127 | EXPECT_TRUE(op.has_type()); |
| 128 | const DeltaArchiveManifest_InstallOperation_Type expected_type = |
| 129 | (i == 0 ? DeltaArchiveManifest_InstallOperation_Type_REPLACE : |
| 130 | DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ); |
| 131 | EXPECT_EQ(expected_type, op.type()); |
| 132 | EXPECT_FALSE(op.has_data_offset()); |
| 133 | EXPECT_FALSE(op.has_data_length()); |
| 134 | EXPECT_EQ(0, op.src_extents_size()); |
| 135 | EXPECT_FALSE(op.has_src_length()); |
| 136 | EXPECT_EQ(1, op.dst_extents_size()); |
| 137 | EXPECT_EQ(new_data.size(), op.dst_length()); |
| 138 | EXPECT_EQ(1, BlocksInExtents(op.dst_extents())); |
| 139 | } |
| 140 | } |
| 141 | |
| 142 | namespace { |
Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 143 | void AppendExtent(vector<Extent>* vect, uint64_t start, uint64_t length) { |
Andrew de los Reyes | b10320d | 2010-03-31 16:44:44 -0700 | [diff] [blame] | 144 | vect->resize(vect->size() + 1); |
| 145 | vect->back().set_start_block(start); |
| 146 | vect->back().set_num_blocks(length); |
| 147 | } |
| 148 | void OpAppendExtent(DeltaArchiveManifest_InstallOperation* op, |
Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 149 | uint64_t start, |
| 150 | uint64_t length) { |
Andrew de los Reyes | b10320d | 2010-03-31 16:44:44 -0700 | [diff] [blame] | 151 | Extent* extent = op->add_src_extents(); |
| 152 | extent->set_start_block(start); |
| 153 | extent->set_num_blocks(length); |
| 154 | } |
| 155 | } |
| 156 | |
| 157 | TEST_F(DeltaDiffGeneratorTest, SubstituteBlocksTest) { |
| 158 | vector<Extent> remove_blocks; |
| 159 | AppendExtent(&remove_blocks, 3, 3); |
| 160 | AppendExtent(&remove_blocks, 7, 1); |
| 161 | vector<Extent> replace_blocks; |
| 162 | AppendExtent(&replace_blocks, 10, 2); |
| 163 | AppendExtent(&replace_blocks, 13, 2); |
| 164 | DeltaArchiveManifest_InstallOperation op; |
| 165 | OpAppendExtent(&op, 4, 3); |
| 166 | OpAppendExtent(&op, kSparseHole, 4); // Sparse hole in file |
| 167 | OpAppendExtent(&op, 3, 1); |
| 168 | OpAppendExtent(&op, 7, 3); |
| 169 | |
| 170 | DeltaDiffGenerator::SubstituteBlocks(&op, remove_blocks, replace_blocks); |
| 171 | |
| 172 | EXPECT_EQ(7, op.src_extents_size()); |
| 173 | EXPECT_EQ(11, op.src_extents(0).start_block()); |
| 174 | EXPECT_EQ(1, op.src_extents(0).num_blocks()); |
| 175 | EXPECT_EQ(13, op.src_extents(1).start_block()); |
| 176 | EXPECT_EQ(1, op.src_extents(1).num_blocks()); |
| 177 | EXPECT_EQ(6, op.src_extents(2).start_block()); |
| 178 | EXPECT_EQ(1, op.src_extents(2).num_blocks()); |
| 179 | EXPECT_EQ(kSparseHole, op.src_extents(3).start_block()); |
| 180 | EXPECT_EQ(4, op.src_extents(3).num_blocks()); |
| 181 | EXPECT_EQ(10, op.src_extents(4).start_block()); |
| 182 | EXPECT_EQ(1, op.src_extents(4).num_blocks()); |
| 183 | EXPECT_EQ(14, op.src_extents(5).start_block()); |
| 184 | EXPECT_EQ(1, op.src_extents(5).num_blocks()); |
| 185 | EXPECT_EQ(8, op.src_extents(6).start_block()); |
| 186 | EXPECT_EQ(2, op.src_extents(6).num_blocks()); |
| 187 | } |
| 188 | |
| 189 | TEST_F(DeltaDiffGeneratorTest, CutEdgesTest) { |
| 190 | Graph graph; |
| 191 | vector<Block> blocks(9); |
| 192 | |
| 193 | // Create nodes in graph |
| 194 | { |
| 195 | graph.resize(graph.size() + 1); |
| 196 | graph.back().op.set_type(DeltaArchiveManifest_InstallOperation_Type_MOVE); |
| 197 | // Reads from blocks 3, 5, 7 |
| 198 | vector<Extent> extents; |
| 199 | graph_utils::AppendBlockToExtents(&extents, 3); |
| 200 | graph_utils::AppendBlockToExtents(&extents, 5); |
| 201 | graph_utils::AppendBlockToExtents(&extents, 7); |
| 202 | DeltaDiffGenerator::StoreExtents(extents, |
| 203 | graph.back().op.mutable_src_extents()); |
| 204 | blocks[3].reader = graph.size() - 1; |
| 205 | blocks[5].reader = graph.size() - 1; |
| 206 | blocks[7].reader = graph.size() - 1; |
| 207 | |
| 208 | // Writes to blocks 1, 2, 4 |
| 209 | extents.clear(); |
| 210 | graph_utils::AppendBlockToExtents(&extents, 1); |
| 211 | graph_utils::AppendBlockToExtents(&extents, 2); |
| 212 | graph_utils::AppendBlockToExtents(&extents, 4); |
| 213 | DeltaDiffGenerator::StoreExtents(extents, |
| 214 | graph.back().op.mutable_dst_extents()); |
| 215 | blocks[1].writer = graph.size() - 1; |
| 216 | blocks[2].writer = graph.size() - 1; |
| 217 | blocks[4].writer = graph.size() - 1; |
| 218 | } |
| 219 | { |
| 220 | graph.resize(graph.size() + 1); |
| 221 | graph.back().op.set_type(DeltaArchiveManifest_InstallOperation_Type_MOVE); |
| 222 | // Reads from blocks 1, 2, 4 |
| 223 | vector<Extent> extents; |
| 224 | graph_utils::AppendBlockToExtents(&extents, 1); |
| 225 | graph_utils::AppendBlockToExtents(&extents, 2); |
| 226 | graph_utils::AppendBlockToExtents(&extents, 4); |
| 227 | DeltaDiffGenerator::StoreExtents(extents, |
| 228 | graph.back().op.mutable_src_extents()); |
| 229 | blocks[1].reader = graph.size() - 1; |
| 230 | blocks[2].reader = graph.size() - 1; |
| 231 | blocks[4].reader = graph.size() - 1; |
| 232 | |
| 233 | // Writes to blocks 3, 5, 6 |
| 234 | extents.clear(); |
| 235 | graph_utils::AppendBlockToExtents(&extents, 3); |
| 236 | graph_utils::AppendBlockToExtents(&extents, 5); |
| 237 | graph_utils::AppendBlockToExtents(&extents, 6); |
| 238 | DeltaDiffGenerator::StoreExtents(extents, |
| 239 | graph.back().op.mutable_dst_extents()); |
| 240 | blocks[3].writer = graph.size() - 1; |
| 241 | blocks[5].writer = graph.size() - 1; |
| 242 | blocks[6].writer = graph.size() - 1; |
| 243 | } |
| 244 | |
| 245 | // Create edges |
| 246 | DeltaDiffGenerator::CreateEdges(&graph, blocks); |
| 247 | |
| 248 | // Find cycles |
| 249 | CycleBreaker cycle_breaker; |
| 250 | set<Edge> cut_edges; |
| 251 | cycle_breaker.BreakCycles(graph, &cut_edges); |
| 252 | |
| 253 | EXPECT_EQ(1, cut_edges.size()); |
| 254 | EXPECT_TRUE(cut_edges.end() != cut_edges.find(make_pair<Vertex::Index>(1, |
| 255 | 0))); |
| 256 | |
| 257 | EXPECT_TRUE(DeltaDiffGenerator::CutEdges(&graph, blocks, cut_edges)); |
| 258 | |
| 259 | EXPECT_EQ(3, graph.size()); |
| 260 | |
| 261 | // Check new node in graph: |
| 262 | EXPECT_EQ(DeltaArchiveManifest_InstallOperation_Type_MOVE, |
| 263 | graph.back().op.type()); |
| 264 | EXPECT_EQ(2, graph.back().op.src_extents_size()); |
| 265 | EXPECT_EQ(2, graph.back().op.dst_extents_size()); |
| 266 | EXPECT_EQ(0, graph.back().op.dst_extents(0).start_block()); |
| 267 | EXPECT_EQ(1, graph.back().op.dst_extents(0).num_blocks()); |
| 268 | EXPECT_EQ(8, graph.back().op.dst_extents(1).start_block()); |
| 269 | EXPECT_EQ(1, graph.back().op.dst_extents(1).num_blocks()); |
| 270 | EXPECT_TRUE(graph.back().out_edges.empty()); |
| 271 | |
| 272 | // Check that old node reads from new blocks |
| 273 | EXPECT_EQ(3, graph[0].op.src_extents_size()); |
| 274 | EXPECT_EQ(0, graph[0].op.src_extents(0).start_block()); |
| 275 | EXPECT_EQ(1, graph[0].op.src_extents(0).num_blocks()); |
| 276 | EXPECT_EQ(8, graph[0].op.src_extents(1).start_block()); |
| 277 | EXPECT_EQ(1, graph[0].op.src_extents(1).num_blocks()); |
| 278 | EXPECT_EQ(7, graph[0].op.src_extents(2).start_block()); |
| 279 | EXPECT_EQ(1, graph[0].op.src_extents(2).num_blocks()); |
| 280 | |
| 281 | // And that the old dst extents haven't changed |
| 282 | EXPECT_EQ(2, graph[0].op.dst_extents_size()); |
| 283 | EXPECT_EQ(1, graph[0].op.dst_extents(0).start_block()); |
| 284 | EXPECT_EQ(2, graph[0].op.dst_extents(0).num_blocks()); |
| 285 | EXPECT_EQ(4, graph[0].op.dst_extents(1).start_block()); |
| 286 | EXPECT_EQ(1, graph[0].op.dst_extents(1).num_blocks()); |
Andrew de los Reyes | d12784c | 2010-07-26 13:55:14 -0700 | [diff] [blame] | 287 | |
| 288 | // Ensure it only depends on the next node and the new temp node |
| 289 | EXPECT_EQ(2, graph[0].out_edges.size()); |
Andrew de los Reyes | b10320d | 2010-03-31 16:44:44 -0700 | [diff] [blame] | 290 | EXPECT_TRUE(graph[0].out_edges.end() != graph[0].out_edges.find(1)); |
Andrew de los Reyes | d12784c | 2010-07-26 13:55:14 -0700 | [diff] [blame] | 291 | EXPECT_TRUE(graph[0].out_edges.end() != graph[0].out_edges.find(graph.size() - |
| 292 | 1)); |
| 293 | |
Andrew de los Reyes | b10320d | 2010-03-31 16:44:44 -0700 | [diff] [blame] | 294 | // Check second node has unchanged extents |
| 295 | EXPECT_EQ(2, graph[1].op.src_extents_size()); |
| 296 | EXPECT_EQ(1, graph[1].op.src_extents(0).start_block()); |
| 297 | EXPECT_EQ(2, graph[1].op.src_extents(0).num_blocks()); |
| 298 | EXPECT_EQ(4, graph[1].op.src_extents(1).start_block()); |
| 299 | EXPECT_EQ(1, graph[1].op.src_extents(1).num_blocks()); |
| 300 | |
| 301 | EXPECT_EQ(2, graph[1].op.dst_extents_size()); |
| 302 | EXPECT_EQ(3, graph[1].op.dst_extents(0).start_block()); |
| 303 | EXPECT_EQ(1, graph[1].op.dst_extents(0).num_blocks()); |
| 304 | EXPECT_EQ(5, graph[1].op.dst_extents(1).start_block()); |
| 305 | EXPECT_EQ(2, graph[1].op.dst_extents(1).num_blocks()); |
| 306 | |
| 307 | // Ensure it only depends on the next node |
| 308 | EXPECT_EQ(1, graph[1].out_edges.size()); |
| 309 | EXPECT_TRUE(graph[1].out_edges.end() != graph[1].out_edges.find(2)); |
| 310 | } |
| 311 | |
| 312 | TEST_F(DeltaDiffGeneratorTest, ReorderBlobsTest) { |
| 313 | string orig_blobs; |
| 314 | EXPECT_TRUE( |
| 315 | utils::MakeTempFile("ReorderBlobsTest.orig.XXXXXX", &orig_blobs, NULL)); |
| 316 | |
| 317 | string orig_data = "abcd"; |
| 318 | EXPECT_TRUE( |
| 319 | utils::WriteFile(orig_blobs.c_str(), orig_data.data(), orig_data.size())); |
| 320 | |
| 321 | string new_blobs; |
| 322 | EXPECT_TRUE( |
| 323 | utils::MakeTempFile("ReorderBlobsTest.new.XXXXXX", &new_blobs, NULL)); |
| 324 | |
| 325 | DeltaArchiveManifest manifest; |
| 326 | DeltaArchiveManifest_InstallOperation* op = |
| 327 | manifest.add_install_operations(); |
| 328 | op->set_data_offset(1); |
| 329 | op->set_data_length(3); |
| 330 | op = manifest.add_install_operations(); |
| 331 | op->set_data_offset(0); |
| 332 | op->set_data_length(1); |
| 333 | |
| 334 | EXPECT_TRUE(DeltaDiffGenerator::ReorderDataBlobs(&manifest, |
| 335 | orig_blobs, |
| 336 | new_blobs)); |
| 337 | |
| 338 | string new_data; |
| 339 | EXPECT_TRUE(utils::ReadFileToString(new_blobs, &new_data)); |
| 340 | EXPECT_EQ("bcda", new_data); |
| 341 | EXPECT_EQ(2, manifest.install_operations_size()); |
| 342 | EXPECT_EQ(0, manifest.install_operations(0).data_offset()); |
| 343 | EXPECT_EQ(3, manifest.install_operations(0).data_length()); |
| 344 | EXPECT_EQ(3, manifest.install_operations(1).data_offset()); |
| 345 | EXPECT_EQ(1, manifest.install_operations(1).data_length()); |
| 346 | |
| 347 | unlink(orig_blobs.c_str()); |
| 348 | unlink(new_blobs.c_str()); |
| 349 | } |
adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 350 | |
| 351 | } // namespace chromeos_update_engine |