diff --git a/r/NAMESPACE b/r/NAMESPACE index 567353876ca..1dda6e82b4a 100644 --- a/r/NAMESPACE +++ b/r/NAMESPACE @@ -62,6 +62,12 @@ S3method(max,ArrowDatum) S3method(mean,ArrowDatum) S3method(median,ArrowDatum) S3method(min,ArrowDatum) +S3method(na.exclude,ArrowDatum) +S3method(na.exclude,ArrowTabular) +S3method(na.fail,ArrowDatum) +S3method(na.fail,ArrowTabular) +S3method(na.omit,ArrowDatum) +S3method(na.omit,ArrowTabular) S3method(names,Dataset) S3method(names,FeatherReader) S3method(names,RecordBatch) @@ -318,6 +324,10 @@ importFrom(rlang,set_names) importFrom(rlang,syms) importFrom(rlang,warn) importFrom(stats,median) +importFrom(stats,na.exclude) +importFrom(stats,na.fail) +importFrom(stats,na.omit) +importFrom(stats,na.pass) importFrom(stats,quantile) importFrom(tidyselect,contains) importFrom(tidyselect,ends_with) diff --git a/r/R/arrow-datum.R b/r/R/arrow-datum.R index dd43307c9cc..4edcb200ea0 100644 --- a/r/R/arrow-datum.R +++ b/r/R/arrow-datum.R @@ -46,6 +46,22 @@ as.vector.ArrowDatum <- function(x, mode) { ) } +#' @export +na.omit.ArrowDatum <- function(object, ...){ + object$Filter(!is.na(object)) +} + +#' @export +na.exclude.ArrowDatum <- na.omit.ArrowDatum + +#' @export +na.fail.ArrowDatum <- function(object, ...){ + if (object$null_count > 0) { + stop("missing values in object", call. = FALSE) + } + object +} + filter_rows <- function(x, i, keep_na = TRUE, ...) { # General purpose function for [ row subsetting with R semantics # Based on the input for `i`, calls x$Filter, x$Slice, or x$Take diff --git a/r/R/arrow-package.R b/r/R/arrow-package.R index 30d59491d79..51f4987484c 100644 --- a/r/R/arrow-package.R +++ b/r/R/arrow-package.R @@ -15,7 +15,7 @@ # specific language governing permissions and limitations # under the License. -#' @importFrom stats quantile median +#' @importFrom stats quantile median na.omit na.exclude na.pass na.fail #' @importFrom R6 R6Class #' @importFrom purrr as_mapper map map2 map_chr map_dfr map_int map_lgl keep #' @importFrom assertthat assert_that is.string diff --git a/r/R/arrow-tabular.R b/r/R/arrow-tabular.R index f32111688a2..bba5ad5f5e6 100644 --- a/r/R/arrow-tabular.R +++ b/r/R/arrow-tabular.R @@ -211,6 +211,26 @@ head.ArrowTabular <- head.ArrowDatum #' @export tail.ArrowTabular <- tail.ArrowDatum +#' @export +na.fail.ArrowTabular <- function(object, ...){ + for (col in seq_len(object$num_columns)) { + if (object$column(col - 1L)$null_count > 0) { + stop("missing values in object", call. = FALSE) + } + } + object +} + +#' @export +na.omit.ArrowTabular <- function(object, ...){ + not_na <- map(object$columns, ~build_array_expression("is_valid", .x)) + not_na_agg <- Reduce("&", not_na) + object$Filter(eval_array_expression(not_na_agg)) +} + +#' @export +na.exclude.ArrowTabular <- na.omit.ArrowTabular + ToString_tabular <- function(x, ...) { # Generic to work with both RecordBatch and Table sch <- unlist(strsplit(x$schema$ToString(), "\n")) diff --git a/r/tests/testthat/helper-expectation.R b/r/tests/testthat/helper-expectation.R index 2ebd44f7bba..595b183e555 100644 --- a/r/tests/testthat/helper-expectation.R +++ b/r/tests/testthat/helper-expectation.R @@ -15,8 +15,9 @@ # specific language governing permissions and limitations # under the License. -expect_vector <- function(x, y, ...) { - expect_equal(as.vector(x), y, ...) +expect_as_vector <- function(x, y, ignore_attr = FALSE, ...) { + expect_fun <- ifelse(ignore_attr, expect_equivalent, expect_equal) + expect_fun(as.vector(x), y, ...) } expect_data_frame <- function(x, y, ...) { @@ -155,10 +156,10 @@ expect_vector_equal <- function(expr, # A vectorized R expression containing `in vec, # A vector as reference, will make Array/ChunkedArray with skip_array = NULL, # Msg, if should skip Array test skip_chunked_array = NULL, # Msg, if should skip ChunkedArray test + ignore_attr = FALSE, # ignore attributes? ...) { expr <- rlang::enquo(expr) expected <- rlang::eval_tidy(expr, rlang::new_data_mask(rlang::env(input = vec))) - skip_msg <- NULL if (is.null(skip_array)) { @@ -166,21 +167,20 @@ expect_vector_equal <- function(expr, # A vectorized R expression containing `in expr, rlang::new_data_mask(rlang::env(input = Array$create(vec))) ) - expect_vector(via_array, expected, ...) + expect_as_vector(via_array, expected, ignore_attr, ...) } else { skip_msg <- c(skip_msg, skip_array) } if (is.null(skip_chunked_array)) { # split input vector into two to exercise ChunkedArray with >1 chunk - vec_split <- length(vec) %/% 2 - vec1 <- vec[seq(from = min(1, length(vec) - 1), to = min(length(vec) - 1, vec_split), by = 1)] - vec2 <- vec[seq(from = min(length(vec), vec_split + 1), to = length(vec), by = 1)] + split_vector <- split_vector_as_list(vec) + via_chunked <- rlang::eval_tidy( expr, - rlang::new_data_mask(rlang::env(input = ChunkedArray$create(vec1, vec2))) + rlang::new_data_mask(rlang::env(input = ChunkedArray$create(split_vector[[1]], split_vector[[2]]))) ) - expect_vector(via_chunked, expected, ...) + expect_as_vector(via_chunked, expected, ignore_attr, ...) } else { skip_msg <- c(skip_msg, skip_chunked_array) } @@ -189,3 +189,71 @@ expect_vector_equal <- function(expr, # A vectorized R expression containing `in skip(paste(skip_msg, collpase = "\n")) } } + +expect_vector_error <- function(expr, # A vectorized R expression containing `input` as its input + vec, # A vector as reference, will make Array/ChunkedArray with + skip_array = NULL, # Msg, if should skip Array test + skip_chunked_array = NULL, # Msg, if should skip ChunkedArray test + ...) { + + expr <- rlang::enquo(expr) + + msg <- tryCatch( + rlang::eval_tidy(expr, rlang::new_data_mask(rlang::env(input = vec))), + error = function (e) { + msg <- conditionMessage(e) + + pattern <- i18ize_error_messages() + + if (grepl(pattern, msg)) { + msg <- sub(paste0("^.*(", pattern, ").*$"), "\\1", msg) + } + msg + } + ) + + expect_true(identical(typeof(msg), "character"), label = "vector errored") + + skip_msg <- NULL + + if (is.null(skip_array)) { + + expect_error( + rlang::eval_tidy( + expr, + rlang::new_data_mask(rlang::env(input = Array$create(vec))) + ), + msg, + ... + ) + } else { + skip_msg <- c(skip_msg, skip_array) + } + + if (is.null(skip_chunked_array)) { + # split input vector into two to exercise ChunkedArray with >1 chunk + split_vector <- split_vector_as_list(vec) + + expect_error( + rlang::eval_tidy( + expr, + rlang::new_data_mask(rlang::env(input = ChunkedArray$create(split_vector[[1]], split_vector[[2]]))) + ), + msg, + ... + ) + } else { + skip_msg <- c(skip_msg, skip_chunked_array) + } + + if (!is.null(skip_msg)) { + skip(paste(skip_msg, collpase = "\n")) + } +} + +split_vector_as_list <- function(vec){ + vec_split <- length(vec) %/% 2 + vec1 <- vec[seq(from = min(1, length(vec) - 1), to = min(length(vec) - 1, vec_split), by = 1)] + vec2 <- vec[seq(from = min(length(vec), vec_split + 1), to = length(vec), by = 1)] + list(vec1, vec2) +} diff --git a/r/tests/testthat/test-Array.R b/r/tests/testthat/test-Array.R index b4fa8296d3a..e064f81cdfa 100644 --- a/r/tests/testthat/test-Array.R +++ b/r/tests/testthat/test-Array.R @@ -94,13 +94,13 @@ test_that("Slice() and RangeEquals()", { y <- x$Slice(10) expect_equal(y$type, int32()) expect_equal(length(y), 15L) - expect_vector(y, c(101:110, 201:205)) + expect_as_vector(y, c(101:110, 201:205)) expect_true(x$RangeEquals(y, 10, 24)) expect_false(x$RangeEquals(y, 9, 23)) expect_false(x$RangeEquals(y, 11, 24)) z <- x$Slice(10, 5) - expect_vector(z, c(101:105)) + expect_as_vector(z, c(101:105)) expect_true(x$RangeEquals(z, 10, 15, 0)) # Input validation @@ -708,12 +708,12 @@ test_that("Array$Take()", { test_that("[ method on Array", { vec <- 11:20 a <- Array$create(vec) - expect_vector(a[5:9], vec[5:9]) - expect_vector(a[c(9, 3, 5)], vec[c(9, 3, 5)]) - expect_vector(a[rep(c(TRUE, FALSE), 5)], vec[c(1, 3, 5, 7, 9)]) - expect_vector(a[rep(c(TRUE, FALSE, NA, FALSE, TRUE), 2)], c(11, NA, 15, 16, NA, 20)) - expect_vector(a[-4], vec[-4]) - expect_vector(a[-1], vec[-1]) + expect_as_vector(a[5:9], vec[5:9]) + expect_as_vector(a[c(9, 3, 5)], vec[c(9, 3, 5)]) + expect_as_vector(a[rep(c(TRUE, FALSE), 5)], vec[c(1, 3, 5, 7, 9)]) + expect_as_vector(a[rep(c(TRUE, FALSE, NA, FALSE, TRUE), 2)], c(11, NA, 15, 16, NA, 20)) + expect_as_vector(a[-4], vec[-4]) + expect_as_vector(a[-1], vec[-1]) }) test_that("[ accepts Arrays and otherwise handles bad input", { @@ -724,12 +724,12 @@ test_that("[ accepts Arrays and otherwise handles bad input", { a[Array$create(ind)], "Cannot extract rows with an Array of type double" ) - expect_vector(a[Array$create(ind - 1, type = int8())], vec[ind]) - expect_vector(a[Array$create(ind - 1, type = uint8())], vec[ind]) - expect_vector(a[ChunkedArray$create(8, 2, 4, type = uint8())], vec[ind]) + expect_as_vector(a[Array$create(ind - 1, type = int8())], vec[ind]) + expect_as_vector(a[Array$create(ind - 1, type = uint8())], vec[ind]) + expect_as_vector(a[ChunkedArray$create(8, 2, 4, type = uint8())], vec[ind]) filt <- seq_along(vec) %in% ind - expect_vector(a[Array$create(filt)], vec[filt]) + expect_as_vector(a[Array$create(filt)], vec[filt]) expect_error( a["string"], @@ -754,21 +754,21 @@ test_that("[ accepts Expressions", { vec <- 11:20 a <- Array$create(vec) b <- Array$create(1:10) - expect_vector(a[b > 4], vec[5:10]) + expect_as_vector(a[b > 4], vec[5:10]) }) test_that("Array head/tail", { vec <- 11:20 a <- Array$create(vec) - expect_vector(head(a), head(vec)) - expect_vector(head(a, 4), head(vec, 4)) - expect_vector(head(a, 40), head(vec, 40)) - expect_vector(head(a, -4), head(vec, -4)) - expect_vector(head(a, -40), head(vec, -40)) - expect_vector(tail(a), tail(vec)) - expect_vector(tail(a, 4), tail(vec, 4)) - expect_vector(tail(a, 40), tail(vec, 40)) - expect_vector(tail(a, -40), tail(vec, -40)) + expect_as_vector(head(a), head(vec)) + expect_as_vector(head(a, 4), head(vec, 4)) + expect_as_vector(head(a, 40), head(vec, 40)) + expect_as_vector(head(a, -4), head(vec, -4)) + expect_as_vector(head(a, -40), head(vec, -40)) + expect_as_vector(tail(a), tail(vec)) + expect_as_vector(tail(a, 4), tail(vec, 4)) + expect_as_vector(tail(a, 40), tail(vec, 40)) + expect_as_vector(tail(a, -40), tail(vec, -40)) }) test_that("Dictionary array: create from arrays, not factor", { diff --git a/r/tests/testthat/test-RecordBatch.R b/r/tests/testthat/test-RecordBatch.R index ff7f17eca6e..c3797914741 100644 --- a/r/tests/testthat/test-RecordBatch.R +++ b/r/tests/testthat/test-RecordBatch.R @@ -155,9 +155,9 @@ test_that("[ on RecordBatch", { }) test_that("[[ and $ on RecordBatch", { - expect_vector(batch[["int"]], tbl$int) - expect_vector(batch$int, tbl$int) - expect_vector(batch[[4]], tbl$chr) + expect_as_vector(batch[["int"]], tbl$int) + expect_as_vector(batch$int, tbl$int) + expect_as_vector(batch[[4]], tbl$chr) expect_null(batch$qwerty) expect_null(batch[["asdf"]]) expect_error(batch[[c(4, 3)]]) @@ -190,16 +190,16 @@ test_that("[[<- assignment", { # can replace a column by index batch[[2]] <- as.numeric(10:1) - expect_vector(batch[[2]], as.numeric(10:1)) + expect_as_vector(batch[[2]], as.numeric(10:1)) # can add a column by index batch[[5]] <- as.numeric(10:1) - expect_vector(batch[[5]], as.numeric(10:1)) - expect_vector(batch[["5"]], as.numeric(10:1)) + expect_as_vector(batch[[5]], as.numeric(10:1)) + expect_as_vector(batch[["5"]], as.numeric(10:1)) # can replace a column batch[["int"]] <- 10:1 - expect_vector(batch[["int"]], 10:1) + expect_as_vector(batch[["int"]], 10:1) # can use $ batch$new <- NULL @@ -207,11 +207,11 @@ test_that("[[<- assignment", { expect_identical(dim(batch), c(10L, 4L)) batch$int <- 1:10 - expect_vector(batch$int, 1:10) + expect_as_vector(batch$int, 1:10) # recycling batch[["atom"]] <- 1L - expect_vector(batch[["atom"]], rep(1L, 10)) + expect_as_vector(batch[["atom"]], rep(1L, 10)) expect_error( batch[["atom"]] <- 1:6, @@ -221,7 +221,7 @@ test_that("[[<- assignment", { # assign Arrow array array <- Array$create(c(10:1)) batch$array <- array - expect_vector(batch$array, 10:1) + expect_as_vector(batch$array, 10:1) # nonsense indexes expect_error(batch[[NA]] <- letters[10:1], "'i' must be character or numeric, not logical") @@ -498,4 +498,4 @@ test_that("Handling string data with embedded nuls", { fixed = TRUE ) }) -}) \ No newline at end of file +}) diff --git a/r/tests/testthat/test-Table.R b/r/tests/testthat/test-Table.R index 86bda393e2d..3788d416426 100644 --- a/r/tests/testthat/test-Table.R +++ b/r/tests/testthat/test-Table.R @@ -105,7 +105,7 @@ test_that("[, [[, $ for Table", { expect_data_frame(tab[6:7,], tbl[6:7,]) expect_data_frame(tab[6:7, 2:4], tbl[6:7, 2:4]) expect_data_frame(tab[, c("dbl", "fct")], tbl[, c(2, 5)]) - expect_vector(tab[, "chr", drop = TRUE], tbl$chr) + expect_as_vector(tab[, "chr", drop = TRUE], tbl$chr) # Take within a single chunk expect_data_frame(tab[c(7, 3, 5), 2:4], tbl[c(7, 3, 5), 2:4]) expect_data_frame(tab[rep(c(FALSE, TRUE), 5),], tbl[c(2, 4, 6, 8, 10),]) @@ -123,9 +123,9 @@ test_that("[, [[, $ for Table", { # Expression expect_data_frame(tab[tab$int > 6,], tbl[tbl$int > 6,]) - expect_vector(tab[["int"]], tbl$int) - expect_vector(tab$int, tbl$int) - expect_vector(tab[[4]], tbl$chr) + expect_as_vector(tab[["int"]], tbl$int) + expect_as_vector(tab$int, tbl$int) + expect_as_vector(tab[[4]], tbl$chr) expect_null(tab$qwerty) expect_null(tab[["asdf"]]) # List-like column slicing @@ -173,16 +173,16 @@ test_that("[[<- assignment", { # can replace a column by index tab[[2]] <- as.numeric(10:1) - expect_vector(tab[[2]], as.numeric(10:1)) + expect_as_vector(tab[[2]], as.numeric(10:1)) # can add a column by index tab[[5]] <- as.numeric(10:1) - expect_vector(tab[[5]], as.numeric(10:1)) - expect_vector(tab[["5"]], as.numeric(10:1)) + expect_as_vector(tab[[5]], as.numeric(10:1)) + expect_as_vector(tab[["5"]], as.numeric(10:1)) # can replace a column tab[["int"]] <- 10:1 - expect_vector(tab[["int"]], 10:1) + expect_as_vector(tab[["int"]], 10:1) # can use $ tab$new <- NULL @@ -190,11 +190,11 @@ test_that("[[<- assignment", { expect_identical(dim(tab), c(10L, 4L)) tab$int <- 1:10 - expect_vector(tab$int, 1:10) + expect_as_vector(tab$int, 1:10) # recycling tab[["atom"]] <- 1L - expect_vector(tab[["atom"]], rep(1L, 10)) + expect_as_vector(tab[["atom"]], rep(1L, 10)) expect_error( tab[["atom"]] <- 1:6, @@ -204,10 +204,10 @@ test_that("[[<- assignment", { # assign Arrow array and chunked_array array <- Array$create(c(10:1)) tab$array <- array - expect_vector(tab$array, 10:1) + expect_as_vector(tab$array, 10:1) tab$chunked <- chunked_array(1:10) - expect_vector(tab$chunked, 1:10) + expect_as_vector(tab$chunked, 1:10) # nonsense indexes expect_error(tab[[NA]] <- letters[10:1], "'i' must be character or numeric, not logical") diff --git a/r/tests/testthat/test-chunked-array.R b/r/tests/testthat/test-chunked-array.R index e72067a6d5f..f5b2dca2e44 100644 --- a/r/tests/testthat/test-chunked-array.R +++ b/r/tests/testthat/test-chunked-array.R @@ -312,30 +312,30 @@ test_that("[ ChunkedArray", { one_chunk <- chunked_array(2:11) x <- chunked_array(1:10, 31:40, 51:55) # Slice - expect_vector(x[8:12], c(8:10, 31:32)) + expect_as_vector(x[8:12], c(8:10, 31:32)) # Take from same chunk - expect_vector(x[c(11, 15, 12)], c(31, 35, 32)) + expect_as_vector(x[c(11, 15, 12)], c(31, 35, 32)) # Take from multiple chunks (calls Concatenate) - expect_vector(x[c(2, 11, 15, 12, 3)], c(2, 31, 35, 32, 3)) + expect_as_vector(x[c(2, 11, 15, 12, 3)], c(2, 31, 35, 32, 3)) # Take with Array (note these are 0-based) take1 <- Array$create(c(10L, 14L, 11L)) - expect_vector(x[take1], c(31, 35, 32)) + expect_as_vector(x[take1], c(31, 35, 32)) # Take with ChunkedArray take2 <- ChunkedArray$create(c(10L, 14L), 11L) - expect_vector(x[take2], c(31, 35, 32)) + expect_as_vector(x[take2], c(31, 35, 32)) # Filter (with recycling) - expect_vector( + expect_as_vector( one_chunk[c(FALSE, TRUE, FALSE, FALSE, TRUE)], c(3, 6, 8, 11) ) # Filter where both are 1-chunk - expect_vector( + expect_as_vector( one_chunk[ChunkedArray$create(rep(c(FALSE, TRUE, FALSE, FALSE, TRUE), 2))], c(3, 6, 8, 11) ) # Filter multi-chunk with logical (-> Array) - expect_vector( + expect_as_vector( x[c(FALSE, TRUE, FALSE, FALSE, TRUE)], c(2, 5, 7, 10, 32, 35, 37, 40, 52, 55) ) @@ -343,7 +343,7 @@ test_that("[ ChunkedArray", { p1 <- c(FALSE, TRUE, FALSE, FALSE, TRUE) p2 <- c(TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE) filt <- ChunkedArray$create(p1, p2, p2) - expect_vector( + expect_as_vector( x[filt], c(2, 5, 6, 8, 9, 35, 36, 38, 39, 55) ) @@ -352,15 +352,15 @@ test_that("[ ChunkedArray", { test_that("ChunkedArray head/tail", { vec <- 11:20 a <- ChunkedArray$create(11:15, 16:20) - expect_vector(head(a), head(vec)) - expect_vector(head(a, 4), head(vec, 4)) - expect_vector(head(a, 40), head(vec, 40)) - expect_vector(head(a, -4), head(vec, -4)) - expect_vector(head(a, -40), head(vec, -40)) - expect_vector(tail(a), tail(vec)) - expect_vector(tail(a, 4), tail(vec, 4)) - expect_vector(tail(a, 40), tail(vec, 40)) - expect_vector(tail(a, -40), tail(vec, -40)) + expect_as_vector(head(a), head(vec)) + expect_as_vector(head(a, 4), head(vec, 4)) + expect_as_vector(head(a, 40), head(vec, 40)) + expect_as_vector(head(a, -4), head(vec, -4)) + expect_as_vector(head(a, -40), head(vec, -40)) + expect_as_vector(tail(a), tail(vec)) + expect_as_vector(tail(a, 4), tail(vec, 4)) + expect_as_vector(tail(a, 40), tail(vec, 40)) + expect_as_vector(tail(a, -40), tail(vec, -40)) }) test_that("ChunkedArray$Equals", { @@ -410,4 +410,4 @@ test_that("Handling string data with embedded nuls", { fixed = TRUE ) }) -}) \ No newline at end of file +}) diff --git a/r/tests/testthat/test-compute-arith.R b/r/tests/testthat/test-compute-arith.R index 9d146fd04e6..0b6d8e8dd17 100644 --- a/r/tests/testthat/test-compute-arith.R +++ b/r/tests/testthat/test-compute-arith.R @@ -23,7 +23,7 @@ test_that("Addition", { expect_equal(a + 4L, Array$create(c(5:8, NA_integer_))) expect_identical(as.vector(a + 4L), c(5:8, NA_integer_)) expect_equal(a + 4L, Array$create(c(5:8, NA_integer_))) - expect_vector(a + 4L, c(5:8, NA_integer_)) + expect_as_vector(a + 4L, c(5:8, NA_integer_)) expect_equal(a + NA_integer_, Array$create(rep(NA_integer_, 5))) a8 <- a$cast(int8()) diff --git a/r/tests/testthat/test-na-omit.R b/r/tests/testthat/test-na-omit.R new file mode 100644 index 00000000000..fd1372fdc5d --- /dev/null +++ b/r/tests/testthat/test-na-omit.R @@ -0,0 +1,73 @@ +# 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. + +data_no_na <- c(2:10) +data_na <- c(data_no_na, NA_real_) +scalar_na <- Scalar$create(NA) +scalar_one <- Scalar$create(1) +tbl <- Table$create(example_data) +batch <- record_batch(example_data) + +test_that("na.fail on Scalar", { + expect_as_vector(na.fail(scalar_one), 1) + expect_error(na.fail(scalar_na), "missing values in object") +}) + +test_that("na.omit on Array and ChunkedArray", { + expect_vector_equal(na.omit(input), data_no_na) + expect_vector_equal(na.omit(input), data_na, ignore_attr=TRUE) +}) + +test_that("na.exclude on Array and ChunkedArray", { + expect_vector_equal(na.exclude(input), data_no_na) + expect_vector_equal(na.exclude(input), data_na, ignore_attr=TRUE) +}) + +test_that("na.fail on Array and ChunkedArray", { + expect_vector_equal(na.fail(input), data_no_na, ignore_attr=TRUE) + expect_vector_error(na.fail(input), data_na) +}) + +test_that("na.fail on Scalar", { + expect_error(na.fail(scalar_na), regexp = "missing values in object") + expect_as_vector(na.fail(scalar_one), na.fail(1)) +}) + +test_that("na.omit on Table", { + expect_equivalent(as.data.frame(na.omit(tbl)), na.omit(example_data)) +}) + +test_that("na.exclude on Table", { + expect_equivalent(as.data.frame(na.exclude(tbl)), na.exclude(example_data)) +}) + +test_that("na.fail on Table", { + expect_error(na.fail(tbl), "missing values in object") +}) + +test_that("na.omit on RecordBatch", { + expect_equivalent(as.data.frame(na.omit(batch)), na.omit(example_data)) +}) + +test_that("na.exclude on RecordBatch", { + expect_equivalent(as.data.frame(na.exclude(batch)), na.omit(example_data)) +}) + +test_that("na.fail on RecordBatch", { + expect_error(na.fail(batch), "missing values in object") +}) +