print_code <- function(filename) {
  cat(readChar(filename, file.info(filename)$size))
}

This vignette builds and tests some example projects which use mockcpp, to show you how you can include them in your package.

Rcpp

We have created a toy random number generator using Rcpp

print_code(
  system.file('testrcpp/src/code.h', package = 'mockcpp', mustWork = TRUE)
)
#> #include <Rcpp.h>
#> 
#> class Random {
#>   public:
#>     virtual Rcpp::NumericVector uniform(size_t size) const {
#>       return Rcpp::runif(size);
#>     };
#> };

Let's say we want to mock the output of our RNG. We can write a testthat test like so:

print_code(
  system.file('testrcpp/src/test-example.cpp', package = 'mockcpp', mustWork = TRUE)
)
#> #include <Rcpp.h>
#> #include <testthat.h>
#> #include <mockcpp.h>
#> #include "code.h"
#> 
#> class MockRandom : public Random {
#>   MAKE_CONST_MOCK1(uniform, Rcpp::NumericVector(size_t), override);
#> };
#> 
#> using trompeloeil::_;
#> 
#> context("Can mock an RNG") {
#>   
#>   test_that("you can change the return value") {
#>     MockRandom rng;
#>     auto mock_values = Rcpp::NumericVector::create(.1, .2, .3, .4, .5);
#>     REQUIRE_CALL(rng, uniform(_)).RETURN(mock_values);
#>     expect_true(Rcpp::is_true(Rcpp::all(rng.uniform(5) == mock_values)));
#>   }
#> 
#> }

We can then build our package and run the test to ensure our mocking works:

# load test package
pkgload::load_all(
  system.file('testrcpp', package = 'mockcpp', mustWork = TRUE)
)
#> ℹ Loading testrcpp
#> Re-compiling testrcpp
#>   
─  installing *source* package ‘testrcpp’ ...
#> 

   ** using staged installation
#> 

   ** libs
#> 

   g++ -std=gnu++14 -I"/usr/local/lib/R/include" -DNDEBUG  -I'/usr/local/lib/R/site-library/testthat/include' -I'/tmp/RtmpPc0dLV/Rinsta1b2504ff/mockcpp/include' -I'/usr/local/lib/R/site-library/Rcpp/include' -I/usr/local/include   -fpic  -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -g  -c test-example.cpp -o test-example.o
#> 

   g++ -std=gnu++14 -I"/usr/local/lib/R/include" -DNDEBUG  -I'/usr/local/lib/R/site-library/testthat/include' -I'/tmp/RtmpPc0dLV/Rinsta1b2504ff/mockcpp/include' -I'/usr/local/lib/R/site-library/Rcpp/include' -I/usr/local/include   -fpic  -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -g  -c test-runner.cpp -o test-runner.o
#> 

   g++ -std=gnu++14 -shared -L/usr/local/lib/R/lib -L/usr/local/lib -o testrcpp.so test-example.o test-runner.o -L/usr/local/lib/R/lib -lR
#> 

   installing to /tmp/RtmptcByDM/devtools_install_647ae35f37/00LOCK-testrcpp/00new/testrcpp/libs
#> 

   ** checking absolute paths in shared objects and dynamic libraries
#> 

─  DONE (testrcpp)
#> 
#> Warning: package 'testthat' was built under R version 4.0.3

# run the test
testthat::test_file(
  system.file(
    'testrcpp/tests/testthat/test-cpp.R',
    package = 'mockcpp',
    mustWork = TRUE
  )
)
#> 
#> ══ Testing test-cpp.R ══════════════════════════════════════════════════════════
#> 
[ FAIL 0 | WARN 0 | SKIP 0 | PASS 0 ]
[ FAIL 0 | WARN 0 | SKIP 0 | PASS 0 ]
[ FAIL 0 | WARN 0 | SKIP 0 | PASS 1 ] Done!

Cpp11

We can create a similar test for code written using cpp11.

Here's a toy RNG:

