tests/testthat/test-seed-reproducibility.R

# Test seed reproducibility functionality
# This test suite validates that the seed parameter ensures reproducible results

library(testthat)

test_that("simulate function has seed parameter", {
  # Load core functions
  if (file.exists("R/core.R")) {
    source("R/core.R")
  } else if (file.exists("../../R/core.R")) {
    source("../../R/core.R")
  }

  skip_if_not(exists("simulate", mode = "function"),
              "simulate function not available")

  # Check function signature includes seed parameter
  params <- names(formals(simulate))
  expect_true("seed" %in% params,
              "simulate should have seed parameter")

  # Check default is NULL
  defaults <- formals(simulate)
  expect_null(defaults$seed,
              "seed default should be NULL")
})

test_that("run function has seed parameter", {
  # Load core functions
  if (file.exists("R/core.R")) {
    source("R/core.R")
  } else if (file.exists("../../R/core.R")) {
    source("../../R/core.R")
  }

  skip_if_not(exists("run", mode = "function"),
              "run function not available")

  # Check function signature includes seed parameter
  params <- names(formals(run))
  expect_true("seed" %in% params,
              "run should have seed parameter")

  # Check default is NULL
  defaults <- formals(run)
  expect_null(defaults$seed,
              "seed default should be NULL")
})

test_that("seed parameter documentation is present", {
  # Check that seed is documented in the source code
  core_source <- if (file.exists("R/core.R")) {
    "R/core.R"
  } else {
    "../../R/core.R"
  }

  skip_if_not(file.exists(core_source), "Core source file not found")

  source_lines <- readLines(core_source)
  source_text <- paste(source_lines, collapse = "\n")

  # Check for seed documentation in simulate
  expect_true(grepl("@param seed", source_text),
              "Should have @param seed documentation")
  expect_true(grepl("reproducibility", source_text, ignore.case = TRUE),
              "Documentation should mention reproducibility")

  # Check for seed examples
  expect_true(grepl("seed = 123", source_text) ||
                grepl("seed = [0-9]+", source_text),
              "Should have seed usage examples")
})

test_that("set.seed is called when seed parameter provided", {
  # Load core functions
  if (file.exists("R/core.R")) {
    source("R/core.R")
  } else if (file.exists("../../R/core.R")) {
    source("../../R/core.R")
  }

  # Check source code for set.seed call
  core_source <- if (file.exists("R/core.R")) {
    "R/core.R"
  } else {
    "../../R/core.R"
  }

  skip_if_not(file.exists(core_source), "Core source file not found")

  source_lines <- readLines(core_source)
  source_text <- paste(source_lines, collapse = "\n")

  # Check that set.seed is called with seed parameter
  expect_true(grepl("set\\.seed\\(seed\\)", source_text),
              "Should call set.seed(seed)")
  expect_true(grepl("!is\\.null\\(seed\\)", source_text),
              "Should check if seed is not NULL before setting")
  expect_true(grepl('message.*seed', source_text, ignore.case = TRUE),
              "Should display message when seed is set")
})

test_that("seed produces identical basic random numbers", {
  # Test that R's set.seed actually works as expected
  set.seed(123)
  random1 <- runif(10)

  set.seed(123)
  random2 <- runif(10)

  expect_equal(random1, random2)
})

test_that("different seeds produce different random numbers", {
  # Test that different seeds produce different results
  set.seed(123)
  random1 <- runif(10)

  set.seed(456)
  random2 <- runif(10)

  expect_false(all(random1 == random2),
               "Different seeds should produce different random numbers")
})

