inplace_generator_unittest.cc revision 8e2c4ef89b1c2c8779d003b40c70c75db1839bfb
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                                                 "/dev/zero",
344                                                 fd,
345                                                 &data_file_size,
346                                                 &op_indexes,
347                                                 &reverse_op_indexes,
348                                                 cuts));
349  EXPECT_FALSE(graph[6].valid);
350  EXPECT_FALSE(graph[7].valid);
351  EXPECT_EQ(1, graph[1].op.src_extents_size());
352  EXPECT_EQ(2, graph[1].op.src_extents(0).start_block());
353  EXPECT_EQ(1, graph[1].op.src_extents(0).num_blocks());
354  EXPECT_EQ(OP_REPLACE_BZ, graph[5].op.type());
355}
356
357TEST_F(InplaceGeneratorTest, MoveFullOpsToBackTest) {
358  Graph graph(4);
359  graph[0].file_name = "A";
360  graph[0].op.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE);
361  graph[1].file_name = "B";
362  graph[1].op.set_type(DeltaArchiveManifest_InstallOperation_Type_BSDIFF);
363  graph[2].file_name = "C";
364  graph[2].op.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ);
365  graph[3].file_name = "D";
366  graph[3].op.set_type(DeltaArchiveManifest_InstallOperation_Type_MOVE);
367
368  vector<Vertex::Index> vect(graph.size());
369
370  for (vector<Vertex::Index>::size_type i = 0; i < vect.size(); ++i) {
371    vect[i] = i;
372  }
373  InplaceGenerator::MoveFullOpsToBack(&graph, &vect);
374  EXPECT_EQ(vect.size(), graph.size());
375  EXPECT_EQ(graph[vect[0]].file_name, "B");
376  EXPECT_EQ(graph[vect[1]].file_name, "D");
377  EXPECT_EQ(graph[vect[2]].file_name, "A");
378  EXPECT_EQ(graph[vect[3]].file_name, "C");
379}
380
381TEST_F(InplaceGeneratorTest, RunAsRootAssignTempBlocksTest) {
382  Graph graph(9);
383  const vector<Extent> empt;  // empty
384  const string kFilename = "/foo";
385
386  // Some scratch space:
387  GenVertex(&graph[0], empt, VectOfExt(200, 1), "", OP_REPLACE);
388  GenVertex(&graph[1], empt, VectOfExt(210, 10), "", OP_REPLACE);
389  GenVertex(&graph[2], empt, VectOfExt(220, 1), "", OP_REPLACE);
390
391  // A cycle that requires 10 blocks to break:
392  GenVertex(&graph[3], VectOfExt(10, 11), VectOfExt(0, 9), "", OP_BSDIFF);
393  graph[3].out_edges[4] = EdgeWithReadDep(VectOfExt(0, 9));
394  GenVertex(&graph[4], VectOfExt(0, 9), VectOfExt(10, 11), "", OP_BSDIFF);
395  graph[4].out_edges[3] = EdgeWithReadDep(VectOfExt(10, 11));
396
397  // A cycle that requires 9 blocks to break:
398  GenVertex(&graph[5], VectOfExt(40, 11), VectOfExt(30, 10), "", OP_BSDIFF);
399  graph[5].out_edges[6] = EdgeWithReadDep(VectOfExt(30, 10));
400  GenVertex(&graph[6], VectOfExt(30, 10), VectOfExt(40, 11), "", OP_BSDIFF);
401  graph[6].out_edges[5] = EdgeWithReadDep(VectOfExt(40, 11));
402
403  // A cycle that requires 40 blocks to break (which is too many):
404  GenVertex(&graph[7],
405            VectOfExt(120, 50),
406            VectOfExt(60, 40),
407            "",
408            OP_BSDIFF);
409  graph[7].out_edges[8] = EdgeWithReadDep(VectOfExt(60, 40));
410  GenVertex(&graph[8],
411            VectOfExt(60, 40),
412            VectOfExt(120, 50),
413            kFilename,
414            OP_BSDIFF);
415  graph[8].out_edges[7] = EdgeWithReadDep(VectOfExt(120, 50));
416
417  graph_utils::DumpGraph(graph);
418
419  vector<Vertex::Index> final_order;
420
421
422  // Prepare the filesystem with the minimum required for this to work
423  string temp_dir;
424  EXPECT_TRUE(utils::MakeTempDirectory("AssignTempBlocksTest.XXXXXX",
425                                       &temp_dir));
426  ScopedDirRemover temp_dir_remover(temp_dir);
427
428  chromeos::Blob temp_data(kBlockSize * 50);
429  test_utils::FillWithData(&temp_data);
430  EXPECT_TRUE(test_utils::WriteFileVector(temp_dir + kFilename, temp_data));
431  ScopedPathUnlinker filename_unlinker(temp_dir + kFilename);
432
433  int fd;
434  EXPECT_TRUE(utils::MakeTempFile("AssignTempBlocksTestData.XXXXXX",
435                                  nullptr,
436                                  &fd));
437  ScopedFdCloser fd_closer(&fd);
438  off_t data_file_size = 0;
439
440  EXPECT_TRUE(InplaceGenerator::ConvertGraphToDag(&graph,
441                                                  temp_dir,
442                                                  "/dev/zero",
443                                                  fd,
444                                                  &data_file_size,
445                                                  &final_order,
446                                                  Vertex::kInvalidIndex));
447
448
449  Graph expected_graph(12);
450  GenVertex(&expected_graph[0], empt, VectOfExt(200, 1), "", OP_REPLACE);
451  GenVertex(&expected_graph[1], empt, VectOfExt(210, 10), "", OP_REPLACE);
452  GenVertex(&expected_graph[2], empt, VectOfExt(220, 1), "", OP_REPLACE);
453  GenVertex(&expected_graph[3],
454            VectOfExt(10, 11),
455            VectOfExt(0, 9),
456            "",
457            OP_BSDIFF);
458  expected_graph[3].out_edges[9] = EdgeWithReadDep(VectOfExt(0, 9));
459  GenVertex(&expected_graph[4],
460            VectOfExt(60, 9),
461            VectOfExt(10, 11),
462            "",
463            OP_BSDIFF);
464  expected_graph[4].out_edges[3] = EdgeWithReadDep(VectOfExt(10, 11));
465  expected_graph[4].out_edges[9] = EdgeWithWriteDep(VectOfExt(60, 9));
466  GenVertex(&expected_graph[5],
467            VectOfExt(40, 11),
468            VectOfExt(30, 10),
469            "",
470            OP_BSDIFF);
471  expected_graph[5].out_edges[10] = EdgeWithReadDep(VectOfExt(30, 10));
472
473  GenVertex(&expected_graph[6],
474            VectOfExt(60, 10),
475            VectOfExt(40, 11),
476            "",
477            OP_BSDIFF);
478  expected_graph[6].out_edges[5] = EdgeWithReadDep(VectOfExt(40, 11));
479  expected_graph[6].out_edges[10] = EdgeWithWriteDep(VectOfExt(60, 10));
480
481  GenVertex(&expected_graph[7],
482            VectOfExt(120, 50),
483            VectOfExt(60, 40),
484            "",
485            OP_BSDIFF);
486  expected_graph[7].out_edges[6] = EdgeWithReadDep(VectOfExt(60, 10));
487
488  GenVertex(&expected_graph[8], empt, VectOfExt(0, 50), "/foo", OP_REPLACE_BZ);
489  expected_graph[8].out_edges[7] = EdgeWithReadDep(VectOfExt(120, 50));
490
491  GenVertex(&expected_graph[9],
492            VectOfExt(0, 9),
493            VectOfExt(60, 9),
494            "",
495            OP_MOVE);
496
497  GenVertex(&expected_graph[10],
498            VectOfExt(30, 10),
499            VectOfExt(60, 10),
500            "",
501            OP_MOVE);
502  expected_graph[10].out_edges[4] = EdgeWithReadDep(VectOfExt(60, 9));
503
504  EXPECT_EQ(12, graph.size());
505  EXPECT_FALSE(graph.back().valid);
506  for (Graph::size_type i = 0; i < graph.size() - 1; i++) {
507    EXPECT_TRUE(graph[i].out_edges == expected_graph[i].out_edges);
508    if (i == 8) {
509      // special case
510    } else {
511      // EXPECT_TRUE(graph[i] == expected_graph[i]) << "i = " << i;
512    }
513  }
514}
515
516TEST_F(InplaceGeneratorTest, CreateScratchNodeTest) {
517  Vertex vertex;
518  InplaceGenerator::CreateScratchNode(12, 34, &vertex);
519  EXPECT_EQ(DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ,
520            vertex.op.type());
521  EXPECT_EQ(0, vertex.op.data_offset());
522  EXPECT_EQ(0, vertex.op.data_length());
523  EXPECT_EQ(1, vertex.op.dst_extents_size());
524  EXPECT_EQ(12, vertex.op.dst_extents(0).start_block());
525  EXPECT_EQ(34, vertex.op.dst_extents(0).num_blocks());
526}
527
528}  // namespace chromeos_update_engine
529