1 //
2 // Copyright (C) 2020 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 <algorithm>
18 #include <vector>
19
20 #include <android-base/file.h>
21 #include <gtest/gtest.h>
22
23 #include "update_engine/common/test_utils.h"
24 #include "update_engine/common/utils.h"
25 #include "update_engine/payload_generator/extent_ranges.h"
26 #include "update_engine/payload_generator/extent_utils.h"
27 #include "update_engine/payload_generator/merge_sequence_generator.h"
28 #include "update_engine/update_metadata.pb.h"
29
30 namespace chromeos_update_engine {
31 using test_utils::GetBuildArtifactsPath;
32
CreateCowMergeOperation(const Extent & src_extent,const Extent & dst_extent)33 CowMergeOperation CreateCowMergeOperation(const Extent& src_extent,
34 const Extent& dst_extent) {
35 return CreateCowMergeOperation(
36 src_extent, dst_extent, CowMergeOperation::COW_COPY);
37 }
38 class MergeSequenceGeneratorTest : public ::testing::Test {
39 protected:
VerifyTransfers(MergeSequenceGenerator * generator,const std::vector<CowMergeOperation> & expected)40 void VerifyTransfers(MergeSequenceGenerator* generator,
41 const std::vector<CowMergeOperation>& expected) {
42 ASSERT_EQ(expected, generator->operations_);
43 }
44
FindDependency(std::vector<CowMergeOperation> transfers,std::map<CowMergeOperation,std::set<CowMergeOperation>> * result)45 void FindDependency(
46 std::vector<CowMergeOperation> transfers,
47 std::map<CowMergeOperation, std::set<CowMergeOperation>>* result) {
48 std::sort(transfers.begin(), transfers.end());
49 *result = MergeSequenceGenerator::FindDependency(transfers);
50 }
51
GenerateSequence(std::vector<CowMergeOperation> transfers)52 void GenerateSequence(std::vector<CowMergeOperation> transfers) {
53 std::sort(transfers.begin(), transfers.end());
54 MergeSequenceGenerator generator(std::move(transfers), "");
55 std::vector<CowMergeOperation> sequence;
56 ASSERT_TRUE(generator.Generate(&sequence));
57 }
58 };
59
TEST_F(MergeSequenceGeneratorTest,Create)60 TEST_F(MergeSequenceGeneratorTest, Create) {
61 std::vector<AnnotatedOperation> aops{{"file1", {}, {}}, {"file2", {}, {}}};
62 aops[0].op.set_type(InstallOperation::SOURCE_COPY);
63 *aops[0].op.add_src_extents() = ExtentForRange(10, 10);
64 *aops[0].op.add_dst_extents() = ExtentForRange(30, 10);
65
66 aops[1].op.set_type(InstallOperation::SOURCE_COPY);
67 *aops[1].op.add_src_extents() = ExtentForRange(20, 10);
68 *aops[1].op.add_dst_extents() = ExtentForRange(40, 10);
69
70 auto generator = MergeSequenceGenerator::Create(aops);
71 ASSERT_TRUE(generator);
72 std::vector<CowMergeOperation> expected = {
73 CreateCowMergeOperation(ExtentForRange(10, 10), ExtentForRange(30, 10)),
74 CreateCowMergeOperation(ExtentForRange(20, 10), ExtentForRange(40, 10))};
75 VerifyTransfers(generator.get(), expected);
76
77 *aops[1].op.add_src_extents() = ExtentForRange(30, 5);
78 *aops[1].op.add_dst_extents() = ExtentForRange(50, 5);
79 generator = MergeSequenceGenerator::Create(aops);
80 ASSERT_FALSE(generator);
81 }
82
TEST_F(MergeSequenceGeneratorTest,Create_SplitSource)83 TEST_F(MergeSequenceGeneratorTest, Create_SplitSource) {
84 InstallOperation op;
85 op.set_type(InstallOperation::SOURCE_COPY);
86 *(op.add_src_extents()) = ExtentForRange(2, 3);
87 *(op.add_src_extents()) = ExtentForRange(6, 1);
88 *(op.add_src_extents()) = ExtentForRange(8, 4);
89 *(op.add_dst_extents()) = ExtentForRange(10, 8);
90
91 AnnotatedOperation aop{"file1", op, {}};
92 auto generator = MergeSequenceGenerator::Create({aop});
93 ASSERT_TRUE(generator);
94 std::vector<CowMergeOperation> expected = {
95 CreateCowMergeOperation(ExtentForRange(2, 3), ExtentForRange(10, 3)),
96 CreateCowMergeOperation(ExtentForRange(6, 1), ExtentForRange(13, 1)),
97 CreateCowMergeOperation(ExtentForRange(8, 4), ExtentForRange(14, 4))};
98 VerifyTransfers(generator.get(), expected);
99 }
100
TEST_F(MergeSequenceGeneratorTest,FindDependency)101 TEST_F(MergeSequenceGeneratorTest, FindDependency) {
102 std::vector<CowMergeOperation> transfers = {
103 CreateCowMergeOperation(ExtentForRange(10, 10), ExtentForRange(15, 10)),
104 CreateCowMergeOperation(ExtentForRange(40, 10), ExtentForRange(50, 10)),
105 };
106
107 std::map<CowMergeOperation, std::set<CowMergeOperation>> merge_after;
108 FindDependency(transfers, &merge_after);
109 ASSERT_EQ(std::set<CowMergeOperation>(), merge_after.at(transfers[0]));
110 ASSERT_EQ(std::set<CowMergeOperation>(), merge_after.at(transfers[1]));
111
112 transfers = {
113 CreateCowMergeOperation(ExtentForRange(10, 10), ExtentForRange(25, 10)),
114 CreateCowMergeOperation(ExtentForRange(24, 5), ExtentForRange(35, 5)),
115 CreateCowMergeOperation(ExtentForRange(30, 10), ExtentForRange(15, 10)),
116 };
117
118 FindDependency(transfers, &merge_after);
119 ASSERT_EQ(std::set<CowMergeOperation>({transfers[2]}),
120 merge_after.at(transfers[0]));
121 ASSERT_EQ(std::set<CowMergeOperation>({transfers[0], transfers[2]}),
122 merge_after.at(transfers[1]));
123 ASSERT_EQ(std::set<CowMergeOperation>({transfers[0], transfers[1]}),
124 merge_after.at(transfers[2]));
125 }
126
TEST_F(MergeSequenceGeneratorTest,FindDependencyEdgeCase)127 TEST_F(MergeSequenceGeneratorTest, FindDependencyEdgeCase) {
128 std::vector<CowMergeOperation> transfers = {
129 CreateCowMergeOperation(ExtentForRange(10, 10), ExtentForRange(15, 10)),
130 CreateCowMergeOperation(ExtentForRange(40, 10), ExtentForRange(50, 10)),
131 CreateCowMergeOperation(ExtentForRange(59, 10), ExtentForRange(60, 10)),
132 };
133
134 std::map<CowMergeOperation, std::set<CowMergeOperation>> merge_after;
135 FindDependency(transfers, &merge_after);
136 ASSERT_EQ(std::set<CowMergeOperation>(), merge_after.at(transfers[0]));
137 ASSERT_EQ(std::set<CowMergeOperation>(), merge_after.at(transfers[1]));
138 ASSERT_EQ(merge_after[transfers[2]].size(), 1U);
139 }
140
TEST_F(MergeSequenceGeneratorTest,FindDependency_ReusedSourceBlocks)141 TEST_F(MergeSequenceGeneratorTest, FindDependency_ReusedSourceBlocks) {
142 std::vector<CowMergeOperation> transfers = {
143 CreateCowMergeOperation(ExtentForRange(5, 10), ExtentForRange(15, 10)),
144 CreateCowMergeOperation(ExtentForRange(6, 5), ExtentForRange(30, 5)),
145 CreateCowMergeOperation(ExtentForRange(50, 5), ExtentForRange(5, 5)),
146 };
147
148 std::map<CowMergeOperation, std::set<CowMergeOperation>> merge_after;
149 FindDependency(transfers, &merge_after);
150 ASSERT_EQ(std::set<CowMergeOperation>({transfers[2]}),
151 merge_after.at(transfers[0]));
152 ASSERT_EQ(std::set<CowMergeOperation>({transfers[2]}),
153 merge_after.at(transfers[1]));
154 }
155
TEST_F(MergeSequenceGeneratorTest,ValidateSequence)156 TEST_F(MergeSequenceGeneratorTest, ValidateSequence) {
157 std::vector<CowMergeOperation> transfers = {
158 CreateCowMergeOperation(ExtentForRange(10, 10), ExtentForRange(15, 10)),
159 CreateCowMergeOperation(ExtentForRange(30, 10), ExtentForRange(40, 10)),
160 };
161
162 // Self overlapping
163 ASSERT_TRUE(MergeSequenceGenerator::ValidateSequence(transfers));
164
165 transfers = {
166 CreateCowMergeOperation(ExtentForRange(30, 10), ExtentForRange(20, 10)),
167 CreateCowMergeOperation(ExtentForRange(15, 10), ExtentForRange(10, 10)),
168 };
169 ASSERT_FALSE(MergeSequenceGenerator::ValidateSequence(transfers));
170 }
171
TEST_F(MergeSequenceGeneratorTest,GenerateSequenceNoCycles)172 TEST_F(MergeSequenceGeneratorTest, GenerateSequenceNoCycles) {
173 std::vector<CowMergeOperation> transfers = {
174 CreateCowMergeOperation(ExtentForRange(10, 10), ExtentForRange(15, 10)),
175 // file3 should merge before file2
176 CreateCowMergeOperation(ExtentForRange(40, 5), ExtentForRange(25, 5)),
177 CreateCowMergeOperation(ExtentForRange(25, 10), ExtentForRange(30, 10)),
178 };
179
180 GenerateSequence(transfers);
181 }
182
TEST_F(MergeSequenceGeneratorTest,GenerateSequenceWithCycles)183 TEST_F(MergeSequenceGeneratorTest, GenerateSequenceWithCycles) {
184 std::vector<CowMergeOperation> transfers = {
185 CreateCowMergeOperation(ExtentForRange(15, 10), ExtentForRange(30, 10)),
186 CreateCowMergeOperation(ExtentForRange(30, 10), ExtentForRange(40, 10)),
187 CreateCowMergeOperation(ExtentForRange(40, 10), ExtentForRange(15, 10)),
188 CreateCowMergeOperation(ExtentForRange(10, 10), ExtentForRange(5, 10)),
189 };
190
191 GenerateSequence(transfers);
192 }
193
TEST_F(MergeSequenceGeneratorTest,GenerateSequenceMultipleCycles)194 TEST_F(MergeSequenceGeneratorTest, GenerateSequenceMultipleCycles) {
195 std::vector<CowMergeOperation> transfers = {
196 // cycle 1
197 CreateCowMergeOperation(ExtentForRange(10, 10), ExtentForRange(25, 10)),
198 CreateCowMergeOperation(ExtentForRange(24, 5), ExtentForRange(35, 5)),
199 CreateCowMergeOperation(ExtentForRange(30, 10), ExtentForRange(15, 10)),
200 // cycle 2
201 CreateCowMergeOperation(ExtentForRange(50, 10), ExtentForRange(60, 10)),
202 CreateCowMergeOperation(ExtentForRange(60, 10), ExtentForRange(70, 10)),
203 CreateCowMergeOperation(ExtentForRange(70, 10), ExtentForRange(50, 10)),
204 };
205
206 GenerateSequence(transfers);
207 }
208
ValidateSplitSequence(const Extent & src_extent,const Extent & dst_extent)209 void ValidateSplitSequence(const Extent& src_extent, const Extent& dst_extent) {
210 std::vector<CowMergeOperation> sequence;
211 SplitSelfOverlapping(src_extent, dst_extent, &sequence);
212 ExtentRanges src_extent_set;
213 src_extent_set.AddExtent(src_extent);
214 ExtentRanges dst_extent_set;
215 dst_extent_set.AddExtent(dst_extent);
216
217 size_t src_block_count = 0;
218 size_t dst_block_count = 0;
219 std::cout << "src_extent: " << src_extent << " dst_extent: " << dst_extent
220 << '\n';
221 for (const auto& merge_op : sequence) {
222 src_extent_set.SubtractExtent(merge_op.src_extent());
223 dst_extent_set.SubtractExtent(merge_op.dst_extent());
224 src_block_count += merge_op.src_extent().num_blocks();
225 dst_block_count += merge_op.dst_extent().num_blocks();
226 std::cout << merge_op.src_extent() << " -> " << merge_op.dst_extent()
227 << '\n';
228 ASSERT_FALSE(ExtentRanges::ExtentsOverlap(merge_op.src_extent(),
229 merge_op.dst_extent()));
230 }
231 std::cout << '\n';
232 // Check that all blocks are covered
233 ASSERT_EQ(src_extent_set.extent_set().size(), 0UL);
234 ASSERT_EQ(dst_extent_set.extent_set().size(), 0UL);
235
236 // Check that the split didn't cover extra blocks
237 ASSERT_EQ(src_block_count, src_extent.num_blocks());
238 ASSERT_EQ(dst_block_count, dst_extent.num_blocks());
239 }
240
TEST_F(MergeSequenceGeneratorTest,SplitSelfOverlappingTest)241 TEST_F(MergeSequenceGeneratorTest, SplitSelfOverlappingTest) {
242 auto a = ExtentForRange(25, 16);
243 auto b = ExtentForRange(30, 16);
244 ValidateSplitSequence(a, b);
245 ValidateSplitSequence(b, a);
246 }
247
TEST_F(MergeSequenceGeneratorTest,GenerateSequenceWithXor)248 TEST_F(MergeSequenceGeneratorTest, GenerateSequenceWithXor) {
249 std::vector<CowMergeOperation> transfers = {
250 // cycle 1
251 CreateCowMergeOperation(ExtentForRange(10, 10),
252 ExtentForRange(25, 10),
253 CowMergeOperation::COW_XOR),
254 CreateCowMergeOperation(ExtentForRange(24, 5), ExtentForRange(35, 5)),
255 CreateCowMergeOperation(ExtentForRange(30, 10),
256 ExtentForRange(15, 10),
257 CowMergeOperation::COW_XOR),
258 // cycle 2
259 CreateCowMergeOperation(ExtentForRange(50, 10), ExtentForRange(60, 10)),
260 CreateCowMergeOperation(ExtentForRange(60, 10),
261 ExtentForRange(70, 10),
262 CowMergeOperation::COW_XOR),
263 CreateCowMergeOperation(ExtentForRange(70, 10), ExtentForRange(50, 10)),
264 };
265
266 GenerateSequence(transfers);
267 }
268
TEST_F(MergeSequenceGeneratorTest,CreateGeneratorWithXor)269 TEST_F(MergeSequenceGeneratorTest, CreateGeneratorWithXor) {
270 std::vector<AnnotatedOperation> aops;
271 auto& aop = aops.emplace_back();
272 aop.op.set_type(InstallOperation::SOURCE_BSDIFF);
273 *aop.op.mutable_src_extents()->Add() = ExtentForRange(10, 5);
274 *aop.op.mutable_dst_extents()->Add() = ExtentForRange(20, 5);
275 auto& xor_map = aop.xor_ops;
276 {
277 // xor_map[i] = i * kBlockSize + 123;
278 auto& op = xor_map.emplace_back();
279 *op.mutable_src_extent() = ExtentForRange(10, 5);
280 *op.mutable_dst_extent() = ExtentForRange(20, 5);
281 op.set_src_offset(123);
282 op.set_type(CowMergeOperation::COW_XOR);
283 }
284 auto generator = MergeSequenceGenerator::Create(aops);
285 ASSERT_NE(generator, nullptr);
286 std::vector<CowMergeOperation> sequence;
287 ASSERT_TRUE(generator->Generate(&sequence));
288 ASSERT_EQ(sequence.size(), 1UL);
289 ASSERT_EQ(sequence[0].src_extent().start_block(), 10UL);
290 ASSERT_EQ(sequence[0].dst_extent().start_block(), 20UL);
291 ASSERT_EQ(sequence[0].src_extent().num_blocks(), 6UL);
292 ASSERT_EQ(sequence[0].dst_extent().num_blocks(), 5UL);
293 ASSERT_EQ(sequence[0].type(), CowMergeOperation::COW_XOR);
294 ASSERT_EQ(sequence[0].src_offset(), 123UL);
295
296 ASSERT_TRUE(generator->ValidateSequence(sequence));
297 }
298
TEST_F(MergeSequenceGeneratorTest,CreateGeneratorWithXorMultipleExtents)299 TEST_F(MergeSequenceGeneratorTest, CreateGeneratorWithXorMultipleExtents) {
300 std::vector<AnnotatedOperation> aops;
301 auto& aop = aops.emplace_back();
302 aop.op.set_type(InstallOperation::SOURCE_BSDIFF);
303 *aop.op.mutable_src_extents()->Add() = ExtentForRange(10, 10);
304 *aop.op.mutable_dst_extents()->Add() = ExtentForRange(30, 5);
305 *aop.op.mutable_dst_extents()->Add() = ExtentForRange(45, 5);
306 auto& xor_map = aop.xor_ops;
307 {
308 // xor_map[i] = i * kBlockSize + 123;
309 auto& op = xor_map.emplace_back();
310 *op.mutable_src_extent() = ExtentForRange(10, 5);
311 *op.mutable_dst_extent() = ExtentForRange(30, 5);
312 op.set_src_offset(123);
313 op.set_type(CowMergeOperation::COW_XOR);
314 }
315 {
316 // xor_map[i] = i * kBlockSize + 123;
317 auto& op = xor_map.emplace_back();
318 *op.mutable_src_extent() = ExtentForRange(15, 5);
319 *op.mutable_dst_extent() = ExtentForRange(45, 5);
320 op.set_src_offset(123);
321 op.set_type(CowMergeOperation::COW_XOR);
322 }
323 auto generator = MergeSequenceGenerator::Create(aops);
324 ASSERT_NE(generator, nullptr);
325 std::vector<CowMergeOperation> sequence;
326 ASSERT_TRUE(generator->Generate(&sequence));
327 ASSERT_EQ(sequence.size(), 2UL);
328 ASSERT_EQ(sequence[0].src_extent().start_block(), 10UL);
329 ASSERT_EQ(sequence[0].dst_extent().start_block(), 30UL);
330 ASSERT_EQ(sequence[0].src_extent().num_blocks(), 6UL);
331 ASSERT_EQ(sequence[0].dst_extent().num_blocks(), 5UL);
332 ASSERT_EQ(sequence[0].type(), CowMergeOperation::COW_XOR);
333 ASSERT_EQ(sequence[0].src_offset(), 123UL);
334
335 ASSERT_EQ(sequence[1].src_extent().start_block(), 15UL);
336 ASSERT_EQ(sequence[1].dst_extent().start_block(), 45UL);
337 ASSERT_EQ(sequence[1].src_extent().num_blocks(), 6UL);
338 ASSERT_EQ(sequence[1].dst_extent().num_blocks(), 5UL);
339 ASSERT_EQ(sequence[1].type(), CowMergeOperation::COW_XOR);
340 ASSERT_EQ(sequence[1].src_offset(), 123UL);
341
342 ASSERT_TRUE(generator->ValidateSequence(sequence));
343 }
344
TEST_F(MergeSequenceGeneratorTest,CreateGeneratorXorAppendBlock)345 TEST_F(MergeSequenceGeneratorTest, CreateGeneratorXorAppendBlock) {
346 std::vector<AnnotatedOperation> aops;
347 auto& aop = aops.emplace_back();
348 aop.op.set_type(InstallOperation::SOURCE_BSDIFF);
349 *aop.op.mutable_src_extents()->Add() = ExtentForRange(10, 10);
350 *aop.op.mutable_dst_extents()->Add() = ExtentForRange(20, 10);
351 auto& xor_map = aop.xor_ops;
352 {
353 auto& op = xor_map.emplace_back();
354 *op.mutable_src_extent() = ExtentForRange(10, 5);
355 *op.mutable_dst_extent() = ExtentForRange(20, 5);
356 op.set_type(CowMergeOperation::COW_XOR);
357 }
358 {
359 auto& op = xor_map.emplace_back();
360 *op.mutable_src_extent() = ExtentForRange(15, 5);
361 *op.mutable_dst_extent() = ExtentForRange(25, 5);
362 op.set_src_offset(123);
363 op.set_type(CowMergeOperation::COW_XOR);
364 }
365 auto generator = MergeSequenceGenerator::Create(aops);
366 ASSERT_NE(generator, nullptr);
367 std::vector<CowMergeOperation> sequence;
368 ASSERT_TRUE(generator->Generate(&sequence));
369 ASSERT_EQ(sequence.size(), 2UL);
370 ASSERT_EQ(sequence[0].src_extent().start_block(), 15UL);
371 ASSERT_EQ(sequence[0].dst_extent().start_block(), 25UL);
372 ASSERT_EQ(sequence[0].src_extent().num_blocks(), 6UL);
373 ASSERT_EQ(sequence[0].dst_extent().num_blocks(), 5UL);
374 ASSERT_EQ(sequence[0].type(), CowMergeOperation::COW_XOR);
375 ASSERT_EQ(sequence[0].src_offset(), 123UL);
376
377 ASSERT_EQ(sequence[1].src_extent().start_block(), 10UL);
378 ASSERT_EQ(sequence[1].dst_extent().start_block(), 20UL);
379 ASSERT_EQ(sequence[1].src_extent().num_blocks(), 5UL);
380 ASSERT_EQ(sequence[1].dst_extent().num_blocks(), 5UL);
381 ASSERT_EQ(sequence[1].type(), CowMergeOperation::COW_XOR);
382
383 ASSERT_TRUE(generator->ValidateSequence(sequence));
384 }
385
TEST_F(MergeSequenceGeneratorTest,CreateGeneratorXorAlreadyPlusOne)386 TEST_F(MergeSequenceGeneratorTest, CreateGeneratorXorAlreadyPlusOne) {
387 std::vector<AnnotatedOperation> aops;
388 auto& aop = aops.emplace_back();
389 aop.op.set_type(InstallOperation::SOURCE_BSDIFF);
390 *aop.op.mutable_src_extents()->Add() = ExtentForRange(10, 10);
391 *aop.op.mutable_dst_extents()->Add() = ExtentForRange(20, 10);
392 auto& xor_map = aop.xor_ops;
393 {
394 auto& op = xor_map.emplace_back();
395 *op.mutable_src_extent() = ExtentForRange(15, 6);
396 *op.mutable_dst_extent() = ExtentForRange(25, 5);
397 op.set_src_offset(123);
398 op.set_type(CowMergeOperation::COW_XOR);
399 }
400 auto generator = MergeSequenceGenerator::Create(aops);
401 ASSERT_NE(generator, nullptr);
402 std::vector<CowMergeOperation> sequence;
403 ASSERT_TRUE(generator->Generate(&sequence));
404 ASSERT_EQ(sequence.size(), 1UL);
405 ASSERT_EQ(sequence[0].src_extent().start_block(), 15UL);
406 ASSERT_EQ(sequence[0].dst_extent().start_block(), 25UL);
407 ASSERT_EQ(sequence[0].src_extent().num_blocks(), 6UL);
408 ASSERT_EQ(sequence[0].dst_extent().num_blocks(), 5UL);
409 ASSERT_EQ(sequence[0].type(), CowMergeOperation::COW_XOR);
410 ASSERT_EQ(sequence[0].src_offset(), 123UL);
411
412 ASSERT_TRUE(generator->ValidateSequence(sequence));
413 }
414
TEST_F(MergeSequenceGeneratorTest,ActualPayloadTest)415 TEST_F(MergeSequenceGeneratorTest, ActualPayloadTest) {
416 auto payload_path =
417 GetBuildArtifactsPath("testdata/cycle_nodes_product_no_xor.bin");
418 ASSERT_FALSE(payload_path.empty());
419 ASSERT_TRUE(utils::FileExists(payload_path.c_str()));
420 PartitionUpdate part;
421 std::string payload;
422 android::base::ReadFileToString(payload_path, &payload);
423 part.ParseFromString(payload);
424 part.set_partition_name("product");
425 std::vector<CowMergeOperation> ops;
426 ops.reserve(part.merge_operations_size());
427 for (const auto& op : part.merge_operations()) {
428 ops.emplace_back(op);
429 }
430 MergeSequenceGenerator generator(ops, part.partition_name());
431 std::vector<CowMergeOperation> sequence;
432 ASSERT_TRUE(generator.Generate(&sequence));
433 }
434
435 } // namespace chromeos_update_engine
436