1test_that("cpp_source works with the `code` parameter", { 2 skip_on_os("solaris") 3 dll_info <- cpp_source( 4 code = ' 5 #include "cpp11/integers.hpp" 6 7 [[cpp11::register]] 8 int num_odd(cpp11::integers x) { 9 int total = 0; 10 for (int val : x) { 11 if ((val % 2) == 1) { 12 ++total; 13 } 14 } 15 return total; 16 } 17 ', clean = TRUE) 18 on.exit(dyn.unload(dll_info[["path"]])) 19 20 expect_equal(num_odd(as.integer(c(1:10, 15, 23))), 7) 21}) 22 23test_that("cpp_source works with the `file` parameter", { 24 skip_on_os("solaris") 25 tf <- tempfile(fileext = ".cpp") 26 writeLines( 27 "[[cpp11::register]] 28 bool always_true() { 29 return true; 30 } 31 ", tf) 32 on.exit(unlink(tf)) 33 34 dll_info <- cpp_source(tf, clean = TRUE, quiet = TRUE) 35 on.exit(dyn.unload(dll_info[["path"]]), add = TRUE) 36 37 expect_true(always_true()) 38}) 39 40test_that("cpp_source works with files called `cpp11.cpp`", { 41 skip_on_os("solaris") 42 tf <- file.path(tempdir(), "cpp11.cpp") 43 writeLines( 44 "[[cpp11::register]] 45 bool always_true() { 46 return true; 47 } 48 ", tf) 49 on.exit(unlink(tf)) 50 51 dll_info <- cpp_source(tf, clean = TRUE, quiet = TRUE) 52 on.exit(dyn.unload(dll_info[["path"]]), add = TRUE) 53 54 expect_true(always_true()) 55}) 56 57test_that("cpp_source returns original file name on error", { 58 59 expect_output(try(cpp_source(test_path("single_error.cpp"), clean = TRUE), silent = TRUE), 60 normalizePath(test_path("single_error.cpp"), winslash = "/"), fixed = TRUE) 61 62 #error generated for incorrect attributes is separate from compilation errors 63 expect_error(cpp_source(test_path("single_incorrect.cpp"), clean = TRUE), 64 normalizePath(test_path("single_incorrect.cpp"), winslash = "/"), fixed = TRUE) 65 66}) 67 68test_that("cpp_source lets you set the C++ standard", { 69 skip_on_os("solaris") 70 skip_on_os("windows") # Older windows toolchains do not support C++14 71 tf <- tempfile(fileext = ".cpp") 72 writeLines( 73 '#include <string> 74 using namespace std::string_literals; 75 [[cpp11::register]] 76 std::string fun() { 77 auto str = "hello_world"s; 78 return str; 79 } 80 ', tf) 81 on.exit(unlink(tf)) 82 83 dll_info <- cpp_source(tf, clean = TRUE, quiet = TRUE, cxx_std = "CXX14") 84 on.exit(dyn.unload(dll_info[["path"]]), add = TRUE) 85 86 expect_equal(fun(), "hello_world") 87}) 88 89test_that("generate_cpp_name works", { 90 expect_equal( 91 generate_cpp_name("foo.cpp"), 92 "foo.cpp" 93 ) 94 95 expect_equal( 96 generate_cpp_name("foo.cpp", loaded_dlls = "foo"), 97 "foo_2.cpp" 98 ) 99 100expect_equal( 101 generate_cpp_name("foo.cpp", loaded_dlls = c("foo", "foo_2")), 102 "foo_3.cpp" 103 ) 104}) 105 106test_that("generate_include_paths handles paths with spaces", { 107 if (is_windows()) { 108 mockery::stub(generate_include_paths, "system.file", "C:\\a path with spaces\\cpp11") 109 expect_equal(generate_include_paths("cpp11"), "-I\"C:\\a path with spaces\\cpp11\"") 110 } else { 111 mockery::stub(generate_include_paths, "system.file", "/a path with spaces/cpp11") 112 expect_equal(generate_include_paths("cpp11"), "-I'/a path with spaces/cpp11'") 113 } 114}) 115 116test_that("check_valid_attributes does not return an error if all registers are correct", { 117 expect_error_free( 118 cpp11::cpp_source(clean = TRUE, code = '#include <cpp11.hpp> 119 using namespace cpp11::literals; 120 [[cpp11::register]] 121 cpp11::list fn() { 122 cpp11::writable::list x; 123 x.push_back({"foo"_nm = 1}); 124 return x; 125 } 126 [[cpp11::register]] 127 cpp11::list fn2() { 128 cpp11::writable::list x; 129 x.push_back({"foo"_nm = 1}); 130 return x; 131 }')) 132 expect_error_free( 133 cpp11::cpp_source(clean = TRUE, 134 code = '#include <cpp11/R.hpp> 135 #include <RProgress.h> 136 137 [[cpp11::linking_to("progress")]] 138 139 [[cpp11::register]] void show_progress() { 140 RProgress::RProgress pb("Processing [:bar] ETA: :eta"); 141 142 pb.tick(0); 143 for (int i = 0; i < 100; i++) { 144 usleep(2.0 / 100 * 1000000); 145 pb.tick(); 146 } 147 } 148 ') 149 ) 150}) 151 152test_that("check_valid_attributes returns an error if one or more registers is incorrect", { 153 expect_error( 154 cpp11::cpp_source(code = '#include <cpp11.hpp> 155 using namespace cpp11::literals; 156 [[cpp11::reg]] 157 cpp11::list fn() { 158 cpp11::writable::list x; 159 x.push_back({"foo"_nm = 1}); 160 return x; 161 } 162 [[cpp11::register]] 163 cpp11::list fn2() { 164 cpp11::writable::list x; 165 x.push_back({"foo"_nm = 1}); 166 return x; 167 }')) 168 169 expect_error( 170 cpp11::cpp_source(code = '#include <cpp11.hpp> 171 using namespace cpp11::literals; 172 [[cpp11::reg]] 173 cpp11::list fn() { 174 cpp11::writable::list x; 175 x.push_back({"foo"_nm = 1}); 176 return x; 177 }')) 178 179 expect_error( 180 cpp11::cpp_source(code = '#include <cpp11.hpp> 181 using namespace cpp11::literals; 182 [[cpp11::reg]] 183 cpp11::list fn() { 184 cpp11::writable::list x; 185 x.push_back({"foo"_nm = 1}); 186 return x; 187 } 188 [[cpp11::egister]] 189 cpp11::list fn2() { 190 cpp11::writable::list x; 191 x.push_back({"foo"_nm = 1}); 192 return x; 193 }')) 194 195 196 197 expect_error( 198 cpp11::cpp_source( 199 code = ' 200 #include <cpp11/R.hpp> 201 #include <RProgress.h> 202 [[cpp11::link_to("progress")]] 203 [[cpp11::register]] void show_progress() { 204 RProgress::RProgress pb("Processing [:bar] ETA: :eta"); 205 pb.tick(0); 206 for (int i = 0; i < 100; i++) { 207 usleep(2.0 / 100 * 1000000); 208 pb.tick(); 209 } 210 } 211')) 212}) 213 214test_that("cpp_source(d) functions work after sourcing file more than once", { 215 cpp11::cpp_source(test_path("single.cpp"), clean = TRUE) 216 expect_equal(foo(), 1) 217 cpp11::cpp_source(test_path("single.cpp"), clean = TRUE) 218 expect_equal(foo(), 1) 219}) 220