This is an automated email from the ASF dual-hosted git repository.

voonhous pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hudi.git


The following commit(s) were added to refs/heads/master by this push:
     new 9db33ba33022 test(core): add file format dispatch exhaustiveness 
coverage (#19253)
9db33ba33022 is described below

commit 9db33ba3302245b5bd077a5be0e76c16858f1461
Author: Y Ethan Guo <[email protected]>
AuthorDate: Fri Aug 28 08:50:00 2026 -0700

    test(core): add file format dispatch exhaustiveness coverage (#19253)
    
    Per-HoodieFileFormat dispatch is duplicated across factory overloads and
    utility switches, so a new format can be wired into one dispatch point and
    missed in another. That shipped with Lance (the getLogBlockType arm landed
    16 days after #17731, in #17768) and twice with Vortex (the StoragePathInfo
    reader overload and getLogBlockType, both still pending in #19252). These
    tests sweep HoodieFileFormat.values() instead of a hardcoded list so such
    gaps fail mechanically.
    
    - TestHoodieHadoopIOFactory: 
testGetFileFormatUtilsEntryPointsCoverEveryBaseFileFormat
      drives every format except HOODIE_LOG through both
      HoodieIOFactory#getFileFormatUtils(StoragePath) (the extension if-chain)
      and HoodieHadoopIOFactory#getFileFormatUtils(HoodieFileFormat) (the format
      switch); a case missing from either surfaces as
      UnsupportedOperationException, so a format wired into neither fails too.
      The hardcoded testGetFileFormatUtils gains its missing VORTEX rows and the
      class builds its factory from HoodieTestUtils.getDefaultStorage().
    - TestCommonClientUtils: testGetLogBlockTypeMapsEveryBaseFileFormat pins the
      expected log block type per base file format through a switch with a
      failing default (HFILE -> HFILE_DATA_BLOCK, the rest -> AVRO_DATA_BLOCK).
      VORTEX is excluded with a TODO(#19252) until its case lands; the expected
      VORTEX arm is already in place, so #19252 only drops the exclusion.
    
    Not covered: the HoodieFileReaderFactory overload sweep is owned by
    TestHoodieFileReaderFactory in #19255; shouldWriteNativeLogs is
    format-independent since #19283; HoodieInputFormatUtils (hudi-hadoop-mr)
    remains the unswept dispatch family.
    
    ---------
    
    Co-authored-by: voon <[email protected]>
---
 .../apache/hudi/utils/TestCommonClientUtils.java   | 35 ++++++++++++
 .../hudi/io/hadoop/TestHoodieHadoopIOFactory.java  | 66 +++++++++++++---------
 2 files changed, 74 insertions(+), 27 deletions(-)

diff --git 
a/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/utils/TestCommonClientUtils.java
 
b/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/utils/TestCommonClientUtils.java
index 59429de2b0d1..572fa34f722e 100644
--- 
a/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/utils/TestCommonClientUtils.java
+++ 
b/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/utils/TestCommonClientUtils.java
@@ -20,8 +20,11 @@
 
 package org.apache.hudi.utils;
 
+import org.apache.hudi.common.model.HoodieFileFormat;
 import org.apache.hudi.common.table.HoodieTableConfig;
 import org.apache.hudi.common.table.HoodieTableVersion;
+import org.apache.hudi.common.table.log.block.HoodieLogBlock;
+import org.apache.hudi.common.util.Option;
 import org.apache.hudi.config.HoodieWriteConfig;
 import org.apache.hudi.exception.HoodieNotSupportedException;
 import org.apache.hudi.util.CommonClientUtils;
@@ -29,6 +32,7 @@ import org.apache.hudi.util.CommonClientUtils;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.EnumSource;
 import org.junit.jupiter.params.provider.MethodSource;
 
 import java.util.stream.Stream;
@@ -110,6 +114,37 @@ class TestCommonClientUtils {
     );
   }
 
+  /**
+   * Every base file format needs a case in the getLogBlockType switch; a 
missing one only surfaces as a
+   * HoodieException on the inline log write path (write version below TEN), 
which is how VORTEX shipped (#19252).
+   */
+  // TODO(#19252): drop the VORTEX exclusion once the VORTEX case lands in 
getLogBlockType.
+  @ParameterizedTest
+  @EnumSource(value = HoodieFileFormat.class, mode = EnumSource.Mode.EXCLUDE, 
names = {"HOODIE_LOG", "VORTEX"})
+  void testGetLogBlockTypeMapsEveryBaseFileFormat(HoodieFileFormat format) {
+    HoodieWriteConfig writeConfig = mock(HoodieWriteConfig.class);
+    HoodieTableConfig tableConfig = mock(HoodieTableConfig.class);
+    when(writeConfig.getLogDataBlockFormat()).thenReturn(Option.empty());
+    when(tableConfig.getBaseFileFormat()).thenReturn(format);
+
+    assertEquals(expectedLogBlockType(format), 
CommonClientUtils.getLogBlockType(writeConfig, tableConfig));
+  }
+
+  private static HoodieLogBlock.HoodieLogBlockType 
expectedLogBlockType(HoodieFileFormat format) {
+    switch (format) {
+      case PARQUET:
+      case ORC:
+      case LANCE:
+      case VORTEX:
+        return HoodieLogBlock.HoodieLogBlockType.AVRO_DATA_BLOCK;
+      case HFILE:
+        return HoodieLogBlock.HoodieLogBlockType.HFILE_DATA_BLOCK;
+      default:
+        throw new IllegalArgumentException("No expected log block type for " + 
format
+            + "; add it here and to CommonClientUtils#getLogBlockType");
+    }
+  }
+
   @ParameterizedTest(name = "Table version {0} with write version {1} should 
be valid: {2}")
   @MethodSource("provideValidTableVersionWriteVersionPairs")
   void testValidTableVersionWriteVersionPairs(
diff --git 
a/hudi-hadoop-common/src/test/java/org/apache/hudi/io/hadoop/TestHoodieHadoopIOFactory.java
 
b/hudi-hadoop-common/src/test/java/org/apache/hudi/io/hadoop/TestHoodieHadoopIOFactory.java
index 053d9c41e66a..31b9b74123d7 100644
--- 
a/hudi-hadoop-common/src/test/java/org/apache/hudi/io/hadoop/TestHoodieHadoopIOFactory.java
+++ 
b/hudi-hadoop-common/src/test/java/org/apache/hudi/io/hadoop/TestHoodieHadoopIOFactory.java
@@ -20,50 +20,62 @@
 package org.apache.hudi.io.hadoop;
 
 import org.apache.hudi.common.model.HoodieFileFormat;
+import org.apache.hudi.common.testutils.HoodieTestUtils;
+import org.apache.hudi.common.util.FileFormatUtils;
 import org.apache.hudi.common.util.HFileUtils;
 import org.apache.hudi.common.util.LanceUtils;
 import org.apache.hudi.common.util.OrcUtils;
 import org.apache.hudi.common.util.ParquetUtils;
+import org.apache.hudi.common.util.VortexUtils;
 import org.apache.hudi.core.io.storage.HoodieIOFactory;
-import org.apache.hudi.hadoop.fs.HadoopFSUtils;
 import org.apache.hudi.io.storage.hadoop.HoodieHadoopIOFactory;
-import org.apache.hudi.storage.HoodieStorage;
 import org.apache.hudi.storage.StoragePath;
-import org.apache.hudi.storage.hadoop.HoodieHadoopStorage;
 
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
 
-import java.io.IOException;
-
-import static org.apache.hudi.common.testutils.HoodieTestUtils.DEFAULT_URI;
-import static 
org.apache.hudi.common.testutils.HoodieTestUtils.getDefaultStorageConf;
+import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 /**
  * Tests {@link HoodieHadoopIOFactory}
  */
-public class TestHoodieHadoopIOFactory {
+class TestHoodieHadoopIOFactory {
+  private final HoodieIOFactory ioFactory = new 
HoodieHadoopIOFactory(HoodieTestUtils.getDefaultStorage());
+
   @Test
-  public void testGetFileFormatUtils() throws IOException {
-    try (HoodieStorage storage =
-             new HoodieHadoopStorage(HadoopFSUtils.getFs(DEFAULT_URI, 
getDefaultStorageConf()))) {
-      HoodieIOFactory ioFactory = new HoodieHadoopIOFactory(storage);
-      assertTrue(ioFactory.getFileFormatUtils(new 
StoragePath("file:///a/b.parquet")) instanceof ParquetUtils);
-      assertTrue(ioFactory.getFileFormatUtils(new 
StoragePath("file:///a/b.orc")) instanceof OrcUtils);
-      assertTrue(ioFactory.getFileFormatUtils(new 
StoragePath("file:///a/b.hfile")) instanceof HFileUtils);
-      assertTrue(ioFactory.getFileFormatUtils(new 
StoragePath("file:///a/b.lance")) instanceof LanceUtils);
-      assertThrows(
-          UnsupportedOperationException.class,
-          () -> ioFactory.getFileFormatUtils(new 
StoragePath("file:///a/b.log")));
+  void testGetFileFormatUtils() {
+    assertTrue(ioFactory.getFileFormatUtils(new 
StoragePath("file:///a/b.parquet")) instanceof ParquetUtils);
+    assertTrue(ioFactory.getFileFormatUtils(new 
StoragePath("file:///a/b.orc")) instanceof OrcUtils);
+    assertTrue(ioFactory.getFileFormatUtils(new 
StoragePath("file:///a/b.hfile")) instanceof HFileUtils);
+    assertTrue(ioFactory.getFileFormatUtils(new 
StoragePath("file:///a/b.lance")) instanceof LanceUtils);
+    assertTrue(ioFactory.getFileFormatUtils(new 
StoragePath("file:///a/b.vortex")) instanceof VortexUtils);
+    assertThrows(
+        UnsupportedOperationException.class,
+        () -> ioFactory.getFileFormatUtils(new 
StoragePath("file:///a/b.log")));
+
+    assertTrue(ioFactory.getFileFormatUtils(HoodieFileFormat.PARQUET) 
instanceof ParquetUtils);
+    assertTrue(ioFactory.getFileFormatUtils(HoodieFileFormat.ORC) instanceof 
OrcUtils);
+    assertTrue(ioFactory.getFileFormatUtils(HoodieFileFormat.HFILE) instanceof 
HFileUtils);
+    assertTrue(ioFactory.getFileFormatUtils(HoodieFileFormat.LANCE) instanceof 
LanceUtils);
+    assertTrue(ioFactory.getFileFormatUtils(HoodieFileFormat.VORTEX) 
instanceof VortexUtils);
+    assertThrows(
+        UnsupportedOperationException.class,
+        () -> ioFactory.getFileFormatUtils(HoodieFileFormat.HOODIE_LOG));
+  }
 
-      assertTrue(ioFactory.getFileFormatUtils(HoodieFileFormat.PARQUET) 
instanceof ParquetUtils);
-      assertTrue(ioFactory.getFileFormatUtils(HoodieFileFormat.ORC) instanceof 
OrcUtils);
-      assertTrue(ioFactory.getFileFormatUtils(HoodieFileFormat.HFILE) 
instanceof HFileUtils);
-      assertTrue(ioFactory.getFileFormatUtils(HoodieFileFormat.LANCE) 
instanceof LanceUtils);
-      assertThrows(
-          UnsupportedOperationException.class,
-          () -> ioFactory.getFileFormatUtils(HoodieFileFormat.HOODIE_LOG));
-    }
+  /**
+   * The extension if-chain in {@link 
HoodieIOFactory#getFileFormatUtils(StoragePath)} and the format switch in
+   * {@link HoodieHadoopIOFactory#getFileFormatUtils(HoodieFileFormat)} must 
both cover every base file format;
+   * a case missing from either surfaces here as {@link 
UnsupportedOperationException}.
+   */
+  @ParameterizedTest
+  @EnumSource(value = HoodieFileFormat.class, mode = EnumSource.Mode.EXCLUDE, 
names = {"HOODIE_LOG"})
+  void 
testGetFileFormatUtilsEntryPointsCoverEveryBaseFileFormat(HoodieFileFormat 
format) {
+    FileFormatUtils byFormat = ioFactory.getFileFormatUtils(format);
+    FileFormatUtils byPath = ioFactory.getFileFormatUtils(new 
StoragePath("file:///a/b" + format.getFileExtension()));
+    assertEquals(byFormat.getClass(), byPath.getClass());
   }
 }

Reply via email to