Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions R/loadfast.R
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,9 @@ load_fast <- function(path = ".", helpers = TRUE, attach_testthat = NULL, full =
"]"
)

.loadfast.run_onload(ns_env, abs_path, pkg_name)
.timer("incr .onLoad")

.loadfast.source_helpers(abs_path, pkg_env, helpers, attach_testthat, pkg_name)
.timer("TOTAL (incremental)")
return(invisible(ns_env))
Expand Down Expand Up @@ -398,6 +401,9 @@ load_fast <- function(path = ".", helpers = TRUE, attach_testthat = NULL, full =
}
.timer("attach testthat")

.loadfast.run_onload(ns_env, abs_path, pkg_name)
.timer(".onLoad")

pkg_env <- attach(NULL, name = pkg_env_name)
list2env(as.list(ns_env, all.names = FALSE), envir = pkg_env)
list2env(as.list(impenv, all.names = TRUE), envir = pkg_env)
Expand Down Expand Up @@ -496,6 +502,17 @@ load_fast_register_reload <- function(path = ".", files, reason = NULL) {
invisible(TRUE)
}

.loadfast.run_onload <- function(ns_env, abs_path, pkg_name) {
if (!exists(".onLoad", envir = ns_env, inherits = FALSE)) return(invisible(NULL))
tryCatch(
get(".onLoad", envir = ns_env, inherits = FALSE)(dirname(abs_path), pkg_name),
error = function(e) {
warning("Error in .onLoad() for '", pkg_name, "': ", conditionMessage(e), call. = FALSE)
}
)
invisible(NULL)
}

.loadfast.source_one <- function(f, ns_env) {
s4_pattern <- "no definition for class"
tryCatch(
Expand Down
18 changes: 0 additions & 18 deletions TECHNICAL_DEBT.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,24 +48,6 @@ If the `Package:` field in `DESCRIPTION` changes in place for the same directory

## Low-priority debt

### 5. Load hook behavior is not explicitly tested
**Why this matters**

The loader aims to behave like a practical development-time package loader, but the test suite does not currently pin down the behavior of `.onLoad()` and `.onAttach()` hooks.

**Risk**
- Hook execution semantics may drift unnoticed during refactors
- Full and incremental loads may behave inconsistently
- Package state initialized by hooks may be wrong in edge cases

**Preferred fix**
- Add focused tests that define and exercise `.onLoad()` / `.onAttach()` in a temp package
- Verify behavior on first load, incremental reload, and `full = TRUE`
- Document any intentional differences from `devtools::load_all()`

**Priority**
- Low

## Low-priority debt

### 3. Testthat detection logic is duplicated
Expand Down
56 changes: 56 additions & 0 deletions test_loadfast.R
Original file line number Diff line number Diff line change
Expand Up @@ -1931,6 +1931,62 @@ check("lockfile: full reload resets warning baseline", quote(
!any(grepl("renv.lock changed", lock_reload_full$warnings))
))

# --- 4l: .onLoad hook is called during full and incremental loads ---
cat("\n--- 4l: .onLoad hook execution ---\n\n")

tmp_onload <- tempfile("loadfast_onload_")
copy_baseline(tmp_onload)
remove_renv_lock(tmp_onload)

writeLines(c(
".onLoad_call_count <- 0L",
".onLoad <- function(libname, pkgname) {",
" .onLoad_call_count <<- .onLoad_call_count + 1L",
" .onLoad_libname <<- libname",
" .onLoad_pkgname <<- pkgname",
"}"
), file.path(tmp_onload, "R", "zzz.R"))

ns_onload <- load_fast(tmp_onload, helpers = FALSE, attach_testthat = FALSE)

check(".onLoad: is called on full load", quote(
exists(".onLoad_call_count", envir = ns_onload, inherits = FALSE) &&
get(".onLoad_call_count", envir = ns_onload) == 1L
))

check(".onLoad: receives correct pkgname", quote(
get(".onLoad_pkgname", envir = ns_onload) == "devpackage"
))

check(".onLoad: receives dirname(abs_path) as libname", quote(
identical(
normalizePath(get(".onLoad_libname", envir = ns_onload), mustWork = FALSE),
normalizePath(dirname(tmp_onload), mustWork = FALSE)
)
))

# Incremental reload — nothing changed => short-circuit, .onLoad not re-called
ns_onload_nochg <- load_fast(tmp_onload, helpers = FALSE, attach_testthat = FALSE)

check(".onLoad: not called again on no-change reload", quote(
get(".onLoad_call_count", envir = ns_onload_nochg) == 1L
))

# Trigger an incremental reload by modifying base.R
writeLines(c(
"add <- function(a, b) a + b + 9999"
), file.path(tmp_onload, "R", "base.R"))

ns_onload_incr <- load_fast(tmp_onload, helpers = FALSE, attach_testthat = FALSE)

check(".onLoad: called again on incremental reload (files changed)", quote(
get(".onLoad_call_count", envir = ns_onload_incr) == 2L
))

check(".onLoad: add() reflects incremental change", quote(
get("add", envir = ns_onload_incr)(1, 2) == 10002
))

# ============================================================================
# Summary
# ============================================================================
Expand Down
Loading