Nothing
# Tests for import/module edge cases
# Covers circular imports, module caching, error cleanup
thin <- make_cran_thinner()
test_that("import non-existent module fails gracefully", {
thin()
engine <- make_engine()
expect_error(
engine$eval_text('(import "non_existent_module_12345")'),
"not found|import|load|module"
)
})
test_that("import with invalid path", {
thin()
engine <- make_engine()
expect_error(
engine$eval_text('(import "/invalid/path/to/module")'),
"not found|import|load|module"
)
})
test_that("same file imported with different path strings uses one module (absolute path alias)", {
thin()
engine <- make_engine()
env <- engine$get_env()
tmp_dir <- tempfile()
dir.create(tmp_dir)
old_dir <- getwd()
setwd(tmp_dir)
on.exit({
setwd(old_dir)
unlink(tmp_dir, recursive = TRUE)
}, add = TRUE)
module_file <- file.path(tmp_dir, "aliasm.arl")
writeLines(c(
"(module aliasm",
" (export getn)",
" (define n 0)",
" (set! n (+ n 1))",
" (define getn (lambda () n)))"
), module_file)
path_abs <- normalizePath(module_file, winslash = "/", mustWork = TRUE)
path_rel <- "aliasm.arl"
engine$eval(engine$read(sprintf('(import "%s" :refer :all)', path_abs))[[1]], env = env)
n_after_first <- engine$eval(engine$read("(getn)")[[1]], env = env)
engine$eval(engine$read(sprintf('(import "%s" :refer :all)', path_rel))[[1]], env = env)
n_after_second <- engine$eval(engine$read("(getn)")[[1]], env = env)
expect_equal(n_after_first, 1L)
expect_equal(n_after_second, 1L)
})
test_that("module is cached after first load", {
thin()
# Create a temporary module file
temp_dir <- tempdir()
module_path <- file.path(temp_dir, "test_cache_module.arl")
writeLines('(define cached-var 42)', module_path)
on.exit(unlink(module_path), add = TRUE)
engine <- make_engine()
env <- toplevel_env(engine)
# First load
engine$eval_text(sprintf('(load "%s")', arl_path(module_path)))
first_result <- engine$eval_text("cached-var")
expect_equal(first_result, 42)
# Modify the file
writeLines('(define cached-var 99)', module_path)
# Second load - should still get cached value
# (Note: actual caching behavior depends on implementation)
engine$eval_text(sprintf('(load "%s")', arl_path(module_path)))
})
test_that("load returns last value from module", {
thin()
# Create a temporary module
temp_dir <- tempdir()
module_path <- file.path(temp_dir, "test_return_module.arl")
writeLines(c(
'(define x 10)',
'(define y 20)',
'(+ x y)'
), module_path)
on.exit(unlink(module_path), add = TRUE)
engine <- make_engine()
result <- engine$eval_text(sprintf('(load "%s")', arl_path(module_path)))
# Should return 30 (the last expression)
expect_equal(result, 30)
})
test_that("load with syntax error in module", {
thin()
# Create a module with syntax error
temp_dir <- tempdir()
module_path <- file.path(temp_dir, "test_syntax_error.arl")
writeLines(c(
'(define x 10', # Unclosed paren
'(+ x 5)'
), module_path)
on.exit(unlink(module_path), add = TRUE)
engine <- make_engine()
expect_error(
engine$eval_text(sprintf('(load "%s")', arl_path(module_path))),
"Unclosed|parse|syntax|EOF|incomplete"
)
})
test_that("load with runtime error in module", {
thin()
# Create a module that errors at runtime
temp_dir <- tempdir()
module_path <- file.path(temp_dir, "test_runtime_error.arl")
writeLines(c(
'(define x 10)',
'(stop "deliberate runtime error")'
), module_path)
on.exit(unlink(module_path), add = TRUE)
engine <- make_engine()
expect_error(
engine$eval_text(sprintf('(load "%s")', arl_path(module_path))),
"deliberate runtime error"
)
})
test_that("multiple loads of same module", {
thin()
# Create a simple module
temp_dir <- tempdir()
module_path <- file.path(temp_dir, "test_multiple_load.arl")
writeLines('(define multi-load-var 123)', module_path)
on.exit(unlink(module_path), add = TRUE)
engine <- make_engine()
# Load multiple times
engine$eval_text(sprintf('(load "%s")', arl_path(module_path)))
engine$eval_text(sprintf('(load "%s")', arl_path(module_path)))
engine$eval_text(sprintf('(load "%s")', arl_path(module_path)))
result <- engine$eval_text("multi-load-var")
expect_equal(result, 123)
})
test_that("load can access previously defined symbols", {
thin()
# Create a module that uses predefined symbols
temp_dir <- tempdir()
module_path <- file.path(temp_dir, "test_access_symbols.arl")
writeLines('(+ existing-x 10)', module_path)
on.exit(unlink(module_path), add = TRUE)
engine <- make_engine()
env <- toplevel_env(engine)
# Define a symbol first
engine$eval_text("(define existing-x 5)")
# Load module that uses it
result <- engine$eval_text(sprintf('(load "%s")', arl_path(module_path)))
expect_equal(result, 15)
})
test_that("nested module loads", {
thin()
temp_dir <- tempdir()
# Create module B
module_b_path <- file.path(temp_dir, "test_module_b.arl")
writeLines('(define b-var 20)', module_b_path)
on.exit(unlink(module_b_path), add = TRUE)
# Create module A that loads B
module_a_path <- file.path(temp_dir, "test_module_a.arl")
writeLines(c(
sprintf('(load "%s")', arl_path(module_b_path)),
'(define a-var (+ b-var 10))'
), module_a_path)
on.exit(unlink(module_a_path), add = TRUE)
engine <- make_engine()
env <- toplevel_env(engine)
# Load module A (which loads B)
engine$eval_text(sprintf('(load "%s")', arl_path(module_a_path)))
# Should have both variables
result_a <- engine$eval_text("a-var")
expect_equal(result_a, 30)
result_b <- engine$eval_text("b-var")
expect_equal(result_b, 20)
})
test_that("circular import dependency produces clear error", {
thin()
temp_dir <- tempfile()
dir.create(temp_dir)
on.exit(unlink(temp_dir, recursive = TRUE), add = TRUE)
old_wd <- getwd()
setwd(temp_dir)
on.exit(setwd(old_wd), add = TRUE)
# Module A imports B, Module B imports A
writeLines(c(
"(module circ-a",
" (export a-fn)",
" (import circ-b)",
" (define a-fn (lambda () 1)))"
), file.path(temp_dir, "circ-a.arl"))
writeLines(c(
"(module circ-b",
" (export b-fn)",
" (import circ-a)",
" (define b-fn (lambda () 2)))"
), file.path(temp_dir, "circ-b.arl"))
engine <- make_engine()
expect_error(
engine$eval_text("(import circ-a)"),
"Circular dependency detected: circ-a -> circ-b -> circ-a"
)
})
test_that("import-runtime is reserved and errors", {
thin()
engine <- make_engine()
expect_error(
engine$eval_text("(import-runtime some-mod)"),
"reserved for future use"
)
})
test_that("load with relative path", {
thin()
# Create a module in current directory
temp_dir <- tempdir()
old_wd <- getwd()
setwd(temp_dir)
on.exit(setwd(old_wd), add = TRUE)
module_path <- "test_relative.arl"
writeLines('(define relative-var 777)', module_path)
on.exit(unlink(file.path(temp_dir, module_path)), add = TRUE)
engine <- make_engine()
env <- toplevel_env(engine)
result <- engine$eval_text(sprintf('(load "%s")', arl_path(module_path)))
var_result <- engine$eval_text("relative-var")
expect_equal(var_result, 777)
})
test_that("load empty module", {
thin()
temp_dir <- tempdir()
module_path <- file.path(temp_dir, "test_empty.arl")
writeLines('', module_path)
on.exit(unlink(module_path), add = TRUE)
engine <- make_engine()
# Loading empty module should succeed and return NULL or similar
result <- engine$eval_text(sprintf('(load "%s")', arl_path(module_path)))
# Result behavior may vary - just ensure no crash
expect_no_error(result)
})
test_that("load module with only comments", {
thin()
temp_dir <- tempdir()
module_path <- file.path(temp_dir, "test_comments_only.arl")
writeLines(c(
'; This is a comment',
'; Another comment',
'; ; More comments'
), module_path)
on.exit(unlink(module_path), add = TRUE)
engine <- make_engine()
# Should succeed
result <- engine$eval_text(sprintf('(load "%s")', arl_path(module_path)))
expect_no_error(result)
})
test_that("module defines macro", {
thin()
temp_dir <- tempdir()
module_path <- file.path(temp_dir, "test_macro_module.arl")
writeLines(c(
'(defmacro triple (x) `(* 3 ,x))'
), module_path)
on.exit(unlink(module_path), add = TRUE)
engine <- make_engine()
env <- toplevel_env(engine)
# Load module with macro
engine$eval_text(sprintf('(load "%s")', arl_path(module_path)))
# Use the macro
result <- engine$eval_text("(triple 7)")
expect_equal(result, 21)
})
# ============================================================================
# Windows backslash path regression tests
# ============================================================================
test_that("Windows-style backslash paths work when properly normalized", {
thin()
engine <- make_engine()
# Create a temp file in a directory whose name would trigger escape issues
# if backslashes weren't normalized (e.g. contains 't', 'n', 'r' after separator)
tmp <- tempfile(pattern = "test_path")
writeLines("(define win-path-test 42)", tmp)
on.exit(unlink(tmp))
# arl_path should convert backslashes to forward slashes
safe <- arl_path(tmp)
expect_false(grepl("\\\\", safe))
# Engine should successfully load via the normalized path
result <- engine$eval_text(sprintf('(load "%s")', safe))
expect_equal(engine$eval_text("win-path-test"), 42)
})
test_that("backslash escapes in Arl strings produce correct characters", {
thin()
engine <- make_engine()
# Verify \t, \n, \\ are processed as escape sequences (standard behavior)
expect_equal(engine$eval_text('"hello\\tworld"'), "hello\tworld")
expect_equal(engine$eval_text('"hello\\nworld"'), "hello\nworld")
expect_equal(engine$eval_text('"back\\\\slash"'), "back\\slash")
})
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.