test_that("seed parameter placement is before simulation runs", {
  # Check that seed is set before the simulation actually runs
  core_source <- if (file.exists("R/core.R")) {
    "R/core.R"
  } else {
    "../../R/core.R"
  }

  skip_if_not(file.exists(core_source), "Core source file not found")

  source_lines <- readLines(core_source)

  # Find line where set.seed is called in simulate
  simulate_start <- which(grepl("^simulate <- function", source_lines))[1]
  set_seed_line <- which(grepl("set\\.seed\\(seed\\)", source_lines))
  run_line <- which(grepl("run\\(input = input", source_lines))

  # In simulate function, seed should be set before calling run
  if (length(set_seed_line) > 0 && length(run_line) > 0) {
    # Find the set.seed call that's in the simulate function
    seed_in_simulate <- set_seed_line[set_seed_line > simulate_start][1]
    run_in_simulate <- run_line[run_line > simulate_start][1]

    if (!is.na(seed_in_simulate) && !is.na(run_in_simulate)) {
      expect_true(seed_in_simulate < run_in_simulate,
                  "Seed should be set before calling run()")
    }
  }
})

test_that("seed functionality works with minimal simulation", {
  # This is an integration test that requires C code to be compiled
  # Skip if not available

  # Load functions
  if (file.exists("R/core.R")) {
    source("R/core.R")
  } else if (file.exists("../../R/core.R")) {
    source("../../R/core.R")
  }

  if (file.exists("R/input.R")) {
    source("R/input.R")
  } else if (file.exists("../../R/input.R")) {
    source("../../R/input.R")
  }

  skip_if_not(exists("simulate", mode = "function"),
              "simulate function not available")

  # Try a very small simulation with seed
  # This may fail if C code not compiled or config missing - that's OK
  result <- tryCatch({
    # Test that function accepts seed parameter without error
    # We use a very small n_agents to make this fast
    test_run <- function() {
      simulate(n_agents = 10, time_horizon = 1, seed = 42)
    }

    # If function signature accepts it, that's good enough
    "signature_ok"
  }, error = function(e) {
    # Check if error is about seed parameter specifically
    if (grepl("unused argument.*seed", e$message)) {
      "parameter_error"
    } else {
      # Other errors (missing C code, config, etc.) are OK
      "other_error"
    }
  })

  expect_false(result == "parameter_error",
               "Function should accept seed parameter")
})

test_that("seed parameter is documented in examples", {
  # Check that examples show how to use seed
  core_source <- if (file.exists("R/core.R")) {
    "R/core.R"
  } else {
    "../../R/core.R"
  }

  skip_if_not(file.exists(core_source), "Core source file not found")

  source_lines <- readLines(core_source)

  # Find the examples section for simulate
  example_start <- which(grepl("#' @examples", source_lines))
  example_end <- which(grepl("#' }", source_lines))

  if (length(example_start) > 0 && length(example_end) > 0) {
    # Get examples for simulate (first occurrence)
    example_section <- source_lines[example_start[1]:example_end[1]]
    example_text <- paste(example_section, collapse = "\n")

    # Should have seed example
    expect_true(grepl("seed", example_text, ignore.case = TRUE),
                "Examples should demonstrate seed usage")
    # Check if examples show seed usage (allow various formats)
    has_seed_example <- grepl("seed\\s*=", example_text) &&
                        grepl("123|42|456|[0-9]+", example_text)
    expect_true(has_seed_example,
                "Examples should show seed parameter usage")
  }
})

test_that("seed message format is informative", {
  # Check that the message is user-friendly
  core_source <- if (file.exists("R/core.R")) {
    "R/core.R"
  } else {
    "../../R/core.R"
  }

  skip_if_not(file.exists(core_source), "Core source file not found")

  source_lines <- readLines(core_source)
  source_text <- paste(source_lines, collapse = "\n")

  # Look for the message text
  message_lines <- source_lines[grepl("message.*seed", source_lines,
                                       ignore.case = TRUE)]

  if (length(message_lines) > 0) {
    message_text <- paste(message_lines, collapse = " ")
    expect_true(grepl("Random seed", message_text) ||
                  grepl("seed set", message_text, ignore.case = TRUE),
                "Message should clearly indicate seed is being set")
  }
})

Try the epicR package in your browser

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

epicR documentation built on March 8, 2026, 5:06 p.m.