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/inplace_generator.h" 18 19 #include <map> 20 #include <set> 21 #include <sstream> 22 #include <string> 23 #include <utility> 24 #include <vector> 25 26 #include <base/format_macros.h> 27 #include <base/logging.h> 28 #include <base/strings/string_util.h> 29 #include <base/strings/stringprintf.h> 30 #include <gtest/gtest.h> 31 32 #include "update_engine/common/test_utils.h" 33 #include "update_engine/common/utils.h" 34 #include "update_engine/payload_generator/cycle_breaker.h" 35 #include "update_engine/payload_generator/delta_diff_generator.h" 36 #include "update_engine/payload_generator/extent_ranges.h" 37 #include "update_engine/payload_generator/graph_types.h" 38 #include "update_engine/payload_generator/graph_utils.h" 39 40 using std::map; 41 using std::set; 42 using std::string; 43 using std::stringstream; 44 using std::vector; 45 46 namespace chromeos_update_engine { 47 48 using Block = InplaceGenerator::Block; 49 50 namespace { 51 52 void GenVertex(Vertex* out, 53 const vector<Extent>& src_extents, 54 const vector<Extent>& dst_extents, 55 const string& path, 56 InstallOperation_Type type) { 57 out->aop.op.set_type(type); 58 out->aop.name = path; 59 StoreExtents(src_extents, out->aop.op.mutable_src_extents()); 60 StoreExtents(dst_extents, out->aop.op.mutable_dst_extents()); 61 } 62 63 vector<Extent> VectOfExt(uint64_t start_block, uint64_t num_blocks) { 64 return vector<Extent>(1, ExtentForRange(start_block, num_blocks)); 65 } 66 67 EdgeProperties EdgeWithReadDep(const vector<Extent>& extents) { 68 EdgeProperties ret; 69 ret.extents = extents; 70 return ret; 71 } 72 73 EdgeProperties EdgeWithWriteDep(const vector<Extent>& extents) { 74 EdgeProperties ret; 75 ret.write_extents = extents; 76 return ret; 77 } 78 79 template<typename T> 80 void DumpVect(const vector<T>& vect) { 81 stringstream ss(stringstream::out); 82 for (typename vector<T>::const_iterator it = vect.begin(), e = vect.end(); 83 it != e; ++it) { 84 ss << *it << ", "; 85 } 86 LOG(INFO) << "{" << ss.str() << "}"; 87 } 88 89 void AppendExtent(vector<Extent>* vect, uint64_t start, uint64_t length) { 90 vect->resize(vect->size() + 1); 91 vect->back().set_start_block(start); 92 vect->back().set_num_blocks(length); 93 } 94 95 void OpAppendExtent(InstallOperation* op, uint64_t start, uint64_t length) { 96 Extent* extent = op->add_src_extents(); 97 extent->set_start_block(start); 98 extent->set_num_blocks(length); 99 } 100 101 } // namespace 102 103 class InplaceGeneratorTest : public ::testing::Test { 104 protected: 105 // Initialize |blob_path_|, |blob_file_size_| and |blob_file_fd_| variables 106 // with a new blob file. The file is closed and removed automatically when 107 // the test finishes. 108 void CreateBlobFile() { 109 // blob_fd_closer_ takes a pointer to blob_fd_. Make sure we destroy a 110 // previous instance before overriding blob_fd_. 111 blob_fd_closer_.reset(); 112 EXPECT_TRUE(utils::MakeTempFile( 113 "InplaceGenerator_blob_file.XXXXXX", &blob_path_, &blob_fd_)); 114 blob_path_unlinker_.reset(new ScopedPathUnlinker(blob_path_)); 115 blob_fd_closer_.reset(new ScopedFdCloser(&blob_fd_)); 116 blob_file_size_ = 0; 117 EXPECT_GE(blob_fd_, 0); 118 blob_file_.reset(new BlobFileWriter(blob_fd_, &blob_file_size_)); 119 } 120 121 // Blob file name, file descriptor and file size used to store operation 122 // blobs. 123 string blob_path_; 124 int blob_fd_{-1}; 125 off_t blob_file_size_{0}; 126 std::unique_ptr<BlobFileWriter> blob_file_; 127 std::unique_ptr<ScopedPathUnlinker> blob_path_unlinker_; 128 std::unique_ptr<ScopedFdCloser> blob_fd_closer_; 129 }; 130 131 TEST_F(InplaceGeneratorTest, BlockDefaultValues) { 132 // Tests that a Block is initialized with the default values as a 133 // Vertex::kInvalidIndex. This is required by the delta generators. 134 Block block; 135 EXPECT_EQ(Vertex::kInvalidIndex, block.reader); 136 EXPECT_EQ(Vertex::kInvalidIndex, block.writer); 137 } 138 139 TEST_F(InplaceGeneratorTest, SubstituteBlocksTest) { 140 vector<Extent> remove_blocks; 141 AppendExtent(&remove_blocks, 3, 3); 142 AppendExtent(&remove_blocks, 7, 1); 143 vector<Extent> replace_blocks; 144 AppendExtent(&replace_blocks, 10, 2); 145 AppendExtent(&replace_blocks, 13, 2); 146 Vertex vertex; 147 InstallOperation& op = vertex.aop.op; 148 OpAppendExtent(&op, 4, 3); 149 OpAppendExtent(&op, kSparseHole, 4); // Sparse hole in file 150 OpAppendExtent(&op, 3, 1); 151 OpAppendExtent(&op, 7, 3); 152 153 InplaceGenerator::SubstituteBlocks(&vertex, remove_blocks, replace_blocks); 154 155 EXPECT_EQ(7, op.src_extents_size()); 156 EXPECT_EQ(11U, op.src_extents(0).start_block()); 157 EXPECT_EQ(1U, op.src_extents(0).num_blocks()); 158 EXPECT_EQ(13U, op.src_extents(1).start_block()); 159 EXPECT_EQ(1U, op.src_extents(1).num_blocks()); 160 EXPECT_EQ(6U, op.src_extents(2).start_block()); 161 EXPECT_EQ(1U, op.src_extents(2).num_blocks()); 162 EXPECT_EQ(kSparseHole, op.src_extents(3).start_block()); 163 EXPECT_EQ(4U, op.src_extents(3).num_blocks()); 164 EXPECT_EQ(10U, op.src_extents(4).start_block()); 165 EXPECT_EQ(1U, op.src_extents(4).num_blocks()); 166 EXPECT_EQ(14U, op.src_extents(5).start_block()); 167 EXPECT_EQ(1U, op.src_extents(5).num_blocks()); 168 EXPECT_EQ(8U, op.src_extents(6).start_block()); 169 EXPECT_EQ(2U, op.src_extents(6).num_blocks()); 170 } 171 172 TEST_F(InplaceGeneratorTest, CutEdgesTest) { 173 Graph graph; 174 vector<Block> blocks(9); 175 176 // Create nodes in graph 177 { 178 graph.resize(graph.size() + 1); 179 graph.back().aop.op.set_type(InstallOperation::MOVE); 180 // Reads from blocks 3, 5, 7 181 vector<Extent> extents; 182 AppendBlockToExtents(&extents, 3); 183 AppendBlockToExtents(&extents, 5); 184 AppendBlockToExtents(&extents, 7); 185 StoreExtents(extents, graph.back().aop.op.mutable_src_extents()); 186 blocks[3].reader = graph.size() - 1; 187 blocks[5].reader = graph.size() - 1; 188 blocks[7].reader = graph.size() - 1; 189 190 // Writes to blocks 1, 2, 4 191 extents.clear(); 192 AppendBlockToExtents(&extents, 1); 193 AppendBlockToExtents(&extents, 2); 194 AppendBlockToExtents(&extents, 4); 195 StoreExtents(extents, graph.back().aop.op.mutable_dst_extents()); 196 blocks[1].writer = graph.size() - 1; 197 blocks[2].writer = graph.size() - 1; 198 blocks[4].writer = graph.size() - 1; 199 } 200 { 201 graph.resize(graph.size() + 1); 202 graph.back().aop.op.set_type(InstallOperation::MOVE); 203 // Reads from blocks 1, 2, 4 204 vector<Extent> extents; 205 AppendBlockToExtents(&extents, 1); 206 AppendBlockToExtents(&extents, 2); 207 AppendBlockToExtents(&extents, 4); 208 StoreExtents(extents, graph.back().aop.op.mutable_src_extents()); 209 blocks[1].reader = graph.size() - 1; 210 blocks[2].reader = graph.size() - 1; 211 blocks[4].reader = graph.size() - 1; 212 213 // Writes to blocks 3, 5, 6 214 extents.clear(); 215 AppendBlockToExtents(&extents, 3); 216 AppendBlockToExtents(&extents, 5); 217 AppendBlockToExtents(&extents, 6); 218 StoreExtents(extents, graph.back().aop.op.mutable_dst_extents()); 219 blocks[3].writer = graph.size() - 1; 220 blocks[5].writer = graph.size() - 1; 221 blocks[6].writer = graph.size() - 1; 222 } 223 224 // Create edges 225 InplaceGenerator::CreateEdges(&graph, blocks); 226 227 // Find cycles 228 CycleBreaker cycle_breaker; 229 set<Edge> cut_edges; 230 cycle_breaker.BreakCycles(graph, &cut_edges); 231 232 EXPECT_EQ(1U, cut_edges.size()); 233 EXPECT_TRUE(cut_edges.end() != cut_edges.find( 234 std::pair<Vertex::Index, Vertex::Index>(1, 0))); 235 236 vector<CutEdgeVertexes> cuts; 237 EXPECT_TRUE(InplaceGenerator::CutEdges(&graph, cut_edges, &cuts)); 238 239 EXPECT_EQ(3U, graph.size()); 240 241 // Check new node in graph: 242 EXPECT_EQ(InstallOperation::MOVE, graph.back().aop.op.type()); 243 EXPECT_EQ(2, graph.back().aop.op.src_extents_size()); 244 EXPECT_EQ(1, graph.back().aop.op.dst_extents_size()); 245 EXPECT_EQ(kTempBlockStart, graph.back().aop.op.dst_extents(0).start_block()); 246 EXPECT_EQ(2U, graph.back().aop.op.dst_extents(0).num_blocks()); 247 EXPECT_TRUE(graph.back().out_edges.empty()); 248 249 // Check that old node reads from new blocks 250 EXPECT_EQ(2, graph[0].aop.op.src_extents_size()); 251 EXPECT_EQ(kTempBlockStart, graph[0].aop.op.src_extents(0).start_block()); 252 EXPECT_EQ(2U, graph[0].aop.op.src_extents(0).num_blocks()); 253 EXPECT_EQ(7U, graph[0].aop.op.src_extents(1).start_block()); 254 EXPECT_EQ(1U, graph[0].aop.op.src_extents(1).num_blocks()); 255 256 // And that the old dst extents haven't changed 257 EXPECT_EQ(2, graph[0].aop.op.dst_extents_size()); 258 EXPECT_EQ(1U, graph[0].aop.op.dst_extents(0).start_block()); 259 EXPECT_EQ(2U, graph[0].aop.op.dst_extents(0).num_blocks()); 260 EXPECT_EQ(4U, graph[0].aop.op.dst_extents(1).start_block()); 261 EXPECT_EQ(1U, graph[0].aop.op.dst_extents(1).num_blocks()); 262 263 // Ensure it only depends on the next node and the new temp node 264 EXPECT_EQ(2U, graph[0].out_edges.size()); 265 EXPECT_TRUE(graph[0].out_edges.end() != graph[0].out_edges.find(1)); 266 EXPECT_TRUE(graph[0].out_edges.end() != graph[0].out_edges.find(graph.size() - 267 1)); 268 269 // Check second node has unchanged extents 270 EXPECT_EQ(2, graph[1].aop.op.src_extents_size()); 271 EXPECT_EQ(1U, graph[1].aop.op.src_extents(0).start_block()); 272 EXPECT_EQ(2U, graph[1].aop.op.src_extents(0).num_blocks()); 273 EXPECT_EQ(4U, graph[1].aop.op.src_extents(1).start_block()); 274 EXPECT_EQ(1U, graph[1].aop.op.src_extents(1).num_blocks()); 275 276 EXPECT_EQ(2, graph[1].aop.op.dst_extents_size()); 277 EXPECT_EQ(3U, graph[1].aop.op.dst_extents(0).start_block()); 278 EXPECT_EQ(1U, graph[1].aop.op.dst_extents(0).num_blocks()); 279 EXPECT_EQ(5U, graph[1].aop.op.dst_extents(1).start_block()); 280 EXPECT_EQ(2U, graph[1].aop.op.dst_extents(1).num_blocks()); 281 282 // Ensure it only depends on the next node 283 EXPECT_EQ(1U, graph[1].out_edges.size()); 284 EXPECT_TRUE(graph[1].out_edges.end() != graph[1].out_edges.find(2)); 285 } 286 287 TEST_F(InplaceGeneratorTest, AssignTempBlocksReuseTest) { 288 Graph graph(9); 289 290 const vector<Extent> empt; 291 uint64_t tmp = kTempBlockStart; 292 const string kFilename = "/foo"; 293 294 vector<CutEdgeVertexes> cuts; 295 cuts.resize(3); 296 297 // Simple broken loop: 298 GenVertex( 299 &graph[0], VectOfExt(0, 1), VectOfExt(1, 1), "", InstallOperation::MOVE); 300 GenVertex(&graph[1], 301 VectOfExt(tmp, 1), 302 VectOfExt(0, 1), 303 "", 304 InstallOperation::MOVE); 305 GenVertex(&graph[2], 306 VectOfExt(1, 1), 307 VectOfExt(tmp, 1), 308 "", 309 InstallOperation::MOVE); 310 // Corresponding edges: 311 graph[0].out_edges[2] = EdgeWithReadDep(VectOfExt(1, 1)); 312 graph[1].out_edges[2] = EdgeWithWriteDep(VectOfExt(tmp, 1)); 313 graph[1].out_edges[0] = EdgeWithReadDep(VectOfExt(0, 1)); 314 // Store the cut: 315 cuts[0].old_dst = 1; 316 cuts[0].old_src = 0; 317 cuts[0].new_vertex = 2; 318 cuts[0].tmp_extents = VectOfExt(tmp, 1); 319 tmp++; 320 321 // Slightly more complex pair of loops: 322 GenVertex( 323 &graph[3], VectOfExt(4, 2), VectOfExt(2, 2), "", InstallOperation::MOVE); 324 GenVertex( 325 &graph[4], VectOfExt(6, 1), VectOfExt(7, 1), "", InstallOperation::MOVE); 326 GenVertex(&graph[5], 327 VectOfExt(tmp, 3), 328 VectOfExt(4, 3), 329 kFilename, 330 InstallOperation::MOVE); 331 GenVertex(&graph[6], 332 VectOfExt(2, 2), 333 VectOfExt(tmp, 2), 334 "", 335 InstallOperation::MOVE); 336 GenVertex(&graph[7], 337 VectOfExt(7, 1), 338 VectOfExt(tmp + 2, 1), 339 "", 340 InstallOperation::MOVE); 341 // Corresponding edges: 342 graph[3].out_edges[6] = EdgeWithReadDep(VectOfExt(2, 2)); 343 graph[4].out_edges[7] = EdgeWithReadDep(VectOfExt(7, 1)); 344 graph[5].out_edges[6] = EdgeWithWriteDep(VectOfExt(tmp, 2)); 345 graph[5].out_edges[7] = EdgeWithWriteDep(VectOfExt(tmp + 2, 1)); 346 graph[5].out_edges[3] = EdgeWithReadDep(VectOfExt(4, 2)); 347 graph[5].out_edges[4] = EdgeWithReadDep(VectOfExt(6, 1)); 348 // Store the cuts: 349 cuts[1].old_dst = 5; 350 cuts[1].old_src = 3; 351 cuts[1].new_vertex = 6; 352 cuts[1].tmp_extents = VectOfExt(tmp, 2); 353 cuts[2].old_dst = 5; 354 cuts[2].old_src = 4; 355 cuts[2].new_vertex = 7; 356 cuts[2].tmp_extents = VectOfExt(tmp + 2, 1); 357 358 // Supplier of temp block: 359 GenVertex(&graph[8], empt, VectOfExt(8, 1), "", InstallOperation::REPLACE); 360 361 // Specify the final order: 362 vector<Vertex::Index> op_indexes; 363 op_indexes.push_back(2); 364 op_indexes.push_back(0); 365 op_indexes.push_back(1); 366 op_indexes.push_back(6); 367 op_indexes.push_back(3); 368 op_indexes.push_back(7); 369 op_indexes.push_back(4); 370 op_indexes.push_back(5); 371 op_indexes.push_back(8); 372 373 vector<vector<Vertex::Index>::size_type> reverse_op_indexes; 374 InplaceGenerator::GenerateReverseTopoOrderMap(op_indexes, 375 &reverse_op_indexes); 376 377 CreateBlobFile(); 378 EXPECT_TRUE(InplaceGenerator::AssignTempBlocks(&graph, 379 "/dev/zero", 380 blob_file_.get(), 381 &op_indexes, 382 &reverse_op_indexes, 383 cuts)); 384 EXPECT_FALSE(graph[6].valid); 385 EXPECT_FALSE(graph[7].valid); 386 EXPECT_EQ(1, graph[1].aop.op.src_extents_size()); 387 EXPECT_EQ(2U, graph[1].aop.op.src_extents(0).start_block()); 388 EXPECT_EQ(1U, graph[1].aop.op.src_extents(0).num_blocks()); 389 EXPECT_EQ(InstallOperation::REPLACE_BZ, graph[5].aop.op.type()); 390 } 391 392 TEST_F(InplaceGeneratorTest, MoveAndSortFullOpsToBackTest) { 393 Graph graph(4); 394 graph[0].aop.name = "A"; 395 graph[0].aop.op.set_type(InstallOperation::REPLACE); 396 graph[1].aop.name = "B"; 397 graph[1].aop.op.set_type(InstallOperation::BSDIFF); 398 graph[2].aop.name = "C"; 399 graph[2].aop.op.set_type(InstallOperation::REPLACE_BZ); 400 graph[3].aop.name = "D"; 401 graph[3].aop.op.set_type(InstallOperation::MOVE); 402 403 vector<Vertex::Index> vect(graph.size()); 404 405 for (vector<Vertex::Index>::size_type i = 0; i < vect.size(); ++i) { 406 vect[i] = i; 407 } 408 InplaceGenerator::MoveAndSortFullOpsToBack(&graph, &vect); 409 EXPECT_EQ(vect.size(), graph.size()); 410 EXPECT_EQ(graph[vect[0]].aop.name, "B"); 411 EXPECT_EQ(graph[vect[1]].aop.name, "D"); 412 EXPECT_EQ(graph[vect[2]].aop.name, "A"); 413 EXPECT_EQ(graph[vect[3]].aop.name, "C"); 414 } 415 416 TEST_F(InplaceGeneratorTest, AssignTempBlocksTest) { 417 Graph graph(9); 418 const vector<Extent> empt; // empty 419 const string kFilename = "/foo"; 420 421 // Some scratch space: 422 GenVertex(&graph[0], empt, VectOfExt(200, 1), "", InstallOperation::REPLACE); 423 GenVertex(&graph[1], empt, VectOfExt(210, 10), "", InstallOperation::REPLACE); 424 GenVertex(&graph[2], empt, VectOfExt(220, 1), "", InstallOperation::REPLACE); 425 426 // A cycle that requires 10 blocks to break: 427 GenVertex(&graph[3], 428 VectOfExt(10, 11), 429 VectOfExt(0, 9), 430 "", 431 InstallOperation::BSDIFF); 432 graph[3].out_edges[4] = EdgeWithReadDep(VectOfExt(0, 9)); 433 GenVertex(&graph[4], 434 VectOfExt(0, 9), 435 VectOfExt(10, 11), 436 "", 437 InstallOperation::BSDIFF); 438 graph[4].out_edges[3] = EdgeWithReadDep(VectOfExt(10, 11)); 439 440 // A cycle that requires 9 blocks to break: 441 GenVertex(&graph[5], 442 VectOfExt(40, 11), 443 VectOfExt(30, 10), 444 "", 445 InstallOperation::BSDIFF); 446 graph[5].out_edges[6] = EdgeWithReadDep(VectOfExt(30, 10)); 447 GenVertex(&graph[6], 448 VectOfExt(30, 10), 449 VectOfExt(40, 11), 450 "", 451 InstallOperation::BSDIFF); 452 graph[6].out_edges[5] = EdgeWithReadDep(VectOfExt(40, 11)); 453 454 // A cycle that requires 40 blocks to break (which is too many): 455 GenVertex(&graph[7], 456 VectOfExt(120, 50), 457 VectOfExt(60, 40), 458 "", 459 InstallOperation::BSDIFF); 460 graph[7].out_edges[8] = EdgeWithReadDep(VectOfExt(60, 40)); 461 GenVertex(&graph[8], 462 VectOfExt(60, 40), 463 VectOfExt(120, 50), 464 kFilename, 465 InstallOperation::BSDIFF); 466 graph[8].out_edges[7] = EdgeWithReadDep(VectOfExt(120, 50)); 467 468 graph_utils::DumpGraph(graph); 469 470 vector<Vertex::Index> final_order; 471 472 CreateBlobFile(); 473 EXPECT_TRUE(InplaceGenerator::ConvertGraphToDag(&graph, 474 "/dev/zero", 475 blob_file_.get(), 476 &final_order, 477 Vertex::kInvalidIndex)); 478 479 Graph expected_graph(12); 480 GenVertex(&expected_graph[0], 481 empt, 482 VectOfExt(200, 1), 483 "", 484 InstallOperation::REPLACE); 485 GenVertex(&expected_graph[1], 486 empt, 487 VectOfExt(210, 10), 488 "", 489 InstallOperation::REPLACE); 490 GenVertex(&expected_graph[2], 491 empt, 492 VectOfExt(220, 1), 493 "", 494 InstallOperation::REPLACE); 495 GenVertex(&expected_graph[3], 496 VectOfExt(10, 11), 497 VectOfExt(0, 9), 498 "", 499 InstallOperation::BSDIFF); 500 expected_graph[3].out_edges[9] = EdgeWithReadDep(VectOfExt(0, 9)); 501 GenVertex(&expected_graph[4], 502 VectOfExt(60, 9), 503 VectOfExt(10, 11), 504 "", 505 InstallOperation::BSDIFF); 506 expected_graph[4].out_edges[3] = EdgeWithReadDep(VectOfExt(10, 11)); 507 expected_graph[4].out_edges[9] = EdgeWithWriteDep(VectOfExt(60, 9)); 508 GenVertex(&expected_graph[5], 509 VectOfExt(40, 11), 510 VectOfExt(30, 10), 511 "", 512 InstallOperation::BSDIFF); 513 expected_graph[5].out_edges[10] = EdgeWithReadDep(VectOfExt(30, 10)); 514 515 GenVertex(&expected_graph[6], 516 VectOfExt(60, 10), 517 VectOfExt(40, 11), 518 "", 519 InstallOperation::BSDIFF); 520 expected_graph[6].out_edges[5] = EdgeWithReadDep(VectOfExt(40, 11)); 521 expected_graph[6].out_edges[10] = EdgeWithWriteDep(VectOfExt(60, 10)); 522 523 GenVertex(&expected_graph[7], 524 VectOfExt(120, 50), 525 VectOfExt(60, 40), 526 "", 527 InstallOperation::BSDIFF); 528 expected_graph[7].out_edges[6] = EdgeWithReadDep(VectOfExt(60, 10)); 529 530 GenVertex(&expected_graph[8], 531 empt, 532 VectOfExt(0, 50), 533 "/foo", 534 InstallOperation::REPLACE_BZ); 535 expected_graph[8].out_edges[7] = EdgeWithReadDep(VectOfExt(120, 50)); 536 537 GenVertex(&expected_graph[9], 538 VectOfExt(0, 9), 539 VectOfExt(60, 9), 540 "", 541 InstallOperation::MOVE); 542 543 GenVertex(&expected_graph[10], 544 VectOfExt(30, 10), 545 VectOfExt(60, 10), 546 "", 547 InstallOperation::MOVE); 548 expected_graph[10].out_edges[4] = EdgeWithReadDep(VectOfExt(60, 9)); 549 550 EXPECT_EQ(12U, graph.size()); 551 EXPECT_FALSE(graph.back().valid); 552 for (Graph::size_type i = 0; i < graph.size() - 1; i++) { 553 EXPECT_TRUE(graph[i].out_edges == expected_graph[i].out_edges); 554 if (i == 8) { 555 // special case 556 } else { 557 // EXPECT_TRUE(graph[i] == expected_graph[i]) << "i = " << i; 558 } 559 } 560 } 561 562 TEST_F(InplaceGeneratorTest, CreateScratchNodeTest) { 563 Vertex vertex; 564 InplaceGenerator::CreateScratchNode(12, 34, &vertex); 565 EXPECT_EQ(InstallOperation::REPLACE_BZ, vertex.aop.op.type()); 566 EXPECT_EQ(0U, vertex.aop.op.data_offset()); 567 EXPECT_EQ(0U, vertex.aop.op.data_length()); 568 EXPECT_EQ(1, vertex.aop.op.dst_extents_size()); 569 EXPECT_EQ(12U, vertex.aop.op.dst_extents(0).start_block()); 570 EXPECT_EQ(34U, vertex.aop.op.dst_extents(0).num_blocks()); 571 } 572 573 TEST_F(InplaceGeneratorTest, ApplyMapTest) { 574 vector<uint64_t> collection = {1, 2, 3, 4, 6}; 575 vector<uint64_t> expected_values = {1, 2, 5, 4, 8}; 576 map<uint64_t, uint64_t> value_map; 577 value_map[3] = 5; 578 value_map[6] = 8; 579 value_map[5] = 10; 580 581 InplaceGenerator::ApplyMap(&collection, value_map); 582 EXPECT_EQ(expected_values, collection); 583 } 584 585 // We can't produce MOVE operations with a source or destination in the block 0. 586 // This test checks that the cycle breaker procedure doesn't produce such 587 // operations. 588 TEST_F(InplaceGeneratorTest, ResolveReadAfterWriteDependenciesAvoidMoveToZero) { 589 size_t block_size = 4096; 590 size_t num_blocks = 4; 591 vector<AnnotatedOperation> aops; 592 593 // Create a REPLACE_BZ for block 0, and a circular dependency among all other 594 // blocks. This situation would prefer to issue a MOVE to scratch space and 595 // the only available block is 0. 596 aops.emplace_back(); 597 aops.back().name = base::StringPrintf("<bz-block-0>"); 598 aops.back().op.set_type(InstallOperation::REPLACE_BZ); 599 StoreExtents({ExtentForRange(0, 1)}, aops.back().op.mutable_dst_extents()); 600 601 for (size_t i = 1; i < num_blocks; i++) { 602 AnnotatedOperation aop; 603 aop.name = base::StringPrintf("<op-%" PRIuS ">", i); 604 aop.op.set_type(InstallOperation::BSDIFF); 605 StoreExtents({ExtentForRange(1 + i % (num_blocks - 1), 1)}, 606 aop.op.mutable_src_extents()); 607 StoreExtents({ExtentForRange(i, 1)}, aop.op.mutable_dst_extents()); 608 aops.push_back(aop); 609 } 610 611 PartitionConfig part("part"); 612 part.path = "/dev/zero"; 613 part.size = num_blocks * block_size; 614 615 CreateBlobFile(); 616 617 // We ran two tests here. The first one without enough blocks for the scratch 618 // space, forcing it to create a new full operation and the second case with 619 // one extra block in the partition that can be used for the move operation. 620 for (const auto part_blocks : vector<uint64_t>{num_blocks, num_blocks + 1}) { 621 SCOPED_TRACE(base::StringPrintf("Using partition_blocs=%" PRIu64, 622 part_blocks)); 623 vector<AnnotatedOperation> result_aops = aops; 624 EXPECT_TRUE(InplaceGenerator::ResolveReadAfterWriteDependencies( 625 part, part_blocks * block_size, block_size, blob_file_.get(), 626 &result_aops)); 627 628 size_t full_ops = 0; 629 for (const auto& aop : result_aops) { 630 if (aop.op.type() == InstallOperation::REPLACE || 631 aop.op.type() == InstallOperation::REPLACE_BZ) { 632 full_ops++; 633 } 634 635 if (aop.op.type() != InstallOperation::MOVE) 636 continue; 637 for (const Extent& extent : aop.op.src_extents()) { 638 EXPECT_NE(0U, extent.start_block()) 639 << "On src extents for aop: " << aop; 640 } 641 for (const Extent& extent : aop.op.dst_extents()) { 642 EXPECT_NE(0U, extent.start_block()) 643 << "On dst extents for aop: " << aop; 644 } 645 } 646 647 // If there's extra space in the partition, it should not use a new full 648 // operation for it. 649 EXPECT_EQ(part_blocks == num_blocks ? 2U : 1U, full_ops); 650 651 if (HasNonfatalFailure()) { 652 LOG(INFO) << "Result operation list:"; 653 for (const auto& aop : result_aops) { 654 LOG(INFO) << aop; 655 } 656 } 657 } 658 } 659 660 } // namespace chromeos_update_engine 661