# 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. test_that("infer_type() for nanoarrow_array works", { skip_if_not_installed("arrow") array <- as_nanoarrow_array(1:5) expect_true( arrow::infer_type(array)$Equals(arrow::int32()) ) }) test_that("infer_nanoarrow_schema() works for arrow objects", { skip_if_not_installed("arrow") int_schema <- infer_nanoarrow_schema(arrow::Array$create(1:10)) expect_true(arrow::as_data_type(int_schema)$Equals(arrow::int32())) int_schema <- infer_nanoarrow_schema(arrow::Scalar$create(1L)) expect_true(arrow::as_data_type(int_schema)$Equals(arrow::int32())) int_schema <- infer_nanoarrow_schema(arrow::ChunkedArray$create(1:10)) expect_true(arrow::as_data_type(int_schema)$Equals(arrow::int32())) int_schema <- infer_nanoarrow_schema(arrow::Expression$scalar(1L)) expect_true(arrow::as_data_type(int_schema)$Equals(arrow::int32())) tbl_schema_expected <- arrow::schema(x = arrow::int32()) tbl_schema <- infer_nanoarrow_schema(arrow::record_batch(x = 1L)) expect_true(arrow::as_schema(tbl_schema)$Equals(tbl_schema_expected)) tbl_schema <- infer_nanoarrow_schema(arrow::arrow_table(x = 1L)) expect_true(arrow::as_schema(tbl_schema)$Equals(tbl_schema_expected)) tbl_schema <- infer_nanoarrow_schema( arrow::RecordBatchReader$create(arrow::record_batch(x = 1L)) ) expect_true(arrow::as_schema(tbl_schema)$Equals(tbl_schema_expected)) skip_if_not(isTRUE(arrow::arrow_info()$capabilities["dataset"])) tbl_schema <- infer_nanoarrow_schema( arrow::InMemoryDataset$create(arrow::record_batch(x = 1L)) ) expect_true(arrow::as_schema(tbl_schema)$Equals(tbl_schema_expected)) tbl_schema <- infer_nanoarrow_schema( arrow::Scanner$create( arrow::InMemoryDataset$create(arrow::record_batch(x = 1L)) ) ) expect_true(arrow::as_schema(tbl_schema)$Equals(tbl_schema_expected)) }) test_that("nanoarrow_array to Array works", { skip_if_not_installed("arrow") int <- arrow::as_arrow_array(as_nanoarrow_array(1:5)) expect_true(int$Equals(arrow::Array$create(1:5))) dbl <- arrow::as_arrow_array(as_nanoarrow_array(1:5, schema = arrow::float64())) expect_true(dbl$Equals(arrow::Array$create(1:5, type = arrow::float64()))) dbl_casted <- arrow::as_arrow_array(as_nanoarrow_array(1:5), type = arrow::float64()) expect_true(dbl_casted$Equals(arrow::Array$create(1:5, type = arrow::float64()))) chr <- arrow::as_arrow_array(as_nanoarrow_array(c("one", "two"))) expect_true(chr$Equals(arrow::Array$create(c("one", "two")))) }) test_that("nanoarrow_array to Array works for child arrays", { skip_if_not_installed("arrow") df <- data.frame(a = 1, b = "two") batch <- as_nanoarrow_array(df) # This type of export is special because batch$children[[2]] has an SEXP # dependency on the original array. When we export it, we reverse that # dependency such that the exported array and the batch->children[1] array # are shells that call nanoarrow_release_sexp on a common object (i.e., sort of like # a shared pointer). array_from_column <- arrow::as_arrow_array(batch$children[[2]]) # The exported array should be valid expect_null(array_from_column$Validate()) # All the nanoarrow pointers should still be valid expect_true(nanoarrow_pointer_is_valid(batch)) expect_true(nanoarrow_pointer_is_valid(batch$children[[1]])) expect_true(nanoarrow_pointer_is_valid(batch$children[[2]])) # Let the exported arrow::Array go out of scope and maximize the # chance that the exported data release callback is called array_from_column <- NULL gc() Sys.sleep(0.1) # All the nanoarrow pointers should *still* be valid even after that # release callback is called expect_true(nanoarrow_pointer_is_valid(batch)) expect_true(nanoarrow_pointer_is_valid(batch$children[[1]])) expect_true(nanoarrow_pointer_is_valid(batch$children[[2]])) # Export one column again but this time let the `batch` go out of scope array_from_column <- arrow::as_arrow_array(batch$children[[1]]) batch <- NULL gc() Sys.sleep(0.1) # The exported array should still be valid expect_null(array_from_column$Validate()) }) test_that("Array to nanoarrow_array works", { skip_if_not_installed("arrow") int <- arrow::Array$create(1:5) int_array <- as_nanoarrow_array(int) expect_s3_class(int_array, "nanoarrow_array") int_schema <- infer_nanoarrow_schema(int_array) expect_s3_class(int_schema, "nanoarrow_schema") expect_true( arrow::as_arrow_array(int_array)$Equals( arrow::Array$create(1:5) ) ) dbl_array <- as_nanoarrow_array(int, schema = arrow::float64()) expect_s3_class(dbl_array, "nanoarrow_array") dbl_schema <- infer_nanoarrow_schema(dbl_array) expect_s3_class(dbl_schema, "nanoarrow_schema") expect_true( arrow::as_arrow_array(dbl_array)$Equals( arrow::Array$create(1:5, type = arrow::float64()) ) ) }) test_that("nanoarrow_array to ChunkedArray works", { skip_if_not_installed("arrow") int <- arrow::as_chunked_array(as_nanoarrow_array(1:5)) expect_true(int$Equals(arrow::ChunkedArray$create(1:5))) dbl_casted <- arrow::as_chunked_array(as_nanoarrow_array(1:5), type = arrow::float64()) expect_true(dbl_casted$Equals(arrow::ChunkedArray$create(1:5, type = arrow::float64()))) }) test_that("ChunkedArray to nanoarrow_array works", { skip_if_not_installed("arrow") int <- arrow::ChunkedArray$create(1:5) int_array <- as_nanoarrow_array(int) expect_s3_class(int_array, "nanoarrow_array") int_schema <- infer_nanoarrow_schema(int_array) expect_s3_class(int_schema, "nanoarrow_schema") expect_true( arrow::as_chunked_array(int_array)$Equals( arrow::ChunkedArray$create(1:5) ) ) dbl_array <- as_nanoarrow_array(int, schema = arrow::float64()) expect_s3_class(dbl_array, "nanoarrow_array") dbl_schema <- infer_nanoarrow_schema(dbl_array) expect_s3_class(dbl_schema, "nanoarrow_schema") expect_true( arrow::as_chunked_array(dbl_array)$Equals( arrow::ChunkedArray$create(1:5, type = arrow::float64()) ) ) }) test_that("ChunkedArray to nanoarrow_array_stream works", { skip_if_not_installed("arrow") int <- arrow::ChunkedArray$create(1:5) int_array_stream <- as_nanoarrow_array_stream(int) expect_s3_class(int_array_stream, "nanoarrow_array_stream") expect_true( arrow::as_chunked_array(int_array_stream)$Equals( arrow::ChunkedArray$create(1:5) ) ) dbl_array_stream <- as_nanoarrow_array_stream(int, schema = arrow::float64()) expect_s3_class(dbl_array_stream, "nanoarrow_array_stream") expect_true( arrow::as_chunked_array(dbl_array_stream)$Equals( arrow::ChunkedArray$create(1:5, type = arrow::float64()) ) ) }) test_that("Array to nanoarrow_array_stream works", { skip_if_not_installed("arrow") int <- arrow::Array$create(1:5) int_array_stream <- as_nanoarrow_array_stream(int) expect_s3_class(int_array_stream, "nanoarrow_array_stream") expect_true( arrow::as_arrow_array(int_array_stream)$Equals( arrow::Array$create(1:5) ) ) dbl_array_stream <- as_nanoarrow_array_stream(int, schema = arrow::float64()) expect_s3_class(dbl_array_stream, "nanoarrow_array_stream") expect_true( arrow::as_arrow_array(dbl_array_stream)$Equals( arrow::Array$create(1:5, type = arrow::float64()) ) ) empty_array_stream <- basic_array_stream(list(), na_int32()) expect_true( arrow::as_arrow_array(empty_array_stream)$Equals( arrow::concat_arrays(type = arrow::int32()) ) ) }) test_that("nanoarrow_array to RecordBatch works", { skip_if_not_installed("arrow") df <- data.frame(a = 1:5, b = letters[1:5]) batch <- arrow::as_record_batch(as_nanoarrow_array(df)) expect_true( batch$Equals(arrow::record_batch(a = 1:5, b = letters[1:5])) ) batch_casted <- arrow::as_record_batch( as_nanoarrow_array(df), schema = arrow::schema(a = arrow::float64(), b = arrow::string()) ) expect_true( batch_casted$Equals( arrow::record_batch(a = as.double(1:5), b = letters[1:5]) ) ) }) test_that("RecordBatch to nanoarrow_array", { skip_if_not_installed("arrow") batch <- arrow::record_batch(a = 1:5, b = letters[1:5]) struct_array <- as_nanoarrow_array(batch) expect_s3_class(struct_array, "nanoarrow_array") struct_schema <- infer_nanoarrow_schema(struct_array) expect_s3_class(struct_schema, "nanoarrow_schema") expect_true( arrow::as_record_batch(struct_array)$Equals( arrow::record_batch(a = 1:5, b = letters[1:5]) ) ) struct_array_casted <- as_nanoarrow_array( batch, schema = arrow::schema(a = arrow::float64(), b = arrow::string()) ) expect_s3_class(struct_array_casted, "nanoarrow_array") struct_schema_casted <- infer_nanoarrow_schema(struct_array_casted) expect_s3_class(struct_schema_casted, "nanoarrow_schema") expect_true( arrow::as_record_batch(struct_array_casted)$Equals( arrow::record_batch(a = as.double(1:5), b = letters[1:5]) ) ) }) test_that("nanoarrow_array to Table works", { skip_if_not_installed("arrow") df <- data.frame(a = 1:5, b = letters[1:5]) table <- arrow::as_arrow_table(as_nanoarrow_array(df)) expect_true( table$Equals(arrow::arrow_table(a = 1:5, b = letters[1:5])) ) table_casted <- arrow::as_arrow_table( as_nanoarrow_array(df), schema = arrow::schema(a = arrow::float64(), b = arrow::string()) ) expect_true( table_casted$Equals( arrow::arrow_table(a = as.double(1:5), b = letters[1:5]) ) ) }) test_that("Table to nanoarrow_array", { skip_if_not_installed("arrow") table <- arrow::arrow_table(a = 1:5, b = letters[1:5]) struct_array <- as_nanoarrow_array(table) expect_s3_class(struct_array, "nanoarrow_array") struct_schema <- infer_nanoarrow_schema(struct_array) expect_s3_class(struct_schema, "nanoarrow_schema") expect_true( arrow::as_arrow_table(struct_array)$Equals( arrow::arrow_table(a = 1:5, b = letters[1:5]) ) ) struct_array_casted <- as_nanoarrow_array( table, schema = arrow::schema(a = arrow::float64(), b = arrow::string()) ) expect_s3_class(struct_array_casted, "nanoarrow_array") struct_schema_casted <- infer_nanoarrow_schema(struct_array_casted) expect_s3_class(struct_schema_casted, "nanoarrow_schema") expect_true( arrow::as_arrow_table(struct_array_casted)$Equals( arrow::arrow_table(a = as.double(1:5), b = letters[1:5]) ) ) }) test_that("Table to nanoarrow_array_stream works", { skip_if_not_installed("arrow") table <- arrow::arrow_table(a = 1:5, b = letters[1:5]) stream <- as_nanoarrow_array_stream(table) expect_s3_class(stream, "nanoarrow_array_stream") expect_true( arrow::as_arrow_table(stream)$Equals( arrow::arrow_table(a = 1:5, b = letters[1:5]) ) ) # Check cast in the stream -> table direction stream <- as_nanoarrow_array_stream(table) expect_true( arrow::as_arrow_table( stream, schema = arrow::schema(a = arrow::float64(), b = arrow::string()) )$Equals( arrow::arrow_table(a = as.double(1:5), b = letters[1:5]) ) ) # Check cast in the table -> stream direction stream_casted <- as_nanoarrow_array_stream( table, schema = arrow::schema(a = arrow::float64(), b = arrow::string()) ) expect_s3_class(stream_casted, "nanoarrow_array_stream") expect_true( arrow::as_arrow_table(stream_casted)$Equals( arrow::arrow_table(a = as.double(1:5), b = letters[1:5]) ) ) }) test_that("Dataset to nanoarrow_array_stream works", { skip_if_not_installed("arrow") skip_if_not(isTRUE(arrow::arrow_info()$capabilities["dataset"])) dataset <- arrow::InMemoryDataset$create(arrow::arrow_table(a = 1:5, b = letters[1:5])) stream <- as_nanoarrow_array_stream(dataset) expect_s3_class(stream, "nanoarrow_array_stream") expect_true( arrow::as_arrow_table(stream)$Equals( arrow::arrow_table(a = 1:5, b = letters[1:5]) ) ) }) test_that("Scanner to nanoarrow_array_stream works", { skip_if_not_installed("arrow") skip_if_not(isTRUE(arrow::arrow_info()$capabilities["dataset"])) dataset <- arrow::InMemoryDataset$create(arrow::arrow_table(a = 1:5, b = letters[1:5])) scanner <- arrow::Scanner$create(dataset) stream <- as_nanoarrow_array_stream(scanner) expect_s3_class(stream, "nanoarrow_array_stream") expect_true( arrow::as_arrow_table(stream)$Equals( arrow::arrow_table(a = 1:5, b = letters[1:5]) ) ) }) test_that("nanoarrow_schema to DataType works", { skip_if_not_installed("arrow") int_schema <- as_nanoarrow_schema(arrow::int32()) arrow_type <- arrow::as_data_type(int_schema) expect_true(arrow_type$Equals(arrow::int32())) }) test_that("DataType to nanoarrow_schema", { skip_if_not_installed("arrow") schema <- as_nanoarrow_schema(arrow::int32()) expect_s3_class(schema, "nanoarrow_schema") expect_true(arrow::as_data_type(schema)$Equals(arrow::int32())) }) test_that("Field to nanoarrow_schema", { skip_if_not_installed("arrow") schema <- as_nanoarrow_schema(arrow::field("name", arrow::int32())) expect_s3_class(schema, "nanoarrow_schema") expect_true(arrow::as_data_type(schema)$Equals(arrow::int32())) }) test_that("nanoarrow_schema to Schema works", { skip_if_not_installed("arrow") struct_schema <- as_nanoarrow_schema( arrow::struct(a = arrow::int32(), b = arrow::string()) ) arrow_schema <- arrow::as_schema(struct_schema) expect_true(arrow_schema$Equals(arrow::schema(a = arrow::int32(), b = arrow::string()))) }) test_that("Schema to nanoarrow_schema", { skip_if_not_installed("arrow") schema <- as_nanoarrow_schema(arrow::schema(name = arrow::int32())) expect_s3_class(schema, "nanoarrow_schema") expect_true(arrow::as_schema(schema)$Equals(arrow::schema(name = arrow::int32()))) }) test_that("nanoarrow_array_stream to RecordBatchReader works", { skip_if_not_installed("arrow") reader <- arrow::as_record_batch_reader( arrow::record_batch(a = 1:5, b = letters[1:5]) ) array_stream <- as_nanoarrow_array_stream(reader) reader_roundtrip <- arrow::as_record_batch_reader(array_stream) expect_false(nanoarrow_pointer_is_valid(array_stream)) expect_true( reader_roundtrip$read_next_batch()$Equals( arrow::record_batch(a = 1:5, b = letters[1:5]) ) ) expect_null(reader_roundtrip$read_next_batch()) }) test_that("RecordBatchReader to nanoarrow_array_stream works", { skip_if_not_installed("arrow") reader <- arrow::as_record_batch_reader( arrow::record_batch(a = 1:5, b = letters[1:5]) ) array_stream <- as_nanoarrow_array_stream(reader) expect_s3_class(array_stream, "nanoarrow_array_stream") reader_roundtrip <- arrow::as_record_batch_reader(array_stream) expect_true( reader_roundtrip$read_next_batch()$Equals( arrow::record_batch(a = 1:5, b = letters[1:5]) ) ) expect_null(reader_roundtrip$read_next_batch()) })