in sources/cg_test_helpers.c [1035:1141]
static void test_helpers_find_ast_misc_attr_callback(
CSTR _Nullable misc_attr_prefix,
CSTR _Nonnull misc_attr_name,
ast_node *_Nullable ast_misc_attr_value_list,
void *_Nullable context)
{
ast_node *stmt = (ast_node *)context;
Contract(is_ast_create_proc_stmt(stmt));
if (misc_attr_prefix &&
misc_attr_name &&
!Strcasecmp(misc_attr_prefix, "cql") &&
!Strcasecmp(misc_attr_name, "autotest")) {
// We're actually using intermediate buffers here only so that
// we can test if they were used (non-empty) at the end so that
// we can emit the test delimeters if and only if they are needed
// these are otherwise going to pass through to gh_th_decls and _procs
// as they came in.
CHARBUF_OPEN(decls_temp);
CHARBUF_OPEN(procs_temp);
charbuf *decls_saved = cg_th_decls;
charbuf *procs_saved = cg_th_procs;
cg_th_decls = &decls_temp;
cg_th_procs = &procs_temp;
EXTRACT_STRING(proc_name, stmt->left);
for (ast_node *list = ast_misc_attr_value_list; list; list = list->right) {
ast_node *misc_attr_value = list->left;
// We found a nested list which should be nested dummy_test with info
// @attribute(cql:autotest=(..., (dummy_test, ...), ...))
if (is_ast_misc_attr_value_list(misc_attr_value)) {
collect_dummy_test_info(misc_attr_value, context);
cg_test_helpers_dummy_test(stmt);
}
// we found autotest attribution
// @attribute(cql:autotest=(dummy_table, dummy_test, dummy_insert, dummy_select, dummy_result_set))
else {
// In principle, any option can be combined with any other but some only make sense for procs with
// a result.
EXTRACT_STRING(autotest_attr_name, misc_attr_value);
if (is_autotest_dummy_test(autotest_attr_name)) {
cg_test_helpers_dummy_test(stmt);
}
// these options are only for procs that return a result set
if (has_result_set(stmt) || has_out_stmt_result(stmt) || has_out_union_stmt_result(stmt)) {
if (is_autotest_dummy_table(autotest_attr_name)) {
helper_flags |= DUMMY_TABLE;
cg_test_helpers_dummy_table(proc_name);
}
else if (is_autotest_dummy_insert(autotest_attr_name)) {
helper_flags |= DUMMY_INSERT;
cg_test_helpers_dummy_insert(proc_name);
}
else if (is_autotest_dummy_select(autotest_attr_name)) {
helper_flags |= DUMMY_SELECT;
cg_test_helpers_dummy_select(proc_name);
}
else if (is_autotest_dummy_result_set(autotest_attr_name)) {
helper_flags |= DUMMY_RESULT_SET;
cg_test_helpers_dummy_result_set(proc_name);
}
}
}
}
if (is_declare_proc_needed()) {
// if we emitted one of the helpers above that sets helper_flags it tells us that we
// need to emit a declaration for the procedure that had the attribute (i.e. the thing
// we are trying to mock). The generated code uses the name of that procedure in a LIKE
// clause and it won't otherwise be in our output so we emit a declaration for it here.
cg_test_helpers_declare_proc(stmt);
}
cg_th_decls = decls_saved;
cg_th_procs = procs_saved;
// generate test delimiters only if needed
if (decls_temp.used > 1) {
if (options.test) {
bprintf(cg_th_decls, "\n-- The statement ending at line %d", stmt->lineno);
}
bprintf(cg_th_decls, "%s", decls_temp.ptr);
}
// We always generate a marker in the procs section, because there are cases
// where we need to verify that we generated nothing.
if (options.test) {
bprintf(cg_th_procs, "\n-- The statement ending at line %d", stmt->lineno);
if (procs_temp.used == 1) {
// this gives us a nice clear message in the output
bprintf(cg_th_procs, "\n-- no output generated --\n");
}
}
bprintf(cg_th_procs, "%s", procs_temp.ptr);
CHARBUF_CLOSE(procs_temp);
CHARBUF_CLOSE(decls_temp);
}
}