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