inplace_generator_unittest.cc revision 14158570d3995008dc93a628004118b87a6bca01
1// Copyright 2015 The Chromium OS 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 "update_engine/payload_generator/inplace_generator.h" 6 7#include <map> 8#include <set> 9#include <sstream> 10#include <string> 11#include <utility> 12#include <vector> 13 14#include <base/logging.h> 15#include <base/strings/string_util.h> 16#include <gtest/gtest.h> 17 18#include "update_engine/payload_generator/cycle_breaker.h" 19#include "update_engine/payload_generator/delta_diff_generator.h" 20#include "update_engine/payload_generator/extent_ranges.h" 21#include "update_engine/payload_generator/graph_types.h" 22#include "update_engine/payload_generator/graph_utils.h" 23#include "update_engine/test_utils.h" 24#include "update_engine/utils.h" 25 26using std::map; 27using std::set; 28using std::string; 29using std::stringstream; 30using std::vector; 31 32namespace chromeos_update_engine { 33 34using Block = InplaceGenerator::Block; 35 36namespace { 37 38#define OP_BSDIFF DeltaArchiveManifest_InstallOperation_Type_BSDIFF 39#define OP_MOVE DeltaArchiveManifest_InstallOperation_Type_MOVE 40#define OP_REPLACE DeltaArchiveManifest_InstallOperation_Type_REPLACE 41#define OP_REPLACE_BZ DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ 42 43void GenVertex(Vertex* out, 44 const vector<Extent>& src_extents, 45 const vector<Extent>& dst_extents, 46 const string& path, 47 DeltaArchiveManifest_InstallOperation_Type type) { 48 out->op.set_type(type); 49 out->file_name = path; 50 StoreExtents(src_extents, out->op.mutable_src_extents()); 51 StoreExtents(dst_extents, out->op.mutable_dst_extents()); 52} 53 54vector<Extent> VectOfExt(uint64_t start_block, uint64_t num_blocks) { 55 return vector<Extent>(1, ExtentForRange(start_block, num_blocks)); 56} 57 58EdgeProperties EdgeWithReadDep(const vector<Extent>& extents) { 59 EdgeProperties ret; 60 ret.extents = extents; 61 return ret; 62} 63 64EdgeProperties EdgeWithWriteDep(const vector<Extent>& extents) { 65 EdgeProperties ret; 66 ret.write_extents = extents; 67 return ret; 68} 69 70template<typename T> 71void DumpVect(const vector<T>& vect) { 72 stringstream ss(stringstream::out); 73 for (typename vector<T>::const_iterator it = vect.begin(), e = vect.end(); 74 it != e; ++it) { 75 ss << *it << ", "; 76 } 77 LOG(INFO) << "{" << ss.str() << "}"; 78} 79 80void AppendExtent(vector<Extent>* vect, uint64_t start, uint64_t length) { 81 vect->resize(vect->size() + 1); 82 vect->back().set_start_block(start); 83 vect->back().set_num_blocks(length); 84} 85 86void OpAppendExtent(DeltaArchiveManifest_InstallOperation* op, 87 uint64_t start, 88 uint64_t length) { 89 Extent* extent = op->add_src_extents(); 90 extent->set_start_block(start); 91 extent->set_num_blocks(length); 92} 93 94} // namespace 95 96class InplaceGeneratorTest : public ::testing::Test { 97}; 98 99TEST_F(InplaceGeneratorTest, BlockDefaultValues) { 100 // Tests that a Block is initialized with the default values as a 101 // Vertex::kInvalidIndex. This is required by the delta generators. 102 Block block; 103 EXPECT_EQ(Vertex::kInvalidIndex, block.reader); 104 EXPECT_EQ(Vertex::kInvalidIndex, block.writer); 105} 106 107TEST_F(InplaceGeneratorTest, SubstituteBlocksTest) { 108 vector<Extent> remove_blocks; 109 AppendExtent(&remove_blocks, 3, 3); 110 AppendExtent(&remove_blocks, 7, 1); 111 vector<Extent> replace_blocks; 112 AppendExtent(&replace_blocks, 10, 2); 113 AppendExtent(&replace_blocks, 13, 2); 114 Vertex vertex; 115 DeltaArchiveManifest_InstallOperation& op = vertex.op; 116 OpAppendExtent(&op, 4, 3); 117 OpAppendExtent(&op, kSparseHole, 4); // Sparse hole in file 118 OpAppendExtent(&op, 3, 1); 119 OpAppendExtent(&op, 7, 3); 120 121 InplaceGenerator::SubstituteBlocks(&vertex, remove_blocks, replace_blocks); 122 123 EXPECT_EQ(7, op.src_extents_size()); 124 EXPECT_EQ(11, op.src_extents(0).start_block()); 125 EXPECT_EQ(1, op.src_extents(0).num_blocks()); 126 EXPECT_EQ(13, op.src_extents(1).start_block()); 127 EXPECT_EQ(1, op.src_extents(1).num_blocks()); 128 EXPECT_EQ(6, op.src_extents(2).start_block()); 129 EXPECT_EQ(1, op.src_extents(2).num_blocks()); 130 EXPECT_EQ(kSparseHole, op.src_extents(3).start_block()); 131 EXPECT_EQ(4, op.src_extents(3).num_blocks()); 132 EXPECT_EQ(10, op.src_extents(4).start_block()); 133 EXPECT_EQ(1, op.src_extents(4).num_blocks()); 134 EXPECT_EQ(14, op.src_extents(5).start_block()); 135 EXPECT_EQ(1, op.src_extents(5).num_blocks()); 136 EXPECT_EQ(8, op.src_extents(6).start_block()); 137 EXPECT_EQ(2, op.src_extents(6).num_blocks()); 138} 139 140TEST_F(InplaceGeneratorTest, CutEdgesTest) { 141 Graph graph; 142 vector<Block> blocks(9); 143 144 // Create nodes in graph 145 { 146 graph.resize(graph.size() + 1); 147 graph.back().op.set_type(DeltaArchiveManifest_InstallOperation_Type_MOVE); 148 // Reads from blocks 3, 5, 7 149 vector<Extent> extents; 150 AppendBlockToExtents(&extents, 3); 151 AppendBlockToExtents(&extents, 5); 152 AppendBlockToExtents(&extents, 7); 153 StoreExtents(extents, 154 graph.back().op.mutable_src_extents()); 155 blocks[3].reader = graph.size() - 1; 156 blocks[5].reader = graph.size() - 1; 157 blocks[7].reader = graph.size() - 1; 158 159 // Writes to blocks 1, 2, 4 160 extents.clear(); 161 AppendBlockToExtents(&extents, 1); 162 AppendBlockToExtents(&extents, 2); 163 AppendBlockToExtents(&extents, 4); 164 StoreExtents(extents, 165 graph.back().op.mutable_dst_extents()); 166 blocks[1].writer = graph.size() - 1; 167 blocks[2].writer = graph.size() - 1; 168 blocks[4].writer = graph.size() - 1; 169 } 170 { 171 graph.resize(graph.size() + 1); 172 graph.back().op.set_type(DeltaArchiveManifest_InstallOperation_Type_MOVE); 173 // Reads from blocks 1, 2, 4 174 vector<Extent> extents; 175 AppendBlockToExtents(&extents, 1); 176 AppendBlockToExtents(&extents, 2); 177 AppendBlockToExtents(&extents, 4); 178 StoreExtents(extents, 179 graph.back().op.mutable_src_extents()); 180 blocks[1].reader = graph.size() - 1; 181 blocks[2].reader = graph.size() - 1; 182 blocks[4].reader = graph.size() - 1; 183 184 // Writes to blocks 3, 5, 6 185 extents.clear(); 186 AppendBlockToExtents(&extents, 3); 187 AppendBlockToExtents(&extents, 5); 188 AppendBlockToExtents(&extents, 6); 189 StoreExtents(extents, 190 graph.back().op.mutable_dst_extents()); 191 blocks[3].writer = graph.size() - 1; 192 blocks[5].writer = graph.size() - 1; 193 blocks[6].writer = graph.size() - 1; 194 } 195 196 // Create edges 197 InplaceGenerator::CreateEdges(&graph, blocks); 198 199 // Find cycles 200 CycleBreaker cycle_breaker; 201 set<Edge> cut_edges; 202 cycle_breaker.BreakCycles(graph, &cut_edges); 203 204 EXPECT_EQ(1, cut_edges.size()); 205 EXPECT_TRUE(cut_edges.end() != cut_edges.find( 206 std::pair<Vertex::Index, Vertex::Index>(1, 0))); 207 208 vector<CutEdgeVertexes> cuts; 209 EXPECT_TRUE(InplaceGenerator::CutEdges(&graph, cut_edges, &cuts)); 210 211 EXPECT_EQ(3, graph.size()); 212 213 // Check new node in graph: 214 EXPECT_EQ(DeltaArchiveManifest_InstallOperation_Type_MOVE, 215 graph.back().op.type()); 216 EXPECT_EQ(2, graph.back().op.src_extents_size()); 217 EXPECT_EQ(1, graph.back().op.dst_extents_size()); 218 EXPECT_EQ(kTempBlockStart, graph.back().op.dst_extents(0).start_block()); 219 EXPECT_EQ(2, graph.back().op.dst_extents(0).num_blocks()); 220 EXPECT_TRUE(graph.back().out_edges.empty()); 221 222 // Check that old node reads from new blocks 223 EXPECT_EQ(2, graph[0].op.src_extents_size()); 224 EXPECT_EQ(kTempBlockStart, graph[0].op.src_extents(0).start_block()); 225 EXPECT_EQ(2, graph[0].op.src_extents(0).num_blocks()); 226 EXPECT_EQ(7, graph[0].op.src_extents(1).start_block()); 227 EXPECT_EQ(1, graph[0].op.src_extents(1).num_blocks()); 228 229 // And that the old dst extents haven't changed 230 EXPECT_EQ(2, graph[0].op.dst_extents_size()); 231 EXPECT_EQ(1, graph[0].op.dst_extents(0).start_block()); 232 EXPECT_EQ(2, graph[0].op.dst_extents(0).num_blocks()); 233 EXPECT_EQ(4, graph[0].op.dst_extents(1).start_block()); 234 EXPECT_EQ(1, graph[0].op.dst_extents(1).num_blocks()); 235 236 // Ensure it only depends on the next node and the new temp node 237 EXPECT_EQ(2, graph[0].out_edges.size()); 238 EXPECT_TRUE(graph[0].out_edges.end() != graph[0].out_edges.find(1)); 239 EXPECT_TRUE(graph[0].out_edges.end() != graph[0].out_edges.find(graph.size() - 240 1)); 241 242 // Check second node has unchanged extents 243 EXPECT_EQ(2, graph[1].op.src_extents_size()); 244 EXPECT_EQ(1, graph[1].op.src_extents(0).start_block()); 245 EXPECT_EQ(2, graph[1].op.src_extents(0).num_blocks()); 246 EXPECT_EQ(4, graph[1].op.src_extents(1).start_block()); 247 EXPECT_EQ(1, graph[1].op.src_extents(1).num_blocks()); 248 249 EXPECT_EQ(2, graph[1].op.dst_extents_size()); 250 EXPECT_EQ(3, graph[1].op.dst_extents(0).start_block()); 251 EXPECT_EQ(1, graph[1].op.dst_extents(0).num_blocks()); 252 EXPECT_EQ(5, graph[1].op.dst_extents(1).start_block()); 253 EXPECT_EQ(2, graph[1].op.dst_extents(1).num_blocks()); 254 255 // Ensure it only depends on the next node 256 EXPECT_EQ(1, graph[1].out_edges.size()); 257 EXPECT_TRUE(graph[1].out_edges.end() != graph[1].out_edges.find(2)); 258} 259 260TEST_F(InplaceGeneratorTest, AssignTempBlocksReuseTest) { 261 Graph graph(9); 262 263 const vector<Extent> empt; 264 uint64_t tmp = kTempBlockStart; 265 const string kFilename = "/foo"; 266 267 vector<CutEdgeVertexes> cuts; 268 cuts.resize(3); 269 270 // Simple broken loop: 271 GenVertex(&graph[0], VectOfExt(0, 1), VectOfExt(1, 1), "", OP_MOVE); 272 GenVertex(&graph[1], VectOfExt(tmp, 1), VectOfExt(0, 1), "", OP_MOVE); 273 GenVertex(&graph[2], VectOfExt(1, 1), VectOfExt(tmp, 1), "", OP_MOVE); 274 // Corresponding edges: 275 graph[0].out_edges[2] = EdgeWithReadDep(VectOfExt(1, 1)); 276 graph[1].out_edges[2] = EdgeWithWriteDep(VectOfExt(tmp, 1)); 277 graph[1].out_edges[0] = EdgeWithReadDep(VectOfExt(0, 1)); 278 // Store the cut: 279 cuts[0].old_dst = 1; 280 cuts[0].old_src = 0; 281 cuts[0].new_vertex = 2; 282 cuts[0].tmp_extents = VectOfExt(tmp, 1); 283 tmp++; 284 285 // Slightly more complex pair of loops: 286 GenVertex(&graph[3], VectOfExt(4, 2), VectOfExt(2, 2), "", OP_MOVE); 287 GenVertex(&graph[4], VectOfExt(6, 1), VectOfExt(7, 1), "", OP_MOVE); 288 GenVertex(&graph[5], VectOfExt(tmp, 3), VectOfExt(4, 3), kFilename, OP_MOVE); 289 GenVertex(&graph[6], VectOfExt(2, 2), VectOfExt(tmp, 2), "", OP_MOVE); 290 GenVertex(&graph[7], VectOfExt(7, 1), VectOfExt(tmp + 2, 1), "", OP_MOVE); 291 // Corresponding edges: 292 graph[3].out_edges[6] = EdgeWithReadDep(VectOfExt(2, 2)); 293 graph[4].out_edges[7] = EdgeWithReadDep(VectOfExt(7, 1)); 294 graph[5].out_edges[6] = EdgeWithWriteDep(VectOfExt(tmp, 2)); 295 graph[5].out_edges[7] = EdgeWithWriteDep(VectOfExt(tmp + 2, 1)); 296 graph[5].out_edges[3] = EdgeWithReadDep(VectOfExt(4, 2)); 297 graph[5].out_edges[4] = EdgeWithReadDep(VectOfExt(6, 1)); 298 // Store the cuts: 299 cuts[1].old_dst = 5; 300 cuts[1].old_src = 3; 301 cuts[1].new_vertex = 6; 302 cuts[1].tmp_extents = VectOfExt(tmp, 2); 303 cuts[2].old_dst = 5; 304 cuts[2].old_src = 4; 305 cuts[2].new_vertex = 7; 306 cuts[2].tmp_extents = VectOfExt(tmp + 2, 1); 307 308 // Supplier of temp block: 309 GenVertex(&graph[8], empt, VectOfExt(8, 1), "", OP_REPLACE); 310 311 // Specify the final order: 312 vector<Vertex::Index> op_indexes; 313 op_indexes.push_back(2); 314 op_indexes.push_back(0); 315 op_indexes.push_back(1); 316 op_indexes.push_back(6); 317 op_indexes.push_back(3); 318 op_indexes.push_back(7); 319 op_indexes.push_back(4); 320 op_indexes.push_back(5); 321 op_indexes.push_back(8); 322 323 vector<vector<Vertex::Index>::size_type> reverse_op_indexes; 324 InplaceGenerator::GenerateReverseTopoOrderMap(op_indexes, 325 &reverse_op_indexes); 326 327 int fd; 328 EXPECT_TRUE(utils::MakeTempFile("AssignTempBlocksReuseTest.XXXXXX", 329 nullptr, 330 &fd)); 331 ScopedFdCloser fd_closer(&fd); 332 off_t data_file_size = 0; 333 334 EXPECT_TRUE(InplaceGenerator::AssignTempBlocks(&graph, 335 "/dev/zero", 336 fd, 337 &data_file_size, 338 &op_indexes, 339 &reverse_op_indexes, 340 cuts)); 341 EXPECT_FALSE(graph[6].valid); 342 EXPECT_FALSE(graph[7].valid); 343 EXPECT_EQ(1, graph[1].op.src_extents_size()); 344 EXPECT_EQ(2, graph[1].op.src_extents(0).start_block()); 345 EXPECT_EQ(1, graph[1].op.src_extents(0).num_blocks()); 346 EXPECT_EQ(OP_REPLACE_BZ, graph[5].op.type()); 347} 348 349TEST_F(InplaceGeneratorTest, MoveFullOpsToBackTest) { 350 Graph graph(4); 351 graph[0].file_name = "A"; 352 graph[0].op.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE); 353 graph[1].file_name = "B"; 354 graph[1].op.set_type(DeltaArchiveManifest_InstallOperation_Type_BSDIFF); 355 graph[2].file_name = "C"; 356 graph[2].op.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ); 357 graph[3].file_name = "D"; 358 graph[3].op.set_type(DeltaArchiveManifest_InstallOperation_Type_MOVE); 359 360 vector<Vertex::Index> vect(graph.size()); 361 362 for (vector<Vertex::Index>::size_type i = 0; i < vect.size(); ++i) { 363 vect[i] = i; 364 } 365 InplaceGenerator::MoveFullOpsToBack(&graph, &vect); 366 EXPECT_EQ(vect.size(), graph.size()); 367 EXPECT_EQ(graph[vect[0]].file_name, "B"); 368 EXPECT_EQ(graph[vect[1]].file_name, "D"); 369 EXPECT_EQ(graph[vect[2]].file_name, "A"); 370 EXPECT_EQ(graph[vect[3]].file_name, "C"); 371} 372 373TEST_F(InplaceGeneratorTest, AssignTempBlocksTest) { 374 Graph graph(9); 375 const vector<Extent> empt; // empty 376 const string kFilename = "/foo"; 377 378 // Some scratch space: 379 GenVertex(&graph[0], empt, VectOfExt(200, 1), "", OP_REPLACE); 380 GenVertex(&graph[1], empt, VectOfExt(210, 10), "", OP_REPLACE); 381 GenVertex(&graph[2], empt, VectOfExt(220, 1), "", OP_REPLACE); 382 383 // A cycle that requires 10 blocks to break: 384 GenVertex(&graph[3], VectOfExt(10, 11), VectOfExt(0, 9), "", OP_BSDIFF); 385 graph[3].out_edges[4] = EdgeWithReadDep(VectOfExt(0, 9)); 386 GenVertex(&graph[4], VectOfExt(0, 9), VectOfExt(10, 11), "", OP_BSDIFF); 387 graph[4].out_edges[3] = EdgeWithReadDep(VectOfExt(10, 11)); 388 389 // A cycle that requires 9 blocks to break: 390 GenVertex(&graph[5], VectOfExt(40, 11), VectOfExt(30, 10), "", OP_BSDIFF); 391 graph[5].out_edges[6] = EdgeWithReadDep(VectOfExt(30, 10)); 392 GenVertex(&graph[6], VectOfExt(30, 10), VectOfExt(40, 11), "", OP_BSDIFF); 393 graph[6].out_edges[5] = EdgeWithReadDep(VectOfExt(40, 11)); 394 395 // A cycle that requires 40 blocks to break (which is too many): 396 GenVertex(&graph[7], 397 VectOfExt(120, 50), 398 VectOfExt(60, 40), 399 "", 400 OP_BSDIFF); 401 graph[7].out_edges[8] = EdgeWithReadDep(VectOfExt(60, 40)); 402 GenVertex(&graph[8], 403 VectOfExt(60, 40), 404 VectOfExt(120, 50), 405 kFilename, 406 OP_BSDIFF); 407 graph[8].out_edges[7] = EdgeWithReadDep(VectOfExt(120, 50)); 408 409 graph_utils::DumpGraph(graph); 410 411 vector<Vertex::Index> final_order; 412 413 int fd; 414 EXPECT_TRUE(utils::MakeTempFile("AssignTempBlocksTestData.XXXXXX", 415 nullptr, 416 &fd)); 417 ScopedFdCloser fd_closer(&fd); 418 off_t data_file_size = 0; 419 420 EXPECT_TRUE(InplaceGenerator::ConvertGraphToDag(&graph, 421 "/dev/zero", 422 fd, 423 &data_file_size, 424 &final_order, 425 Vertex::kInvalidIndex)); 426 427 Graph expected_graph(12); 428 GenVertex(&expected_graph[0], empt, VectOfExt(200, 1), "", OP_REPLACE); 429 GenVertex(&expected_graph[1], empt, VectOfExt(210, 10), "", OP_REPLACE); 430 GenVertex(&expected_graph[2], empt, VectOfExt(220, 1), "", OP_REPLACE); 431 GenVertex(&expected_graph[3], 432 VectOfExt(10, 11), 433 VectOfExt(0, 9), 434 "", 435 OP_BSDIFF); 436 expected_graph[3].out_edges[9] = EdgeWithReadDep(VectOfExt(0, 9)); 437 GenVertex(&expected_graph[4], 438 VectOfExt(60, 9), 439 VectOfExt(10, 11), 440 "", 441 OP_BSDIFF); 442 expected_graph[4].out_edges[3] = EdgeWithReadDep(VectOfExt(10, 11)); 443 expected_graph[4].out_edges[9] = EdgeWithWriteDep(VectOfExt(60, 9)); 444 GenVertex(&expected_graph[5], 445 VectOfExt(40, 11), 446 VectOfExt(30, 10), 447 "", 448 OP_BSDIFF); 449 expected_graph[5].out_edges[10] = EdgeWithReadDep(VectOfExt(30, 10)); 450 451 GenVertex(&expected_graph[6], 452 VectOfExt(60, 10), 453 VectOfExt(40, 11), 454 "", 455 OP_BSDIFF); 456 expected_graph[6].out_edges[5] = EdgeWithReadDep(VectOfExt(40, 11)); 457 expected_graph[6].out_edges[10] = EdgeWithWriteDep(VectOfExt(60, 10)); 458 459 GenVertex(&expected_graph[7], 460 VectOfExt(120, 50), 461 VectOfExt(60, 40), 462 "", 463 OP_BSDIFF); 464 expected_graph[7].out_edges[6] = EdgeWithReadDep(VectOfExt(60, 10)); 465 466 GenVertex(&expected_graph[8], empt, VectOfExt(0, 50), "/foo", OP_REPLACE_BZ); 467 expected_graph[8].out_edges[7] = EdgeWithReadDep(VectOfExt(120, 50)); 468 469 GenVertex(&expected_graph[9], 470 VectOfExt(0, 9), 471 VectOfExt(60, 9), 472 "", 473 OP_MOVE); 474 475 GenVertex(&expected_graph[10], 476 VectOfExt(30, 10), 477 VectOfExt(60, 10), 478 "", 479 OP_MOVE); 480 expected_graph[10].out_edges[4] = EdgeWithReadDep(VectOfExt(60, 9)); 481 482 EXPECT_EQ(12, graph.size()); 483 EXPECT_FALSE(graph.back().valid); 484 for (Graph::size_type i = 0; i < graph.size() - 1; i++) { 485 EXPECT_TRUE(graph[i].out_edges == expected_graph[i].out_edges); 486 if (i == 8) { 487 // special case 488 } else { 489 // EXPECT_TRUE(graph[i] == expected_graph[i]) << "i = " << i; 490 } 491 } 492} 493 494TEST_F(InplaceGeneratorTest, CreateScratchNodeTest) { 495 Vertex vertex; 496 InplaceGenerator::CreateScratchNode(12, 34, &vertex); 497 EXPECT_EQ(DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ, 498 vertex.op.type()); 499 EXPECT_EQ(0, vertex.op.data_offset()); 500 EXPECT_EQ(0, vertex.op.data_length()); 501 EXPECT_EQ(1, vertex.op.dst_extents_size()); 502 EXPECT_EQ(12, vertex.op.dst_extents(0).start_block()); 503 EXPECT_EQ(34, vertex.op.dst_extents(0).num_blocks()); 504} 505 506TEST_F(InplaceGeneratorTest, ApplyMapTest) { 507 vector<uint64_t> collection = {1, 2, 3, 4, 6}; 508 vector<uint64_t> expected_values = {1, 2, 5, 4, 8}; 509 map<uint64_t, uint64_t> value_map; 510 value_map[3] = 5; 511 value_map[6] = 8; 512 value_map[5] = 10; 513 514 InplaceGenerator::ApplyMap(&collection, value_map); 515 EXPECT_EQ(expected_values, collection); 516} 517 518} // namespace chromeos_update_engine 519