1 //
2 // Copyright (C) 2015 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 "update_engine/payload_generator/ab_generator.h"
18 
19 #include <fcntl.h>
20 #include <sys/stat.h>
21 #include <sys/types.h>
22 
23 #include <random>
24 #include <string>
25 #include <vector>
26 
27 #include <gtest/gtest.h>
28 
29 #include "update_engine/common/hash_calculator.h"
30 #include "update_engine/common/test_utils.h"
31 #include "update_engine/common/utils.h"
32 #include "update_engine/payload_generator/annotated_operation.h"
33 #include "update_engine/payload_generator/bzip.h"
34 #include "update_engine/payload_generator/delta_diff_generator.h"
35 #include "update_engine/payload_generator/extent_ranges.h"
36 #include "update_engine/payload_generator/extent_utils.h"
37 
38 using std::string;
39 using std::vector;
40 
41 namespace chromeos_update_engine {
42 
43 namespace {
44 
45 bool ExtentEquals(const Extent& ext,
46                   uint64_t start_block,
47                   uint64_t num_blocks) {
48   return ext.start_block() == start_block && ext.num_blocks() == num_blocks;
49 }
50 
51 // Tests splitting of a REPLACE/REPLACE_BZ operation.
52 void TestSplitReplaceOrReplaceBzOperation(InstallOperation::Type orig_type,
53                                           bool compressible) {
54   const size_t op_ex1_start_block = 2;
55   const size_t op_ex1_num_blocks = 2;
56   const size_t op_ex2_start_block = 6;
57   const size_t op_ex2_num_blocks = 1;
58   const size_t part_num_blocks = 7;
59 
60   // Create the target partition data.
61   const size_t part_size = part_num_blocks * kBlockSize;
62   brillo::Blob part_data;
63   if (compressible) {
64     part_data.resize(part_size);
65     test_utils::FillWithData(&part_data);
66   } else {
67     std::mt19937 gen(12345);
68     std::uniform_int_distribution<uint8_t> dis(0, 255);
69     for (uint32_t i = 0; i < part_size; i++)
70       part_data.push_back(dis(gen));
71   }
72   ASSERT_EQ(part_size, part_data.size());
73   test_utils::ScopedTempFile part_file(
74       "SplitReplaceOrReplaceBzTest_part.XXXXXX");
75   ASSERT_TRUE(test_utils::WriteFileVector(part_file.path(), part_data));
76 
77   // Create original operation and blob data.
78   const size_t op_ex1_offset = op_ex1_start_block * kBlockSize;
79   const size_t op_ex1_size = op_ex1_num_blocks * kBlockSize;
80   const size_t op_ex2_offset = op_ex2_start_block * kBlockSize;
81   const size_t op_ex2_size = op_ex2_num_blocks * kBlockSize;
82   InstallOperation op;
83   op.set_type(orig_type);
84   *(op.add_dst_extents()) =
85       ExtentForRange(op_ex1_start_block, op_ex1_num_blocks);
86   *(op.add_dst_extents()) =
87       ExtentForRange(op_ex2_start_block, op_ex2_num_blocks);
88 
89   brillo::Blob op_data;
90   op_data.insert(op_data.end(),
91                  part_data.begin() + op_ex1_offset,
92                  part_data.begin() + op_ex1_offset + op_ex1_size);
93   op_data.insert(op_data.end(),
94                  part_data.begin() + op_ex2_offset,
95                  part_data.begin() + op_ex2_offset + op_ex2_size);
96   brillo::Blob op_blob;
97   if (orig_type == InstallOperation::REPLACE) {
98     op_blob = op_data;
99   } else {
100     ASSERT_TRUE(BzipCompress(op_data, &op_blob));
101   }
102   op.set_data_offset(0);
103   op.set_data_length(op_blob.size());
104 
105   AnnotatedOperation aop;
106   aop.op = op;
107   aop.name = "SplitTestOp";
108 
109   // Create the data file.
110   test_utils::ScopedTempFile data_file(
111       "SplitReplaceOrReplaceBzTest_data.XXXXXX");
112   EXPECT_TRUE(test_utils::WriteFileVector(data_file.path(), op_blob));
113   int data_fd = open(data_file.path().c_str(), O_RDWR, 000);
114   EXPECT_GE(data_fd, 0);
115   ScopedFdCloser data_fd_closer(&data_fd);
116   off_t data_file_size = op_blob.size();
117   BlobFileWriter blob_file(data_fd, &data_file_size);
118 
119   // Split the operation.
120   vector<AnnotatedOperation> result_ops;
121   PayloadVersion version(kChromeOSMajorPayloadVersion,
122                          kSourceMinorPayloadVersion);
123   ASSERT_TRUE(ABGenerator::SplitAReplaceOp(
124       version, aop, part_file.path(), &result_ops, &blob_file));
125 
126   // Check the result.
127   InstallOperation::Type expected_type =
128       compressible ? InstallOperation::REPLACE_BZ : InstallOperation::REPLACE;
129 
130   ASSERT_EQ(2U, result_ops.size());
131 
132   EXPECT_EQ("SplitTestOp:0", result_ops[0].name);
133   InstallOperation first_op = result_ops[0].op;
134   EXPECT_EQ(expected_type, first_op.type());
135   EXPECT_FALSE(first_op.has_src_length());
136   EXPECT_FALSE(first_op.has_dst_length());
137   EXPECT_EQ(1, first_op.dst_extents().size());
138   EXPECT_TRUE(ExtentEquals(
139       first_op.dst_extents(0), op_ex1_start_block, op_ex1_num_blocks));
140   // Obtain the expected blob.
141   brillo::Blob first_expected_data(
142       part_data.begin() + op_ex1_offset,
143       part_data.begin() + op_ex1_offset + op_ex1_size);
144   brillo::Blob first_expected_blob;
145   if (compressible) {
146     ASSERT_TRUE(BzipCompress(first_expected_data, &first_expected_blob));
147   } else {
148     first_expected_blob = first_expected_data;
149   }
150   EXPECT_EQ(first_expected_blob.size(), first_op.data_length());
151   // Check that the actual blob matches what's expected.
152   brillo::Blob first_data_blob(first_op.data_length());
153   ssize_t bytes_read;
154   ASSERT_TRUE(utils::PReadAll(data_fd,
155                               first_data_blob.data(),
156                               first_op.data_length(),
157                               first_op.data_offset(),
158                               &bytes_read));
159   ASSERT_EQ(bytes_read, static_cast<ssize_t>(first_op.data_length()));
160   EXPECT_EQ(first_expected_blob, first_data_blob);
161 
162   EXPECT_EQ("SplitTestOp:1", result_ops[1].name);
163   InstallOperation second_op = result_ops[1].op;
164   EXPECT_EQ(expected_type, second_op.type());
165   EXPECT_FALSE(second_op.has_src_length());
166   EXPECT_FALSE(second_op.has_dst_length());
167   EXPECT_EQ(1, second_op.dst_extents().size());
168   EXPECT_TRUE(ExtentEquals(
169       second_op.dst_extents(0), op_ex2_start_block, op_ex2_num_blocks));
170   // Obtain the expected blob.
171   brillo::Blob second_expected_data(
172       part_data.begin() + op_ex2_offset,
173       part_data.begin() + op_ex2_offset + op_ex2_size);
174   brillo::Blob second_expected_blob;
175   if (compressible) {
176     ASSERT_TRUE(BzipCompress(second_expected_data, &second_expected_blob));
177   } else {
178     second_expected_blob = second_expected_data;
179   }
180   EXPECT_EQ(second_expected_blob.size(), second_op.data_length());
181   // Check that the actual blob matches what's expected.
182   brillo::Blob second_data_blob(second_op.data_length());
183   ASSERT_TRUE(utils::PReadAll(data_fd,
184                               second_data_blob.data(),
185                               second_op.data_length(),
186                               second_op.data_offset(),
187                               &bytes_read));
188   ASSERT_EQ(bytes_read, static_cast<ssize_t>(second_op.data_length()));
189   EXPECT_EQ(second_expected_blob, second_data_blob);
190 
191   // Check relative layout of data blobs.
192   EXPECT_EQ(first_op.data_offset() + first_op.data_length(),
193             second_op.data_offset());
194   EXPECT_EQ(second_op.data_offset() + second_op.data_length(),
195             static_cast<uint64_t>(data_file_size));
196   // If we split a REPLACE into multiple ones, ensure reuse of preexisting blob.
197   if (!compressible && orig_type == InstallOperation::REPLACE) {
198     EXPECT_EQ(0U, first_op.data_offset());
199   }
200 }
201 
202 // Tests merging of REPLACE/REPLACE_BZ operations.
203 void TestMergeReplaceOrReplaceBzOperations(InstallOperation::Type orig_type,
204                                            bool compressible) {
205   const size_t first_op_num_blocks = 1;
206   const size_t second_op_num_blocks = 2;
207   const size_t total_op_num_blocks = first_op_num_blocks + second_op_num_blocks;
208   const size_t part_num_blocks = total_op_num_blocks + 2;
209 
210   // Create the target partition data.
211   const size_t part_size = part_num_blocks * kBlockSize;
212   brillo::Blob part_data;
213   if (compressible) {
214     part_data.resize(part_size);
215     test_utils::FillWithData(&part_data);
216   } else {
217     std::mt19937 gen(12345);
218     std::uniform_int_distribution<uint8_t> dis(0, 255);
219     for (uint32_t i = 0; i < part_size; i++)
220       part_data.push_back(dis(gen));
221   }
222   ASSERT_EQ(part_size, part_data.size());
223   test_utils::ScopedTempFile part_file(
224       "MergeReplaceOrReplaceBzTest_part.XXXXXX");
225   ASSERT_TRUE(test_utils::WriteFileVector(part_file.path(), part_data));
226 
227   // Create original operations and blob data.
228   vector<AnnotatedOperation> aops;
229   brillo::Blob blob_data;
230   const size_t total_op_size = total_op_num_blocks * kBlockSize;
231 
232   InstallOperation first_op;
233   first_op.set_type(orig_type);
234   const size_t first_op_size = first_op_num_blocks * kBlockSize;
235   *(first_op.add_dst_extents()) = ExtentForRange(0, first_op_num_blocks);
236   brillo::Blob first_op_data(part_data.begin(),
237                              part_data.begin() + first_op_size);
238   brillo::Blob first_op_blob;
239   if (orig_type == InstallOperation::REPLACE) {
240     first_op_blob = first_op_data;
241   } else {
242     ASSERT_TRUE(BzipCompress(first_op_data, &first_op_blob));
243   }
244   first_op.set_data_offset(0);
245   first_op.set_data_length(first_op_blob.size());
246   blob_data.insert(blob_data.end(), first_op_blob.begin(), first_op_blob.end());
247   AnnotatedOperation first_aop;
248   first_aop.op = first_op;
249   first_aop.name = "first";
250   aops.push_back(first_aop);
251 
252   InstallOperation second_op;
253   second_op.set_type(orig_type);
254   *(second_op.add_dst_extents()) =
255       ExtentForRange(first_op_num_blocks, second_op_num_blocks);
256   brillo::Blob second_op_data(part_data.begin() + first_op_size,
257                               part_data.begin() + total_op_size);
258   brillo::Blob second_op_blob;
259   if (orig_type == InstallOperation::REPLACE) {
260     second_op_blob = second_op_data;
261   } else {
262     ASSERT_TRUE(BzipCompress(second_op_data, &second_op_blob));
263   }
264   second_op.set_data_offset(first_op_blob.size());
265   second_op.set_data_length(second_op_blob.size());
266   blob_data.insert(
267       blob_data.end(), second_op_blob.begin(), second_op_blob.end());
268   AnnotatedOperation second_aop;
269   second_aop.op = second_op;
270   second_aop.name = "second";
271   aops.push_back(second_aop);
272 
273   // Create the data file.
274   test_utils::ScopedTempFile data_file(
275       "MergeReplaceOrReplaceBzTest_data.XXXXXX");
276   EXPECT_TRUE(test_utils::WriteFileVector(data_file.path(), blob_data));
277   int data_fd = open(data_file.path().c_str(), O_RDWR, 000);
278   EXPECT_GE(data_fd, 0);
279   ScopedFdCloser data_fd_closer(&data_fd);
280   off_t data_file_size = blob_data.size();
281   BlobFileWriter blob_file(data_fd, &data_file_size);
282 
283   // Merge the operations.
284   PayloadVersion version(kChromeOSMajorPayloadVersion,
285                          kSourceMinorPayloadVersion);
286   EXPECT_TRUE(ABGenerator::MergeOperations(
287       &aops, version, 5, part_file.path(), &blob_file));
288 
289   // Check the result.
290   InstallOperation::Type expected_op_type =
291       compressible ? InstallOperation::REPLACE_BZ : InstallOperation::REPLACE;
292   EXPECT_EQ(1U, aops.size());
293   InstallOperation new_op = aops[0].op;
294   EXPECT_EQ(expected_op_type, new_op.type());
295   EXPECT_FALSE(new_op.has_src_length());
296   EXPECT_FALSE(new_op.has_dst_length());
297   EXPECT_EQ(1, new_op.dst_extents().size());
298   EXPECT_TRUE(ExtentEquals(new_op.dst_extents(0), 0, total_op_num_blocks));
299   EXPECT_EQ("first,second", aops[0].name);
300 
301   // Check to see if the blob pointed to in the new extent has what we expect.
302   brillo::Blob expected_data(part_data.begin(),
303                              part_data.begin() + total_op_size);
304   brillo::Blob expected_blob;
305   if (compressible) {
306     ASSERT_TRUE(BzipCompress(expected_data, &expected_blob));
307   } else {
308     expected_blob = expected_data;
309   }
310   ASSERT_EQ(expected_blob.size(), new_op.data_length());
311   ASSERT_EQ(blob_data.size() + expected_blob.size(),
312             static_cast<size_t>(data_file_size));
313   brillo::Blob new_op_blob(new_op.data_length());
314   ssize_t bytes_read;
315   ASSERT_TRUE(utils::PReadAll(data_fd,
316                               new_op_blob.data(),
317                               new_op.data_length(),
318                               new_op.data_offset(),
319                               &bytes_read));
320   ASSERT_EQ(static_cast<ssize_t>(new_op.data_length()), bytes_read);
321   EXPECT_EQ(expected_blob, new_op_blob);
322 }
323 
324 }  // namespace
325 
326 class ABGeneratorTest : public ::testing::Test {};
327 
328 TEST_F(ABGeneratorTest, SplitSourceCopyTest) {
329   InstallOperation op;
330   op.set_type(InstallOperation::SOURCE_COPY);
331   *(op.add_src_extents()) = ExtentForRange(2, 3);
332   *(op.add_src_extents()) = ExtentForRange(6, 1);
333   *(op.add_src_extents()) = ExtentForRange(8, 4);
334   *(op.add_dst_extents()) = ExtentForRange(10, 2);
335   *(op.add_dst_extents()) = ExtentForRange(14, 3);
336   *(op.add_dst_extents()) = ExtentForRange(18, 3);
337 
338   AnnotatedOperation aop;
339   aop.op = op;
340   aop.name = "SplitSourceCopyTestOp";
341   vector<AnnotatedOperation> result_ops;
342   EXPECT_TRUE(ABGenerator::SplitSourceCopy(aop, &result_ops));
343   EXPECT_EQ(3U, result_ops.size());
344 
345   EXPECT_EQ("SplitSourceCopyTestOp:0", result_ops[0].name);
346   InstallOperation first_op = result_ops[0].op;
347   EXPECT_EQ(InstallOperation::SOURCE_COPY, first_op.type());
348   EXPECT_FALSE(first_op.has_src_length());
349   EXPECT_EQ(1, first_op.src_extents().size());
350   EXPECT_EQ(2U, first_op.src_extents(0).start_block());
351   EXPECT_EQ(2U, first_op.src_extents(0).num_blocks());
352   EXPECT_FALSE(first_op.has_dst_length());
353   EXPECT_EQ(1, first_op.dst_extents().size());
354   EXPECT_EQ(10U, first_op.dst_extents(0).start_block());
355   EXPECT_EQ(2U, first_op.dst_extents(0).num_blocks());
356 
357   EXPECT_EQ("SplitSourceCopyTestOp:1", result_ops[1].name);
358   InstallOperation second_op = result_ops[1].op;
359   EXPECT_EQ(InstallOperation::SOURCE_COPY, second_op.type());
360   EXPECT_FALSE(second_op.has_src_length());
361   EXPECT_EQ(3, second_op.src_extents().size());
362   EXPECT_EQ(4U, second_op.src_extents(0).start_block());
363   EXPECT_EQ(1U, second_op.src_extents(0).num_blocks());
364   EXPECT_EQ(6U, second_op.src_extents(1).start_block());
365   EXPECT_EQ(1U, second_op.src_extents(1).num_blocks());
366   EXPECT_EQ(8U, second_op.src_extents(2).start_block());
367   EXPECT_EQ(1U, second_op.src_extents(2).num_blocks());
368   EXPECT_FALSE(second_op.has_dst_length());
369   EXPECT_EQ(1, second_op.dst_extents().size());
370   EXPECT_EQ(14U, second_op.dst_extents(0).start_block());
371   EXPECT_EQ(3U, second_op.dst_extents(0).num_blocks());
372 
373   EXPECT_EQ("SplitSourceCopyTestOp:2", result_ops[2].name);
374   InstallOperation third_op = result_ops[2].op;
375   EXPECT_EQ(InstallOperation::SOURCE_COPY, third_op.type());
376   EXPECT_FALSE(third_op.has_src_length());
377   EXPECT_EQ(1, third_op.src_extents().size());
378   EXPECT_EQ(9U, third_op.src_extents(0).start_block());
379   EXPECT_EQ(3U, third_op.src_extents(0).num_blocks());
380   EXPECT_FALSE(third_op.has_dst_length());
381   EXPECT_EQ(1, third_op.dst_extents().size());
382   EXPECT_EQ(18U, third_op.dst_extents(0).start_block());
383   EXPECT_EQ(3U, third_op.dst_extents(0).num_blocks());
384 }
385 
386 TEST_F(ABGeneratorTest, SplitReplaceTest) {
387   TestSplitReplaceOrReplaceBzOperation(InstallOperation::REPLACE, false);
388 }
389 
390 TEST_F(ABGeneratorTest, SplitReplaceIntoReplaceBzTest) {
391   TestSplitReplaceOrReplaceBzOperation(InstallOperation::REPLACE, true);
392 }
393 
394 TEST_F(ABGeneratorTest, SplitReplaceBzTest) {
395   TestSplitReplaceOrReplaceBzOperation(InstallOperation::REPLACE_BZ, true);
396 }
397 
398 TEST_F(ABGeneratorTest, SplitReplaceBzIntoReplaceTest) {
399   TestSplitReplaceOrReplaceBzOperation(InstallOperation::REPLACE_BZ, false);
400 }
401 
402 TEST_F(ABGeneratorTest, SortOperationsByDestinationTest) {
403   vector<AnnotatedOperation> aops;
404   // One operation with multiple destination extents.
405   InstallOperation first_op;
406   *(first_op.add_dst_extents()) = ExtentForRange(6, 1);
407   *(first_op.add_dst_extents()) = ExtentForRange(10, 2);
408   AnnotatedOperation first_aop;
409   first_aop.op = first_op;
410   first_aop.name = "first";
411   aops.push_back(first_aop);
412 
413   // One with no destination extent. Should end up at the end of the vector.
414   InstallOperation second_op;
415   AnnotatedOperation second_aop;
416   second_aop.op = second_op;
417   second_aop.name = "second";
418   aops.push_back(second_aop);
419 
420   // One with one destination extent.
421   InstallOperation third_op;
422   *(third_op.add_dst_extents()) = ExtentForRange(3, 2);
423   AnnotatedOperation third_aop;
424   third_aop.op = third_op;
425   third_aop.name = "third";
426   aops.push_back(third_aop);
427 
428   ABGenerator::SortOperationsByDestination(&aops);
429   EXPECT_EQ(3U, aops.size());
430   EXPECT_EQ(third_aop.name, aops[0].name);
431   EXPECT_EQ(first_aop.name, aops[1].name);
432   EXPECT_EQ(second_aop.name, aops[2].name);
433 }
434 
435 TEST_F(ABGeneratorTest, MergeSourceCopyOperationsTest) {
436   vector<AnnotatedOperation> aops;
437   InstallOperation first_op;
438   first_op.set_type(InstallOperation::SOURCE_COPY);
439   *(first_op.add_src_extents()) = ExtentForRange(1, 1);
440   *(first_op.add_dst_extents()) = ExtentForRange(6, 1);
441   AnnotatedOperation first_aop;
442   first_aop.op = first_op;
443   first_aop.name = "1";
444   aops.push_back(first_aop);
445 
446   InstallOperation second_op;
447   second_op.set_type(InstallOperation::SOURCE_COPY);
448   *(second_op.add_src_extents()) = ExtentForRange(2, 2);
449   *(second_op.add_src_extents()) = ExtentForRange(8, 2);
450   *(second_op.add_dst_extents()) = ExtentForRange(7, 3);
451   *(second_op.add_dst_extents()) = ExtentForRange(11, 1);
452   AnnotatedOperation second_aop;
453   second_aop.op = second_op;
454   second_aop.name = "2";
455   aops.push_back(second_aop);
456 
457   InstallOperation third_op;
458   third_op.set_type(InstallOperation::SOURCE_COPY);
459   *(third_op.add_src_extents()) = ExtentForRange(11, 1);
460   *(third_op.add_dst_extents()) = ExtentForRange(12, 1);
461   AnnotatedOperation third_aop;
462   third_aop.op = third_op;
463   third_aop.name = "3";
464   aops.push_back(third_aop);
465 
466   BlobFileWriter blob_file(0, nullptr);
467   PayloadVersion version(kChromeOSMajorPayloadVersion,
468                          kSourceMinorPayloadVersion);
469   EXPECT_TRUE(ABGenerator::MergeOperations(&aops, version, 5, "", &blob_file));
470 
471   EXPECT_EQ(1U, aops.size());
472   InstallOperation first_result_op = aops[0].op;
473   EXPECT_EQ(InstallOperation::SOURCE_COPY, first_result_op.type());
474   EXPECT_FALSE(first_result_op.has_src_length());
475   EXPECT_EQ(3, first_result_op.src_extents().size());
476   EXPECT_TRUE(ExtentEquals(first_result_op.src_extents(0), 1, 3));
477   EXPECT_TRUE(ExtentEquals(first_result_op.src_extents(1), 8, 2));
478   EXPECT_TRUE(ExtentEquals(first_result_op.src_extents(2), 11, 1));
479   EXPECT_FALSE(first_result_op.has_dst_length());
480   EXPECT_EQ(2, first_result_op.dst_extents().size());
481   EXPECT_TRUE(ExtentEquals(first_result_op.dst_extents(0), 6, 4));
482   EXPECT_TRUE(ExtentEquals(first_result_op.dst_extents(1), 11, 2));
483   EXPECT_EQ(aops[0].name, "1,2,3");
484 }
485 
486 TEST_F(ABGeneratorTest, MergeReplaceOperationsTest) {
487   TestMergeReplaceOrReplaceBzOperations(InstallOperation::REPLACE, false);
488 }
489 
490 TEST_F(ABGeneratorTest, MergeReplaceOperationsToReplaceBzTest) {
491   TestMergeReplaceOrReplaceBzOperations(InstallOperation::REPLACE, true);
492 }
493 
494 TEST_F(ABGeneratorTest, MergeReplaceBzOperationsTest) {
495   TestMergeReplaceOrReplaceBzOperations(InstallOperation::REPLACE_BZ, true);
496 }
497 
498 TEST_F(ABGeneratorTest, MergeReplaceBzOperationsToReplaceTest) {
499   TestMergeReplaceOrReplaceBzOperations(InstallOperation::REPLACE_BZ, false);
500 }
501 
502 TEST_F(ABGeneratorTest, NoMergeOperationsTest) {
503   // Test to make sure we don't merge operations that shouldn't be merged.
504   vector<AnnotatedOperation> aops;
505   InstallOperation first_op;
506   first_op.set_type(InstallOperation::ZERO);
507   *(first_op.add_dst_extents()) = ExtentForRange(0, 1);
508   AnnotatedOperation first_aop;
509   first_aop.op = first_op;
510   aops.push_back(first_aop);
511 
512   // Should merge with first, except op types don't match...
513   InstallOperation second_op;
514   second_op.set_type(InstallOperation::REPLACE);
515   *(second_op.add_dst_extents()) = ExtentForRange(1, 2);
516   second_op.set_data_length(2 * kBlockSize);
517   AnnotatedOperation second_aop;
518   second_aop.op = second_op;
519   aops.push_back(second_aop);
520 
521   // Should merge with second, except it would exceed chunk size...
522   InstallOperation third_op;
523   third_op.set_type(InstallOperation::REPLACE);
524   *(third_op.add_dst_extents()) = ExtentForRange(3, 3);
525   third_op.set_data_length(3 * kBlockSize);
526   AnnotatedOperation third_aop;
527   third_aop.op = third_op;
528   aops.push_back(third_aop);
529 
530   // Should merge with third, except they aren't contiguous...
531   InstallOperation fourth_op;
532   fourth_op.set_type(InstallOperation::REPLACE);
533   *(fourth_op.add_dst_extents()) = ExtentForRange(7, 2);
534   fourth_op.set_data_length(2 * kBlockSize);
535   AnnotatedOperation fourth_aop;
536   fourth_aop.op = fourth_op;
537   aops.push_back(fourth_aop);
538 
539   BlobFileWriter blob_file(0, nullptr);
540   PayloadVersion version(kChromeOSMajorPayloadVersion,
541                          kSourceMinorPayloadVersion);
542   EXPECT_TRUE(ABGenerator::MergeOperations(&aops, version, 4, "", &blob_file));
543 
544   // No operations were merged, the number of ops is the same.
545   EXPECT_EQ(4U, aops.size());
546 }
547 
548 TEST_F(ABGeneratorTest, AddSourceHashTest) {
549   vector<AnnotatedOperation> aops;
550   InstallOperation first_op;
551   first_op.set_type(InstallOperation::SOURCE_COPY);
552   first_op.set_src_length(kBlockSize);
553   *(first_op.add_src_extents()) = ExtentForRange(0, 1);
554   AnnotatedOperation first_aop;
555   first_aop.op = first_op;
556   aops.push_back(first_aop);
557 
558   InstallOperation second_op;
559   second_op.set_type(InstallOperation::REPLACE);
560   AnnotatedOperation second_aop;
561   second_aop.op = second_op;
562   aops.push_back(second_aop);
563 
564   test_utils::ScopedTempFile src_part_file("AddSourceHashTest_src_part.XXXXXX");
565   brillo::Blob src_data(kBlockSize);
566   test_utils::FillWithData(&src_data);
567   ASSERT_TRUE(test_utils::WriteFileVector(src_part_file.path(), src_data));
568 
569   EXPECT_TRUE(ABGenerator::AddSourceHash(&aops, src_part_file.path()));
570 
571   EXPECT_TRUE(aops[0].op.has_src_sha256_hash());
572   EXPECT_FALSE(aops[1].op.has_src_sha256_hash());
573   brillo::Blob expected_hash;
574   EXPECT_TRUE(HashCalculator::RawHashOfData(src_data, &expected_hash));
575   brillo::Blob result_hash(aops[0].op.src_sha256_hash().begin(),
576                            aops[0].op.src_sha256_hash().end());
577   EXPECT_EQ(expected_hash, result_hash);
578 }
579 
580 }  // namespace chromeos_update_engine
581