inplace_generator_unittest.cc revision 5c6c65570013bbdbd67f9bf6391dd295ef5b5ee6
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
32typedef DeltaDiffGenerator::Block 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, SubstituteBlocksTest) {
98  vector<Extent> remove_blocks;
99  AppendExtent(&remove_blocks, 3, 3);
100  AppendExtent(&remove_blocks, 7, 1);
101  vector<Extent> replace_blocks;
102  AppendExtent(&replace_blocks, 10, 2);
103  AppendExtent(&replace_blocks, 13, 2);
104  Vertex vertex;
105  DeltaArchiveManifest_InstallOperation& op = vertex.op;
106  OpAppendExtent(&op, 4, 3);
107  OpAppendExtent(&op, kSparseHole, 4);  // Sparse hole in file
108  OpAppendExtent(&op, 3, 1);
109  OpAppendExtent(&op, 7, 3);
110
111  InplaceGenerator::SubstituteBlocks(&vertex, remove_blocks, replace_blocks);
112
113  EXPECT_EQ(7, op.src_extents_size());
114  EXPECT_EQ(11, op.src_extents(0).start_block());
115  EXPECT_EQ(1, op.src_extents(0).num_blocks());
116  EXPECT_EQ(13, op.src_extents(1).start_block());
117  EXPECT_EQ(1, op.src_extents(1).num_blocks());
118  EXPECT_EQ(6, op.src_extents(2).start_block());
119  EXPECT_EQ(1, op.src_extents(2).num_blocks());
120  EXPECT_EQ(kSparseHole, op.src_extents(3).start_block());
121  EXPECT_EQ(4, op.src_extents(3).num_blocks());
122  EXPECT_EQ(10, op.src_extents(4).start_block());
123  EXPECT_EQ(1, op.src_extents(4).num_blocks());
124  EXPECT_EQ(14, op.src_extents(5).start_block());
125  EXPECT_EQ(1, op.src_extents(5).num_blocks());
126  EXPECT_EQ(8, op.src_extents(6).start_block());
127  EXPECT_EQ(2, op.src_extents(6).num_blocks());
128}
129
130TEST_F(InplaceGeneratorTest, CutEdgesTest) {
131  Graph graph;
132  vector<Block> blocks(9);
133
134  // Create nodes in graph
135  {
136    graph.resize(graph.size() + 1);
137    graph.back().op.set_type(DeltaArchiveManifest_InstallOperation_Type_MOVE);
138    // Reads from blocks 3, 5, 7
139    vector<Extent> extents;
140    AppendBlockToExtents(&extents, 3);
141    AppendBlockToExtents(&extents, 5);
142    AppendBlockToExtents(&extents, 7);
143    DeltaDiffGenerator::StoreExtents(extents,
144                                     graph.back().op.mutable_src_extents());
145    blocks[3].reader = graph.size() - 1;
146    blocks[5].reader = graph.size() - 1;
147    blocks[7].reader = graph.size() - 1;
148
149    // Writes to blocks 1, 2, 4
150    extents.clear();
151    AppendBlockToExtents(&extents, 1);
152    AppendBlockToExtents(&extents, 2);
153    AppendBlockToExtents(&extents, 4);
154    DeltaDiffGenerator::StoreExtents(extents,
155                                     graph.back().op.mutable_dst_extents());
156    blocks[1].writer = graph.size() - 1;
157    blocks[2].writer = graph.size() - 1;
158    blocks[4].writer = graph.size() - 1;
159  }
160  {
161    graph.resize(graph.size() + 1);
162    graph.back().op.set_type(DeltaArchiveManifest_InstallOperation_Type_MOVE);
163    // Reads from blocks 1, 2, 4
164    vector<Extent> extents;
165    AppendBlockToExtents(&extents, 1);
166    AppendBlockToExtents(&extents, 2);
167    AppendBlockToExtents(&extents, 4);
168    DeltaDiffGenerator::StoreExtents(extents,
169                                     graph.back().op.mutable_src_extents());
170    blocks[1].reader = graph.size() - 1;
171    blocks[2].reader = graph.size() - 1;
172    blocks[4].reader = graph.size() - 1;
173
174    // Writes to blocks 3, 5, 6
175    extents.clear();
176    AppendBlockToExtents(&extents, 3);
177    AppendBlockToExtents(&extents, 5);
178    AppendBlockToExtents(&extents, 6);
179    DeltaDiffGenerator::StoreExtents(extents,
180                                     graph.back().op.mutable_dst_extents());
181    blocks[3].writer = graph.size() - 1;
182    blocks[5].writer = graph.size() - 1;
183    blocks[6].writer = graph.size() - 1;
184  }
185
186  // Create edges
187  InplaceGenerator::CreateEdges(&graph, blocks);
188
189  // Find cycles
190  CycleBreaker cycle_breaker;
191  set<Edge> cut_edges;
192  cycle_breaker.BreakCycles(graph, &cut_edges);
193
194  EXPECT_EQ(1, cut_edges.size());
195  EXPECT_TRUE(cut_edges.end() != cut_edges.find(
196      std::pair<Vertex::Index, Vertex::Index>(1, 0)));
197
198  vector<CutEdgeVertexes> cuts;
199  EXPECT_TRUE(InplaceGenerator::CutEdges(&graph, cut_edges, &cuts));
200
201  EXPECT_EQ(3, graph.size());
202
203  // Check new node in graph:
204  EXPECT_EQ(DeltaArchiveManifest_InstallOperation_Type_MOVE,
205            graph.back().op.type());
206  EXPECT_EQ(2, graph.back().op.src_extents_size());
207  EXPECT_EQ(1, graph.back().op.dst_extents_size());
208  EXPECT_EQ(kTempBlockStart, graph.back().op.dst_extents(0).start_block());
209  EXPECT_EQ(2, graph.back().op.dst_extents(0).num_blocks());
210  EXPECT_TRUE(graph.back().out_edges.empty());
211
212  // Check that old node reads from new blocks
213  EXPECT_EQ(2, graph[0].op.src_extents_size());
214  EXPECT_EQ(kTempBlockStart, graph[0].op.src_extents(0).start_block());
215  EXPECT_EQ(2, graph[0].op.src_extents(0).num_blocks());
216  EXPECT_EQ(7, graph[0].op.src_extents(1).start_block());
217  EXPECT_EQ(1, graph[0].op.src_extents(1).num_blocks());
218
219  // And that the old dst extents haven't changed
220  EXPECT_EQ(2, graph[0].op.dst_extents_size());
221  EXPECT_EQ(1, graph[0].op.dst_extents(0).start_block());
222  EXPECT_EQ(2, graph[0].op.dst_extents(0).num_blocks());
223  EXPECT_EQ(4, graph[0].op.dst_extents(1).start_block());
224  EXPECT_EQ(1, graph[0].op.dst_extents(1).num_blocks());
225
226  // Ensure it only depends on the next node and the new temp node
227  EXPECT_EQ(2, graph[0].out_edges.size());
228  EXPECT_TRUE(graph[0].out_edges.end() != graph[0].out_edges.find(1));
229  EXPECT_TRUE(graph[0].out_edges.end() != graph[0].out_edges.find(graph.size() -
230                                                                  1));
231
232  // Check second node has unchanged extents
233  EXPECT_EQ(2, graph[1].op.src_extents_size());
234  EXPECT_EQ(1, graph[1].op.src_extents(0).start_block());
235  EXPECT_EQ(2, graph[1].op.src_extents(0).num_blocks());
236  EXPECT_EQ(4, graph[1].op.src_extents(1).start_block());
237  EXPECT_EQ(1, graph[1].op.src_extents(1).num_blocks());
238
239  EXPECT_EQ(2, graph[1].op.dst_extents_size());
240  EXPECT_EQ(3, graph[1].op.dst_extents(0).start_block());
241  EXPECT_EQ(1, graph[1].op.dst_extents(0).num_blocks());
242  EXPECT_EQ(5, graph[1].op.dst_extents(1).start_block());
243  EXPECT_EQ(2, graph[1].op.dst_extents(1).num_blocks());
244
245  // Ensure it only depends on the next node
246  EXPECT_EQ(1, graph[1].out_edges.size());
247  EXPECT_TRUE(graph[1].out_edges.end() != graph[1].out_edges.find(2));
248}
249
250TEST_F(InplaceGeneratorTest, RunAsRootAssignTempBlocksReuseTest) {
251  // AssignTempBlocks(Graph* graph,
252  // const string& new_root,
253  // int data_fd,
254  // off_t* data_file_size,
255  // vector<Vertex::Index>* op_indexes,
256  // vector<vector<Vertex::Index>::size_type>* reverse_op_indexes,
257  // const vector<CutEdgeVertexes>& cuts
258  Graph graph(9);
259
260  const vector<Extent> empt;
261  uint64_t tmp = kTempBlockStart;
262  const string kFilename = "/foo";
263
264  vector<CutEdgeVertexes> cuts;
265  cuts.resize(3);
266
267  // Simple broken loop:
268  GenVertex(&graph[0], VectOfExt(0, 1), VectOfExt(1, 1), "", OP_MOVE);
269  GenVertex(&graph[1], VectOfExt(tmp, 1), VectOfExt(0, 1), "", OP_MOVE);
270  GenVertex(&graph[2], VectOfExt(1, 1), VectOfExt(tmp, 1), "", OP_MOVE);
271  // Corresponding edges:
272  graph[0].out_edges[2] = EdgeWithReadDep(VectOfExt(1, 1));
273  graph[1].out_edges[2] = EdgeWithWriteDep(VectOfExt(tmp, 1));
274  graph[1].out_edges[0] = EdgeWithReadDep(VectOfExt(0, 1));
275  // Store the cut:
276  cuts[0].old_dst = 1;
277  cuts[0].old_src = 0;
278  cuts[0].new_vertex = 2;
279  cuts[0].tmp_extents = VectOfExt(tmp, 1);
280  tmp++;
281
282  // Slightly more complex pair of loops:
283  GenVertex(&graph[3], VectOfExt(4, 2), VectOfExt(2, 2), "", OP_MOVE);
284  GenVertex(&graph[4], VectOfExt(6, 1), VectOfExt(7, 1), "", OP_MOVE);
285  GenVertex(&graph[5], VectOfExt(tmp, 3), VectOfExt(4, 3), kFilename, OP_MOVE);
286  GenVertex(&graph[6], VectOfExt(2, 2), VectOfExt(tmp, 2), "", OP_MOVE);
287  GenVertex(&graph[7], VectOfExt(7, 1), VectOfExt(tmp + 2, 1), "", OP_MOVE);
288  // Corresponding edges:
289  graph[3].out_edges[6] = EdgeWithReadDep(VectOfExt(2, 2));
290  graph[4].out_edges[7] = EdgeWithReadDep(VectOfExt(7, 1));
291  graph[5].out_edges[6] = EdgeWithWriteDep(VectOfExt(tmp, 2));
292  graph[5].out_edges[7] = EdgeWithWriteDep(VectOfExt(tmp + 2, 1));
293  graph[5].out_edges[3] = EdgeWithReadDep(VectOfExt(4, 2));
294  graph[5].out_edges[4] = EdgeWithReadDep(VectOfExt(6, 1));
295  // Store the cuts:
296  cuts[1].old_dst = 5;
297  cuts[1].old_src = 3;
298  cuts[1].new_vertex = 6;
299  cuts[1].tmp_extents = VectOfExt(tmp, 2);
300  cuts[2].old_dst = 5;
301  cuts[2].old_src = 4;
302  cuts[2].new_vertex = 7;
303  cuts[2].tmp_extents = VectOfExt(tmp + 2, 1);
304
305  // Supplier of temp block:
306  GenVertex(&graph[8], empt, VectOfExt(8, 1), "", OP_REPLACE);
307
308  // Specify the final order:
309  vector<Vertex::Index> op_indexes;
310  op_indexes.push_back(2);
311  op_indexes.push_back(0);
312  op_indexes.push_back(1);
313  op_indexes.push_back(6);
314  op_indexes.push_back(3);
315  op_indexes.push_back(7);
316  op_indexes.push_back(4);
317  op_indexes.push_back(5);
318  op_indexes.push_back(8);
319
320  vector<vector<Vertex::Index>::size_type> reverse_op_indexes;
321  InplaceGenerator::GenerateReverseTopoOrderMap(op_indexes,
322                                                &reverse_op_indexes);
323
324  // Prepare the filesystem with the minimum required for this to work
325  string temp_dir;
326  EXPECT_TRUE(utils::MakeTempDirectory("AssignTempBlocksReuseTest.XXXXXX",
327                                       &temp_dir));
328  ScopedDirRemover temp_dir_remover(temp_dir);
329
330  chromeos::Blob temp_data(kBlockSize * 3);
331  test_utils::FillWithData(&temp_data);
332  EXPECT_TRUE(test_utils::WriteFileVector(temp_dir + kFilename, temp_data));
333  ScopedPathUnlinker filename_unlinker(temp_dir + kFilename);
334
335  int fd;
336  EXPECT_TRUE(utils::MakeTempFile("AssignTempBlocksReuseTest.XXXXXX",
337                                  nullptr,
338                                  &fd));
339  ScopedFdCloser fd_closer(&fd);
340  off_t data_file_size = 0;
341
342  EXPECT_TRUE(InplaceGenerator::AssignTempBlocks(&graph,
343                                                 temp_dir,
344                                                 "/dev/zero",
345                                                 fd,
346                                                 &data_file_size,
347                                                 &op_indexes,
348                                                 &reverse_op_indexes,
349                                                 cuts));
350  EXPECT_FALSE(graph[6].valid);
351  EXPECT_FALSE(graph[7].valid);
352  EXPECT_EQ(1, graph[1].op.src_extents_size());
353  EXPECT_EQ(2, graph[1].op.src_extents(0).start_block());
354  EXPECT_EQ(1, graph[1].op.src_extents(0).num_blocks());
355  EXPECT_EQ(OP_REPLACE_BZ, graph[5].op.type());
356}
357
358TEST_F(InplaceGeneratorTest, MoveFullOpsToBackTest) {
359  Graph graph(4);
360  graph[0].file_name = "A";
361  graph[0].op.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE);
362  graph[1].file_name = "B";
363  graph[1].op.set_type(DeltaArchiveManifest_InstallOperation_Type_BSDIFF);
364  graph[2].file_name = "C";
365  graph[2].op.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ);
366  graph[3].file_name = "D";
367  graph[3].op.set_type(DeltaArchiveManifest_InstallOperation_Type_MOVE);
368
369  vector<Vertex::Index> vect(graph.size());
370
371  for (vector<Vertex::Index>::size_type i = 0; i < vect.size(); ++i) {
372    vect[i] = i;
373  }
374  InplaceGenerator::MoveFullOpsToBack(&graph, &vect);
375  EXPECT_EQ(vect.size(), graph.size());
376  EXPECT_EQ(graph[vect[0]].file_name, "B");
377  EXPECT_EQ(graph[vect[1]].file_name, "D");
378  EXPECT_EQ(graph[vect[2]].file_name, "A");
379  EXPECT_EQ(graph[vect[3]].file_name, "C");
380}
381
382TEST_F(InplaceGeneratorTest, RunAsRootAssignTempBlocksTest) {
383  Graph graph(9);
384  const vector<Extent> empt;  // empty
385  const string kFilename = "/foo";
386
387  // Some scratch space:
388  GenVertex(&graph[0], empt, VectOfExt(200, 1), "", OP_REPLACE);
389  GenVertex(&graph[1], empt, VectOfExt(210, 10), "", OP_REPLACE);
390  GenVertex(&graph[2], empt, VectOfExt(220, 1), "", OP_REPLACE);
391
392  // A cycle that requires 10 blocks to break:
393  GenVertex(&graph[3], VectOfExt(10, 11), VectOfExt(0, 9), "", OP_BSDIFF);
394  graph[3].out_edges[4] = EdgeWithReadDep(VectOfExt(0, 9));
395  GenVertex(&graph[4], VectOfExt(0, 9), VectOfExt(10, 11), "", OP_BSDIFF);
396  graph[4].out_edges[3] = EdgeWithReadDep(VectOfExt(10, 11));
397
398  // A cycle that requires 9 blocks to break:
399  GenVertex(&graph[5], VectOfExt(40, 11), VectOfExt(30, 10), "", OP_BSDIFF);
400  graph[5].out_edges[6] = EdgeWithReadDep(VectOfExt(30, 10));
401  GenVertex(&graph[6], VectOfExt(30, 10), VectOfExt(40, 11), "", OP_BSDIFF);
402  graph[6].out_edges[5] = EdgeWithReadDep(VectOfExt(40, 11));
403
404  // A cycle that requires 40 blocks to break (which is too many):
405  GenVertex(&graph[7],
406            VectOfExt(120, 50),
407            VectOfExt(60, 40),
408            "",
409            OP_BSDIFF);
410  graph[7].out_edges[8] = EdgeWithReadDep(VectOfExt(60, 40));
411  GenVertex(&graph[8],
412            VectOfExt(60, 40),
413            VectOfExt(120, 50),
414            kFilename,
415            OP_BSDIFF);
416  graph[8].out_edges[7] = EdgeWithReadDep(VectOfExt(120, 50));
417
418  graph_utils::DumpGraph(graph);
419
420  vector<Vertex::Index> final_order;
421
422
423  // Prepare the filesystem with the minimum required for this to work
424  string temp_dir;
425  EXPECT_TRUE(utils::MakeTempDirectory("AssignTempBlocksTest.XXXXXX",
426                                       &temp_dir));
427  ScopedDirRemover temp_dir_remover(temp_dir);
428
429  chromeos::Blob temp_data(kBlockSize * 50);
430  test_utils::FillWithData(&temp_data);
431  EXPECT_TRUE(test_utils::WriteFileVector(temp_dir + kFilename, temp_data));
432  ScopedPathUnlinker filename_unlinker(temp_dir + kFilename);
433
434  int fd;
435  EXPECT_TRUE(utils::MakeTempFile("AssignTempBlocksTestData.XXXXXX",
436                                  nullptr,
437                                  &fd));
438  ScopedFdCloser fd_closer(&fd);
439  off_t data_file_size = 0;
440
441  EXPECT_TRUE(InplaceGenerator::ConvertGraphToDag(&graph,
442                                                  temp_dir,
443                                                  "/dev/zero",
444                                                  fd,
445                                                  &data_file_size,
446                                                  &final_order,
447                                                  Vertex::kInvalidIndex));
448
449
450  Graph expected_graph(12);
451  GenVertex(&expected_graph[0], empt, VectOfExt(200, 1), "", OP_REPLACE);
452  GenVertex(&expected_graph[1], empt, VectOfExt(210, 10), "", OP_REPLACE);
453  GenVertex(&expected_graph[2], empt, VectOfExt(220, 1), "", OP_REPLACE);
454  GenVertex(&expected_graph[3],
455            VectOfExt(10, 11),
456            VectOfExt(0, 9),
457            "",
458            OP_BSDIFF);
459  expected_graph[3].out_edges[9] = EdgeWithReadDep(VectOfExt(0, 9));
460  GenVertex(&expected_graph[4],
461            VectOfExt(60, 9),
462            VectOfExt(10, 11),
463            "",
464            OP_BSDIFF);
465  expected_graph[4].out_edges[3] = EdgeWithReadDep(VectOfExt(10, 11));
466  expected_graph[4].out_edges[9] = EdgeWithWriteDep(VectOfExt(60, 9));
467  GenVertex(&expected_graph[5],
468            VectOfExt(40, 11),
469            VectOfExt(30, 10),
470            "",
471            OP_BSDIFF);
472  expected_graph[5].out_edges[10] = EdgeWithReadDep(VectOfExt(30, 10));
473
474  GenVertex(&expected_graph[6],
475            VectOfExt(60, 10),
476            VectOfExt(40, 11),
477            "",
478            OP_BSDIFF);
479  expected_graph[6].out_edges[5] = EdgeWithReadDep(VectOfExt(40, 11));
480  expected_graph[6].out_edges[10] = EdgeWithWriteDep(VectOfExt(60, 10));
481
482  GenVertex(&expected_graph[7],
483            VectOfExt(120, 50),
484            VectOfExt(60, 40),
485            "",
486            OP_BSDIFF);
487  expected_graph[7].out_edges[6] = EdgeWithReadDep(VectOfExt(60, 10));
488
489  GenVertex(&expected_graph[8], empt, VectOfExt(0, 50), "/foo", OP_REPLACE_BZ);
490  expected_graph[8].out_edges[7] = EdgeWithReadDep(VectOfExt(120, 50));
491
492  GenVertex(&expected_graph[9],
493            VectOfExt(0, 9),
494            VectOfExt(60, 9),
495            "",
496            OP_MOVE);
497
498  GenVertex(&expected_graph[10],
499            VectOfExt(30, 10),
500            VectOfExt(60, 10),
501            "",
502            OP_MOVE);
503  expected_graph[10].out_edges[4] = EdgeWithReadDep(VectOfExt(60, 9));
504
505  EXPECT_EQ(12, graph.size());
506  EXPECT_FALSE(graph.back().valid);
507  for (Graph::size_type i = 0; i < graph.size() - 1; i++) {
508    EXPECT_TRUE(graph[i].out_edges == expected_graph[i].out_edges);
509    if (i == 8) {
510      // special case
511    } else {
512      // EXPECT_TRUE(graph[i] == expected_graph[i]) << "i = " << i;
513    }
514  }
515}
516
517TEST_F(InplaceGeneratorTest, CreateScratchNodeTest) {
518  Vertex vertex;
519  InplaceGenerator::CreateScratchNode(12, 34, &vertex);
520  EXPECT_EQ(DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ,
521            vertex.op.type());
522  EXPECT_EQ(0, vertex.op.data_offset());
523  EXPECT_EQ(0, vertex.op.data_length());
524  EXPECT_EQ(1, vertex.op.dst_extents_size());
525  EXPECT_EQ(12, vertex.op.dst_extents(0).start_block());
526  EXPECT_EQ(34, vertex.op.dst_extents(0).num_blocks());
527}
528
529}  // namespace chromeos_update_engine
530