sunchao commented on code in PR #5935:
URL: https://github.com/apache/datafusion-comet/pull/5935#discussion_r4018560672


##########
spark/src/test/scala/org/apache/comet/CometIcebergWriteDetectionSuite.scala:
##########
@@ -455,16 +455,135 @@ class CometIcebergWriteDetectionSuite extends 
CometTestBase with CometIcebergTes
     }
   }
 
-  test("Compatible for the remaining supported data location schemes") {
+  test("Compatible when the data location scheme is memory") {
     withDetectionCatalog { dir =>
-      Seq("gs", "memory").foreach { scheme =>
-        val table = s"${scheme}_scheme"
-        createTable(
-          dir,
-          table,
-          partitionSpec = "",
-          properties = 
Some(s"'write.data.path'='$scheme://nonexistent/iceberg/db/$table'"))
-        assertSupportLevelIs[Compatible](table, allowWriteFailure = true)
+      createTable(
+        dir,
+        "memory_scheme",
+        partitionSpec = "",
+        properties = 
Some("'write.data.path'='memory://nonexistent/iceberg/db/memory_scheme'"))
+      assertSupportLevelIs[Compatible]("memory_scheme", allowWriteFailure = 
true)
+    }
+  }
+
+  test("fall-back: gs data location under HadoopFileIO (fs.gs.* is not 
forwarded)") {
+    // The hadoop catalog's table.io() is a HadoopFileIO. Planned only, never 
executed: running
+    // the write would have the Hadoop GCS connector look for credentials over 
the network.
+    withDetectionCatalog { dir =>
+      createTable(
+        dir,
+        "gs_hadoop_io",
+        partitionSpec = "",
+        properties = 
Some("'write.data.path'='gs://nonexistent/iceberg/db/gs_hadoop_io'"))
+      assertUnsupportedContains(
+        planInsertWriteExec(s"$catalog.$ns.gs_hadoop_io"),
+        "gs_hadoop_io",
+        "gs://",
+        classOf[HadoopFileIO].getName)
+    }
+  }
+
+  test("gs data location gate decides on the resolved FileIO class") {
+    // Deterministic coverage of every branch; the ResolvingFileIO test below 
depends on which
+    // delegate this classpath yields. GCSFileIO is loaded without 
initialization so the
+    // optional GCS client libraries are never touched.
+    val location = "gs://bucket/iceberg/db/t"
+    val gcsFileIO =
+      Class.forName(IcebergReflection.ClassNames.GCS_FILE_IO, false, 
getClass.getClassLoader)
+    assert(CometIcebergNativeWrite.gcsDataLocationRejection(location, 
Some(gcsFileIO)).isEmpty)
+    val hadoop =
+      CometIcebergNativeWrite.gcsDataLocationRejection(location, 
Some(classOf[HadoopFileIO]))
+    assert(
+      hadoop.exists(r => r.contains("gs://") && 
r.contains(classOf[HadoopFileIO].getName)),
+      hadoop)
+    val unresolved = 
CometIcebergNativeWrite.gcsDataLocationRejection(location, None)
+    assert(unresolved.exists(_.contains("gs://")), unresolved)
+  }
+
+  test("fall-back: gs data location under ResolvingFileIO whose GCSFileIO 
fails to initialize") {
+    // ResolvingFileIO.ioClass maps gs:// to GCSFileIO, but the delegate it 
instantiates is a
+    // HadoopFileIO whenever loading or initializing GCSFileIO throws an 
IllegalArgumentException,
+    // so the gate must judge the instantiated delegate. An unparseable GCS 
chunk size makes
+    // GCSFileIO.initialize throw NumberFormatException where the GCS client 
libraries are
+    // present; where they are absent, loading fails earlier (or, when only 
some of them are
+    // present, construction fails with an error Iceberg does not fall back 
from, and the writer
+    // itself would fail). On every classpath the effective delegate is never 
GCSFileIO and the
+    // write must be declined.
+    withTempIcebergDir { warehouseDir =>
+      val location = "gs://nonexistent/iceberg/db/gs_resolving_bad"
+      val badProperty = "gcs.channel.read.chunk-size-bytes" -> "invalid"
+      val resolving = new ResolvingFileIO()
+      resolving.setConf(new Configuration())
+      resolving.initialize(java.util.Collections.singletonMap(badProperty._1, 
badProperty._2))
+      val delegate =
+        try IcebergReflection.resolveFileIOClass(resolving, location)
+        finally resolving.close()
+      logInfo(s"ResolvingFileIO delegate with $badProperty on this classpath: 
$delegate")
+      assert(delegate.forall(_ == classOf[HadoopFileIO]), delegate)

Review Comment:
   [P2] Make the initialization-failure fixture version-aware
   
   ### Correctness
   
   With Iceberg 1.10.0 or 1.11.0 and the dependencies needed to construct 
GCSFileIO, this assertion fails: `GCSFileIO.initialize` only stores the 
properties and initializes metrics ([1.10 
source](https://github.com/apache/iceberg/blob/2114bf631e49af532d66e2ce148ee49dd1dd1f1f/gcp/src/main/java/org/apache/iceberg/gcp/gcs/GCSFileIO.java#L170-L173),
 [1.11 
source](https://github.com/apache/iceberg/blob/6976e020b894f6a6777704df2b8c4458cb291ae9/gcp/src/main/java/org/apache/iceberg/gcp/gcs/GCSFileIO.java#L164-L167)).
 Parsing the invalid chunk size is deferred until client construction. I 
reproduced this with both real Iceberg runtimes and the exact current 
reflection helper: it returns `Some(GCSFileIO)`, so `delegate.forall(_ == 
classOf[HadoopFileIO])` is false. The test can currently pass through an 
earlier missing-class failure/None instead of the intended initialization 
fallback. Please use a fixture that actually fails during delegate 
initialization on the tested version, or scope this p
 roperty-based case to the versions where it does so and preserve the GCS 
delegate expectation on newer versions.



-- 
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