Skip to content

Commit

Permalink
Use catch for C++ unit testing.
Browse files Browse the repository at this point in the history
  • Loading branch information
astamm committed Nov 29, 2021
1 parent 4d6631a commit c685e96
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 150 deletions.
3 changes: 2 additions & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,11 @@ Encoding: UTF-8
LazyData: true
Roxygen: list(markdown = TRUE)
RoxygenNote: 7.1.2
LinkingTo: testthat
Suggests:
knitr,
rmarkdown,
inline,
xml2,
testthat (>= 3.0.0)
VignetteBuilder: knitr
Config/testthat/edition: 3
6 changes: 6 additions & 0 deletions R/catch-routine-registration.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# This dummy function definition is included with the package to ensure that
# 'tools::package_native_routine_registration_skeleton()' generates the required
# registration info for the 'run_testthat_tests' symbol.
(function() {
.Call("run_testthat_tests", FALSE, PACKAGE = "nloptr")
})
7 changes: 5 additions & 2 deletions src/init_nloptr.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,12 @@
#include "nloptr.h"
#include "nlopt.h"

extern SEXP run_testthat_tests(SEXP);

static const R_CallMethodDef CallEntries[] = {
{"NLoptR_Optimize", (DL_FUNC) &NLoptR_Optimize, 1},
{NULL, NULL, 0}
{"run_testthat_tests", (DL_FUNC) &run_testthat_tests, 1},
{"NLoptR_Optimize", (DL_FUNC) &NLoptR_Optimize, 1},
{NULL, NULL, 0}
};

void R_init_nloptr(DllInfo *info) {
Expand Down
121 changes: 121 additions & 0 deletions src/test-C-API.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/*
* This file uses the Catch unit testing library, alongside
* testthat's simple bindings, to test a C++ function.
*
* For your own packages, ensure that your test files are
* placed within the `src/` folder, and that you include
* `LinkingTo: testthat` within your DESCRIPTION file.
*/

// All test files should include the <testthat.h>
// header file.
#include <testthat.h>
#include <math.h>
#include <vector>
#include "../inst/include/nloptrAPI.h"

std::vector<int> get_nlopt_version()
{
// get version of NLopt
int major, minor, bugfix;
nlopt_version(&major, &minor, &bugfix);

std::vector<int> retvec(3);
retvec[0] = major;
retvec[1] = minor;
retvec[2] = bugfix;

return retvec;
}

double myfunc(unsigned n, const double *x, double *grad, void *my_func_data)
{
if (grad) {
grad[0] = 0.0;
grad[1] = 0.5 / sqrt(x[1]);
}
return sqrt(x[1]);
}

typedef struct {
double a, b;
} my_constraint_data;

double myconstraint(unsigned n, const double *x, double *grad, void *data)
{
my_constraint_data *d = (my_constraint_data *) data;
double a = d->a, b = d->b;
if (grad) {
grad[0] = 3 * a * (a*x[0] + b) * (a*x[0] + b);
grad[1] = -1.0;
}
return ((a*x[0] + b) * (a*x[0] + b) * (a*x[0] + b) - x[1]);
}

std::vector<double> solve_example()
{
double lb[2] = { -HUGE_VAL, 0 }; // lower bounds
nlopt_opt opt;

opt = nlopt_create(NLOPT_LD_MMA, 2); // algorithm and dimensionality
nlopt_set_lower_bounds(opt, lb);
nlopt_set_min_objective(opt, myfunc, NULL);

my_constraint_data data[2] = { {2,0}, {-1,1} };

nlopt_add_inequality_constraint(opt, myconstraint, &data[0], 1e-8);
nlopt_add_inequality_constraint(opt, myconstraint, &data[1], 1e-8);

nlopt_set_xtol_rel(opt, 1e-4);

double x[2] = { 1.234, 5.678 }; // some initial guess
double minf; // the minimum objective value, upon return
if (nlopt_optimize(opt, x, &minf) < 0) {
// Rprintf("nlopt failed!\\n");
}
else {
// Rprintf("found minimum at f(%g,%g) = %0.10g\\n", x[0], x[1], minf);
}

nlopt_destroy(opt);

std::vector<double> result(2);
result[0] = x[0];
result[1] = x[1];

return result;
}

// Initialize a unit test context. This is similar to how you
// might begin an R test file with 'context()', expect the
// associated context should be wrapped in braced.
context("Test C API")
{
// The format for specifying tests is similar to that of
// testthat's R functions. Use 'test_that()' to define a
// unit test, and use 'expect_true()' and 'expect_false()'
// to test the desired conditions.
test_that("Test exposing NLopt C function nlopt_version")
{
// Get nlopt version, which consists of an integer vector:
// (major, minor, bugfix)
std::vector<int> res = get_nlopt_version();

// Check return value
expect_true(res.size() == 3);
expect_true(res[0] == 2);
expect_true(res[1] == 7);
expect_true(res[2] == 0);
}

test_that("Test exposed NLopt C code using example from NLopt tutorial")
{
// Get optimal x values.
std::vector<double> res = solve_example();

// Check return value.
expect_true(res.size() == 2);
expect_true(abs(res[0] - 1./3) < 1.0e-6);
expect_true(abs(res[1] - 8./27) < 1.0e-6);
}
}
7 changes: 7 additions & 0 deletions src/test-runner.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/*
* Please do not edit this file -- it ensures that your package will export a
* 'run_testthat_tests()' C routine that can be used to run the Catch unit tests
* available in your package.
*/
#define TESTTHAT_TEST_RUNNER
#include <testthat.h>
147 changes: 0 additions & 147 deletions tests/testthat/test-C-api.R

This file was deleted.

1 change: 1 addition & 0 deletions tests/testthat/test-cpp.R
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
run_cpp_tests("nloptr")

0 comments on commit c685e96

Please sign in to comment.