print_code(
  system.file('testcpp11/src/code.h', package = 'mockcpp', mustWork = TRUE)
)
#> #include <cpp11/doubles.hpp>
#> #include <cstdlib>
#> 
#> class Random {
#>   public:
#>     virtual cpp11::doubles uniform(size_t size) const {
#>       auto values = cpp11::writable::doubles(size);
#>       // Newer GCC builds (Mac OS and Linux can use auto i = 0
#>       // Older windows builds need to specify a type to avoid ambiguity errors 3/11/20 R 4.0.3
#>       for (cpp11::writable::doubles::size_type i = 0; i < size; ++i) {
#>         values[i] = ((double) std::rand() / (RAND_MAX)) + 1;
#>       }
#>       return values;
#>     };
#> };

And the corresponding test:

print_code(
  system.file('testcpp11/src/test-example.cpp', package = 'mockcpp', mustWork = TRUE)
)
#> #include <cpp11/doubles.hpp>
#> #include <testthat.h>
#> #include <mockcpp.h>
#> #include "code.h"
#> 
#> class MockRandom : public Random {
#>   MAKE_CONST_MOCK1(uniform, cpp11::doubles(size_t), override);
#> };
#> 
#> using trompeloeil::_;
#> 
#> context("Can mock an RNG") {
#>   
#>   test_that("you can change the return value") {
#>     MockRandom rng;
#>     auto mock_values = cpp11::writable::doubles{1, .2, .3, .4, .5};
#>     auto size = mock_values.size();
#>     REQUIRE_CALL(rng, uniform(_)).RETURN(mock_values);
#>     auto actual = rng.uniform(size);
#>     // Newer GCC builds (Mac OS and Linux can use auto i = 0
#>     // Older windows builds need to specify a type to avoid ambiguity errors 3/11/20 R 4.0.3
#>     for (cpp11::writable::doubles::size_type i = 0; i < size; ++i) {
#>       expect_true(actual[i] == mock_values[i] );
#>     }
#>   }
#> 
#> }

We can build and test this package in a similar way:

# load test package
pkgload::load_all(
  system.file('testcpp11', package = 'mockcpp', mustWork = TRUE)
)
#> ℹ Loading testcpp11
#> Re-compiling testcpp11
#>   
─  installing *source* package ‘testcpp11’ ...
#> 

   ** using staged installation
#> 

   ** libs
#> 

   g++ -std=gnu++14 -I"/usr/local/lib/R/include" -DNDEBUG  -I'/usr/local/lib/R/site-library/testthat/include' -I'/tmp/RtmpPc0dLV/Rinsta1b2504ff/mockcpp/include' -I'/usr/local/lib/R/site-library/cpp11/include' -I/usr/local/include   -fpic  -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -g  -c test-example.cpp -o test-example.o
#> 

   g++ -std=gnu++14 -I"/usr/local/lib/R/include" -DNDEBUG  -I'/usr/local/lib/R/site-library/testthat/include' -I'/tmp/RtmpPc0dLV/Rinsta1b2504ff/mockcpp/include' -I'/usr/local/lib/R/site-library/cpp11/include' -I/usr/local/include   -fpic  -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -g  -c test-runner.cpp -o test-runner.o
#> 

   g++ -std=gnu++14 -shared -L/usr/local/lib/R/lib -L/usr/local/lib -o testcpp11.so test-example.o test-runner.o -L/usr/local/lib/R/lib -lR
#> 

   installing to /tmp/RtmptcByDM/devtools_install_641a368b70/00LOCK-testcpp11/00new/testcpp11/libs
#> 

   ** checking absolute paths in shared objects and dynamic libraries
#> 

─  DONE (testcpp11)
#> 

# run the test
testthat::test_file(
  system.file(
    'testcpp11/tests/testthat/test-cpp.R',
    package = 'mockcpp',
    mustWork = TRUE
  )
)
#> 
#> ══ Testing test-cpp.R ══════════════════════════════════════════════════════════
#> 
[ FAIL 0 | WARN 0 | SKIP 0 | PASS 0 ]
[ FAIL 0 | WARN 0 | SKIP 0 | PASS 0 ]
[ FAIL 0 | WARN 0 | SKIP 0 | PASS 1 ] Done!