tests/testthat/test-geom_stream.R

library(testthat)
library(ggvfields)

# Create sample stream data for testing
stream_1 <- data.frame(
  x = c(0, 3),
  y = c(0, 0),
  t = 0:1
)
stream_2 <- data.frame(
  x = c(1, 1),
  y = c(1, 5),
  t = 0:1
)
stream_3 <- data.frame(
  x = c(2, 5),
  y = c(2, 6),
  t = 0:1
)
streams <- rbind(
  cbind(stream_1, id = 1),
  cbind(stream_2, id = 2),
  cbind(stream_3, id = 3)
)

# Build a ggplot with geom_stream and check that the computed data is as expected.
test_that("ggplot with geom_stream processes data correctly", {
  p <- ggplot(streams, aes(x = x, y = y, t = t, group = id)) +
    geom_stream()

  # Build the plot to inspect computed data.
  pb <- ggplot_build(p)
  expect_true(length(pb$data) > 0)

  stream_data <- pb$data[[1]]

  # Verify that the computed data contains the required 't' aesthetic.
  expect_true("t" %in% names(stream_data))

  # For each group, the t values should be sorted.
  grouped_t <- split(stream_data$t, stream_data$group)
  expect_true(all(vapply(grouped_t, function(vec) identical(vec, sort(vec)), logical(1))))

  # Check that key aesthetics (like x and y positions) are present.
  expect_true(all(c("x", "y") %in% names(stream_data)))
})


# Also test the layer constructors using data-based reasoning
test_that("geom_stream and stat_stream create proper layers", {
  # Using data-based mapping with id converted to group automatically.
  mapping <- aes(x = x, y = y, t = t, id = I(1))
  # Expect a warning about the unknown 'id' aesthetic; capture it.
  layer_obj1 <- geom_stream(mapping = mapping)
  expect_s3_class(layer_obj1, "LayerInstance")
  expect_equal(layer_obj1$stat, StatStream)
  expect_equal(layer_obj1$geom, GeomStream)
  expect_true("group" %in% names(layer_obj1$mapping))

  # Similarly for stat_stream.
  layer_obj2 <- stat_stream(mapping = mapping)

  expect_s3_class(layer_obj2, "LayerInstance")
  expect_equal(layer_obj2$stat, StatStream)
  expect_equal(layer_obj2$geom, GeomStream)
  expect_true("group" %in% names(layer_obj2$mapping))
})

# Test error condition when required aesthetic 't' is missing.
test_that("StatStream errors when 't' aesthetic is missing", {
  data_no_t <- data.frame(x = 1:5, y = 1:5)
  expect_error(
    StatStream$compute_group(data_no_t, scales::identity_trans()),
    "StatStream requires a 't'"
  )
})

# Test compute_group with valid data to ensure norm is computed.
test_that("StatStream compute_group adds 'norm' correctly", {
  data_valid <- data.frame(x = 1:5, y = 6:10, t = seq(0, 1, length.out = 5))
  computed <- StatStream$compute_group(data_valid, scales::identity_trans())
  expect_true("norm" %in% names(computed))

  # Calculate expected norm from the range of x and y.
  expected_norm <- sqrt((diff(range(data_valid$x)))^2 + (diff(range(data_valid$y)))^2)
  expect_equal(computed$norm[1], expected_norm)
})

# Test that GeomStream's setup_data properly removes infinite t values.
test_that("GeomStream setup_data removes infinite t values", {
  data_inf <- data.frame(
    x = 1:4,
    y = 1:4,
    t = c(0, 1, Inf, 2),
    group = rep(1, 4)
  )
  clean_data <- GeomStream$setup_data(data_inf, NULL)
  expect_false(any(is.infinite(clean_data$t)))
})

# Test the scale_length_continuous function using a ggplot example.
test_that("scale_length_continuous works as expected", {
  # For default max_range (<= 0.5), it returns a continuous scale.
  scale_obj <- scale_length_continuous()
  expect_true(inherits(scale_obj, "ScaleContinuous"))

  # For max_range > 0.5, it returns a list (scale + theme modification).
  scale_list <- scale_length_continuous(max_range = 0.6)
  expect_true(is.list(scale_list))
  expect_length(scale_list, 2)
})

Try the ggvfields package in your browser

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

ggvfields documentation built on April 3, 2025, 9:04 p.m.