1 #include <catch2/generators/catch_generators_range.hpp>
2 #include <catch2/matchers/catch_matchers_string.hpp>
3 #include <catch2/catch_test_macros.hpp>
13 TEST_CASE(
"Test hoc interpreter",
"[NEURON][hoc_interpreter]") {
31 TEST_CASE(
"Test hoc_register_var",
"[NEURON][hoc_interpreter][istmpobj]") {
38 SCENARIO(
"Test for issue #1995",
"[NEURON][hoc_interpreter][issue-1995]") {
40 GIVEN(
"Test calling a function that returns an Object that lived on the stack") {
41 constexpr
auto vector_size = 5;
42 REQUIRE(
hoc_oc((
"obfunc foo() {localobj lv1\n"
43 " lv1 = new Vector(" +
50 WHEN(
"It is called") {
51 REQUIRE(
hoc_oc(
"objref v1\n"
52 "v1 = foo()\n") == 0);
53 THEN(
"The returned value should be correct") {
54 auto const i = GENERATE_COPY(
range(1, vector_size));
63 struct UtilityThatLikesThrowing {
64 UtilityThatLikesThrowing(
int data)
66 [[nodiscard]]
bool hoc_destructor_should_throw()
const {
69 [[noreturn]]
void throw_error()
const {
70 throw std::runtime_error(
"throwing from throw_error");
72 [[noreturn]]
void call_execerror()
const {
79 double throwing_util_member_call_execerror(
void* ob_void) {
80 auto*
const ob =
static_cast<UtilityThatLikesThrowing*
>(ob_void);
83 double throwing_util_member_throw_error(
void* ob_void) {
84 auto*
const ob =
static_cast<UtilityThatLikesThrowing*
>(ob_void);
87 Object** throwing_util_member_call_execerror_obj_func(
void* ob_void) {
88 auto*
const ob =
static_cast<UtilityThatLikesThrowing*
>(ob_void);
91 Object** throwing_util_member_throw_error_obj_func(
void* ob_void) {
92 auto*
const ob =
static_cast<UtilityThatLikesThrowing*
>(ob_void);
95 const char** throwing_util_member_call_execerror_str_func(
void* ob_void) {
96 auto*
const ob =
static_cast<UtilityThatLikesThrowing*
>(ob_void);
99 const char** throwing_util_member_throw_error_str_func(
void* ob_void) {
100 auto*
const ob =
static_cast<UtilityThatLikesThrowing*
>(ob_void);
103 static Member_func throwing_util_members[] = {{
"call_execerror",
104 throwing_util_member_call_execerror},
105 {
"throw_error", throwing_util_member_throw_error},
108 {
"call_execerror_obj_func", throwing_util_member_call_execerror_obj_func},
109 {
"throw_error_obj_func", throwing_util_member_throw_error_obj_func},
112 {
"call_execerror_str_func", throwing_util_member_call_execerror_str_func},
113 {
"throw_error_str_func", throwing_util_member_throw_error_str_func},
115 void* throwing_util_constructor(
Object*) {
117 throw std::runtime_error(
"need at least one argument");
119 return new UtilityThatLikesThrowing{
static_cast<int>(*
hoc_getarg(1))};
121 void throwing_util_destructor(
void* ob_void) {
122 auto*
const ob =
static_cast<UtilityThatLikesThrowing*
>(ob_void);
123 bool const should_throw = ob->hoc_destructor_should_throw();
126 throw std::runtime_error(
"throwing from HOC destructor");
132 std::ostringstream oss;
135 auto const output =
std::move(oss).str();
136 REQUIRE(!output.empty());
140 using Catch::Matchers::ContainsSubstring;
141 SCENARIO(
"Test calling code from HOC that throws exceptions",
"[NEURON][hoc_interpreter]") {
142 static bool registered =
false;
144 class2oc(
"UtilityThatLikesThrowing",
145 throwing_util_constructor,
146 throwing_util_destructor,
147 throwing_util_members,
148 throwing_util_ret_obj_members,
149 throwing_util_ret_str_members);
152 REQUIRE(
hoc_oc(
"objref nil, util\n") == 0);
154 GIVEN(
"A HOC object constructor that throws") {
157 "hoc_execerror: UtilityThatLikesThrowing[0] constructor: need at "
158 "least one argument"));
160 GIVEN(
"A HOC object destructor that throws") {
162 "util = new UtilityThatLikesThrowing(1)\n"
165 "hoc_execerror: UtilityThatLikesThrowing[0] destructor: throwing "
166 "from HOC destructor"));
168 GIVEN(
"A HOC object whose constructor and destructor succeed") {
171 REQUIRE(
hoc_oc(
"util = nil\n") == 0);
173 REQUIRE(
hoc_oc(
"util = new UtilityThatLikesThrowing(2)\n") == 0);
175 auto const method_suffix = GENERATE(as<std::string>{},
"",
"_obj_func",
"_str_func");
176 WHEN(
"A member function that throws is called") {
179 ContainsSubstring(
"hoc_execerror: UtilityThatLikesThrowing[0]::throw_error" +
182 "from throw_error"));
184 WHEN(
"A member function that calls hoc_execerror is called") {
186 (
"util.call_execerror" + method_suffix +
"()\n").c_str()),
187 ContainsSubstring(
"hoc_execerror: throwing a tantrum"));
193 TEST_CASE(
"Test hoc_array_access",
"[NEURON][hoc_interpreter][nrnpython][array_access]") {
194 REQUIRE(
hoc_oc(
"nrnpython(\"avec = [0,1,2]\")\n"
196 "po = new PythonObject()\n"
197 "po = po.avec\n") == 0);
198 THEN(
"The avec can value should be correct") {
199 auto const i = GENERATE_COPY(
range(0, 3));
void class2oc(const char *, ctor_f *cons, dtor_f *destruct, Member_func *, Member_ret_obj_func *, Member_ret_str_func *)
constexpr auto range(T &&iterable)
double * hoc_getarg(int narg)
double hoc_call_func(Symbol *s, int narg)
void hoc_pushobj(Object **d)
int hoc_oc(const char *buf)
void hoc_retpushx(double x)
Symbol * hoc_lookup(const char *)
int hoc_is_tempobj_arg(int narg)
SCENARIO("Test for issue #1995", "[NEURON][hoc_interpreter][issue-1995]")
std::string hoc_oc_require_error(const char *buf)
TEST_CASE("Test hoc interpreter", "[NEURON][hoc_interpreter]")
constexpr static auto check_tempobj_canary
static VoidFunc hoc_intfunc[]
void move(Item *q1, Item *q2, Item *q3)
void hoc_execerror(const char *s1, const char *s2)
void hoc_register_var(DoubScal *ds, DoubVec *dv, VoidFunc *)
std::string to_string(const T &obj)
Object ** hoc_temp_objptr(Object *)