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

alamb pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-rs.git


The following commit(s) were added to refs/heads/main by this push:
     new 36f29053a7 Minor: consolidate some parquet_testing tests into 
arrow_reader (#10584)
36f29053a7 is described below

commit 36f29053a72044505919e35f3b7c8095a8b31e21
Author: Andrew Lamb <[email protected]>
AuthorDate: Fri Aug 7 10:40:52 2026 -0400

    Minor: consolidate some parquet_testing tests into arrow_reader (#10584)
    
    # Which issue does this PR close?
    
    - part of #10540
    
    # Rationale for this change
    
    The arrow_reader tests are over 4000 lines in one module. I would like
    to try and break it up a bit so the tests are easier to understand and
    navigate
    
    I think tests that read example fixtures from parquet_testing is a
    natural thing to consolidate (so among other things we can be sure we
    are testing all the parquet_testing files)
    
    # What changes are included in this PR?
    
    1. Add parquet_testing.rs to the `arrow_reader` test
    2. Move some tests
    
    # Are these changes tested?
    
    Only tests
    
    # Are there any user-facing changes?
    
    No
---
 parquet/src/arrow/arrow_reader/mod.rs         | 167 +---------------------
 parquet/tests/arrow_reader/mod.rs             |   1 +
 parquet/tests/arrow_reader/parquet_testing.rs | 192 ++++++++++++++++++++++++++
 3 files changed, 194 insertions(+), 166 deletions(-)

diff --git a/parquet/src/arrow/arrow_reader/mod.rs 
b/parquet/src/arrow/arrow_reader/mod.rs
index 8d85dc088d..7ab219086f 100644
--- a/parquet/src/arrow/arrow_reader/mod.rs
+++ b/parquet/src/arrow/arrow_reader/mod.rs
@@ -1678,7 +1678,7 @@ pub(crate) mod tests {
         virtual_type::{RowGroupIndex, RowNumber},
     };
     use crate::arrow::{ArrowWriter, ProjectionMask};
-    use crate::basic::{ConvertedType, Encoding, LogicalType, Repetition, Type 
as PhysicalType};
+    use crate::basic::{ConvertedType, Encoding, Repetition, Type as 
PhysicalType};
     use crate::column::reader::decoder::REPETITION_LEVELS_BATCH_SIZE;
     use crate::data_type::{
         BoolType, ByteArray, ByteArrayType, DataType, DoubleType, 
FixedLenByteArray,
@@ -2417,106 +2417,6 @@ pub(crate) mod tests {
         })
     }
 
-    #[test]
-    fn test_int96_from_spark_file_with_provided_schema() {
-        // int96_from_spark.parquet was written based on Spark's microsecond 
timestamps which trade
-        // range for resolution compared to a nanosecond timestamp. We must 
provide a schema with
-        // microsecond resolution for the Parquet reader to interpret these 
values correctly.
-        use arrow_schema::DataType::Timestamp;
-        let test_data = arrow::util::test_util::parquet_test_data();
-        let path = format!("{test_data}/int96_from_spark.parquet");
-        let file = File::open(path).unwrap();
-
-        let supplied_schema = Arc::new(Schema::new(vec![Field::new(
-            "a",
-            Timestamp(TimeUnit::Microsecond, None),
-            true,
-        )]));
-        let options = 
ArrowReaderOptions::new().with_schema(supplied_schema.clone());
-
-        let mut record_reader =
-            ParquetRecordBatchReaderBuilder::try_new_with_options(file, 
options)
-                .unwrap()
-                .build()
-                .unwrap();
-
-        let batch = record_reader.next().unwrap().unwrap();
-        assert_eq!(batch.num_columns(), 1);
-        let column = batch.column(0);
-        assert_eq!(column.data_type(), &Timestamp(TimeUnit::Microsecond, 
None));
-
-        let expected = Arc::new(Int64Array::from(vec![
-            Some(1704141296123456),
-            Some(1704070800000000),
-            Some(253402225200000000),
-            Some(1735599600000000),
-            None,
-            Some(9089380393200000000),
-        ]));
-
-        // arrow-rs relies on the chrono library to convert between timestamps 
and strings, so
-        // instead compare as Int64. The underlying type should be a 
PrimitiveArray of Int64
-        // anyway, so this should be a zero-copy non-modifying cast.
-
-        let binding = arrow_cast::cast(batch.column(0), 
&arrow_schema::DataType::Int64).unwrap();
-        let casted_timestamps = binding.as_primitive::<types::Int64Type>();
-
-        assert_eq!(casted_timestamps.len(), expected.len());
-
-        casted_timestamps
-            .iter()
-            .zip(expected.iter())
-            .for_each(|(lhs, rhs)| {
-                assert_eq!(lhs, rhs);
-            });
-    }
-
-    #[test]
-    fn test_int96_from_spark_file_without_provided_schema() {
-        // int96_from_spark.parquet was written based on Spark's microsecond 
timestamps which trade
-        // range for resolution compared to a nanosecond timestamp. Without a 
provided schema, some
-        // values when read as nanosecond resolution overflow and result in 
garbage values.
-        use arrow_schema::DataType::Timestamp;
-        let test_data = arrow::util::test_util::parquet_test_data();
-        let path = format!("{test_data}/int96_from_spark.parquet");
-        let file = File::open(path).unwrap();
-
-        let mut record_reader = ParquetRecordBatchReaderBuilder::try_new(file)
-            .unwrap()
-            .build()
-            .unwrap();
-
-        let batch = record_reader.next().unwrap().unwrap();
-        assert_eq!(batch.num_columns(), 1);
-        let column = batch.column(0);
-        assert_eq!(column.data_type(), &Timestamp(TimeUnit::Nanosecond, None));
-
-        let expected = Arc::new(Int64Array::from(vec![
-            Some(1704141296123456000),  // Reads as nanosecond fine (note 3 
extra 0s)
-            Some(1704070800000000000),  // Reads as nanosecond fine (note 3 
extra 0s)
-            Some(-4852191831933722624), // Cannot be represented with nanos 
timestamp (year 9999)
-            Some(1735599600000000000),  // Reads as nanosecond fine (note 3 
extra 0s)
-            None,
-            Some(-4864435138808946688), // Cannot be represented with nanos 
timestamp (year 290000)
-        ]));
-
-        // arrow-rs relies on the chrono library to convert between timestamps 
and strings, so
-        // instead compare as Int64. The underlying type should be a 
PrimitiveArray of Int64
-        // anyway, so this should be a zero-copy non-modifying cast.
-
-        let binding = arrow_cast::cast(batch.column(0), 
&arrow_schema::DataType::Int64).unwrap();
-        let casted_timestamps = binding.as_primitive::<types::Int64Type>();
-
-        assert_eq!(casted_timestamps.len(), expected.len());
-
-        casted_timestamps
-            .iter()
-            .zip(expected.iter())
-            .for_each(|(lhs, rhs)| {
-                assert_eq!(lhs, rhs);
-            });
-    }
-
     struct RandUtf8Gen {}
 
     impl RandGen<ByteArrayType> for RandUtf8Gen {
@@ -5408,71 +5308,6 @@ pub(crate) mod tests {
         assert_eq!(r0arr, &a);
     }
 
-    #[test]
-    fn test_map_no_value() {
-        // File schema:
-        // message schema {
-        //   required group my_map (MAP) {
-        //     repeated group key_value {
-        //       required int32 key;
-        //       optional int32 value;
-        //     }
-        //   }
-        //   required group my_map_no_v (MAP) {
-        //     repeated group key_value {
-        //       required int32 key;
-        //     }
-        //   }
-        //   required group my_list (LIST) {
-        //     repeated group list {
-        //       required int32 element;
-        //     }
-        //   }
-        // }
-        let testdata = arrow::util::test_util::parquet_test_data();
-        let path = format!("{testdata}/map_no_value.parquet");
-        let file = File::open(path).unwrap();
-
-        let mut reader = ParquetRecordBatchReaderBuilder::try_new(file)
-            .unwrap()
-            .build()
-            .unwrap();
-        let out = reader.next().unwrap().unwrap();
-        assert_eq!(out.num_rows(), 3);
-        assert_eq!(out.num_columns(), 3);
-        // my_map_no_v and my_list columns should now be equivalent
-        let c0 = out.column(1).as_list::<i32>();
-        let c1 = out.column(2).as_list::<i32>();
-        assert_eq!(c0.len(), c1.len());
-        c0.iter().zip(c1.iter()).for_each(|(l, r)| assert_eq!(l, r));
-    }
-
-    #[test]
-    fn test_read_unknown_logical_type() {
-        let testdata = arrow::util::test_util::parquet_test_data();
-        let path = format!("{testdata}/unknown-logical-type.parquet");
-        let test_file = File::open(path).unwrap();
-
-        let builder = ParquetRecordBatchReaderBuilder::try_new(test_file)
-            .expect("Error creating reader builder");
-
-        let schema = builder.metadata().file_metadata().schema_descr();
-        assert_eq!(
-            schema.column(0).logical_type_ref(),
-            Some(&LogicalType::String)
-        );
-        assert_eq!(
-            schema.column(1).logical_type_ref(),
-            Some(&LogicalType::_Unknown { field_id: 2555 })
-        );
-        assert_eq!(schema.column(1).physical_type(), PhysicalType::BYTE_ARRAY);
-
-        let mut reader = builder.build().unwrap();
-        let out = reader.next().unwrap().unwrap();
-        assert_eq!(out.num_rows(), 3);
-        assert_eq!(out.num_columns(), 2);
-    }
-
     #[test]
     fn test_read_row_numbers() {
         let file = write_parquet_from_iter(vec![(
diff --git a/parquet/tests/arrow_reader/mod.rs 
b/parquet/tests/arrow_reader/mod.rs
index 404bebc05d..c4c15d77a2 100644
--- a/parquet/tests/arrow_reader/mod.rs
+++ b/parquet/tests/arrow_reader/mod.rs
@@ -46,6 +46,7 @@ mod int96_stats_roundtrip;
 mod invalid_utf8;
 mod io;
 mod large_string_overflow;
+mod parquet_testing;
 #[cfg(feature = "async")]
 mod predicate_cache;
 mod row_filter;
diff --git a/parquet/tests/arrow_reader/parquet_testing.rs 
b/parquet/tests/arrow_reader/parquet_testing.rs
new file mode 100644
index 0000000000..daa3ff79d2
--- /dev/null
+++ b/parquet/tests/arrow_reader/parquet_testing.rs
@@ -0,0 +1,192 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+//! Tests with interoperability files in [parquet-testing]
+//!
+//! [parquet-testing]: https://github.com/apache/parquet-testing
+
+use arrow_array::cast::AsArray;
+use arrow_array::{Array, Int64Array, types};
+use arrow_schema::{Field, Schema, TimeUnit};
+use parquet::arrow::arrow_reader::{ArrowReaderOptions, 
ParquetRecordBatchReaderBuilder};
+use parquet::basic::{LogicalType, Type as PhysicalType};
+use std::fs::File;
+use std::sync::Arc;
+
+#[test]
+fn test_int96_from_spark_file_with_provided_schema() {
+    // int96_from_spark.parquet was written based on Spark's microsecond 
timestamps which trade
+    // range for resolution compared to a nanosecond timestamp. We must 
provide a schema with
+    // microsecond resolution for the Parquet reader to interpret these values 
correctly.
+    use arrow_schema::DataType::Timestamp;
+    let test_data = arrow::util::test_util::parquet_test_data();
+    let path = format!("{test_data}/int96_from_spark.parquet");
+    let file = File::open(path).unwrap();
+
+    let supplied_schema = Arc::new(Schema::new(vec![Field::new(
+        "a",
+        Timestamp(TimeUnit::Microsecond, None),
+        true,
+    )]));
+    let options = 
ArrowReaderOptions::new().with_schema(supplied_schema.clone());
+
+    let mut record_reader = 
ParquetRecordBatchReaderBuilder::try_new_with_options(file, options)
+        .unwrap()
+        .build()
+        .unwrap();
+
+    let batch = record_reader.next().unwrap().unwrap();
+    assert_eq!(batch.num_columns(), 1);
+    let column = batch.column(0);
+    assert_eq!(column.data_type(), &Timestamp(TimeUnit::Microsecond, None));
+
+    let expected = Arc::new(Int64Array::from(vec![
+        Some(1704141296123456),
+        Some(1704070800000000),
+        Some(253402225200000000),
+        Some(1735599600000000),
+        None,
+        Some(9089380393200000000),
+    ]));
+
+    // arrow-rs relies on the chrono library to convert between timestamps and 
strings, so
+    // instead compare as Int64. The underlying type should be a 
PrimitiveArray of Int64
+    // anyway, so this should be a zero-copy non-modifying cast.
+
+    let binding = arrow_cast::cast(batch.column(0), 
&arrow_schema::DataType::Int64).unwrap();
+    let casted_timestamps = binding.as_primitive::<types::Int64Type>();
+
+    assert_eq!(casted_timestamps.len(), expected.len());
+
+    casted_timestamps
+        .iter()
+        .zip(expected.iter())
+        .for_each(|(lhs, rhs)| {
+            assert_eq!(lhs, rhs);
+        });
+}
+
+#[test]
+fn test_int96_from_spark_file_without_provided_schema() {
+    // int96_from_spark.parquet was written based on Spark's microsecond 
timestamps which trade
+    // range for resolution compared to a nanosecond timestamp. Without a 
provided schema, some
+    // values when read as nanosecond resolution overflow and result in 
garbage values.
+    use arrow_schema::DataType::Timestamp;
+    let test_data = arrow::util::test_util::parquet_test_data();
+    let path = format!("{test_data}/int96_from_spark.parquet");
+    let file = File::open(path).unwrap();
+
+    let mut record_reader = ParquetRecordBatchReaderBuilder::try_new(file)
+        .unwrap()
+        .build()
+        .unwrap();
+
+    let batch = record_reader.next().unwrap().unwrap();
+    assert_eq!(batch.num_columns(), 1);
+    let column = batch.column(0);
+    assert_eq!(column.data_type(), &Timestamp(TimeUnit::Nanosecond, None));
+
+    let expected = Arc::new(Int64Array::from(vec![
+        Some(1704141296123456000),  // Reads as nanosecond fine (note 3 extra 
0s)
+        Some(1704070800000000000),  // Reads as nanosecond fine (note 3 extra 
0s)
+        Some(-4852191831933722624), // Cannot be represented with nanos 
timestamp (year 9999)
+        Some(1735599600000000000),  // Reads as nanosecond fine (note 3 extra 
0s)
+        None,
+        Some(-4864435138808946688), // Cannot be represented with nanos 
timestamp (year 290000)
+    ]));
+
+    // arrow-rs relies on the chrono library to convert between timestamps and 
strings, so
+    // instead compare as Int64. The underlying type should be a 
PrimitiveArray of Int64
+    // anyway, so this should be a zero-copy non-modifying cast.
+
+    let binding = arrow_cast::cast(batch.column(0), 
&arrow_schema::DataType::Int64).unwrap();
+    let casted_timestamps = binding.as_primitive::<types::Int64Type>();
+
+    assert_eq!(casted_timestamps.len(), expected.len());
+
+    casted_timestamps
+        .iter()
+        .zip(expected.iter())
+        .for_each(|(lhs, rhs)| {
+            assert_eq!(lhs, rhs);
+        });
+}
+
+#[test]
+fn test_map_no_value() {
+    // File schema:
+    // message schema {
+    //   required group my_map (MAP) {
+    //     repeated group key_value {
+    //       required int32 key;
+    //       optional int32 value;
+    //     }
+    //   }
+    //   required group my_map_no_v (MAP) {
+    //     repeated group key_value {
+    //       required int32 key;
+    //     }
+    //   }
+    //   required group my_list (LIST) {
+    //     repeated group list {
+    //       required int32 element;
+    //     }
+    //   }
+    // }
+    let testdata = arrow::util::test_util::parquet_test_data();
+    let path = format!("{testdata}/map_no_value.parquet");
+    let file = File::open(path).unwrap();
+
+    let mut reader = ParquetRecordBatchReaderBuilder::try_new(file)
+        .unwrap()
+        .build()
+        .unwrap();
+    let out = reader.next().unwrap().unwrap();
+    assert_eq!(out.num_rows(), 3);
+    assert_eq!(out.num_columns(), 3);
+    // my_map_no_v and my_list columns should now be equivalent
+    let c0 = out.column(1).as_list::<i32>();
+    let c1 = out.column(2).as_list::<i32>();
+    assert_eq!(c0.len(), c1.len());
+    c0.iter().zip(c1.iter()).for_each(|(l, r)| assert_eq!(l, r));
+}
+
+#[test]
+fn test_read_unknown_logical_type() {
+    let testdata = arrow::util::test_util::parquet_test_data();
+    let path = format!("{testdata}/unknown-logical-type.parquet");
+    let test_file = File::open(path).unwrap();
+
+    let builder =
+        ParquetRecordBatchReaderBuilder::try_new(test_file).expect("Error 
creating reader builder");
+
+    let schema = builder.metadata().file_metadata().schema_descr();
+    assert_eq!(
+        schema.column(0).logical_type_ref(),
+        Some(&LogicalType::String)
+    );
+    assert_eq!(
+        schema.column(1).logical_type_ref(),
+        Some(&LogicalType::_Unknown { field_id: 2555 })
+    );
+    assert_eq!(schema.column(1).physical_type(), PhysicalType::BYTE_ARRAY);
+
+    let mut reader = builder.build().unwrap();
+    let out = reader.next().unwrap().unwrap();
+    assert_eq!(out.num_rows(), 3);
+    assert_eq!(out.num_columns(), 2);
+}

Reply via email to