Skip to content

Commit f701ff3

Browse files
committed
Additional unit tests and removal of #nocov tags
1 parent b96082e commit f701ff3

File tree

8 files changed

+227
-16
lines changed

8 files changed

+227
-16
lines changed

ChangeLog

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
2026-01-13 Dirk Eddelbuettel <edd@debian.org>
2+
3+
* inst/tinytest/cpp/attributes_extended.cpp: New unit tests
4+
* inst/tinytest/test_attributes_extended.R: Idem
5+
* inst/tinytest/test_compile_attributes_errors.R: Idem
6+
* inst/tinytest/test_cppfunction_errors.R: Idem
7+
* inst/tinytest/test_sourcecpp_errors.R: Idem
8+
* R/Attributes.R: Remove #nocov tags
9+
* src/attributes.cpp: Idem
10+
111
2026-01-12 Dirk Eddelbuettel <edd@debian.org>
212

313
* DESCRIPTION (Version, Date): Roll micro version and date

R/Attributes.R

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,13 @@ sourceCpp <- function(file = "",
5555
file <- normalizePath(file, winslash = "/")
5656

5757
# error if the file extension isn't one supported by R CMD SHLIB
58-
if (! tools::file_ext(file) %in% c("cc", "cpp")) { # #nocov start
58+
if (! tools::file_ext(file) %in% c("cc", "cpp")) {
5959
stop("The filename '", basename(file), "' does not have an ",
6060
"extension of .cc or .cpp so cannot be compiled.")
61-
} # #nocov end
61+
}
6262

6363
# validate that there are no spaces in the path on windows
64-
if (.Platform$OS.type == "windows") { # #nocov start
64+
if (.Platform$OS.type == "windows") {
6565
if (grepl(' ', basename(file), fixed=TRUE)) {
6666
stop("The filename '", basename(file), "' contains spaces. This ",
6767
"is not permitted.")
@@ -73,7 +73,7 @@ sourceCpp <- function(file = "",
7373
"non-Windows platforms.")
7474
}
7575
windowsDebugDLL <- FALSE # now we do not need to deal with OS choice below
76-
} # #nocov end
76+
}
7777
}
7878

