test_that("a5_cell constructor", { cell <- a5_cell("0800000000000006") expect_s3_class(cell, "a5_cell") expect_s3_class(cell, "vctrs_rcrd") expect_true(is.raw(vctrs::field(cell, "b1"))) expect_true(is.raw(vctrs::field(cell, "b8"))) }) test_that("a5_cell coercion with character", { cell <- a5_cell("0800000000000006") combined <- vctrs::vec_c(cell, "0800000000000016") expect_s3_class(combined, "a5_cell") expect_length(combined, 2L) }) test_that("is_a5_cell works", { expect_true(is_a5_cell(a5_cell("0000000000000abc"))) expect_false(is_a5_cell("0000000000000abc")) }) test_that("a5_cell parses short and 0x-prefixed hex, and rejects non-hex", { # a5-js and DuckDB write ids without leading zeros expect_identical(format(a5_cell("800000000000006")), "0800000000000006") expect_identical(format(a5_cell("0x0800000000000006")), "0800000000000006") expect_identical(format(a5_cell("0X800000000000006")), "0800000000000006") expect_identical(format(a5_cell("ABC")), "0000000000000abc") expect_error(a5_cell("0800000000000006f"), "not a hex cell id") expect_error(a5_cell(c("0800000000000006", "zz")), "Element 2") expect_error(a5_cell("+800000000000006"), "not a hex cell id") expect_error(a5_cell(" 800000000000006"), "not a hex cell id") expect_error(a5_cell(""), "not a hex cell id") expect_error(a5_cell("0x"), "not a hex cell id") expect_length(a5_cell(c("0800000000000006", NA)), 2L) expect_error(vctrs::vec_cast("short", a5_cell()), "not a hex cell id") expect_error(vctrs::vec_c(a5_cell("0800000000000006"), "zz"), "not a hex cell id") }) test_that("NA cells are missing to vctrs", { x <- a5_cell(c("0800000000000006", NA, "fc00000000000001")) expect_identical(vctrs::vec_detect_missing(x), c(FALSE, TRUE, FALSE)) expect_identical(x == a5_cell(NA), c(NA, NA, NA)) expect_identical(x == x, c(TRUE, NA, TRUE)) # NA sorts last, not among the resolution-30 ids starting fc expect_identical(format(vctrs::vec_sort(x)), c("0800000000000006", "fc00000000000001", NA)) expect_identical(format(max(x, na.rm = TRUE)), "fc00000000000001") expect_identical(vctrs::vec_unique_count(c(x, x)), 3L) # u64 order across the 32-bit halves y <- a5_cell(c("00000000ffffffff", "0000000100000000", "ff00000000000000")) expect_identical(vctrs::vec_order(y), 1:3) }) test_that("a5_is_valid validates", { result <- a5_is_valid(c("0800000000000006", "not_valid", NA)) expect_equal(result[1], TRUE) expect_equal(result[2], FALSE) expect_true(is.na(result[3])) }) test_that("a5_is_valid works with a5_cell input", { cell <- a5_cell("0800000000000006") expect_true(a5_is_valid(cell)) }) test_that("as_a5_cell passes through a5_cell unchanged", { cell <- a5_cell("0800000000000006") expect_identical(as_a5_cell(cell), cell) }) test_that("as_a5_cell coerces character", { cell <- as_a5_cell("0800000000000006") expect_s3_class(cell, "a5_cell") }) test_that("a5_cell handles NA values", { cell <- a5_cell(c("0800000000000006", NA)) expect_length(cell, 2L) # NA is represented by the sentinel value (b8 == 0xFC) expect_true(is.na(cell[2])) }) test_that("format.a5_cell preserves hex strings", { cell <- a5_cell(c("0800000000000006", NA)) formatted <- format(cell) expect_equal(formatted[1], "0800000000000006") expect_true(is.na(formatted[2])) }) test_that("a5_u64_to_hex works", { cell <- a5_cell("0800000000000006") expect_equal(a5_u64_to_hex(cell), "0800000000000006") }) test_that("a5_hex_to_u64 works", { cell <- a5_hex_to_u64("0800000000000006") expect_s3_class(cell, "a5_cell") expect_equal(format(cell), "0800000000000006") }) test_that("vec_cast round-trips a5_cell <-> character", { cell <- a5_cell("0800000000000006") chr <- vctrs::vec_cast(cell, character()) expect_equal(chr, "0800000000000006") back <- vctrs::vec_cast(chr, a5_cell()) expect_s3_class(back, "a5_cell") expect_equal(format(back), "0800000000000006") }) test_that("vec_c combines a5_cell + a5_cell", { a <- a5_cell("0800000000000006") b <- a5_cell("0800000000000016") combined <- vctrs::vec_c(a, b) expect_s3_class(combined, "a5_cell") expect_length(combined, 2L) expect_equal( format(combined), c("0800000000000006", "0800000000000016") ) }) test_that("vec_c combines character + a5_cell (character first)", { cell <- a5_cell("0800000000000016") combined <- vctrs::vec_c("0800000000000006", cell) expect_s3_class(combined, "a5_cell") expect_length(combined, 2L) }) test_that("vec_ptype_abbr and vec_ptype_full dispatch correctly", { cell <- a5_cell("0800000000000006") expect_equal(vec_ptype_abbr.a5_cell(cell), "a5_cell") expect_equal(vec_ptype_full.a5_cell(cell), "a5_cell") }) test_that("pillar_shaft.a5_cell formats correctly", { skip_if_not_installed("pillar") cell <- a5_cell(c("0800000000000006", NA)) shaft <- pillar_shaft.a5_cell(cell) expect_s3_class(shaft, "pillar_shaft") }) test_that("a5_cell displays correctly in tibbles", { skip_if_not_installed("tibble") skip_if_not_installed("pillar") tbl <- tibble::tibble( cell = a5_cell(c("0800000000000006", "0800000000000016")) ) testthat::expect_snapshot(tbl) out <- format(tbl) expect_true(any(grepl("a5_cell", out))) expect_true(any(grepl("0800000000000006", out))) }) test_that("str works on a5_cell", { cell <- a5_cell(c("0800000000000006", "0800000000000016")) out <- capture.output(str(cell)) expect_true(any(grepl("a5_cell", out))) }) test_that("wk_crs.a5_cell returns longlat CRS", { cell <- a5_lonlat_to_cell(0, 0, resolution = 5) expect_identical(wk::wk_crs(cell), wk::wk_crs_longlat()) }) test_that("wk_handle.a5_cell produces boundary geometry", { cell <- a5_lonlat_to_cell(0, 0, resolution = 5) wkt <- wk::wk_handle(cell, wk::wkt_writer()) expect_s3_class(wkt, "wk_wkt") expect_true(grepl("^POLYGON", as.character(wkt))) }) # -- sentinel / NA byte-level tests ------------------------------------------- test_that("NA is stored as eight 00 bytes", { cell <- a5_cell(c("0800000000000006", NA)) bytes <- vapply(unclass(cell), function(b) b[2], raw(1)) expect_true(all(bytes == as.raw(0))) expect_false(vctrs::field(cell, "b8")[1] == as.raw(0)) }) test_that("sentinel hex value fc00000000000000 becomes NA", { cell <- a5_cell("fc00000000000000") expect_true(is.na(cell)) expect_true(is.na(format(cell))) }) test_that("known hex encodes to little-endian bytes with b8 stored XOR 0xFC", { cell <- a5_cell("0800000000000006") # 0x0800000000000006 in little-endian: 06 00 00 00 00 00 00 08, # with b8 stored as 0x08 XOR 0xFC = 0xF4 expect_equal(vctrs::field(cell, "b1"), as.raw(0x06)) expect_equal(vctrs::field(cell, "b2"), as.raw(0x00)) expect_equal(vctrs::field(cell, "b7"), as.raw(0x00)) expect_equal(vctrs::field(cell, "b8"), as.raw(0xF4)) }) test_that("the world cell is stored as b8 = 0xFC and is not NA", { world <- a5_cell("0000000000000000") expect_equal(vctrs::field(world, "b8"), as.raw(0xFC)) expect_false(is.na(world)) expect_identical(format(world), "0000000000000000") }) # -- empty / all-NA edge cases ------------------------------------------------ test_that("empty a5_cell works", { cell <- a5_cell(character()) expect_length(cell, 0L) expect_s3_class(cell, "a5_cell") expect_equal(format(cell), character()) }) test_that("all-NA a5_cell works", { cell <- a5_cell(c(NA, NA, NA)) expect_length(cell, 3L) expect_true(all(is.na(cell))) }) test_that("single NA a5_cell works", { cell <- a5_cell(NA) expect_length(cell, 1L) expect_true(is.na(cell)) }) # -- vctrs operations: unique, duplicated, match ------------------------------ test_that("unique works on a5_cell", { cells <- a5_cell(c( "0800000000000006", "0800000000000016", "0800000000000006" )) u <- unique(cells) expect_length(u, 2L) expect_equal(sort(format(u)), c("0800000000000006", "0800000000000016")) }) test_that("duplicated works on a5_cell", { cells <- a5_cell(c( "0800000000000006", "0800000000000016", "0800000000000006" )) expect_equal(duplicated(cells), c(FALSE, FALSE, TRUE)) }) test_that("match works on a5_cell", { haystack <- a5_cell(c("0800000000000006", "0800000000000016")) needle <- a5_cell("0800000000000016") expect_equal(match(needle, haystack), 2L) }) test_that("%in% works on a5_cell", { cells <- a5_cell(c( "0800000000000006", "0800000000000016", "0800000000000026" )) subset <- a5_cell(c("0800000000000006", "0800000000000026")) expect_equal(cells %in% subset, c(TRUE, FALSE, TRUE)) }) test_that("unique handles NA correctly", { cells <- a5_cell(c("0800000000000006", NA, NA)) u <- unique(cells) expect_length(u, 2L) expect_equal(sum(is.na(u)), 1L) }) # -- ordering ----------------------------------------------------------------- test_that("sort orders by u64 value", { # Create cells with known ordering: lower hex = lower u64 cells <- a5_cell(c( "0800000000000016", "0800000000000006", "0800000000000026" )) sorted <- sort(cells) expect_equal( format(sorted), c("0800000000000006", "0800000000000016", "0800000000000026") ) }) test_that("sort handles NA (pushed to end)", { cells <- a5_cell(c(NA, "0800000000000016", "0800000000000006")) sorted <- sort(cells, na.last = TRUE) expect_equal(format(sorted)[1:2], c("0800000000000006", "0800000000000016")) expect_true(is.na(sorted[3])) }) test_that("order returns correct indices", { cells <- a5_cell(c( "0800000000000016", "0800000000000006", "0800000000000026" )) expect_equal(order(cells), c(2L, 1L, 3L)) }) test_that("comparison operators work", { a <- a5_cell("0800000000000006") b <- a5_cell("0800000000000016") expect_true(a < b) expect_false(a > b) expect_true(a == a) expect_true(a != b) }) # -- memory ------------------------------------------------------------------- test_that("a5_cell memory is compact (8 raw vectors)", { cells <- a5_lonlat_to_cell(runif(1000, -180, 180), runif(1000, -80, 80), 10L) # Should be ~8 KB (8 x 1 byte x 1000), not ~56 KB+ from list-of-raw sz <- as.numeric(object.size(cells)) expect_true(sz < 50000) # well under list-of-raw overhead }) test_that("== and != on cells match vctrs, with NA and recycling", { x <- a5_cell(c("0800000000000006", NA, "fc00000000000001", "0800000000000016")) y <- a5_cell(c("0800000000000006", "0800000000000006", NA, "0800000000000006")) expect_identical(x == y, c(TRUE, NA, NA, FALSE)) expect_identical(x != y, c(FALSE, NA, NA, TRUE)) expect_identical(x == x[1], c(TRUE, NA, FALSE, FALSE)) expect_identical(x == "0800000000000006", c(TRUE, NA, FALSE, FALSE)) expect_identical("0800000000000016" == x, c(FALSE, NA, FALSE, TRUE)) expect_identical(x == x, vctrs::vec_equal(x, x, na_equal = FALSE)) expect_error(x[1:2] == x[1:3], class = "vctrs_error_incompatible_size") expect_identical(x[0] == x[0], logical()) })