aswinshakil commented on code in PR #7293:
URL: https://github.com/apache/ozone/pull/7293#discussion_r1847116700


##########
hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/checksum/ContainerMerkleTreeTestUtils.java:
##########
@@ -128,24 +138,196 @@ public static ContainerProtos.ContainerChecksumInfo 
readChecksumFile(ContainerDa
    * structure is preserved throughout serialization, deserialization, and API 
calls.
    */
   public static ContainerMerkleTree buildTestTree(ConfigurationSource conf) {
-    final long blockID1 = 1;
-    final long blockID2 = 2;
-    final long blockID3 = 3;
-    ContainerProtos.ChunkInfo b1c1 = buildChunk(conf, 0, ByteBuffer.wrap(new 
byte[]{1, 2, 3}));
-    ContainerProtos.ChunkInfo b1c2 = buildChunk(conf, 1, ByteBuffer.wrap(new 
byte[]{4, 5, 6}));
-    ContainerProtos.ChunkInfo b2c1 = buildChunk(conf, 0, ByteBuffer.wrap(new 
byte[]{7, 8, 9}));
-    ContainerProtos.ChunkInfo b2c2 = buildChunk(conf, 1, ByteBuffer.wrap(new 
byte[]{12, 11, 10}));
-    ContainerProtos.ChunkInfo b3c1 = buildChunk(conf, 0, ByteBuffer.wrap(new 
byte[]{13, 14, 15}));
-    ContainerProtos.ChunkInfo b3c2 = buildChunk(conf, 1, ByteBuffer.wrap(new 
byte[]{16, 17, 18}));
-
     ContainerMerkleTree tree = new ContainerMerkleTree();
-    tree.addChunks(blockID1, Arrays.asList(b1c1, b1c2));
-    tree.addChunks(blockID2, Arrays.asList(b2c1, b2c2));
-    tree.addChunks(blockID3, Arrays.asList(b3c1, b3c2));
-
+    byte byteValue = 1;
+    for (int blockIndex = 1; blockIndex <= 5; blockIndex++) {
+      List<ContainerProtos.ChunkInfo> chunks = new ArrayList<>();
+      for (int chunkIndex = 0; chunkIndex < 4; chunkIndex++) {
+        chunks.add(buildChunk(conf, chunkIndex, ByteBuffer.wrap(new 
byte[]{byteValue++, byteValue++, byteValue++})));
+      }
+      tree.addChunks(blockIndex, chunks);
+    }
     return tree;
   }
 
+  /**
+   * Returns a Pair of merkle tree and the expected container diff for that 
merkle tree.
+   */
+  public static Pair<ContainerProtos.ContainerMerkleTree, 
ContainerChecksumTreeManager.ContainerDiff>
+      buildTestTreeWithMismatches(ContainerMerkleTree originalTree, int 
numMissingBlocks, int numMissingChunks,
+                                  int numCorruptChunks) {
+
+    ContainerProtos.ContainerMerkleTree.Builder treeBuilder = 
originalTree.toProto().toBuilder();
+    ContainerChecksumTreeManager.ContainerDiff diff = new 
ContainerChecksumTreeManager.ContainerDiff();
+
+    introduceMissingBlocks(treeBuilder, numMissingBlocks, diff);
+    introduceMissingChunks(treeBuilder, numMissingChunks, diff);
+    introduceCorruptChunks(treeBuilder, numCorruptChunks, diff);
+    ContainerProtos.ContainerMerkleTree build = treeBuilder.build();
+    return Pair.of(build, diff);
+  }
+
+  /**
+   * Introduces missing blocks by removing random blocks from the tree.
+   */
+  private static void 
introduceMissingBlocks(ContainerProtos.ContainerMerkleTree.Builder treeBuilder,
+                                             int numMissingBlocks,
+                                             
ContainerChecksumTreeManager.ContainerDiff diff) {
+    // Set to track unique blocks selected for mismatches
+    Set<Integer> selectedBlocks = new HashSet<>();
+    Random random = new Random();
+    for (int i = 0; i < numMissingBlocks; i++) {
+      int randomBlockIndex;
+      do {
+        randomBlockIndex = 
random.nextInt(treeBuilder.getBlockMerkleTreeCount());
+      } while (selectedBlocks.contains(randomBlockIndex));
+      selectedBlocks.add(randomBlockIndex);
+      ContainerProtos.BlockMerkleTree blockMerkleTree = 
treeBuilder.getBlockMerkleTree(randomBlockIndex);
+      diff.addMissingBlock(blockMerkleTree);
+      treeBuilder.removeBlockMerkleTree(randomBlockIndex);
+      treeBuilder.setDataChecksum(random.nextLong());
+    }
+  }
+
+  /**
+   * Introduces missing chunks by removing random chunks from selected blocks.
+   */
+  private static void 
introduceMissingChunks(ContainerProtos.ContainerMerkleTree.Builder treeBuilder,
+                                             int numMissingChunks,
+                                             
ContainerChecksumTreeManager.ContainerDiff diff) {
+    // Set to track unique blocks selected for mismatches
+    Random random = new Random();
+    for (int i = 0; i < numMissingChunks; i++) {
+      int randomBlockIndex = 
random.nextInt(treeBuilder.getBlockMerkleTreeCount());
+
+      // Work on the chosen block to remove a random chunk
+      ContainerProtos.BlockMerkleTree.Builder blockBuilder = 
treeBuilder.getBlockMerkleTreeBuilder(randomBlockIndex);
+      if (blockBuilder.getChunkMerkleTreeCount() > 0) {
+        int randomChunkIndex = 
random.nextInt(blockBuilder.getChunkMerkleTreeCount());
+        ContainerProtos.ChunkMerkleTree chunkMerkleTree = 
blockBuilder.getChunkMerkleTree(randomChunkIndex);
+        diff.addMissingChunk(blockBuilder.getBlockID(), chunkMerkleTree);
+        blockBuilder.removeChunkMerkleTree(randomChunkIndex);
+        blockBuilder.setBlockChecksum(random.nextLong());
+        treeBuilder.setDataChecksum(random.nextLong());
+      }
+    }
+  }
+
+  /**
+   * Introduces corrupt chunks by altering the checksum and setting them as 
unhealthy,
+   * ensuring each chunk in a block is only selected once for corruption.
+   */
+  private static void 
introduceCorruptChunks(ContainerProtos.ContainerMerkleTree.Builder treeBuilder,
+                                             int numCorruptChunks,
+                                             
ContainerChecksumTreeManager.ContainerDiff diff) {
+    Map<Integer, Set<Integer>> corruptedChunksByBlock = new HashMap<>();
+    Random random = new Random();
+
+    for (int i = 0; i < numCorruptChunks; i++) {
+      // Select a random block
+      int randomBlockIndex = 
random.nextInt(treeBuilder.getBlockMerkleTreeCount());
+      ContainerProtos.BlockMerkleTree.Builder blockBuilder = 
treeBuilder.getBlockMerkleTreeBuilder(randomBlockIndex);
+
+      // Ensure each chunk in the block is only corrupted once
+      Set<Integer> corruptedChunks = 
corruptedChunksByBlock.computeIfAbsent(randomBlockIndex, k -> new HashSet<>());
+      if (corruptedChunks.size() < blockBuilder.getChunkMerkleTreeCount()) {
+        int randomChunkIndex;
+        do {
+          randomChunkIndex = 
random.nextInt(blockBuilder.getChunkMerkleTreeCount());
+        } while (corruptedChunks.contains(randomChunkIndex));
+        corruptedChunks.add(randomChunkIndex);
+
+        // Corrupt the selected chunk
+        ContainerProtos.ChunkMerkleTree.Builder chunkBuilder = 
blockBuilder.getChunkMerkleTreeBuilder(randomChunkIndex);
+        diff.addCorruptChunk(blockBuilder.getBlockID(), chunkBuilder.build());
+        chunkBuilder.setChunkChecksum(chunkBuilder.getChunkChecksum() + 
random.nextInt(1000) + 1);
+        chunkBuilder.setIsHealthy(false);
+        blockBuilder.setBlockChecksum(random.nextLong());
+        treeBuilder.setDataChecksum(random.nextLong());
+      }
+    }
+  }
+
+  public static void 
assertContainerDiffMatch(ContainerChecksumTreeManager.ContainerDiff 
expectedDiff,
+      ContainerChecksumTreeManager.ContainerDiff actualDiff) {
+    assertNotNull(expectedDiff, "Expected diff is null");
+    assertNotNull(actualDiff, "Actual diff is null");
+    assertEquals(expectedDiff.getMissingBlocks().size(), 
actualDiff.getMissingBlocks().size(),
+        "Mismatch in number of missing blocks");
+    assertEquals(expectedDiff.getMissingChunks().size(), 
actualDiff.getMissingChunks().size(),
+        "Mismatch in number of missing chunks");
+    assertEquals(expectedDiff.getCorruptChunks().size(), 
actualDiff.getCorruptChunks().size(),
+        "Mismatch in number of corrupt chunks");
+
+    List<ContainerProtos.BlockMerkleTree> expectedMissingBlocks = 
expectedDiff.getMissingBlocks().stream().sorted(
+                
Comparator.comparing(ContainerProtos.BlockMerkleTree::getBlockID)).collect(Collectors.toList());
+    List<ContainerProtos.BlockMerkleTree> actualMissingBlocks = 
expectedDiff.getMissingBlocks().stream().sorted(
+        
Comparator.comparing(ContainerProtos.BlockMerkleTree::getBlockID)).collect(Collectors.toList());
+    for (int i = 0; i < expectedMissingBlocks.size(); i++) {
+      ContainerProtos.BlockMerkleTree expectedBlockMerkleTree = 
expectedMissingBlocks.get(i);
+      ContainerProtos.BlockMerkleTree actualBlockMerkleTree = 
actualMissingBlocks.get(i);
+      assertEquals(expectedBlockMerkleTree.getBlockID(), 
actualBlockMerkleTree.getBlockID());
+      assertEquals(expectedBlockMerkleTree.getChunkMerkleTreeCount(),
+          actualBlockMerkleTree.getChunkMerkleTreeCount());
+      assertEquals(expectedBlockMerkleTree.getBlockChecksum(), 
actualBlockMerkleTree.getBlockChecksum());
+      
assertEqualsChunkMerkleTree(expectedBlockMerkleTree.getChunkMerkleTreeList(),
+          actualBlockMerkleTree.getChunkMerkleTreeList());
+    }
+
+    // Check missing chunks
+    Map<Long, List<ContainerProtos.ChunkMerkleTree>> expectedMissingChunks = 
expectedDiff.getMissingChunks();
+    Map<Long, List<ContainerProtos.ChunkMerkleTree>> actualMissingChunks = 
actualDiff.getMissingChunks();
+
+    for (Map.Entry<Long, List<ContainerProtos.ChunkMerkleTree>> entry : 
expectedMissingChunks.entrySet()) {
+      Long blockId = entry.getKey();
+      List<ContainerProtos.ChunkMerkleTree> expectedChunks = 
entry.getValue().stream().sorted(
+          
Comparator.comparing(ContainerProtos.ChunkMerkleTree::getOffset)).collect(Collectors.toList());
+      List<ContainerProtos.ChunkMerkleTree> actualChunks = 
actualMissingChunks.get(blockId).stream().sorted(
+          
Comparator.comparing(ContainerProtos.ChunkMerkleTree::getOffset)).collect(Collectors.toList());
+
+      assertNotNull(actualChunks, "Missing chunks for block " + blockId + " 
not found in actual diff");
+      assertEquals(expectedChunks.size(), actualChunks.size(),
+          "Mismatch in number of missing chunks for block " + blockId);
+      assertEqualsChunkMerkleTree(expectedChunks, actualChunks);
+      for (int i = 0; i < expectedChunks.size(); i++) {
+        ContainerProtos.ChunkMerkleTree expectedChunk = expectedChunks.get(i);
+        ContainerProtos.ChunkMerkleTree actualChunk = actualChunks.get(i);
+        assertEquals(expectedChunk.getOffset(), actualChunk.getOffset(),
+            "Mismatch in chunk offset for block " + blockId);
+        assertEquals(expectedChunk.getChunkChecksum(), 
actualChunk.getChunkChecksum(),
+            "Mismatch in chunk checksum for block " + blockId);
+      }

Review Comment:
   Nice catch , I missed removing it. I updated it with better log messages. 



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to