tests/testthat/test-visualization-ticks.R

box::use(
  testthat[
    expect_equal,
    expect_false,
    expect_gte,
    expect_identical,
    expect_length,
    expect_true,
    test_that
  ],
  artma / visualization / ticks[
    MAX_AXIS_TICKS,
    format_tick_labels,
    generate_regular_ticks,
    resolve_tick_interval,
    thin_ticks,
    tick_label_digits,
    tick_min_separation
  ]
)


# --- Label formatting -------------------------------------------------------

# Regression guard for the ggtext removal: tick labels used to be wrapped in
# HTML color spans that no renderer in the package interprets any more, so
# every axis printed its markup as literal text.
test_that("format_tick_labels emits plain text, never markup", {
  labels <- format_tick_labels(c(1, 50, 72.05, 369.54))

  expect_false(any(grepl("<", labels, fixed = TRUE)))
  expect_false(any(grepl("span", labels, fixed = TRUE)))
})


test_that("format_tick_labels drops decimals when ticks are far apart", {
  expect_identical(
    format_tick_labels(c(1, 50, 72.05, 100, 369.54)),
    c("1", "50", "72", "100", "370")
  )
})


test_that("format_tick_labels keeps enough precision for small effects", {
  labels <- format_tick_labels(c(-0.004, 0, 0.0035, 0.01))

  expect_identical(anyDuplicated(labels), 0L)
  expect_identical(labels[2], "0")
  expect_false(labels[3] == "0")
})


# A critical value labelled "2" tells the reader the line sits at 2.0. The
# label may be rounded, but not by enough to change what it says.
test_that("format_tick_labels keeps landmark values honest", {
  labels <- format_tick_labels(c(-6.24, -4, -1.96, 0.69, 1.96, 4, 7.67))

  expect_true("1.96" %in% labels)
  expect_false("2" %in% labels)
  # Grid ticks stay free of pointless decimals.
  expect_true("4" %in% labels)
})


test_that("format_tick_labels still rounds where the error is immaterial", {
  # 0.46 out of a 368-wide range is invisible on the axis.
  expect_true("368" %in% format_tick_labels(c(1, 50, 100, 150, 181, 250, 300, 368.46)))
})


test_that("tick_label_digits never rounds a non-zero tick to zero", {
  expect_equal(tick_label_digits(c(0, 100, 200)), 0L)
  expect_gte(tick_label_digits(c(0, 0.002, 0.5)), 3L)
})


test_that("format_tick_labels preserves NA", {
  expect_true(is.na(format_tick_labels(c(1, NA, 3))[2]))
})


# --- Interval selection -----------------------------------------------------

test_that("resolve_tick_interval returns round numbers across magnitudes", {
  expect_equal(resolve_tick_interval(0.008), 0.001)
  expect_equal(resolve_tick_interval(21.3), 2)
  expect_equal(resolve_tick_interval(368.5), 50)
  expect_equal(resolve_tick_interval(5000), 500)
})


test_that("resolve_tick_interval keeps the tick count near the target", {
  for (range_size in c(0.03, 0.7, 4, 21.3, 240, 368.5, 5000, 1e6)) {
    n_ticks <- range_size / resolve_tick_interval(range_size)
    expect_true(n_ticks >= 3 && n_ticks <= MAX_AXIS_TICKS)
  }
})


test_that("resolve_tick_interval stays positive for degenerate ranges", {
  expect_true(resolve_tick_interval(0) > 0)
  expect_true(resolve_tick_interval(-1) > 0)
  expect_true(resolve_tick_interval(NA_real_) > 0)
})


# --- Separation and thinning ------------------------------------------------

test_that("tick_min_separation caps the axis at MAX_AXIS_TICKS labels", {
  expect_equal(tick_min_separation(368.5), 368.5 / MAX_AXIS_TICKS)
  expect_equal(tick_min_separation(1), 1 / MAX_AXIS_TICKS)
  expect_equal(tick_min_separation(0), 0)
})


test_that("thin_ticks collapses identical ticks even without a separation", {
  kept <- thin_ticks(c(5, 5, 5), min_distance = 0, priority = c(1, 9, 1))

  expect_identical(kept, 2L)
})


test_that("thin_ticks keeps high-priority ticks and drops colliding filler", {
  ticks <- c(72.05, 1, 369.54, 50, 100, 350)
  priority <- c(4, 2, 2, 1, 1, 1)

  kept <- thin_ticks(ticks, min_distance = 26.3, priority = priority)

  expect_equal(ticks[kept], c(1, 72.05, 100, 369.54))
})


test_that("thin_ticks returns indices ordered by tick value", {
  ticks <- c(10, -5, 3)
  kept <- thin_ticks(ticks, min_distance = 1)

  expect_identical(kept, c(2L, 3L, 1L))
})


test_that("thin_ticks resolves exact duplicates in favor of priority", {
  ticks <- c(2, 2)
  kept <- thin_ticks(ticks, min_distance = 0.5, priority = c(1, 5))

  expect_identical(kept, 2L)
})


test_that("thin_ticks drops non-finite ticks", {
  kept <- thin_ticks(c(1, NA, 5, Inf), min_distance = 1)

  expect_identical(kept, c(1L, 3L))
})


test_that("thin_ticks keeps everything when no separation is required", {
  expect_length(thin_ticks(c(1, 1.001, 2), min_distance = 0), 3L)
})


test_that("thin_ticks handles an empty tick set", {
  expect_length(thin_ticks(numeric(0), min_distance = 1), 0L)
})


# --- Regular tick generation ------------------------------------------------

test_that("generate_regular_ticks avoids bounds and special values", {
  ticks <- generate_regular_ticks(
    lower = -11.3,
    upper = 10,
    interval = 2,
    edge_distance = 1.52,
    special_values = 0.2,
    special_distance = 1.52
  )

  expect_false(any(abs(ticks - 0.2) < 1.52))
  expect_false(any(abs(ticks - (-11.3)) < 1.52))
  expect_true(all(ticks > -11.3 & ticks < 10))
})

Try the artma package in your browser

Any scripts or data that you put into this service are public.

artma documentation built on Oct. 27, 2026, 9:06 a.m.