Skip to content

Commit baecb40

Browse files
committed
add tests for compiler var whitespace normalization
1 parent 059fd10 commit baecb40

1 file changed

Lines changed: 116 additions & 0 deletions

File tree

tests/testInstallLibs.R

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
# Unit tests for the install-time helpers defined in src/install.libs.R.
2+
#
3+
# These helpers run during 'R CMD INSTALL' (before the package is loadable),
4+
# so they live in src/install.libs.R rather than in R/ and cannot be reached
5+
# via library(RcppParallel). Instead, we locate that source file, evaluate the
6+
# helper definitions that precede its '# Main' section (evaluating the whole
7+
# file would trigger an actual install), and exercise them directly.
8+
9+
# locate src/install.libs.R by walking up from the working directory; this is
10+
# reachable when tests are run from the package sources. If it cannot be found
11+
# (e.g. only the installed package is available), skip rather than fail.
12+
findInstallLibs <- function() {
13+
dir <- normalizePath(getwd(), mustWork = FALSE)
14+
for (i in 1:8) {
15+
candidate <- file.path(dir, "src", "install.libs.R")
16+
if (file.exists(candidate))
17+
return(candidate)
18+
parent <- dirname(dir)
19+
if (identical(parent, dir))
20+
break
21+
dir <- parent
22+
}
23+
NA_character_
24+
}
25+
26+
path <- findInstallLibs()
27+
if (is.na(path)) {
28+
writeLines("skipping: could not locate src/install.libs.R")
29+
quit(save = "no", status = 0L)
30+
}
31+
32+
# evaluate only the helper definitions, stopping before the '# Main' section
33+
lines <- readLines(path)
34+
marker <- grep("^# Main", lines)
35+
if (length(marker))
36+
lines <- lines[seq_len(marker[[1L]] - 1L)]
37+
38+
env <- new.env(parent = globalenv())
39+
eval(parse(text = paste(lines, collapse = "\n")), envir = env)
40+
splitCompilerVar <- get("splitCompilerVar", envir = env)
41+
42+
# minimal assertion harness
43+
failures <- 0L
44+
check <- function(cond, label) {
45+
ok <- isTRUE(cond)
46+
if (!ok)
47+
failures <<- failures + 1L
48+
writeLines(sprintf("%s - %s", if (ok) "PASS" else "FAIL", label))
49+
}
50+
51+
# run 'expr' with the given environment variables set, restoring the previous
52+
# environment afterwards so tests can't leak into one another (or into CC/CXX)
53+
withEnv <- function(vars, expr) {
54+
keys <- names(vars)
55+
previous <- Sys.getenv(keys, unset = NA, names = TRUE)
56+
on.exit({
57+
set <- previous[!is.na(previous)]
58+
if (length(set))
59+
do.call(Sys.setenv, as.list(set))
60+
unset <- keys[is.na(previous)]
61+
if (length(unset))
62+
Sys.unsetenv(unset)
63+
}, add = TRUE)
64+
do.call(Sys.setenv, as.list(vars))
65+
force(expr)
66+
}
67+
68+
# the regression this branch fixes: '$(CCACHE) g++' with an empty CCACHE
69+
# expands to ' g++' (leading space). scan() yields a single token, and the
70+
# old early-return left the leading space in place, forwarding an invalid
71+
# '-DCMAKE_CXX_COMPILER= g++' to CMake. The compiler must be normalized.
72+
withEnv(c(TEST_CXX = " g++", TEST_CXXFLAGS = ""), {
73+
result <- splitCompilerVar("TEST_CXX", "TEST_CXXFLAGS")
74+
check(isTRUE(result), "leading-whitespace compiler returns TRUE")
75+
check(identical(Sys.getenv("TEST_CXX"), "g++"),
76+
"leading-whitespace compiler is normalized (no leading space)")
77+
check(identical(Sys.getenv("TEST_CXXFLAGS"), ""),
78+
"leading-whitespace compiler leaves flags untouched")
79+
})
80+
81+
# a plain single-token compiler is already clean, but should still be
82+
# re-set (and report TRUE) so the normalization path is exercised uniformly
83+
withEnv(c(TEST_CXX = "g++", TEST_CXXFLAGS = ""), {
84+
result <- splitCompilerVar("TEST_CXX", "TEST_CXXFLAGS")
85+
check(isTRUE(result), "single-token compiler returns TRUE")
86+
check(identical(Sys.getenv("TEST_CXX"), "g++"),
87+
"single-token compiler is preserved")
88+
})
89+
90+
# trailing tokens are split off as flags and prepended to any existing flags
91+
withEnv(c(TEST_CXX = "g++ -std=c++17 -O2", TEST_CXXFLAGS = "-Wall"), {
92+
result <- splitCompilerVar("TEST_CXX", "TEST_CXXFLAGS")
93+
check(isTRUE(result), "compiler with flags returns TRUE")
94+
check(identical(Sys.getenv("TEST_CXX"), "g++"),
95+
"compiler with flags splits off the compiler token")
96+
check(identical(Sys.getenv("TEST_CXXFLAGS"), "-std=c++17 -O2 -Wall"),
97+
"compiler with flags prepends split flags to existing flags")
98+
})
99+
100+
# a whitespace-only value tokenizes to nothing: report FALSE and change nothing
101+
withEnv(c(TEST_CXX = " ", TEST_CXXFLAGS = "-Wall"), {
102+
result <- splitCompilerVar("TEST_CXX", "TEST_CXXFLAGS")
103+
check(identical(result, FALSE), "whitespace-only compiler returns FALSE")
104+
check(identical(Sys.getenv("TEST_CXXFLAGS"), "-Wall"),
105+
"whitespace-only compiler leaves flags untouched")
106+
})
107+
108+
# an unset compiler variable is a no-op that reports FALSE
109+
Sys.unsetenv("TEST_CXX_UNSET")
110+
check(identical(splitCompilerVar("TEST_CXX_UNSET", "TEST_CXXFLAGS"), FALSE),
111+
"unset compiler variable returns FALSE")
112+
113+
if (failures > 0L)
114+
stop(sprintf("%d install.libs.R helper test(s) failed", failures))
115+
116+
writeLines("all install.libs.R helper tests passed")

0 commit comments

Comments
 (0)