Write CowReplace blocks in batch

snapuserd is optimizing merge performance, sending blocks in batch will
help.

Test: th
Change-Id: Ie358137768b1f0d7e03530e5f1ddb57e40e183ed
diff --git a/common/cow_operation_convert_unittest.cc b/common/cow_operation_convert_unittest.cc
index 93173fe..d5b522a 100644
--- a/common/cow_operation_convert_unittest.cc
+++ b/common/cow_operation_convert_unittest.cc
@@ -46,7 +46,8 @@
 }
 
 std::ostream& operator<<(std::ostream& out, const CowOperation& c) {
-  out << "{" << c.op << ", " << c.src_block << ", " << c.dst_block << "}";
+  out << "{" << c.op << ", " << c.src_block << ", " << c.dst_block << ", "
+      << c.block_count << "}";
   return out;
 }
 
@@ -63,18 +64,24 @@
     ExtentRanges modified_extents;
     for (auto&& cow_op : cow_ops) {
       if (cow_op.op == CowOperation::CowCopy) {
-        EXPECT_TRUE(src_extent_set.ContainsBlock(cow_op.src_block));
-        // converted operations should be conflict free.
-        EXPECT_FALSE(modified_extents.ContainsBlock(cow_op.src_block))
-            << "SOURCE_COPY operation " << cow_op
-            << " read from a modified block";
+        for (size_t i = 0; i < cow_op.block_count; i++) {
+          ASSERT_TRUE(src_extent_set.ContainsBlock(cow_op.src_block + i));
+          // converted operations should be conflict free.
+          ASSERT_FALSE(modified_extents.ContainsBlock(cow_op.src_block + i))
+              << "SOURCE_COPY operation " << cow_op
+              << " read from a modified block";
+        }
       }
-      EXPECT_TRUE(dst_extent_set.ContainsBlock(cow_op.dst_block));
-      dst_extent_set.SubtractExtent(ExtentForRange(cow_op.dst_block, 1));
-      modified_extents.AddBlock(cow_op.dst_block);
+      for (size_t i = 0; i < cow_op.block_count; i++) {
+        ASSERT_TRUE(dst_extent_set.ContainsBlock(cow_op.dst_block + i));
+      }
+      dst_extent_set.SubtractExtent(
+          ExtentForRange(cow_op.dst_block, cow_op.block_count));
+      modified_extents.AddExtent(
+          ExtentForRange(cow_op.dst_block, cow_op.block_count));
     }
     // The generated CowOps should cover all extents in InstallOps.
-    EXPECT_EQ(dst_extent_set.blocks(), 0UL);
+    ASSERT_EQ(dst_extent_set.blocks(), 0UL);
     // It's possible that src_extent_set is non-empty, because some operations
     // will be converted to CowReplace, and we don't count the source extent for
     // those.
@@ -233,4 +240,61 @@
   VerifyCowMergeOp(cow_ops);
 }
 
+TEST_F(CowOperationConvertTest, CowReplaceCoalesce) {
+  AddOperation(
+      &operations_, InstallOperation::SOURCE_COPY, {{30, 10}}, {{0, 10}});
+
+  AddMergeOperation(
+      &merge_operations_, CowMergeOperation::COW_COPY, {30, 1}, {0, 1});
+  AddMergeOperation(
+      &merge_operations_, CowMergeOperation::COW_COPY, {31, 1}, {1, 1});
+  AddMergeOperation(
+      &merge_operations_, CowMergeOperation::COW_COPY, {32, 1}, {2, 1});
+
+  auto cow_ops = ConvertToCowOperations(operations_, merge_operations_);
+  for (const auto& op : cow_ops) {
+    LOG(INFO) << op;
+  }
+  ASSERT_EQ(cow_ops.size(), 4UL);
+  // Expect 3 COW_COPY and 1 COW_REPLACE
+  ASSERT_EQ(std::count_if(cow_ops.begin(),
+                          cow_ops.end(),
+                          [](auto&& cow_op) {
+                            return cow_op.op == CowOperation::CowCopy;
+                          }),
+            3);
+  ASSERT_EQ(std::count_if(cow_ops.begin(),
+                          cow_ops.end(),
+                          [](auto&& cow_op) {
+                            return cow_op.op == CowOperation::CowReplace;
+                          }),
+            1);
+  VerifyCowMergeOp(cow_ops);
+}
+
+TEST_F(CowOperationConvertTest, CowReplaceDifferenceSrc) {
+  AddOperation(
+      &operations_, InstallOperation::SOURCE_COPY, {{30, 10}}, {{0, 10}});
+  AddOperation(
+      &operations_, InstallOperation::SOURCE_COPY, {{100, 10}}, {{10, 10}});
+
+  auto cow_ops = ConvertToCowOperations(operations_, merge_operations_);
+  for (const auto& op : cow_ops) {
+    LOG(INFO) << op;
+  }
+  ASSERT_EQ(cow_ops.size(), 2UL);
+  // |src_block| matters. Even for ReplaceOperation.
+  // As update_engine still need to know where the data come from.
+  ASSERT_EQ(cow_ops[0].op, CowOperation::CowReplace);
+  ASSERT_EQ(cow_ops[0].src_block, 30UL);
+  ASSERT_EQ(cow_ops[0].dst_block, 0UL);
+  ASSERT_EQ(cow_ops[0].block_count, 10UL);
+
+  ASSERT_EQ(cow_ops[1].op, CowOperation::CowReplace);
+  ASSERT_EQ(cow_ops[1].src_block, 100UL);
+  ASSERT_EQ(cow_ops[1].dst_block, 10UL);
+  ASSERT_EQ(cow_ops[1].block_count, 10UL);
+  VerifyCowMergeOp(cow_ops);
+}
+
 }  // namespace chromeos_update_engine