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