# 0. Set up -------------------------------------------------------------- ## skip tests on CRAN because they take too much time skip_if(Sys.getenv("TEST_ONE") != "") testthat::skip_on_cran() testthat::skip_if_not_installed("duckdb") testthat::skip_if_not_installed("duckspatial") ## create duckdb connection conn_test <- duckh3::ddbh3_create_conn() ## Load example data test_data <- read.csv( system.file("extdata/example_pts.csv", package = "duckh3") ) |> ddbh3_lonlat_to_h3(resolution = 10) test_data_5 <- read.csv( system.file("extdata/example_pts.csv", package = "duckh3") ) |> head(1) |> ddbh3_lonlat_to_h3(resolution = 5) # 1. get_parent() --------------------------------------------------------- ## 1.1. Input data in different formats ---------- testthat::describe("ddbh3_get_parent() works in different formats", { ## FORMAT 1 - TBL_DUCKDB_CONNECTION testthat::it("returns the correct data for tbl_duckdb_connection", { ## Check class res <- ddbh3_get_parent(test_data) expect_s3_class(res, "tbl_duckdb_connection") ## Check type res_col <- dplyr::collect(res) expect_in("h3parent", colnames(res_col)) expect_type(res_col$h3parent, "character") }) ## FORMAT 2 - DATA.FRAME testthat::it("returns the correct data for data.frame", { ## Check class test_data_df <- dplyr::collect(test_data) res <- ddbh3_get_parent(test_data_df) expect_s3_class(res, "tbl_duckdb_connection") ## Check type res_col <- dplyr::collect(res) expect_in("h3parent", colnames(res_col)) expect_type(res_col$h3parent, "character") }) ## FORMAT 3 - SF testthat::it("returns the correct data for sf", { ## Convert to sf test_data_sf <- test_data |> dplyr::collect() |> sf::st_as_sf( coords = c("lon", "lat"), crs = 4326, remove = FALSE ) ## Check class res <- ddbh3_get_parent(test_data_sf) expect_s3_class(res, "duckspatial_df") ## Check type res_col <- dplyr::collect(res) expect_in("h3parent", colnames(res_col)) expect_type(res_col$h3parent, "character") }) ## FORMAT 4 - DUCKSPATIAL_DF testthat::it("returns the correct data for duckspatial_df", { ## Convert to duckspatial_df test_data_ddbs <- test_data |> dplyr::collect() |> duckspatial::ddbs_as_points(coords = c("lon", "lat"), crs = 4326) ## Check class res <- ddbh3_get_parent(test_data_ddbs) expect_s3_class(res, "duckspatial_df") ## Check type res_col <- dplyr::collect(res) expect_in("h3parent", colnames(res_col)) expect_type(res_col$h3parent, "character") }) ## FORMAT 5 - TABLE IN DUCKDB testthat::it("returns the correct data for table", { ## Create connection conn_test <- ddbh3_create_conn() ## Convert to sf test_data_sf <- test_data |> dplyr::collect() |> sf::st_as_sf( coords = c("lon", "lat"), crs = 4326, remove = FALSE ) ## Store table in connection duckspatial::ddbs_write_table(conn_test, test_data_sf, "sf_pts") ## Apply operation res <- ddbh3_get_parent("sf_pts", conn = conn_test) ## Check type res_col <- dplyr::collect(res) expect_in("h3parent", colnames(res_col)) expect_type(res_col$h3parent, "character") }) }) testthat::test_that("parent has lower resolution than child", { res <- ddbh3_get_parent(test_data, resolution = 5) |> dplyr::collect() ## Get resolution of parent cells parent_res <- ddbh3_get_resolution( data.frame(h3string = res$h3parent) ) |> dplyr::collect() expect_true(all(parent_res$h3resolution == 5)) }) testthat::test_that("lower resolution returns coarser parent", { res_coarse <- ddbh3_get_parent(test_data, resolution = 3) |> dplyr::collect() res_fine <- ddbh3_get_parent(test_data, resolution = 6) |> dplyr::collect() ## Coarser parent strings should differ from finer parent strings expect_false(identical(res_coarse$h3parent, res_fine$h3parent)) }) ## 1.2. Arguments work ------------ testthat::describe("ddbh3_get_parent() arguments work", { ## ARGUMENT 1 - H3 testthat::it("h3 argument works", { ## Rename h3string column test_data_mod <- test_data |> dplyr::rename(h3 = h3string) ## Apply operation with new h3 column name res <- ddbh3_get_parent(test_data_mod, h3 = "h3") ## Check type res_col <- dplyr::collect(res) expect_in("h3parent", colnames(res_col)) expect_type(res_col$h3parent, "character") }) ## ARGUMENT 2 - RESOLUTION testthat::it("resolution argument works", { ## All valid resolutions return results for (r in c(0, 3, 7, 15)) { res <- ddbh3_get_parent(test_data, resolution = r) res_col <- dplyr::collect(res) expect_type(res_col$h3parent, "character") } }) ## ARGUMENT 3 - NEW_COLUMN testthat::it("new_column argument works", { res <- ddbh3_get_parent(test_data, new_column = "res") expect_true("res" %in% colnames(res)) res_col <- dplyr::collect(res) expect_in("res", colnames(res_col)) expect_type(res_col$res, "character") }) ## ARGUMENT 4 - DATABASE ARGUMENTS testthat::it("database arguments work", { ## Create connection conn_test <- ddbh3_create_conn() ## Apply operation with connection expect_message(ddbh3_get_parent( dplyr::collect(test_data), new_column = "res", conn = conn_test, name = "test_data_tbl" )) ## Apply operation with connection, quiet expect_no_message(ddbh3_get_parent( dplyr::collect(test_data), new_column = "res", conn = conn_test, name = "test_data_tbl2", quiet = TRUE )) ## Doesn't overwrite existing table expect_error(ddbh3_get_parent( dplyr::collect(test_data), new_column = "res", conn = conn_test, name = "test_data_tbl" )) ## Overwrites existing table when overwrite = TRUE expect_true(ddbh3_get_parent( dplyr::collect(test_data), new_column = "res", conn = conn_test, name = "test_data_tbl", overwrite = TRUE )) }) }) ## 1.3. Errors on weird inputs ----------- describe("errors", { ## Get h3strings conn_test <- ddbh3_create_conn() duckdb::dbWriteTable(conn_test, "test_data_tbl", dplyr::collect(test_data)) ## Tests it("requires h3 argument as character", { expect_error(ddbh3_get_parent(test_data, h3 = NULL)) expect_error(ddbh3_get_parent(test_data, h3 = TRUE)) expect_error(ddbh3_get_parent(test_data, h3 = 2)) }) it("requires resolution to be an integer scalar", { expect_error(ddbh3_get_parent(test_data, resolution = NULL)) expect_error(ddbh3_get_parent(test_data, resolution = "a")) expect_error(ddbh3_get_parent(test_data, resolution = 1.5)) expect_error(ddbh3_get_parent(test_data, resolution = c(1, 2))) }) it("requires resolution to be in the range 0-15", { expect_error(ddbh3_get_parent(test_data, resolution = -1)) expect_error(ddbh3_get_parent(test_data, resolution = 16)) }) it("requires new_column argument as character", { expect_error(ddbh3_get_parent(test_data, new_column = NULL)) expect_error(ddbh3_get_parent(test_data, new_column = FALSE)) expect_error(ddbh3_get_parent(test_data, new_column = 25)) }) it("requires connection when using table names", { expect_warning( expect_true(is.na(ddbh3_get_parent("test_data_tbl", conn = NULL))) ) }) it("validates x argument type", { expect_error(ddbh3_get_parent(x = 999)) }) it("validates conn argument type", { expect_error(ddbh3_get_parent(test_data, conn = 999)) }) it("validates overwrite argument type", { expect_error(ddbh3_get_parent(test_data, overwrite = 999)) }) it("validates quiet argument type", { expect_error(ddbh3_get_parent(test_data, quiet = 999)) }) it("validates table name exists", { expect_error(ddbh3_get_parent(x = "999", conn = conn_test)) }) it("requires name to be single character string", { expect_error(ddbh3_get_parent(test_data, conn = conn_test, name = c('banana', 'banana'))) }) }) # 2. get_children() --------------------------------------------------------- ## 2.1. Input data in different formats ---------- testthat::describe("ddbh3_get_children() works in different formats", { ## FORMAT 1 - TBL_DUCKDB_CONNECTION testthat::it("returns the correct data for tbl_duckdb_connection", { ## Check class res <- ddbh3_get_children(test_data_5) expect_s3_class(res, "tbl_duckdb_connection") ## Check type res_col <- dplyr::collect(res) expect_in("h3children", colnames(res_col)) expect_type(res_col$h3children, "character") }) ## FORMAT 2 - DATA.FRAME testthat::it("returns the correct data for data.frame", { ## Check class test_data_5_df <- dplyr::collect(test_data_5) res <- ddbh3_get_children(test_data_5_df) expect_s3_class(res, "tbl_duckdb_connection") ## Check type res_col <- dplyr::collect(res) expect_in("h3children", colnames(res_col)) expect_type(res_col$h3children, "character") }) ## FORMAT 3 - SF testthat::it("returns the correct data for sf", { ## Convert to sf test_data_5_sf <- test_data_5 |> dplyr::collect() |> sf::st_as_sf( coords = c("lon", "lat"), crs = 4326, remove = FALSE ) ## Check class res <- ddbh3_get_children(test_data_5_sf) expect_s3_class(res, "duckspatial_df") ## Check type res_col <- dplyr::collect(res) expect_in("h3children", colnames(res_col)) expect_type(res_col$h3children, "character") }) ## FORMAT 4 - DUCKSPATIAL_DF testthat::it("returns the correct data for duckspatial_df", { ## Convert to duckspatial_df test_data_5_ddbs <- test_data_5 |> dplyr::collect() |> duckspatial::ddbs_as_points(coords = c("lon", "lat"), crs = 4326) ## Check class res <- ddbh3_get_children(test_data_5_ddbs) expect_s3_class(res, "duckspatial_df") ## Check type res_col <- dplyr::collect(res) expect_in("h3children", colnames(res_col)) expect_type(res_col$h3children, "character") }) ## FORMAT 5 - TABLE IN DUCKDB testthat::it("returns the correct data for table", { ## Create connection conn_test <- ddbh3_create_conn() ## Convert to sf test_data_5_sf <- test_data_5 |> dplyr::collect() |> sf::st_as_sf( coords = c("lon", "lat"), crs = 4326, remove = FALSE ) ## Store table in connection duckspatial::ddbs_write_table(conn_test, test_data_5_sf, "sf_pts") ## Apply operation res <- ddbh3_get_children("sf_pts", conn = conn_test) ## Check type res_col <- dplyr::collect(res) expect_in("h3children", colnames(res_col)) expect_type(res_col$h3children, "character") }) }) testthat::test_that("unnested result has more rows than nested", { ## Unnested (default): one row per child res_unnested <- ddbh3_get_children(test_data_5, nested = FALSE) |> dplyr::collect() ## Nested: one row per parent cell, children column contains a list res_nested <- ddbh3_get_children(test_data_5, nested = TRUE) |> dplyr::collect() expect_gt(nrow(res_unnested), nrow(res_nested)) }) testthat::test_that("nested result has same number of rows as input", { res_nested <- ddbh3_get_children(test_data_5, nested = TRUE) |> dplyr::collect() expect_equal(nrow(res_nested), nrow(dplyr::collect(test_data_5))) }) testthat::test_that("children have higher resolution than parent", { res <- ddbh3_get_children(test_data_5, resolution = 6, nested = FALSE) |> dplyr::collect() ## Get resolution of children cells children_res <- ddbh3_get_resolution( data.frame(h3string = res$h3children) ) |> dplyr::collect() expect_true(all(children_res$h3resolution == 6)) }) testthat::test_that("higher resolution returns more children", { res_fine <- ddbh3_get_children(test_data_5, resolution = 7, nested = FALSE) |> dplyr::collect() res_coarse <- ddbh3_get_children(test_data_5, resolution = 6, nested = FALSE) |> dplyr::collect() expect_gt(nrow(res_fine), nrow(res_coarse)) }) ## 2.2. Arguments work ------------ testthat::describe("ddbh3_get_children() arguments work", { ## ARGUMENT 1 - H3 testthat::it("h3 argument works", { ## Rename h3string column test_data_5_mod <- test_data_5 |> dplyr::rename(h3 = h3string) ## Apply operation with new h3 column name res <- ddbh3_get_children(test_data_5_mod, h3 = "h3") ## Check type res_col <- dplyr::collect(res) expect_in("h3children", colnames(res_col)) expect_type(res_col$h3children, "character") }) ## ARGUMENT 2 - RESOLUTION testthat::it("resolution argument works", { ## Sample of valid resolutions return results for (r in c(7, 8, 9)) { res <- ddbh3_get_children(test_data_5, resolution = r) res_col <- dplyr::collect(res) expect_type(res_col$h3children, "character") } }) ## ARGUMENT 3 - NESTED testthat::it("nested argument works", { ## nested = FALSE returns character column (unnested) res_false <- ddbh3_get_children(test_data_5, nested = FALSE) res_false_col <- dplyr::collect(res_false) expect_type(res_false_col$h3children, "character") ## nested = TRUE returns list column res_true <- ddbh3_get_children(test_data_5, nested = TRUE) res_true_col <- dplyr::collect(res_true) expect_type(res_true_col$h3children, "list") }) ## ARGUMENT 4 - NEW_COLUMN testthat::it("new_column argument works", { res <- ddbh3_get_children(test_data_5, new_column = "res") expect_true("res" %in% colnames(res)) res_col <- dplyr::collect(res) expect_in("res", colnames(res_col)) expect_type(res_col$res, "character") }) ## ARGUMENT 5 - DATABASE ARGUMENTS testthat::it("database arguments work", { ## Create connection conn_test <- ddbh3_create_conn() ## Apply operation with connection expect_message(ddbh3_get_children( dplyr::collect(test_data_5), new_column = "res", conn = conn_test, name = "test_data_5_tbl" )) ## Apply operation with connection, quiet expect_no_message(ddbh3_get_children( dplyr::collect(test_data_5), new_column = "res", conn = conn_test, name = "test_data_5_tbl2", quiet = TRUE )) ## Doesn't overwrite existing table expect_error(ddbh3_get_children( dplyr::collect(test_data_5), new_column = "res", conn = conn_test, name = "test_data_5_tbl" )) ## Overwrites existing table when overwrite = TRUE expect_true(ddbh3_get_children( dplyr::collect(test_data_5), new_column = "res", conn = conn_test, name = "test_data_5_tbl", overwrite = TRUE )) }) }) ## 2.3. Errors on weird inputs ----------- describe("errors", { ## Get h3strings conn_test <- ddbh3_create_conn() duckdb::dbWriteTable(conn_test, "test_data_5_tbl", dplyr::collect(test_data_5)) ## Tests it("requires h3 argument as character", { expect_error(ddbh3_get_children(test_data_5, h3 = NULL)) expect_error(ddbh3_get_children(test_data_5, h3 = TRUE)) expect_error(ddbh3_get_children(test_data_5, h3 = 2)) }) it("requires resolution to be an integer scalar", { expect_error(ddbh3_get_children(test_data_5, resolution = NULL)) expect_error(ddbh3_get_children(test_data_5, resolution = "a")) expect_error(ddbh3_get_children(test_data_5, resolution = 1.5)) expect_error(ddbh3_get_children(test_data_5, resolution = c(1, 2))) }) it("requires resolution to be in the range 0-15", { expect_error(ddbh3_get_children(test_data_5, resolution = -1)) expect_error(ddbh3_get_children(test_data_5, resolution = 16)) }) it("requires nested argument as logical", { expect_error(ddbh3_get_children(test_data_5, nested = NULL)) expect_error(ddbh3_get_children(test_data_5, nested = "yes")) expect_error(ddbh3_get_children(test_data_5, nested = 1)) }) it("requires new_column argument as character", { expect_error(ddbh3_get_children(test_data_5, new_column = NULL)) expect_error(ddbh3_get_children(test_data_5, new_column = FALSE)) expect_error(ddbh3_get_children(test_data_5, new_column = 25)) }) it("validates x argument type", { expect_error(ddbh3_get_children(x = 999)) }) it("validates conn argument type", { expect_error(ddbh3_get_children(test_data_5, conn = 999)) }) it("validates overwrite argument type", { expect_error(ddbh3_get_children(test_data_5, overwrite = 999)) }) it("validates quiet argument type", { expect_error(ddbh3_get_children(test_data_5, quiet = 999)) }) it("validates table name exists", { expect_error(ddbh3_get_children(x = "999", conn = conn_test)) }) it("requires name to be single character string", { expect_error(ddbh3_get_children(test_data_5, conn = conn_test, name = c('banana', 'banana'))) }) }) # 3. get_n_children() --------------------------------------------------------- ## 3.1. Input data in different formats ---------- testthat::describe("ddbh3_get_n_children() works in different formats", { ## FORMAT 1 - TBL_DUCKDB_CONNECTION testthat::it("returns the correct data for tbl_duckdb_connection", { ## Check class res <- ddbh3_get_n_children(test_data_5) expect_s3_class(res, "tbl_duckdb_connection") ## Check type res_col <- dplyr::collect(res) expect_in("h3n_children", colnames(res_col)) expect_s3_class(res_col$h3n_children, "integer64") }) ## FORMAT 2 - DATA.FRAME testthat::it("returns the correct data for data.frame", { ## Check class test_data_5_df <- dplyr::collect(test_data_5) res <- ddbh3_get_n_children(test_data_5_df) expect_s3_class(res, "tbl_duckdb_connection") ## Check type res_col <- dplyr::collect(res) expect_in("h3n_children", colnames(res_col)) expect_s3_class(res_col$h3n_children, "integer64") }) ## FORMAT 3 - SF testthat::it("returns the correct data for sf", { ## Convert to sf test_data_5_sf <- test_data_5 |> dplyr::collect() |> sf::st_as_sf( coords = c("lon", "lat"), crs = 4326, remove = FALSE ) ## Check class res <- ddbh3_get_n_children(test_data_5_sf) expect_s3_class(res, "duckspatial_df") ## Check type res_col <- dplyr::collect(res) expect_in("h3n_children", colnames(res_col)) expect_s3_class(res_col$h3n_children, "integer64") }) ## FORMAT 4 - DUCKSPATIAL_DF testthat::it("returns the correct data for duckspatial_df", { ## Convert to duckspatial_df test_data_5_ddbs <- test_data_5 |> dplyr::collect() |> duckspatial::ddbs_as_points(coords = c("lon", "lat"), crs = 4326) ## Check class res <- ddbh3_get_n_children(test_data_5_ddbs) expect_s3_class(res, "duckspatial_df") ## Check type res_col <- dplyr::collect(res) expect_in("h3n_children", colnames(res_col)) expect_s3_class(res_col$h3n_children, "integer64") }) ## FORMAT 5 - TABLE IN DUCKDB testthat::it("returns the correct data for table", { ## Create connection conn_test <- ddbh3_create_conn() ## Convert to sf test_data_5_sf <- test_data_5 |> dplyr::collect() |> sf::st_as_sf( coords = c("lon", "lat"), crs = 4326, remove = FALSE ) ## Store table in connection duckspatial::ddbs_write_table(conn_test, test_data_5_sf, "sf_pts") ## Apply operation res <- ddbh3_get_n_children("sf_pts", conn = conn_test) ## Check type res_col <- dplyr::collect(res) expect_in("h3n_children", colnames(res_col)) expect_s3_class(res_col$h3n_children, "integer64") }) }) testthat::test_that("n_children matches actual number of children", { ## Get number of children reported by the function res_n <- ddbh3_get_n_children(test_data_5, resolution = 10) |> dplyr::collect() ## Get actual number of children by unnesting res_children <- ddbh3_get_children(test_data_5, resolution = 10, nested = FALSE) |> dplyr::collect() expect_equal(sum(res_n$h3n_children), nrow(res_children)) }) testthat::test_that("n_children is positive", { res <- ddbh3_get_n_children(test_data_5, resolution = 10) |> dplyr::collect() expect_true(all(res$h3n_children > 0)) }) testthat::test_that("finer resolution returns more children", { res_fine <- ddbh3_get_n_children(test_data_5, resolution = 11) |> dplyr::collect() res_coarse <- ddbh3_get_n_children(test_data_5, resolution = 9) |> dplyr::collect() expect_true(all(res_fine$h3n_children > res_coarse$h3n_children)) }) ## 3.2. Arguments work ------------ testthat::describe("ddbh3_get_n_children() arguments work", { ## ARGUMENT 1 - H3 testthat::it("h3 argument works", { ## Rename h3string column test_data_5_mod <- test_data_5 |> dplyr::rename(h3 = h3string) ## Apply operation with new h3 column name res <- ddbh3_get_n_children(test_data_5_mod, h3 = "h3") ## Check type res_col <- dplyr::collect(res) expect_in("h3n_children", colnames(res_col)) expect_s3_class(res_col$h3n_children, "integer64") }) ## ARGUMENT 2 - RESOLUTION testthat::it("resolution argument works", { ## Sample of valid resolutions return results for (r in c(9, 11, 13, 15)) { res <- ddbh3_get_n_children(test_data_5, resolution = r) res_col <- dplyr::collect(res) expect_s3_class(res_col$h3n_children, "integer64") } }) ## ARGUMENT 3 - NEW_COLUMN testthat::it("new_column argument works", { res <- ddbh3_get_n_children(test_data_5, new_column = "res") expect_true("res" %in% colnames(res)) res_col <- dplyr::collect(res) expect_in("res", colnames(res_col)) expect_s3_class(res_col$res, "integer64") }) ## ARGUMENT 4 - DATABASE ARGUMENTS testthat::it("database arguments work", { ## Create connection conn_test <- ddbh3_create_conn() ## Apply operation with connection expect_message(ddbh3_get_n_children( dplyr::collect(test_data_5), new_column = "res", conn = conn_test, name = "test_data_5_tbl" )) ## Apply operation with connection, quiet expect_no_message(ddbh3_get_n_children( dplyr::collect(test_data_5), new_column = "res", conn = conn_test, name = "test_data_5_tbl2", quiet = TRUE )) ## Doesn't overwrite existing table expect_error(ddbh3_get_n_children( dplyr::collect(test_data_5), new_column = "res", conn = conn_test, name = "test_data_5_tbl" )) ## Overwrites existing table when overwrite = TRUE expect_true(ddbh3_get_n_children( dplyr::collect(test_data_5), new_column = "res", conn = conn_test, name = "test_data_5_tbl", overwrite = TRUE )) }) }) ## 3.3. Errors on weird inputs ----------- describe("errors", { ## Get h3strings conn_test <- ddbh3_create_conn() duckdb::dbWriteTable(conn_test, "test_data_5_tbl", dplyr::collect(test_data_5)) ## Tests it("requires h3 argument as character", { expect_error(ddbh3_get_n_children(test_data_5, h3 = NULL)) expect_error(ddbh3_get_n_children(test_data_5, h3 = TRUE)) expect_error(ddbh3_get_n_children(test_data_5, h3 = 2)) }) it("requires resolution to be an integer scalar", { expect_error(ddbh3_get_n_children(test_data_5, resolution = NULL)) expect_error(ddbh3_get_n_children(test_data_5, resolution = "a")) expect_error(ddbh3_get_n_children(test_data_5, resolution = 1.5)) expect_error(ddbh3_get_n_children(test_data_5, resolution = c(1, 2))) }) it("requires resolution to be in the range 0-15", { expect_error(ddbh3_get_n_children(test_data_5, resolution = -1)) expect_error(ddbh3_get_n_children(test_data_5, resolution = 16)) }) it("requires new_column argument as character", { expect_error(ddbh3_get_n_children(test_data_5, new_column = NULL)) expect_error(ddbh3_get_n_children(test_data_5, new_column = FALSE)) expect_error(ddbh3_get_n_children(test_data_5, new_column = 25)) }) it("requires connection when using table names", { expect_warning( expect_true(is.na(ddbh3_get_n_children("test_data_5_tbl", conn = NULL))) ) }) it("validates x argument type", { expect_error(ddbh3_get_n_children(x = 999)) }) it("validates conn argument type", { expect_error(ddbh3_get_n_children(test_data_5, conn = 999)) }) it("validates overwrite argument type", { expect_error(ddbh3_get_n_children(test_data_5, overwrite = 999)) }) it("validates quiet argument type", { expect_error(ddbh3_get_n_children(test_data_5, quiet = 999)) }) it("validates table name exists", { expect_error(ddbh3_get_n_children(x = "999", conn = conn_test)) }) it("requires name to be single character string", { expect_error(ddbh3_get_n_children(test_data_5, conn = conn_test, name = c('banana', 'banana'))) }) }) # 4. get_center_child() --------------------------------------------------------- ## 4.1. Input data in different formats ---------- testthat::describe("ddbh3_get_center_child() works in different formats", { ## FORMAT 1 - TBL_DUCKDB_CONNECTION testthat::it("returns the correct data for tbl_duckdb_connection", { ## Check class res <- ddbh3_get_center_child(test_data) expect_s3_class(res, "tbl_duckdb_connection") ## Check type res_col <- dplyr::collect(res) expect_in("h3center_child", colnames(res_col)) expect_type(res_col$h3center_child, "character") }) ## FORMAT 2 - DATA.FRAME testthat::it("returns the correct data for data.frame", { ## Check class test_data_df <- dplyr::collect(test_data) res <- ddbh3_get_center_child(test_data_df) expect_s3_class(res, "tbl_duckdb_connection") ## Check type res_col <- dplyr::collect(res) expect_in("h3center_child", colnames(res_col)) expect_type(res_col$h3center_child, "character") }) ## FORMAT 3 - SF testthat::it("returns the correct data for sf", { ## Convert to sf test_data_sf <- test_data |> dplyr::collect() |> sf::st_as_sf( coords = c("lon", "lat"), crs = 4326, remove = FALSE ) ## Check class res <- ddbh3_get_center_child(test_data_sf) expect_s3_class(res, "duckspatial_df") ## Check type res_col <- dplyr::collect(res) expect_in("h3center_child", colnames(res_col)) expect_type(res_col$h3center_child, "character") }) ## FORMAT 4 - DUCKSPATIAL_DF testthat::it("returns the correct data for duckspatial_df", { ## Convert to duckspatial_df test_data_ddbs <- test_data |> dplyr::collect() |> duckspatial::ddbs_as_points(coords = c("lon", "lat"), crs = 4326) ## Check class res <- ddbh3_get_center_child(test_data_ddbs) expect_s3_class(res, "duckspatial_df") ## Check type res_col <- dplyr::collect(res) expect_in("h3center_child", colnames(res_col)) expect_type(res_col$h3center_child, "character") }) ## FORMAT 5 - TABLE IN DUCKDB testthat::it("returns the correct data for table", { ## Create connection conn_test <- ddbh3_create_conn() ## Convert to sf test_data_sf <- test_data |> dplyr::collect() |> sf::st_as_sf( coords = c("lon", "lat"), crs = 4326, remove = FALSE ) ## Store table in connection duckspatial::ddbs_write_table(conn_test, test_data_sf, "sf_pts") ## Apply operation res <- ddbh3_get_center_child("sf_pts", conn = conn_test) ## Check type res_col <- dplyr::collect(res) expect_in("h3center_child", colnames(res_col)) expect_type(res_col$h3center_child, "character") }) }) testthat::test_that("center child has the requested resolution", { res <- ddbh3_get_center_child(test_data, resolution = 10) |> dplyr::collect() ## Verify resolution of center child cells child_res <- ddbh3_get_resolution( data.frame(h3string = res$h3center_child) ) |> dplyr::collect() expect_true(all(child_res$h3resolution == 10)) }) testthat::test_that("center child is among the children", { ## Get center child res_center <- ddbh3_get_center_child(test_data, resolution = 10) |> dplyr::collect() ## Get all children res_children <- ddbh3_get_children(test_data, resolution = 10, nested = FALSE) |> dplyr::collect() ## Center child must be a subset of all children expect_true(all(res_center$h3center_child %in% res_children$h3children)) }) testthat::test_that("returns one row per input row", { res <- ddbh3_get_center_child(test_data, resolution = 10) |> dplyr::collect() expect_equal(nrow(res), nrow(dplyr::collect(test_data))) }) testthat::test_that("different resolutions return different center children", { res_fine <- ddbh3_get_center_child(test_data, resolution = 11) |> dplyr::collect() res_coarse <- ddbh3_get_center_child(test_data, resolution = 9) |> dplyr::collect() expect_false(identical(res_fine$h3center_child, res_coarse$h3center_child)) }) ## 4.2. Arguments work ------------ testthat::describe("ddbh3_get_center_child() arguments work", { ## ARGUMENT 1 - H3 testthat::it("h3 argument works", { ## Rename h3string column test_data_mod <- test_data |> dplyr::rename(h3 = h3string) ## Apply operation with new h3 column name res <- ddbh3_get_center_child(test_data_mod, h3 = "h3") ## Check type res_col <- dplyr::collect(res) expect_in("h3center_child", colnames(res_col)) expect_type(res_col$h3center_child, "character") }) ## ARGUMENT 2 - RESOLUTION testthat::it("resolution argument works", { ## Sample of valid resolutions return results for (r in c(9, 11, 13, 15)) { res <- ddbh3_get_center_child(test_data, resolution = r) res_col <- dplyr::collect(res) expect_type(res_col$h3center_child, "character") } }) ## ARGUMENT 3 - NEW_COLUMN testthat::it("new_column argument works", { res <- ddbh3_get_center_child(test_data, new_column = "res") expect_true("res" %in% colnames(res)) res_col <- dplyr::collect(res) expect_in("res", colnames(res_col)) expect_type(res_col$res, "character") }) ## ARGUMENT 4 - DATABASE ARGUMENTS testthat::it("database arguments work", { ## Create connection conn_test <- ddbh3_create_conn() ## Apply operation with connection expect_message(ddbh3_get_center_child( dplyr::collect(test_data), new_column = "res", conn = conn_test, name = "test_data_tbl" )) ## Apply operation with connection, quiet expect_no_message(ddbh3_get_center_child( dplyr::collect(test_data), new_column = "res", conn = conn_test, name = "test_data_tbl2", quiet = TRUE )) ## Doesn't overwrite existing table expect_error(ddbh3_get_center_child( dplyr::collect(test_data), new_column = "res", conn = conn_test, name = "test_data_tbl" )) ## Overwrites existing table when overwrite = TRUE expect_true(ddbh3_get_center_child( dplyr::collect(test_data), new_column = "res", conn = conn_test, name = "test_data_tbl", overwrite = TRUE )) }) }) ## 4.3. Errors on weird inputs ----------- describe("errors", { ## Get h3strings conn_test <- ddbh3_create_conn() duckdb::dbWriteTable(conn_test, "test_data_tbl", dplyr::collect(test_data)) ## Tests it("requires h3 argument as character", { expect_error(ddbh3_get_center_child(test_data, h3 = NULL)) expect_error(ddbh3_get_center_child(test_data, h3 = TRUE)) expect_error(ddbh3_get_center_child(test_data, h3 = 2)) }) it("requires resolution to be an integer scalar", { expect_error(ddbh3_get_center_child(test_data, resolution = NULL)) expect_error(ddbh3_get_center_child(test_data, resolution = "a")) expect_error(ddbh3_get_center_child(test_data, resolution = 1.5)) expect_error(ddbh3_get_center_child(test_data, resolution = c(1, 2))) }) it("requires resolution to be in the range 0-15", { expect_error(ddbh3_get_center_child(test_data, resolution = -1)) expect_error(ddbh3_get_center_child(test_data, resolution = 16)) }) it("requires new_column argument as character", { expect_error(ddbh3_get_center_child(test_data, new_column = NULL)) expect_error(ddbh3_get_center_child(test_data, new_column = FALSE)) expect_error(ddbh3_get_center_child(test_data, new_column = 25)) }) it("requires connection when using table names", { expect_warning( expect_true(is.na(ddbh3_get_center_child("test_data_tbl", conn = NULL))) ) }) it("validates x argument type", { expect_error(ddbh3_get_center_child(x = 999)) }) it("validates conn argument type", { expect_error(ddbh3_get_center_child(test_data, conn = 999)) }) it("validates overwrite argument type", { expect_error(ddbh3_get_center_child(test_data, overwrite = 999)) }) it("validates quiet argument type", { expect_error(ddbh3_get_center_child(test_data, quiet = 999)) }) it("validates table name exists", { expect_error(ddbh3_get_center_child(x = "999", conn = conn_test)) }) it("requires name to be single character string", { expect_error(ddbh3_get_center_child(test_data, conn = conn_test, name = c('banana', 'banana'))) }) }) # 5. get_icosahedron_faces() --------------------------------------------------------- ## 5.1. Input data in different formats ---------- testthat::describe("ddbh3_get_icosahedron_faces() works in different formats", { ## FORMAT 1 - TBL_DUCKDB_CONNECTION testthat::it("returns the correct data for tbl_duckdb_connection", { ## Check class res <- ddbh3_get_icosahedron_faces(test_data) expect_s3_class(res, "tbl_duckdb_connection") ## Check type res_col <- dplyr::collect(res) expect_in("h3faces", colnames(res_col)) expect_type(res_col$h3faces, "integer") }) ## FORMAT 2 - DATA.FRAME testthat::it("returns the correct data for data.frame", { ## Check class test_data_df <- dplyr::collect(test_data) res <- ddbh3_get_icosahedron_faces(test_data_df) expect_s3_class(res, "tbl_duckdb_connection") ## Check type res_col <- dplyr::collect(res) expect_in("h3faces", colnames(res_col)) expect_type(res_col$h3faces, "integer") }) ## FORMAT 3 - SF testthat::it("returns the correct data for sf", { ## Convert to sf test_data_sf <- test_data |> dplyr::collect() |> sf::st_as_sf( coords = c("lon", "lat"), crs = 4326, remove = FALSE ) ## Check class res <- ddbh3_get_icosahedron_faces(test_data_sf) expect_s3_class(res, "duckspatial_df") ## Check type res_col <- dplyr::collect(res) expect_in("h3faces", colnames(res_col)) expect_type(res_col$h3faces, "integer") }) ## FORMAT 4 - DUCKSPATIAL_DF testthat::it("returns the correct data for duckspatial_df", { ## Convert to duckspatial_df test_data_ddbs <- test_data |> dplyr::collect() |> duckspatial::ddbs_as_points(coords = c("lon", "lat"), crs = 4326) ## Check class res <- ddbh3_get_icosahedron_faces(test_data_ddbs) expect_s3_class(res, "duckspatial_df") ## Check type res_col <- dplyr::collect(res) expect_in("h3faces", colnames(res_col)) expect_type(res_col$h3faces, "integer") }) ## FORMAT 5 - TABLE IN DUCKDB testthat::it("returns the correct data for table", { ## Create connection conn_test <- ddbh3_create_conn() ## Convert to sf test_data_sf <- test_data |> dplyr::collect() |> sf::st_as_sf( coords = c("lon", "lat"), crs = 4326, remove = FALSE ) ## Store table in connection duckspatial::ddbs_write_table(conn_test, test_data_sf, "sf_pts") ## Apply operation res <- ddbh3_get_icosahedron_faces("sf_pts", conn = conn_test) ## Check type res_col <- dplyr::collect(res) expect_in("h3faces", colnames(res_col)) expect_type(res_col$h3faces, "integer") }) }) # testthat::test_that("unnested result has more rows than nested", { # ## Unnested (default): one row per face # res_unnested <- ddbh3_get_icosahedron_faces(test_data, nested = FALSE) |> # dplyr::collect() # ## Nested: one row per cell, faces column contains a list # res_nested <- ddbh3_get_icosahedron_faces(test_data, nested = TRUE) |> # dplyr::collect() # expect_gt(nrow(res_unnested), nrow(res_nested)) # }) testthat::test_that("nested result has same number of rows as input", { res_nested <- ddbh3_get_icosahedron_faces(test_data, nested = TRUE) |> dplyr::collect() expect_equal(nrow(res_nested), nrow(dplyr::collect(test_data))) }) testthat::test_that("face indices are within valid range", { ## Icosahedron has 20 faces, indexed 0-19 res <- ddbh3_get_icosahedron_faces(test_data, nested = FALSE) |> dplyr::collect() expect_true(all(res$h3faces >= 0 & res$h3faces <= 19)) }) ## 5.2. Arguments work ------------ testthat::describe("ddbh3_get_icosahedron_faces() arguments work", { ## ARGUMENT 1 - H3 testthat::it("h3 argument works", { ## Rename h3string column test_data_mod <- test_data |> dplyr::rename(h3 = h3string) ## Apply operation with new h3 column name res <- ddbh3_get_icosahedron_faces(test_data_mod, h3 = "h3") ## Check type res_col <- dplyr::collect(res) expect_in("h3faces", colnames(res_col)) expect_type(res_col$h3faces, "integer") }) ## ARGUMENT 2 - NESTED testthat::it("nested argument works", { ## nested = FALSE returns integer column (unnested) res_false <- ddbh3_get_icosahedron_faces(test_data, nested = FALSE) res_false_col <- dplyr::collect(res_false) expect_type(res_false_col$h3faces, "integer") ## nested = TRUE returns list column res_true <- ddbh3_get_icosahedron_faces(test_data, nested = TRUE) res_true_col <- dplyr::collect(res_true) expect_type(res_true_col$h3faces, "list") }) ## ARGUMENT 3 - NEW_COLUMN testthat::it("new_column argument works", { res <- ddbh3_get_icosahedron_faces(test_data, new_column = "res") expect_true("res" %in% colnames(res)) res_col <- dplyr::collect(res) expect_in("res", colnames(res_col)) expect_type(res_col$res, "integer") }) ## ARGUMENT 4 - DATABASE ARGUMENTS testthat::it("database arguments work", { ## Create connection conn_test <- ddbh3_create_conn() ## Apply operation with connection expect_message(ddbh3_get_icosahedron_faces( dplyr::collect(test_data), new_column = "res", conn = conn_test, name = "test_data_tbl" )) ## Apply operation with connection, quiet expect_no_message(ddbh3_get_icosahedron_faces( dplyr::collect(test_data), new_column = "res", conn = conn_test, name = "test_data_tbl2", quiet = TRUE )) ## Doesn't overwrite existing table expect_error(ddbh3_get_icosahedron_faces( dplyr::collect(test_data), new_column = "res", conn = conn_test, name = "test_data_tbl" )) ## Overwrites existing table when overwrite = TRUE expect_true(ddbh3_get_icosahedron_faces( dplyr::collect(test_data), new_column = "res", conn = conn_test, name = "test_data_tbl", overwrite = TRUE )) }) }) ## 5.3. Errors on weird inputs ----------- describe("errors", { ## Get h3strings conn_test <- ddbh3_create_conn() duckdb::dbWriteTable(conn_test, "test_data_tbl", dplyr::collect(test_data)) ## Tests it("requires h3 argument as character", { expect_error(ddbh3_get_icosahedron_faces(test_data, h3 = NULL)) expect_error(ddbh3_get_icosahedron_faces(test_data, h3 = TRUE)) expect_error(ddbh3_get_icosahedron_faces(test_data, h3 = 2)) }) it("requires nested argument as logical", { expect_error(ddbh3_get_icosahedron_faces(test_data, nested = NULL)) expect_error(ddbh3_get_icosahedron_faces(test_data, nested = "yes")) expect_error(ddbh3_get_icosahedron_faces(test_data, nested = 1)) }) it("requires new_column argument as character", { expect_error(ddbh3_get_icosahedron_faces(test_data, new_column = NULL)) expect_error(ddbh3_get_icosahedron_faces(test_data, new_column = FALSE)) expect_error(ddbh3_get_icosahedron_faces(test_data, new_column = 25)) }) it("validates x argument type", { expect_error(ddbh3_get_icosahedron_faces(x = 999)) }) it("validates conn argument type", { expect_error(ddbh3_get_icosahedron_faces(test_data, conn = 999)) }) it("validates overwrite argument type", { expect_error(ddbh3_get_icosahedron_faces(test_data, overwrite = 999)) }) it("validates quiet argument type", { expect_error(ddbh3_get_icosahedron_faces(test_data, quiet = 999)) }) it("validates table name exists", { expect_error(ddbh3_get_icosahedron_faces(x = "999", conn = conn_test)) }) it("requires name to be single character string", { expect_error(ddbh3_get_icosahedron_faces(test_data, conn = conn_test, name = c('banana', 'banana'))) }) }) # 6. get_child_pos() --------------------------------------------------------- ## 6.1. Input data in different formats ---------- testthat::describe("ddbh3_get_child_pos() works in different formats", { ## FORMAT 1 - TBL_DUCKDB_CONNECTION testthat::it("returns the correct data for tbl_duckdb_connection", { ## Check class res <- ddbh3_get_child_pos(test_data) expect_s3_class(res, "tbl_duckdb_connection") ## Check type res_col <- dplyr::collect(res) expect_in("h3child_pos", colnames(res_col)) expect_s3_class(res_col$h3child_pos, "integer64") }) ## FORMAT 2 - DATA.FRAME testthat::it("returns the correct data for data.frame", { ## Check class test_data_df <- dplyr::collect(test_data) res <- ddbh3_get_child_pos(test_data_df) expect_s3_class(res, "tbl_duckdb_connection") ## Check type res_col <- dplyr::collect(res) expect_in("h3child_pos", colnames(res_col)) expect_s3_class(res_col$h3child_pos, "integer64") }) ## FORMAT 3 - SF testthat::it("returns the correct data for sf", { ## Convert to sf test_data_sf <- test_data |> dplyr::collect() |> sf::st_as_sf( coords = c("lon", "lat"), crs = 4326, remove = FALSE ) ## Check class res <- ddbh3_get_child_pos(test_data_sf) expect_s3_class(res, "duckspatial_df") ## Check type res_col <- dplyr::collect(res) expect_in("h3child_pos", colnames(res_col)) expect_s3_class(res_col$h3child_pos, "integer64") }) ## FORMAT 4 - DUCKSPATIAL_DF testthat::it("returns the correct data for duckspatial_df", { ## Convert to duckspatial_df test_data_ddbs <- test_data |> dplyr::collect() |> duckspatial::ddbs_as_points(coords = c("lon", "lat"), crs = 4326) ## Check class res <- ddbh3_get_child_pos(test_data_ddbs) expect_s3_class(res, "duckspatial_df") ## Check type res_col <- dplyr::collect(res) expect_in("h3child_pos", colnames(res_col)) expect_s3_class(res_col$h3child_pos, "integer64") }) ## FORMAT 5 - TABLE IN DUCKDB testthat::it("returns the correct data for table", { ## Create connection conn_test <- ddbh3_create_conn() ## Convert to sf test_data_sf <- test_data |> dplyr::collect() |> sf::st_as_sf( coords = c("lon", "lat"), crs = 4326, remove = FALSE ) ## Store table in connection duckspatial::ddbs_write_table(conn_test, test_data_sf, "sf_pts") ## Apply operation res <- ddbh3_get_child_pos("sf_pts", conn = conn_test) ## Check type res_col <- dplyr::collect(res) expect_in("h3child_pos", colnames(res_col)) expect_s3_class(res_col$h3child_pos, "integer64") }) }) testthat::test_that("child position is non-negative", { res <- ddbh3_get_child_pos(test_data, resolution = 10) |> dplyr::collect() expect_true(all(res$h3child_pos >= 0)) }) testthat::test_that("child position is within the number of children at that resolution", { resolution <- 10 ## Get child positions res_pos <- ddbh3_get_child_pos(test_data, resolution = resolution) |> dplyr::collect() ## Get number of children at that resolution res_n <- ddbh3_get_n_children(test_data, resolution = resolution) |> dplyr::collect() ## Position must be strictly less than the number of children expect_true(all(res_pos$h3child_pos < res_n$h3n_children)) }) testthat::test_that("returns one row per input row", { res <- ddbh3_get_child_pos(test_data, resolution = 10) |> dplyr::collect() expect_equal(nrow(res), nrow(dplyr::collect(test_data))) }) testthat::test_that("different resolutions return different positions", { res_fine <- ddbh3_get_child_pos(test_data, resolution = 11) |> dplyr::collect() res_coarse <- ddbh3_get_child_pos(test_data, resolution = 9) |> dplyr::collect() expect_false(identical(res_fine$h3child_pos, res_coarse$h3child_pos)) }) ## 1.2. Arguments work ------------ testthat::describe("ddbh3_get_child_pos() arguments work", { ## ARGUMENT 1 - H3 testthat::it("h3 argument works", { ## Rename h3string column test_data_mod <- test_data |> dplyr::rename(h3 = h3string) ## Apply operation with new h3 column name res <- ddbh3_get_child_pos(test_data_mod, h3 = "h3") ## Check type res_col <- dplyr::collect(res) expect_in("h3child_pos", colnames(res_col)) expect_s3_class(res_col$h3child_pos, "integer64") }) ## ARGUMENT 2 - RESOLUTION testthat::it("resolution argument works", { ## Sample of valid resolutions return results for (r in c(9, 11, 13, 15)) { res <- ddbh3_get_child_pos(test_data, resolution = r) res_col <- dplyr::collect(res) expect_s3_class(res_col$h3child_pos, "integer64") } }) ## ARGUMENT 3 - NEW_COLUMN testthat::it("new_column argument works", { res <- ddbh3_get_child_pos(test_data, new_column = "res") expect_true("res" %in% colnames(res)) res_col <- dplyr::collect(res) expect_in("res", colnames(res_col)) expect_s3_class(res_col$res, "integer64") }) ## ARGUMENT 4 - DATABASE ARGUMENTS testthat::it("database arguments work", { ## Create connection conn_test <- ddbh3_create_conn() ## Apply operation with connection expect_message(ddbh3_get_child_pos( dplyr::collect(test_data), new_column = "res", conn = conn_test, name = "test_data_tbl" )) ## Apply operation with connection, quiet expect_no_message(ddbh3_get_child_pos( dplyr::collect(test_data), new_column = "res", conn = conn_test, name = "test_data_tbl2", quiet = TRUE )) ## Doesn't overwrite existing table expect_error(ddbh3_get_child_pos( dplyr::collect(test_data), new_column = "res", conn = conn_test, name = "test_data_tbl" )) ## Overwrites existing table when overwrite = TRUE expect_true(ddbh3_get_child_pos( dplyr::collect(test_data), new_column = "res", conn = conn_test, name = "test_data_tbl", overwrite = TRUE )) }) }) ## 1.3. Errors on weird inputs ----------- describe("errors", { ## Get h3strings conn_test <- ddbh3_create_conn() duckdb::dbWriteTable(conn_test, "test_data_tbl", dplyr::collect(test_data)) ## Tests it("requires h3 argument as character", { expect_error(ddbh3_get_child_pos(test_data, h3 = NULL)) expect_error(ddbh3_get_child_pos(test_data, h3 = TRUE)) expect_error(ddbh3_get_child_pos(test_data, h3 = 2)) }) it("requires resolution to be an integer scalar", { expect_error(ddbh3_get_child_pos(test_data, resolution = NULL)) expect_error(ddbh3_get_child_pos(test_data, resolution = "a")) expect_error(ddbh3_get_child_pos(test_data, resolution = 1.5)) expect_error(ddbh3_get_child_pos(test_data, resolution = c(1, 2))) }) it("requires resolution to be in the range 0-15", { expect_error(ddbh3_get_child_pos(test_data, resolution = -1)) expect_error(ddbh3_get_child_pos(test_data, resolution = 16)) }) it("requires new_column argument as character", { expect_error(ddbh3_get_child_pos(test_data, new_column = NULL)) expect_error(ddbh3_get_child_pos(test_data, new_column = FALSE)) expect_error(ddbh3_get_child_pos(test_data, new_column = 25)) }) it("requires connection when using table names", { expect_warning( expect_true(is.na(ddbh3_get_child_pos("test_data_tbl", conn = NULL))) ) }) it("validates x argument type", { expect_error(ddbh3_get_child_pos(x = 999)) }) it("validates conn argument type", { expect_error(ddbh3_get_child_pos(test_data, conn = 999)) }) it("validates overwrite argument type", { expect_error(ddbh3_get_child_pos(test_data, overwrite = 999)) }) it("validates quiet argument type", { expect_error(ddbh3_get_child_pos(test_data, quiet = 999)) }) it("validates table name exists", { expect_error(ddbh3_get_child_pos(x = "999", conn = conn_test)) }) it("requires name to be single character string", { expect_error(ddbh3_get_child_pos(test_data, conn = conn_test, name = c('banana', 'banana'))) }) })