inplace_generator_unittest.cc revision 703022b71fc6a89796f2f97448b1a419007a52ca
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 <set> 8#include <sstream> 9#include <string> 10#include <utility> 11#include <vector> 12 13#include <base/logging.h> 14#include <base/strings/string_util.h> 15#include <gtest/gtest.h> 16 17#include "update_engine/extent_ranges.h" 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/graph_types.h" 21#include "update_engine/test_utils.h" 22#include "update_engine/utils.h" 23 24using std::set; 25using std::string; 26using std::stringstream; 27using std::vector; 28 29namespace chromeos_update_engine { 30 31typedef DeltaDiffGenerator::Block Block; 32 33namespace { 34 35#define OP_BSDIFF DeltaArchiveManifest_InstallOperation_Type_BSDIFF 36#define OP_MOVE DeltaArchiveManifest_InstallOperation_Type_MOVE 37#define OP_REPLACE DeltaArchiveManifest_InstallOperation_Type_REPLACE 38#define OP_REPLACE_BZ DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ 39 40void GenVertex(Vertex* out, 41 const vector<Extent>& src_extents, 42 const vector<Extent>& dst_extents, 43 const string& path, 44 DeltaArchiveManifest_InstallOperation_Type type) { 45 out->op.set_type(type); 46 out->file_name = path; 47 DeltaDiffGenerator::StoreExtents(src_extents, out->op.mutable_src_extents()); 48 DeltaDiffGenerator::StoreExtents(dst_extents, out->op.mutable_dst_extents()); 49} 50 51vector<Extent> VectOfExt(uint64_t start_block, uint64_t num_blocks) { 52 return vector<Extent>(1, ExtentForRange(start_block, num_blocks)); 53} 54 55EdgeProperties EdgeWithReadDep(const vector<Extent>& extents) { 56 EdgeProperties ret; 57 ret.extents = extents; 58 return ret; 59} 60 61EdgeProperties EdgeWithWriteDep(const vector<Extent>& extents) { 62 EdgeProperties ret; 63 ret.write_extents = extents; 64 return ret; 65} 66 67template<typename T> 68void DumpVect(const vector<T>& vect) { 69 stringstream ss(stringstream::out); 70 for (typename vector<T>::const_iterator it = vect.begin(), e = vect.end(); 71 it != e; ++it) { 72 ss << *it << ", "; 73 } 74 LOG(INFO) << "{" << ss.str() << "}"; 75} 76 77void AppendExtent(vector<Extent>* vect, uint64_t start, uint64_t length) { 78 vect->resize(vect->size() + 1); 79 vect->back().set_start_block(start); 80 vect->back().set_num_blocks(length); 81} 82 83void OpAppendExtent(DeltaArchiveManifest_InstallOperation* op, 84 uint64_t start, 85 uint64_t length) { 86 Extent* extent = op->add_src_extents(); 87 extent->set_start_block(start); 88 extent->set_num_blocks(length); 89} 90 91} // namespace 92 93class InplaceGeneratorTest : public ::testing::Test { 94}; 95 96TEST_F(InplaceGeneratorTest, SubstituteBlocksTest) { 97 vector<Extent> remove_blocks; 98 AppendExtent(&remove_blocks, 3, 3); 99 AppendExtent(&remove_blocks, 7, 1); 100 vector<Extent> replace_blocks; 101 AppendExtent(&replace_blocks, 10, 2); 102 AppendExtent(&replace_blocks, 13, 2); 103 Vertex vertex; 104 DeltaArchiveManifest_InstallOperation& op = vertex.op; 105 OpAppendExtent(&op, 4, 3); 106 OpAppendExtent(&op, kSparseHole, 4); // Sparse hole in file 107 OpAppendExtent(&op, 3, 1); 108 OpAppendExtent(&op, 7, 3); 109 110 InplaceGenerator::SubstituteBlocks(&vertex, remove_blocks, replace_blocks); 111 112 EXPECT_EQ(7, op.src_extents_size()); 113 EXPECT_EQ(11, op.src_extents(0).start_block()); 114 EXPECT_EQ(1, op.src_extents(0).num_blocks()); 115 EXPECT_EQ(13, op.src_extents(1).start_block()); 116 EXPECT_EQ(1, op.src_extents(1).num_blocks()); 117 EXPECT_EQ(6, op.src_extents(2).start_block()); 118 EXPECT_EQ(1, op.src_extents(2).num_blocks()); 119 EXPECT_EQ(kSparseHole, op.src_extents(3).start_block()); 120 EXPECT_EQ(4, op.src_extents(3).num_blocks()); 121 EXPECT_EQ(10, op.src_extents(4).start_block()); 122 EXPECT_EQ(1, op.src_extents(4).num_blocks()); 123 EXPECT_EQ(14, op.src_extents(5).start_block()); 124 EXPECT_EQ(1, op.src_extents(5).num_blocks()); 125 EXPECT_EQ(8, op.src_extents(6).start_block()); 126 EXPECT_EQ(2, op.src_extents(6).num_blocks()); 127} 128 129TEST_F(InplaceGeneratorTest, CutEdgesTest) { 130 Graph graph; 131 vector<Block> blocks(9); 132 133 // Create nodes in graph 134 { 135 graph.resize(graph.size() + 1); 136 graph.back().op.set_type(DeltaArchiveManifest_InstallOperation_Type_MOVE); 137 // Reads from blocks 3, 5, 7 138 vector<Extent> extents; 139 graph_utils::AppendBlockToExtents(&extents, 3); 140 graph_utils::AppendBlockToExtents(&extents, 5); 141 graph_utils::AppendBlockToExtents(&extents, 7); 142 DeltaDiffGenerator::StoreExtents(extents, 143 graph.back().op.mutable_src_extents()); 144 blocks[3].reader = graph.size() - 1; 145 blocks[5].reader = graph.size() - 1; 146 blocks[7].reader = graph.size() - 1; 147 148 // Writes to blocks 1, 2, 4 149 extents.clear(); 150 graph_utils::AppendBlockToExtents(&extents, 1); 151 graph_utils::AppendBlockToExtents(&extents, 2); 152 graph_utils::AppendBlockToExtents(&extents, 4); 153 DeltaDiffGenerator::StoreExtents(extents, 154 graph.back().op.mutable_dst_extents()); 155 blocks[1].writer = graph.size() - 1; 156 blocks[2].writer = graph.size() - 1; 157 blocks[4].writer = graph.size() - 1; 158 } 159 { 160 graph.resize(graph.size() + 1); 161 graph.back().op.set_type(DeltaArchiveManifest_InstallOperation_Type_MOVE); 162 // Reads from blocks 1, 2, 4 163 vector<Extent> extents; 164 graph_utils::AppendBlockToExtents(&extents, 1); 165 graph_utils::AppendBlockToExtents(&extents, 2); 166 graph_utils::AppendBlockToExtents(&extents, 4); 167 DeltaDiffGenerator::StoreExtents(extents, 168 graph.back().op.mutable_src_extents()); 169 blocks[1].reader = graph.size() - 1; 170 blocks[2].reader = graph.size() - 1; 171 blocks[4].reader = graph.size() - 1; 172 173 // Writes to blocks 3, 5, 6 174 extents.clear(); 175 graph_utils::AppendBlockToExtents(&extents, 3); 176 graph_utils::AppendBlockToExtents(&extents, 5); 177 graph_utils::AppendBlockToExtents(&extents, 6); 178 DeltaDiffGenerator::StoreExtents(extents, 179 graph.back().op.mutable_dst_extents()); 180 blocks[3].writer = graph.size() - 1; 181 blocks[5].writer = graph.size() - 1; 182 blocks[6].writer = graph.size() - 1; 183 } 184 185 // Create edges 186 InplaceGenerator::CreateEdges(&graph, blocks); 187 188 // Find cycles 189 CycleBreaker cycle_breaker; 190 set<Edge> cut_edges; 191 cycle_breaker.BreakCycles(graph, &cut_edges); 192 193 EXPECT_EQ(1, cut_edges.size()); 194 EXPECT_TRUE(cut_edges.end() != cut_edges.find( 195 std::pair<Vertex::Index, Vertex::Index>(1, 0))); 196 197 vector<CutEdgeVertexes> cuts; 198 EXPECT_TRUE(InplaceGenerator::CutEdges(&graph, cut_edges, &cuts)); 199 200 EXPECT_EQ(3, graph.size()); 201 202 // Check new node in graph: 203 EXPECT_EQ(DeltaArchiveManifest_InstallOperation_Type_MOVE, 204 graph.back().op.type()); 205 EXPECT_EQ(2, graph.back().op.src_extents_size()); 206 EXPECT_EQ(1, graph.back().op.dst_extents_size()); 207 EXPECT_EQ(kTempBlockStart, graph.back().op.dst_extents(0).start_block()); 208 EXPECT_EQ(2, graph.back().op.dst_extents(0).num_blocks()); 209 EXPECT_TRUE(graph.back().out_edges.empty()); 210 211 // Check that old node reads from new blocks 212 EXPECT_EQ(2, graph[0].op.src_extents_size()); 213 EXPECT_EQ(kTempBlockStart, graph[0].op.src_extents(0).start_block()); 214 EXPECT_EQ(2, graph[0].op.src_extents(0).num_blocks()); 215 EXPECT_EQ(7, graph[0].op.src_extents(1).start_block()); 216 EXPECT_EQ(1, graph[0].op.src_extents(1).num_blocks()); 217 218 // And that the old dst extents haven't changed 219 EXPECT_EQ(2, graph[0].op.dst_extents_size()); 220 EXPECT_EQ(1, graph[0].op.dst_extents(0).start_block()); 221 EXPECT_EQ(2, graph[0].op.dst_extents(0).num_blocks()); 222 EXPECT_EQ(4, graph[0].op.dst_extents(1).start_block()); 223 EXPECT_EQ(1, graph[0].op.dst_extents(1).num_blocks()); 224 225 // Ensure it only depends on the next node and the new temp node 226 EXPECT_EQ(2, graph[0].out_edges.size()); 227 EXPECT_TRUE(graph[0].out_edges.end() != graph[0].out_edges.find(1)); 228 EXPECT_TRUE(graph[0].out_edges.end() != graph[0].out_edges.find(graph.size() - 229 1)); 230 231 // Check second node has unchanged extents 232 EXPECT_EQ(2, graph[1].op.src_extents_size()); 233 EXPECT_EQ(1, graph[1].op.src_extents(0).start_block()); 234 EXPECT_EQ(2, graph[1].op.src_extents(0).num_blocks()); 235 EXPECT_EQ(4, graph[1].op.src_extents(1).start_block()); 236 EXPECT_EQ(1, graph[1].op.src_extents(1).num_blocks()); 237 238 EXPECT_EQ(2, graph[1].op.dst_extents_size()); 239 EXPECT_EQ(3, graph[1].op.dst_extents(0).start_block()); 240 EXPECT_EQ(1, graph[1].op.dst_extents(0).num_blocks()); 241 EXPECT_EQ(5, graph[1].op.dst_extents(1).start_block()); 242 EXPECT_EQ(2, graph[1].op.dst_extents(1).num_blocks()); 243 244 // Ensure it only depends on the next node 245 EXPECT_EQ(1, graph[1].out_edges.size()); 246 EXPECT_TRUE(graph[1].out_edges.end() != graph[1].out_edges.find(2)); 247} 248 249TEST_F(InplaceGeneratorTest, RunAsRootAssignTempBlocksReuseTest) { 250 // AssignTempBlocks(Graph* graph, 251 // const string& new_root, 252 // int data_fd, 253 // off_t* data_file_size, 254 // vector<Vertex::Index>* op_indexes, 255 // vector<vector<Vertex::Index>::size_type>* reverse_op_indexes, 256 // const vector<CutEdgeVertexes>& cuts 257 Graph graph(9); 258 259 const vector<Extent> empt; 260 uint64_t tmp = kTempBlockStart; 261 const string kFilename = "/foo"; 262 263 vector<CutEdgeVertexes> cuts; 264 cuts.resize(3); 265 266 // Simple broken loop: 267 GenVertex(&graph[0], VectOfExt(0, 1), VectOfExt(1, 1), "", OP_MOVE); 268 GenVertex(&graph[1], VectOfExt(tmp, 1), VectOfExt(0, 1), "", OP_MOVE); 269 GenVertex(&graph[2], VectOfExt(1, 1), VectOfExt(tmp, 1), "", OP_MOVE); 270 // Corresponding edges: 271 graph[0].out_edges[2] = EdgeWithReadDep(VectOfExt(1, 1)); 272 graph[1].out_edges[2] = EdgeWithWriteDep(VectOfExt(tmp, 1)); 273 graph[1].out_edges[0] = EdgeWithReadDep(VectOfExt(0, 1)); 274 // Store the cut: 275 cuts[0].old_dst = 1; 276 cuts[0].old_src = 0; 277 cuts[0].new_vertex = 2; 278 cuts[0].tmp_extents = VectOfExt(tmp, 1); 279 tmp++; 280 281 // Slightly more complex pair of loops: 282 GenVertex(&graph[3], VectOfExt(4, 2), VectOfExt(2, 2), "", OP_MOVE); 283 GenVertex(&graph[4], VectOfExt(6, 1), VectOfExt(7, 1), "", OP_MOVE); 284 GenVertex(&graph[5], VectOfExt(tmp, 3), VectOfExt(4, 3), kFilename, OP_MOVE); 285 GenVertex(&graph[6], VectOfExt(2, 2), VectOfExt(tmp, 2), "", OP_MOVE); 286 GenVertex(&graph[7], VectOfExt(7, 1), VectOfExt(tmp + 2, 1), "", OP_MOVE); 287 // Corresponding edges: 288 graph[3].out_edges[6] = EdgeWithReadDep(VectOfExt(2, 2)); 289 graph[4].out_edges[7] = EdgeWithReadDep(VectOfExt(7, 1)); 290 graph[5].out_edges[6] = EdgeWithWriteDep(VectOfExt(tmp, 2)); 291 graph[5].out_edges[7] = EdgeWithWriteDep(VectOfExt(tmp + 2, 1)); 292 graph[5].out_edges[3] = EdgeWithReadDep(VectOfExt(4, 2)); 293 graph[5].out_edges[4] = EdgeWithReadDep(VectOfExt(6, 1)); 294 // Store the cuts: 295 cuts[1].old_dst = 5; 296 cuts[1].old_src = 3; 297 cuts[1].new_vertex = 6; 298 cuts[1].tmp_extents = VectOfExt(tmp, 2); 299 cuts[2].old_dst = 5; 300 cuts[2].old_src = 4; 301 cuts[2].new_vertex = 7; 302 cuts[2].tmp_extents = VectOfExt(tmp + 2, 1); 303 304 // Supplier of temp block: 305 GenVertex(&graph[8], empt, VectOfExt(8, 1), "", OP_REPLACE); 306 307 // Specify the final order: 308 vector<Vertex::Index> op_indexes; 309 op_indexes.push_back(2); 310 op_indexes.push_back(0); 311 op_indexes.push_back(1); 312 op_indexes.push_back(6); 313 op_indexes.push_back(3); 314 op_indexes.push_back(7); 315 op_indexes.push_back(4); 316 op_indexes.push_back(5); 317 op_indexes.push_back(8); 318 319 vector<vector<Vertex::Index>::size_type> reverse_op_indexes; 320 InplaceGenerator::GenerateReverseTopoOrderMap(op_indexes, 321 &reverse_op_indexes); 322 323 // Prepare the filesystem with the minimum required for this to work 324 string temp_dir; 325 EXPECT_TRUE(utils::MakeTempDirectory("AssignTempBlocksReuseTest.XXXXXX", 326 &temp_dir)); 327 ScopedDirRemover temp_dir_remover(temp_dir); 328 329 chromeos::Blob temp_data(kBlockSize * 3); 330 test_utils::FillWithData(&temp_data); 331 EXPECT_TRUE(test_utils::WriteFileVector(temp_dir + kFilename, temp_data)); 332 ScopedPathUnlinker filename_unlinker(temp_dir + kFilename); 333 334 int fd; 335 EXPECT_TRUE(utils::MakeTempFile("AssignTempBlocksReuseTest.XXXXXX", 336 nullptr, 337 &fd)); 338 ScopedFdCloser fd_closer(&fd); 339 off_t data_file_size = 0; 340 341 EXPECT_TRUE(InplaceGenerator::AssignTempBlocks(&graph, 342 temp_dir, 343 fd, 344 &data_file_size, 345 &op_indexes, 346 &reverse_op_indexes, 347 cuts)); 348 EXPECT_FALSE(graph[6].valid); 349 EXPECT_FALSE(graph[7].valid); 350 EXPECT_EQ(1, graph[1].op.src_extents_size()); 351 EXPECT_EQ(2, graph[1].op.src_extents(0).start_block()); 352 EXPECT_EQ(1, graph[1].op.src_extents(0).num_blocks()); 353 EXPECT_EQ(OP_REPLACE_BZ, graph[5].op.type()); 354} 355 356TEST_F(InplaceGeneratorTest, MoveFullOpsToBackTest) { 357 Graph graph(4); 358 graph[0].file_name = "A"; 359 graph[0].op.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE); 360 graph[1].file_name = "B"; 361 graph[1].op.set_type(DeltaArchiveManifest_InstallOperation_Type_BSDIFF); 362 graph[2].file_name = "C"; 363 graph[2].op.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ); 364 graph[3].file_name = "D"; 365 graph[3].op.set_type(DeltaArchiveManifest_InstallOperation_Type_MOVE); 366 367 vector<Vertex::Index> vect(graph.size()); 368 369 for (vector<Vertex::Index>::size_type i = 0; i < vect.size(); ++i) { 370 vect[i] = i; 371 } 372 InplaceGenerator::MoveFullOpsToBack(&graph, &vect); 373 EXPECT_EQ(vect.size(), graph.size()); 374 EXPECT_EQ(graph[vect[0]].file_name, "B"); 375 EXPECT_EQ(graph[vect[1]].file_name, "D"); 376 EXPECT_EQ(graph[vect[2]].file_name, "A"); 377 EXPECT_EQ(graph[vect[3]].file_name, "C"); 378} 379 380TEST_F(InplaceGeneratorTest, RunAsRootAssignTempBlocksTest) { 381 Graph graph(9); 382 const vector<Extent> empt; // empty 383 const string kFilename = "/foo"; 384 385 // Some scratch space: 386 GenVertex(&graph[0], empt, VectOfExt(200, 1), "", OP_REPLACE); 387 GenVertex(&graph[1], empt, VectOfExt(210, 10), "", OP_REPLACE); 388 GenVertex(&graph[2], empt, VectOfExt(220, 1), "", OP_REPLACE); 389 390 // A cycle that requires 10 blocks to break: 391 GenVertex(&graph[3], VectOfExt(10, 11), VectOfExt(0, 9), "", OP_BSDIFF); 392 graph[3].out_edges[4] = EdgeWithReadDep(VectOfExt(0, 9)); 393 GenVertex(&graph[4], VectOfExt(0, 9), VectOfExt(10, 11), "", OP_BSDIFF); 394 graph[4].out_edges[3] = EdgeWithReadDep(VectOfExt(10, 11)); 395 396 // A cycle that requires 9 blocks to break: 397 GenVertex(&graph[5], VectOfExt(40, 11), VectOfExt(30, 10), "", OP_BSDIFF); 398 graph[5].out_edges[6] = EdgeWithReadDep(VectOfExt(30, 10)); 399 GenVertex(&graph[6], VectOfExt(30, 10), VectOfExt(40, 11), "", OP_BSDIFF); 400 graph[6].out_edges[5] = EdgeWithReadDep(VectOfExt(40, 11)); 401 402 // A cycle that requires 40 blocks to break (which is too many): 403 GenVertex(&graph[7], 404 VectOfExt(120, 50), 405 VectOfExt(60, 40), 406 "", 407 OP_BSDIFF); 408 graph[7].out_edges[8] = EdgeWithReadDep(VectOfExt(60, 40)); 409 GenVertex(&graph[8], 410 VectOfExt(60, 40), 411 VectOfExt(120, 50), 412 kFilename, 413 OP_BSDIFF); 414 graph[8].out_edges[7] = EdgeWithReadDep(VectOfExt(120, 50)); 415 416 graph_utils::DumpGraph(graph); 417 418 vector<Vertex::Index> final_order; 419 420 421 // Prepare the filesystem with the minimum required for this to work 422 string temp_dir; 423 EXPECT_TRUE(utils::MakeTempDirectory("AssignTempBlocksTest.XXXXXX", 424 &temp_dir)); 425 ScopedDirRemover temp_dir_remover(temp_dir); 426 427 chromeos::Blob temp_data(kBlockSize * 50); 428 test_utils::FillWithData(&temp_data); 429 EXPECT_TRUE(test_utils::WriteFileVector(temp_dir + kFilename, temp_data)); 430 ScopedPathUnlinker filename_unlinker(temp_dir + kFilename); 431 432 int fd; 433 EXPECT_TRUE(utils::MakeTempFile("AssignTempBlocksTestData.XXXXXX", 434 nullptr, 435 &fd)); 436 ScopedFdCloser fd_closer(&fd); 437 off_t data_file_size = 0; 438 439 440 EXPECT_TRUE(InplaceGenerator::ConvertGraphToDag(&graph, 441 temp_dir, 442 fd, 443 &data_file_size, 444 &final_order, 445 Vertex::kInvalidIndex)); 446 447 448 Graph expected_graph(12); 449 GenVertex(&expected_graph[0], empt, VectOfExt(200, 1), "", OP_REPLACE); 450 GenVertex(&expected_graph[1], empt, VectOfExt(210, 10), "", OP_REPLACE); 451 GenVertex(&expected_graph[2], empt, VectOfExt(220, 1), "", OP_REPLACE); 452 GenVertex(&expected_graph[3], 453 VectOfExt(10, 11), 454 VectOfExt(0, 9), 455 "", 456 OP_BSDIFF); 457 expected_graph[3].out_edges[9] = EdgeWithReadDep(VectOfExt(0, 9)); 458 GenVertex(&expected_graph[4], 459 VectOfExt(60, 9), 460 VectOfExt(10, 11), 461 "", 462 OP_BSDIFF); 463 expected_graph[4].out_edges[3] = EdgeWithReadDep(VectOfExt(10, 11)); 464 expected_graph[4].out_edges[9] = EdgeWithWriteDep(VectOfExt(60, 9)); 465 GenVertex(&expected_graph[5], 466 VectOfExt(40, 11), 467 VectOfExt(30, 10), 468 "", 469 OP_BSDIFF); 470 expected_graph[5].out_edges[10] = EdgeWithReadDep(VectOfExt(30, 10)); 471 472 GenVertex(&expected_graph[6], 473 VectOfExt(60, 10), 474 VectOfExt(40, 11), 475 "", 476 OP_BSDIFF); 477 expected_graph[6].out_edges[5] = EdgeWithReadDep(VectOfExt(40, 11)); 478 expected_graph[6].out_edges[10] = EdgeWithWriteDep(VectOfExt(60, 10)); 479 480 GenVertex(&expected_graph[7], 481 VectOfExt(120, 50), 482 VectOfExt(60, 40), 483 "", 484 OP_BSDIFF); 485 expected_graph[7].out_edges[6] = EdgeWithReadDep(VectOfExt(60, 10)); 486 487 GenVertex(&expected_graph[8], empt, VectOfExt(0, 50), "/foo", OP_REPLACE_BZ); 488 expected_graph[8].out_edges[7] = EdgeWithReadDep(VectOfExt(120, 50)); 489 490 GenVertex(&expected_graph[9], 491 VectOfExt(0, 9), 492 VectOfExt(60, 9), 493 "", 494 OP_MOVE); 495 496 GenVertex(&expected_graph[10], 497 VectOfExt(30, 10), 498 VectOfExt(60, 10), 499 "", 500 OP_MOVE); 501 expected_graph[10].out_edges[4] = EdgeWithReadDep(VectOfExt(60, 9)); 502 503 EXPECT_EQ(12, graph.size()); 504 EXPECT_FALSE(graph.back().valid); 505 for (Graph::size_type i = 0; i < graph.size() - 1; i++) { 506 EXPECT_TRUE(graph[i].out_edges == expected_graph[i].out_edges); 507 if (i == 8) { 508 // special case 509 } else { 510 // EXPECT_TRUE(graph[i] == expected_graph[i]) << "i = " << i; 511 } 512 } 513} 514 515// Test that sparse holes are not used as scratch. More specifically, test that 516// if all full operations write to sparse holes and there's no extra scratch 517// space, delta operations that need scratch are converted to full. See 518// crbug.com/238440. 519TEST_F(InplaceGeneratorTest, RunAsRootNoSparseAsTempTest) { 520 Graph graph(3); 521 const vector<Extent> kEmpty; 522 const string kFilename = "/foo"; 523 524 // Make sure this sparse hole is not used as scratch. 525 GenVertex(&graph[0], kEmpty, VectOfExt(kSparseHole, 1), "", OP_REPLACE); 526 527 // Create a single-block cycle. 528 GenVertex(&graph[1], VectOfExt(0, 1), VectOfExt(1, 1), "", OP_BSDIFF); 529 graph[1].out_edges[2] = EdgeWithReadDep(VectOfExt(1, 1)); 530 GenVertex(&graph[2], VectOfExt(1, 1), VectOfExt(0, 1), kFilename, OP_BSDIFF); 531 graph[2].out_edges[1] = EdgeWithReadDep(VectOfExt(0, 1)); 532 533 graph_utils::DumpGraph(graph); 534 535 vector<Vertex::Index> final_order; 536 537 // Prepare the filesystem with the minimum required for this to work. 538 string temp_dir; 539 EXPECT_TRUE(utils::MakeTempDirectory("NoSparseAsTempTest.XXXXXX", 540 &temp_dir)); 541 ScopedDirRemover temp_dir_remover(temp_dir); 542 543 chromeos::Blob temp_data(kBlockSize); 544 test_utils::FillWithData(&temp_data); 545 EXPECT_TRUE(test_utils::WriteFileVector(temp_dir + kFilename, temp_data)); 546 ScopedPathUnlinker filename_unlinker(temp_dir + kFilename); 547 548 int fd = -1; 549 EXPECT_TRUE(utils::MakeTempFile("NoSparseAsTempTestData.XXXXXX", 550 nullptr, 551 &fd)); 552 ScopedFdCloser fd_closer(&fd); 553 off_t data_file_size = 0; 554 555 EXPECT_TRUE(InplaceGenerator::ConvertGraphToDag(&graph, 556 temp_dir, 557 fd, 558 &data_file_size, 559 &final_order, 560 Vertex::kInvalidIndex)); 561 562 ASSERT_EQ(4, graph.size()); 563 564 // The second BSDIFF operation must have been converted to a full operation 565 // (due to insufficient scratch space). 566 EXPECT_TRUE(graph[2].op.type() == OP_REPLACE || 567 graph[2].op.type() == OP_REPLACE_BZ); 568 569 // The temporary node created for breaking the cycle must have been marked as 570 // invalid. 571 EXPECT_FALSE(graph[3].valid); 572} 573 574// Test that sparse holes are not used as scratch. More specifically, test that 575// if scratch space comes only from full operations writing to real blocks as 576// well as sparse holes, only the real blocks are utilized. See 577// crbug.com/238440. 578TEST_F(InplaceGeneratorTest, NoSparseAsTempTest) { 579 Graph graph; 580 vector<Block> blocks(4); 581 582 // Create nodes in |graph|. 583 { 584 graph.resize(graph.size() + 1); 585 graph.back().op.set_type( 586 DeltaArchiveManifest_InstallOperation_Type_REPLACE); 587 588 // Write to a sparse hole -- basically a no-op to ensure sparse holes are 589 // not used as scratch. 590 vector<Extent> extents; 591 graph_utils::AppendBlockToExtents(&extents, kSparseHole); 592 DeltaDiffGenerator::StoreExtents(extents, 593 graph.back().op.mutable_dst_extents()); 594 } 595 { 596 graph.resize(graph.size() + 1); 597 graph.back().op.set_type(OP_REPLACE); 598 599 // Scratch space: write to block 0 with sparse holes around. 600 vector<Extent> extents; 601 graph_utils::AppendBlockToExtents(&extents, kSparseHole); 602 graph_utils::AppendBlockToExtents(&extents, 0); 603 graph_utils::AppendBlockToExtents(&extents, kSparseHole); 604 DeltaDiffGenerator::StoreExtents(extents, 605 graph.back().op.mutable_dst_extents()); 606 blocks[0].writer = graph.size() - 1; 607 } 608 { 609 graph.resize(graph.size() + 1); 610 graph.back().op.set_type(OP_REPLACE); 611 612 // Write to a sparse hole. 613 vector<Extent> extents; 614 graph_utils::AppendBlockToExtents(&extents, kSparseHole); 615 DeltaDiffGenerator::StoreExtents(extents, 616 graph.back().op.mutable_dst_extents()); 617 } 618 // Create a two-node cycle between (2, sparse, sparse) and (1, sparse, 3). 619 { 620 graph.resize(graph.size() + 1); 621 graph.back().op.set_type(OP_MOVE); 622 // Read from (2, sparse, sparse). 623 vector<Extent> extents; 624 graph_utils::AppendBlockToExtents(&extents, 2); 625 graph_utils::AppendBlockToExtents(&extents, kSparseHole); 626 graph_utils::AppendBlockToExtents(&extents, kSparseHole); 627 DeltaDiffGenerator::StoreExtents(extents, 628 graph.back().op.mutable_src_extents()); 629 blocks[2].reader = graph.size() - 1; 630 631 // Write to (1, sparse, 3). 632 extents.clear(); 633 graph_utils::AppendBlockToExtents(&extents, 1); 634 graph_utils::AppendBlockToExtents(&extents, kSparseHole); 635 graph_utils::AppendBlockToExtents(&extents, 3); 636 DeltaDiffGenerator::StoreExtents(extents, 637 graph.back().op.mutable_dst_extents()); 638 blocks[1].writer = graph.size() - 1; 639 blocks[3].writer = graph.size() - 1; 640 } 641 { 642 graph.resize(graph.size() + 1); 643 graph.back().op.set_type(OP_MOVE); 644 // Read from (1, sparse, 3). 645 vector<Extent> extents; 646 graph_utils::AppendBlockToExtents(&extents, 1); 647 graph_utils::AppendBlockToExtents(&extents, kSparseHole); 648 graph_utils::AppendBlockToExtents(&extents, 3); 649 DeltaDiffGenerator::StoreExtents(extents, 650 graph.back().op.mutable_src_extents()); 651 blocks[1].reader = graph.size() - 1; 652 blocks[3].reader = graph.size() - 1; 653 654 // Write to (2, sparse, sparse). 655 extents.clear(); 656 graph_utils::AppendBlockToExtents(&extents, 2); 657 graph_utils::AppendBlockToExtents(&extents, kSparseHole); 658 graph_utils::AppendBlockToExtents(&extents, kSparseHole); 659 DeltaDiffGenerator::StoreExtents(extents, 660 graph.back().op.mutable_dst_extents()); 661 blocks[2].writer = graph.size() - 1; 662 } 663 664 graph_utils::DumpGraph(graph); 665 666 // Create edges 667 InplaceGenerator::CreateEdges(&graph, blocks); 668 669 graph_utils::DumpGraph(graph); 670 671 vector<Vertex::Index> final_order; 672 off_t data_file_size = 0; 673 EXPECT_TRUE(InplaceGenerator::ConvertGraphToDag(&graph, 674 "/non/existent/dir", 675 -1, 676 &data_file_size, 677 &final_order, 678 Vertex::kInvalidIndex)); 679 680 // Check for a single temporary node writing to scratch. 681 ASSERT_EQ(6, graph.size()); 682 EXPECT_EQ(DeltaArchiveManifest_InstallOperation_Type_MOVE, 683 graph[5].op.type()); 684 EXPECT_EQ(1, graph[5].op.src_extents_size()); 685 ASSERT_EQ(1, graph[5].op.dst_extents_size()); 686 EXPECT_EQ(0, graph[5].op.dst_extents(0).start_block()); 687 EXPECT_EQ(1, graph[5].op.dst_extents(0).num_blocks()); 688 689 // Make sure the cycle nodes still read from and write to sparse holes. 690 for (int i = 3; i < 5; i++) { 691 ASSERT_GE(graph[i].op.src_extents_size(), 2); 692 EXPECT_EQ(kSparseHole, graph[i].op.src_extents(1).start_block()); 693 ASSERT_GE(graph[i].op.dst_extents_size(), 2); 694 EXPECT_EQ(kSparseHole, graph[i].op.dst_extents(1).start_block()); 695 } 696} 697 698TEST_F(InplaceGeneratorTest, CreateScratchNodeTest) { 699 Vertex vertex; 700 InplaceGenerator::CreateScratchNode(12, 34, &vertex); 701 EXPECT_EQ(DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ, 702 vertex.op.type()); 703 EXPECT_EQ(0, vertex.op.data_offset()); 704 EXPECT_EQ(0, vertex.op.data_length()); 705 EXPECT_EQ(1, vertex.op.dst_extents_size()); 706 EXPECT_EQ(12, vertex.op.dst_extents(0).start_block()); 707 EXPECT_EQ(34, vertex.op.dst_extents(0).num_blocks()); 708} 709 710} // namespace chromeos_update_engine 711