static void test_helpers_find_ast_misc_attr_callback()

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);
  }
}