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