7979
# get the context (does code generation as necessary)
@@ -323,9 +323,9 @@ cppFunction <- function(code,
323323

324324
# verify that a single function was exported and return it
325325
if (length(exported$functions) == 0)
326-
stop("No function definition found") # #nocov
326+
stop("No function definition found")
327327
else if (length(exported$functions) > 1)
328-
stop("More than one function definition") # #nocov
328+
stop("More than one function definition")
329329
else {
330330
functionName <- exported$functions[[1]]
331331
invisible(get(functionName, env))
@@ -417,7 +417,7 @@ compileAttributes <- function(pkgdir = ".", verbose = getOption("verbose")) {
417417
pkgdir <- normalizePath(pkgdir, winslash = "/")
418418
descFile <- file.path(pkgdir,"DESCRIPTION")
419419
if (!file.exists(descFile))
420-
stop("pkgdir must refer to the directory containing an R package") # #nocov
420+
stop("pkgdir must refer to the directory containing an R package")
421421
pkgDesc <- read.dcf(descFile)[1,]
422422
pkgname = .readPkgDescField(pkgDesc, "Package")
423423
depends <- c(.readPkgDescField(pkgDesc, "Depends", character()),
@@ -429,7 +429,7 @@ compileAttributes <- function(pkgdir = ".", verbose = getOption("verbose")) {
429429
# check the NAMESPACE file to see if dynamic registration is enabled
430430
namespaceFile <- file.path(pkgdir, "NAMESPACE")
431431
if (!file.exists(namespaceFile))
432-
stop("pkgdir must refer to the directory containing an R package") # #nocov
432+
stop("pkgdir must refer to the directory containing an R package")
433433
pkgNamespace <- readLines(namespaceFile, warn = FALSE)
434434
registration <- any(grepl("^\\s*useDynLib.*\\.registration\\s*=\\s*TRUE.*$", pkgNamespace))
435435

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#include <Rcpp.h>
2+
using namespace Rcpp;
3+
4+
// Test 4.1: Named Export Parameter
5+
// Coverage target: src/attributes.cpp:360
6+
7+
// [[Rcpp::export(name = "custom_exported_name")]]
8+
int test_named_export() { return 123; }
9+
10+
// [[Rcpp::export(name = "another.custom.name")]]
11+
std::string test_named_export_with_dots() { return "dotted name"; }
12+
13+
// Test 4.2: Unnamed Export Parameter
14+
// Coverage target: src/attributes.cpp:365
15+
16+
// [[Rcpp::export(my_custom_name)]]
17+
int test_unnamed_export_param() { return 456; }
18+
19+
// Test 4.3: RNG Parameter Variations
20+
// Coverage target: src/attributes.cpp:382-383
21+
22+
// [[Rcpp::export(rng = true)]]
23+
int test_rng_lowercase_true() { return 789; }
24+
25+
// [[Rcpp::export(rng = TRUE)]]
26+
int test_rng_uppercase_true() { return 101; }
27+
28+
// [[Rcpp::export(rng = false)]]
29+
int test_rng_lowercase_false() { return 202; }
30+
31+
// Test 4.4: Invisible Parameter Variations
32+
// Coverage target: src/attributes.cpp:391-392
33+
34+
// [[Rcpp::export(invisible = true)]]
35+
void test_invisible_lowercase_true() {
36+
// Side effect only - returns invisibly
37+
}
38+
39+
// [[Rcpp::export(invisible = TRUE)]]
40+
void test_invisible_uppercase_true() {
41+
// Side effect only - returns invisibly
42+
}
43+
44+
// Test 4.5: exportedCppName with Dots
45+
// Coverage target: src/attributes.cpp:373-377
46+
// This tests the conversion of dots to underscores in C++ names
47+
48+
// [[Rcpp::export(name = "test.with.dots")]]
49+
int test_cpp_name_conversion() { return 999; }
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
## Copyright (C) 2026 Dirk Eddelbuettel
2+
##
3+
## This file is part of Rcpp.
4+
##
5+
## Rcpp is free software: you can redistribute it and/or modify it
6+
## under the terms of the GNU General Public License as published by
7+
## the Free Software Foundation, either version 2 of the License, or
8+
## (at your option) any later version.
9+
##
10+
## Rcpp is distributed in the hope that it will be useful, but
11+
## WITHOUT ANY WARRANTY; without even the implied warranty of
12+
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
## GNU General Public License for more details.
14+
##
15+
## You should have received a copy of the GNU General Public License
16+
## along with Rcpp. If not, see <http://www.gnu.org/licenses/>.
17+
18+
if (Sys.getenv("RunAllRcppTests") != "yes") exit_file("Set 'RunAllRcppTests' to 'yes' to run.")
19+
20+
Rcpp::sourceCpp("cpp/attributes_extended.cpp")
21+
22+
## Test named exports
23+
expect_equal(custom_exported_name(), 123)
24+
expect_equal(another.custom.name(), "dotted name")
25+
26+
## Test unnamed export parameter
27+
expect_equal(my_custom_name(), 456)
28+
29+
## Test RNG parameters
30+
expect_equal(test_rng_lowercase_true(), 789)
31+
expect_equal(test_rng_uppercase_true(), 101)
32+
expect_equal(test_rng_lowercase_false(), 202)
33+
34+
## Test invisible parameters (should return invisibly)
35+
result1 <- withVisible(test_invisible_lowercase_true())
36+
expect_false(result1$visible)
37+
38+
result2 <- withVisible(test_invisible_uppercase_true())
39+
expect_false(result2$visible)
40+
41+
## Test C++ name conversion (dots to underscores)
42+
expect_equal(test.with.dots(), 999)
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
## Copyright (C) 2026 Dirk Eddelbuettel
2+
##
3+
## This file is part of Rcpp.
4+
##
5+
## Rcpp is free software: you can redistribute it and/or modify it
6+
## under the terms of the GNU General Public License as published by
7+
## the Free Software Foundation, either version 2 of the License, or
8+
## (at your option) any later version.
9+
##
10+
## Rcpp is distributed in the hope that it will be useful, but
11+
## WITHOUT ANY WARRANTY; without even the implied warranty of
12+
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
## GNU General Public License for more details.
14+
##
15+
## You should have received a copy of the GNU General Public License
16+
## along with Rcpp. If not, see <http://www.gnu.org/licenses/>.
17+
18+
if (Sys.getenv("RunAllRcppTests") != "yes") exit_file("Set 'RunAllRcppTests' to 'yes' to run.")
19+
20+
## Test 3.1: Missing DESCRIPTION File
21+
## Coverage target: R/Attributes.R:420
22+
tmpdir <- tempfile()
23+
dir.create(tmpdir)
24+
expect_error(compileAttributes(tmpdir), "must refer to the directory containing an R package")
25+
unlink(tmpdir, recursive = TRUE)
26+
27+
## Test 3.2: Missing NAMESPACE File
28+
## Coverage target: R/Attributes.R:432
29+
tmpdir <- tempfile()
30+
dir.create(tmpdir)
31+
writeLines("Package: TestPkg", file.path(tmpdir, "DESCRIPTION"))
32+
expect_error(compileAttributes(tmpdir), "must refer to the directory containing an R package")
33+
unlink(tmpdir, recursive = TRUE)
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
## Copyright (C) 2026 Dirk Eddelbuettel
2+
##
3+
## This file is part of Rcpp.
4+
##
5+
## Rcpp is free software: you can redistribute it and/or modify it
6+
## under the terms of the GNU General Public License as published by
7+
## the Free Software Foundation, either version 2 of the License, or
8+
## (at your option) any later version.
9+
##
10+
## Rcpp is distributed in the hope that it will be useful, but
11+
## WITHOUT ANY WARRANTY; without even the implied warranty of
12+
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
## GNU General Public License for more details.
14+
##
15+
## You should have received a copy of the GNU General Public License
16+
## along with Rcpp. If not, see <http://www.gnu.org/licenses/>.
17+
18+
if (Sys.getenv("RunAllRcppTests") != "yes") exit_file("Set 'RunAllRcppTests' to 'yes' to run.")
19+
20+
## Test 2.1: No Function Definition
21+
## Coverage target: R/Attributes.R:326
22+
code <- "int x = 5;" # No function, just a variable
23+
expect_error(cppFunction(code), "No function definition found")
24+
25+
## Test 2.2: Multiple Function Definitions
26+
## Coverage target: R/Attributes.R:328
27+
code <- "
28+
// [[Rcpp::export]]
29+
int foo() { return 1; }
30+
31+
// [[Rcpp::export]]
32+
int bar() { return 2; }
33+
"
34+
## compilation dies sooner so we never actually see the messages
35+
expect_error(cppFunction(code)) #, "More than one function definition")
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
## Copyright (C) 2026 Dirk Eddelbuettel
2+
##
3+
## This file is part of Rcpp.
4+
##
5+
## Rcpp is free software: you can redistribute it and/or modify it
6+
## under the terms of the GNU General Public License as published by
7+
## the Free Software Foundation, either version 2 of the License, or
8+
## (at your option) any later version.
9+
##
10+
## Rcpp is distributed in the hope that it will be useful, but
11+
## WITHOUT ANY WARRANTY; without even the implied warranty of
12+
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
## GNU General Public License for more details.
14+
##
15+
## You should have received a copy of the GNU General Public License
16+
## along with Rcpp. If not, see <http://www.gnu.org/licenses/>.
17+
18+
if (Sys.getenv("RunAllRcppTests") != "yes") exit_file("Set 'RunAllRcppTests' to 'yes' to run.")
19+
20+
## Test 1.1: Invalid File Extension
21+
## Coverage target: R/Attributes.R:58-61
22+
tmpfile <- tempfile(fileext = ".c")
23+
writeLines("int main() { return 0; }", tmpfile)
24+
expect_error(sourceCpp(tmpfile), "does not have an extension of .cc or .cpp")
25+
unlink(tmpfile)
26+
27+
## Test 1.2: Filename with Spaces on Windows
28+
## Coverage target: R/Attributes.R:64-68
29+
if (.Platform$OS.type == "windows") {
30+
tmpfile <- file.path(tempdir(), "test file.cpp")
31+
writeLines("#include <Rcpp.h>", tmpfile)
32+
expect_error(sourceCpp(tmpfile), "contains spaces")
33+
unlink(tmpfile)
34+
}
35+
36+
## Test 1.3: windowsDebugDLL on Non-Windows
37+
## Coverage target: R/Attributes.R:70-76
38+
if (.Platform$OS.type != "windows") {
39+
code <- "// [[Rcpp::export]]\nint test_fn() { return 42; }"
40+
expect_message(sourceCpp(code = code, windowsDebugDLL = TRUE, verbose = TRUE),
41+
"windowsDebugDLL.*ignored")
42+
}

src/attributes.cpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -357,39 +357,39 @@ namespace attributes {
357357
// check for explicit name parameter
358358
if (hasParameter(kExportName))
359359
{
360-
return paramNamed(kExportName).value(); // #nocov
360+
return paramNamed(kExportName).value();
361361
}
362362
// otherwise un-named parameter in the first slot
363363
else if (!params().empty() && params()[0].value().empty())
364364
{
365-
return params()[0].name(); // #nocov
365+
return params()[0].name();
366366
}
367367
// otherwise the actual function name
368368
{
369369
return function().name();
370370
}
371371
}
372372

373-
std::string exportedCppName() const { // #nocov start
373+
std::string exportedCppName() const {
374374
std::string name = exportedName();
375375
std::replace(name.begin(), name.end(), '.', '_');
376376
return name;
377-
} // #nocov end
377+
}
378378

379379
bool rng() const {
380380
Param rngParam = paramNamed(kExportRng);
381381
if (!rngParam.empty())
382-
return rngParam.value() == kParamValueTrue || // #nocov
383-
rngParam.value() == kParamValueTRUE; // #nocov
382+
return rngParam.value() == kParamValueTrue ||
383+
rngParam.value() == kParamValueTRUE;
384384
else
385385
return true;
386386
}
387387

388388
bool invisible() const {
389389
Param invisibleParam = paramNamed(kExportInvisible);
390390
if (!invisibleParam.empty())
391-
return invisibleParam.value() == kParamValueTrue || // #nocov
392-
invisibleParam.value() == kParamValueTRUE; // #nocov
391+
return invisibleParam.value() == kParamValueTrue ||
392+
invisibleParam.value() == kParamValueTRUE;
393393
else
394394
return false;
395395
}

0 commit comments

Comments
 (0)