tests: add libbabeltrace2 pre/postcondition testing infrastructure
authorPhilippe Proulx <eeppeliteloop@gmail.com>
Tue, 14 Apr 2020 01:38:22 +0000 (21:38 -0400)
committerPhilippe Proulx <eeppeliteloop@gmail.com>
Thu, 23 Apr 2020 03:45:20 +0000 (23:45 -0400)
This patch adds a basic libbabeltrace2 pre/postcondition testing
infrastructure to the project.

When a libbabeltrace2 public function precondition or postcondition is
not satisfied, the function logs a FATAL-level message which indicates
what's wrong and then aborts. Slow path functions check such conditions
unconditionally, while fast path functions check them only in developer
mode.

Testing that a libbabeltrace2 function catches an unsatisfied
pre/postcondition and reports it is not straightforward: the library
aborts the program, so we can't use the usual approach because the test
program itself would abort.

The solution brought by this patch is the following process in
`tests/lib/conds`:

1. The `conds-triggers.c` program contains all the pre/postcondition
   failure triggering instructions to test, one condition per function.

   Each triggering function triggers a single pre/postcondition check.
   It is expected that the function actually aborts.

   main() calls ppc_main(), an internal utility, with its command-line
   arguments and an array of condition trigger descriptors.

   Each condition trigger descriptor has:

   * A condition type: precondition or postcondition.
   * The ID of the condition to trigger.
   * A name suffix.
   * A condition triggering function.

   Each condition trigger descriptor has a unique name: its condition ID
   and an optional name suffix.

   As of this patch, the PPC_TRIGGER_*_RUN_IN_COMP_CLS_INIT() macros
   create condition trigger descriptors of which the function runs
   within a component class initialization function (accepts a
   `bt_self_component *` parameter). This is often needed as many
   libbabeltrace2 functions are only accessible through a self component
   (all the trace IR API, for example).

   ppc_main() handles two command-line subcommands:

   `list`:
       Prints a JSON array of objects which represent the triggering
       descriptors, for example (output for this patch):

           [
             {
               "cond-id": "pre:field-class-integer-set-field-value-range:valid-n",
               "name": "pre:field-class-integer-set-field-value-range:valid-n-0"
             },
             {
               "cond-id": "pre:field-class-integer-set-field-value-range:valid-n",
               "name": "pre:field-class-integer-set-field-value-range:valid-n-gt-64"
             },
             {
               "cond-id": "pre:field-class-integer-set-field-value-range:not-null:field-class",
               "name": "pre:field-class-integer-set-field-value-range:not-null:field-class"
             }
           ]

   `run INDEX`:
       Runs the condition triggering function for the descriptor at
       index `INDEX` in the `list` array.

       It is expected that this command aborts.

2. `test_conds` is a Bash script which only does this:

       reldir=lib/conds
       export BT_TESTS_LIB_CONDS_TRIGGER_BIN="$BT_TESTS_BUILDDIR/$reldir/conds-triggers"

       if [ "$BT_OS_TYPE" = "mingw" ]; then
         BT_TESTS_LIB_CONDS_TRIGGER_BIN="$BT_TESTS_LIB_CONDS_TRIGGER_BIN.exe"
       fi

       run_python_bt2_test "$BT_TESTS_SRCDIR/$reldir" test.py

   In other words, it runs the Python TAP test runner to discover and
   run tests in `test.py`.

   This script is part of the `make check` test set.

3. In `test.py`, a function:

   a) Runs `conds-triggers list` and decodes the output to get the list
      of available condition trigger descriptors.

      This step also validates the regular expressions and checks that
      there are no duplicate descriptor names.

   b) For each condition trigger descriptor, creates and adds a test to
      its single test case class which:

        I. Executes `conds-triggers run INDEX` as a subprocess,
           where `INDEX` is the descriptor's index.

       II. Reads the complete process's standard error.

      III. Asserts that the process aborts (`SIGABRT` signal).

           This seems to be only possible on a POSIX system with the
           `subprocess.Popen` API.

       IV. Asserts that the standard error (II) contains the
           descriptor's condition ID.

All this is only enabled if, at configuration time:

* Python 3 is available.
* The Babeltrace 2 developer mode is enabled (`BABELTRACE_DEV_MODE=1`).

This patch's `conds-triggers.c` includes two precondition triggering
functions to confirm that everything works as expected:

trigger_fc_int_set_field_value_range_n_0():
    When calling bt_field_class_integer_set_field_value_range(), the
    parameter `N` cannot be greater than 64.

trigger_fc_int_set_field_value_range_n_gt_64():
    When calling bt_field_class_integer_set_field_value_range(), the
    parameter `N` cannot be 0.

trigger_fc_int_set_field_value_range_null():
    When calling bt_field_class_integer_set_field_value_range(), the
    field class cannot be `NULL`.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Change-Id: I70713d690f7dbfeac5804e6cfcec989242823611
Reviewed-on: https://review.lttng.org/c/babeltrace/+/3401

.gitignore
configure.ac
tests/Makefile.am
tests/lib/Makefile.am
tests/lib/conds/Makefile.am [new file with mode: 0644]
tests/lib/conds/conds-triggers.c [new file with mode: 0644]
tests/lib/conds/test.py [new file with mode: 0644]
tests/lib/conds/test_conds [new file with mode: 0755]
tests/lib/conds/utils.c [new file with mode: 0644]
tests/lib/conds/utils.h [new file with mode: 0644]

index a9a11a57c5a58f954027ff9fc7ecf2873559ccd4..709ac0711e3864e9a244a3637994af6dcbfbc169 100644 (file)
@@ -8,6 +8,7 @@
 /tests/lib/test_trace_ir_ref
 /tests/lib/test_simple_sink
 /tests/lib/test_remove_destruction_listener_in_destruction_listener
+/tests/lib/conds/conds-triggers
 /tests/param-validation/test_param_validation
 /tests/plugins/flt.lttng-utils.debug-info/test_bin_info
 /tests/plugins/flt.lttng-utils.debug-info/test_dwarf
index e52538bd6770b76d6b7e209f47a53ad65fed7733..91d82300db976c4f8bee1dc2be525e4d3f5798c1 100644 (file)
@@ -330,6 +330,7 @@ AC_ARG_VAR([BABELTRACE_DEV_MODE], [Set to 1 to enable the Babeltrace developer m
 AS_IF([test "x$BABELTRACE_DEV_MODE" = x1], [
        AC_DEFINE([BT_DEV_MODE], 1, [Babeltrace developer mode])
 ], [BABELTRACE_DEV_MODE=0])
+AM_CONDITIONAL([DEV_MODE], [test "x$BABELTRACE_DEV_MODE" = x1])
 
 # BABELTRACE_DEBUG_MODE:
 AC_ARG_VAR([BABELTRACE_DEBUG_MODE], [Set to 1 to enable the Babeltrace debug mode (enables internal assertions for Babeltrace maintainers)])
@@ -816,6 +817,7 @@ AC_CONFIG_FILES([
        tests/ctf-writer/Makefile
        tests/lib/Makefile
        tests/lib/test-plugin-plugins/Makefile
+       tests/lib/conds/Makefile
        tests/Makefile
        tests/param-validation/Makefile
        tests/plugins/Makefile
index c545f9a71695a930ae334abee1551aea9ef82034..bd7ae03d7fff8a436e579eb65a59c7c84e78b647 100644 (file)
@@ -141,6 +141,10 @@ endif
 
 if HAVE_PYTHON
 TESTS_PLUGINS += plugins/src.ctf.lttng-live/test_live
+
+if DEV_MODE
+TESTS_LIB += lib/conds/test_conds
+endif
 endif
 
 TESTS_PYTHON_PLUGIN_PROVIDER =
index ff7ab1720806fe237fd0f1b6daa9156b04820cd4..c1b68d7ffeb2c978ee3496b0fce678b20da9a87a 100644 (file)
@@ -54,3 +54,9 @@ SUBDIRS += test-plugin-plugins
 endif
 
 dist_check_SCRIPTS = test_plugin
+
+if HAVE_PYTHON
+if DEV_MODE
+SUBDIRS += conds
+endif
+endif
diff --git a/tests/lib/conds/Makefile.am b/tests/lib/conds/Makefile.am
new file mode 100644 (file)
index 0000000..7b0b6d3
--- /dev/null
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: MIT
+
+AM_CPPFLAGS += -I$(top_srcdir)/tests/utils
+
+conds_triggers_SOURCES = conds-triggers.c utils.c utils.h
+conds_triggers_LDADD = \
+       $(top_builddir)/src/common/libbabeltrace2-common.la \
+       $(top_builddir)/src/logging/libbabeltrace2-logging.la \
+       $(top_builddir)/src/lib/libbabeltrace2.la
+
+noinst_PROGRAMS = conds-triggers
+
+dist_check_SCRIPTS = test_conds test.py
diff --git a/tests/lib/conds/conds-triggers.c b/tests/lib/conds/conds-triggers.c
new file mode 100644 (file)
index 0000000..810af97
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (C) 2020 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#include <babeltrace2/babeltrace.h>
+
+#include "common/assert.h"
+#include "utils.h"
+
+static
+bt_field_class *get_uint_fc(bt_self_component *self_comp)
+{
+       bt_trace_class *tc = bt_trace_class_create(self_comp);
+       bt_field_class *fc;
+
+       BT_ASSERT(tc);
+       fc = bt_field_class_integer_unsigned_create(tc);
+       BT_ASSERT(fc);
+       return fc;
+}
+
+static
+void trigger_fc_int_set_field_value_range_n_0(bt_self_component *self_comp)
+{
+       bt_field_class_integer_set_field_value_range(get_uint_fc(self_comp), 0);
+}
+
+static
+void trigger_fc_int_set_field_value_range_n_gt_64(bt_self_component *self_comp)
+{
+       bt_field_class_integer_set_field_value_range(get_uint_fc(self_comp),
+               65);
+}
+
+static
+void trigger_fc_int_set_field_value_range_null(bt_self_component *self_comp)
+{
+       bt_field_class_integer_set_field_value_range(NULL, 23);
+}
+
+static
+const struct cond_trigger triggers[] = {
+       COND_TRIGGER_PRE_RUN_IN_COMP_CLS_INIT(
+               "pre:field-class-integer-set-field-value-range:valid-n",
+               "0",
+               trigger_fc_int_set_field_value_range_n_0
+       ),
+       COND_TRIGGER_PRE_RUN_IN_COMP_CLS_INIT(
+               "pre:field-class-integer-set-field-value-range:valid-n",
+               "gt-64",
+               trigger_fc_int_set_field_value_range_n_gt_64
+       ),
+       COND_TRIGGER_PRE_RUN_IN_COMP_CLS_INIT(
+               "pre:field-class-integer-set-field-value-range:not-null:field-class",
+               NULL,
+               trigger_fc_int_set_field_value_range_null
+       ),
+};
+
+int main(int argc, const char *argv[])
+{
+       cond_main(argc, argv, triggers, sizeof(triggers) / sizeof(*triggers));
+       return 0;
+}
diff --git a/tests/lib/conds/test.py b/tests/lib/conds/test.py
new file mode 100644 (file)
index 0000000..0f39d2a
--- /dev/null
@@ -0,0 +1,148 @@
+# SPDX-License-Identifier: MIT
+#
+# Copyright (c) 2020 Philippe Proulx <pproulx@efficios.com>
+
+import unittest
+import subprocess
+import functools
+import signal
+import os
+import os.path
+import re
+import json
+
+
+# the `conds-triggers` program's full path
+_CONDS_TRIGGERS_PATH = os.environ['BT_TESTS_LIB_CONDS_TRIGGER_BIN']
+
+
+# test methods are added by _create_tests()
+class LibPrePostCondsTestCase(unittest.TestCase):
+    pass
+
+
+# a condition trigger descriptor (base)
+class _CondTriggerDescriptor:
+    def __init__(self, index, trigger_name, cond_id):
+        self._index = index
+        self._trigger_name = trigger_name
+        self._cond_id = cond_id
+
+    @property
+    def index(self):
+        return self._index
+
+    @property
+    def trigger_name(self):
+        return self._trigger_name
+
+    @property
+    def cond_id(self):
+        return self._cond_id
+
+
+# precondition trigger descriptor
+class _PreCondTriggerDescriptor(_CondTriggerDescriptor):
+    @property
+    def type_str(self):
+        return 'pre'
+
+
+# postcondition trigger descriptor
+class _PostCondTriggerDescriptor(_CondTriggerDescriptor):
+    @property
+    def type_str(self):
+        return 'post'
+
+
+# test method template for `LibPrePostCondsTestCase`
+def _test(self, descriptor):
+    # Execute:
+    #
+    #     $ conds-triggers run <index>
+    #
+    # where `<index>` is the descriptor's index.
+    with subprocess.Popen(
+        [_CONDS_TRIGGERS_PATH, 'run', str(descriptor.index)],
+        stderr=subprocess.PIPE,
+        universal_newlines=True,
+    ) as proc:
+        # wait for termination and get standard output/error data
+        timeout = 5
+
+        try:
+            # wait for program end and get standard error pipe's contents
+            _, stderr = proc.communicate(timeout=timeout)
+        except subprocess.TimeoutExpired:
+            self.fail('Process hanged for {} seconds'.format(timeout))
+            return
+
+        # assert that program aborted (only available on POSIX)
+        if os.name == 'posix':
+            self.assertEqual(proc.returncode, -int(signal.SIGABRT))
+
+        # assert that the standard error text contains the condition ID
+        text = 'Condition ID: `{}`.'.format(descriptor.cond_id)
+        self.assertIn(text, stderr)
+
+
+# Condition trigger descriptors from the JSON array returned by
+#
+#     $ conds-triggers list
+def _cond_trigger_descriptors_from_json(json_descr_array):
+    descriptors = []
+    descriptor_names = set()
+
+    for index, json_descr in enumerate(json_descr_array):
+        # sanity check: check for duplicate
+        trigger_name = json_descr['name']
+
+        if trigger_name in descriptor_names:
+            raise ValueError(
+                'Duplicate condition trigger name `{}`'.format(trigger_name)
+            )
+
+        # condition ID
+        cond_id = json_descr['cond-id']
+
+        if cond_id.startswith('pre'):
+            cond_type = _PreCondTriggerDescriptor
+        elif cond_id.startswith('post'):
+            cond_type = _PostCondTriggerDescriptor
+        else:
+            raise ValueError('Invalid condition ID `{}`'.format(cond_id))
+
+        descriptors.append(cond_type(index, trigger_name, cond_id))
+        descriptor_names.add(trigger_name)
+
+    return descriptors
+
+
+# creates the individual tests of `LibPrePostCondsTestCase`
+def _create_tests():
+    # Execute `conds-triggers list` to get a JSON array of condition
+    # trigger descriptors.
+    json_descr_array = json.loads(
+        subprocess.check_output([_CONDS_TRIGGERS_PATH, 'list'], universal_newlines=True)
+    )
+
+    # get condition trigger descriptor objects from JSON
+    descriptors = _cond_trigger_descriptors_from_json(json_descr_array)
+
+    # create test methods
+    for descriptor in descriptors:
+        # test method name
+        test_meth_name = 'test_{}'.format(
+            re.sub(r'[^a-zA-Z0-9_]', '_', descriptor.trigger_name)
+        )
+
+        # test method
+        meth = functools.partialmethod(_test, descriptor)
+        setattr(LibPrePostCondsTestCase, test_meth_name, meth)
+
+
+_create_tests()
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/lib/conds/test_conds b/tests/lib/conds/test_conds
new file mode 100755 (executable)
index 0000000..48b4fe7
--- /dev/null
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2020 Philippe Proulx <pproulx@efficios.com>
+
+if [ "x${BT_TESTS_SRCDIR:-}" != "x" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
+fi
+
+# shellcheck source=../../utils/utils.sh
+source "$UTILSSH"
+
+reldir=lib/conds
+export BT_TESTS_LIB_CONDS_TRIGGER_BIN="$BT_TESTS_BUILDDIR/$reldir/conds-triggers"
+
+if [ "$BT_OS_TYPE" = "mingw" ]; then
+       BT_TESTS_LIB_CONDS_TRIGGER_BIN="$BT_TESTS_LIB_CONDS_TRIGGER_BIN.exe"
+fi
+
+run_python_bt2_test "$BT_TESTS_SRCDIR/$reldir" test.py
diff --git a/tests/lib/conds/utils.c b/tests/lib/conds/utils.c
new file mode 100644 (file)
index 0000000..8fb5bc8
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (C) 2020 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <babeltrace2/babeltrace.h>
+#include <glib.h>
+
+#include "common/assert.h"
+#include "utils.h"
+
+typedef void (* run_in_comp_cls_init_func)(
+               bt_self_component *self_comp, void *user_data);
+
+struct comp_cls_init_method_data {
+       run_in_comp_cls_init_func func;
+       void *user_data;
+};
+
+static
+bt_component_class_initialize_method_status comp_cls_init(
+               bt_self_component_source *self_comp,
+               bt_self_component_source_configuration *conf,
+               const bt_value *params, void *init_method_data)
+{
+       struct comp_cls_init_method_data *data = init_method_data;
+
+       /* Call user function which is expected to abort */
+       data->func(bt_self_component_source_as_self_component(self_comp),
+               data->user_data);
+
+       /* Never reached! */
+       return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
+}
+
+static
+bt_message_iterator_class_next_method_status msg_iter_cls_next(
+               bt_self_message_iterator *self_msg_iter,
+               bt_message_array_const msgs, uint64_t capacity,
+               uint64_t *count)
+{
+       /* Not used */
+       return BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_ERROR;
+}
+
+static
+void run_in_comp_cls_init(run_in_comp_cls_init_func func,
+               void *user_data)
+{
+       bt_message_iterator_class *msg_iter_cls;
+       bt_component_class_source *comp_cls;
+       bt_component_class_set_method_status set_method_status;
+       bt_graph *graph;
+       struct comp_cls_init_method_data init_method_data = {
+               .func = func,
+               .user_data = user_data,
+       };
+
+       /* Create component class */
+       msg_iter_cls = bt_message_iterator_class_create(msg_iter_cls_next);
+       BT_ASSERT(msg_iter_cls);
+       comp_cls = bt_component_class_source_create("yo", msg_iter_cls);
+       BT_ASSERT(comp_cls);
+       set_method_status = bt_component_class_source_set_initialize_method(
+               comp_cls, comp_cls_init);
+       BT_ASSERT(set_method_status == BT_COMPONENT_CLASS_SET_METHOD_STATUS_OK);
+
+       /* Create graph */
+       graph = bt_graph_create(0);
+       BT_ASSERT(graph);
+
+       /*
+        * Add source component: this calls the initialization method,
+        * calling `func`.
+        */
+       (void) bt_graph_add_source_component_with_initialize_method_data(graph,
+                       comp_cls, "whatever", NULL, &init_method_data,
+                       BT_LOGGING_LEVEL_NONE, NULL);
+
+       /*
+        * This point is not expected to be reached as func() is
+        * expected to abort.
+        */
+}
+
+static
+void run_in_comp_cls_init_defer(bt_self_component *self_comp,
+               void *user_data)
+{
+       cond_trigger_run_in_comp_cls_init_func user_func = user_data;
+
+       user_func(self_comp);
+}
+
+static
+void run_trigger(const struct cond_trigger *trigger)
+{
+       switch (trigger->func_type) {
+       case COND_TRIGGER_FUNC_TYPE_BASIC:
+               trigger->func.basic();
+               break;
+       case COND_TRIGGER_FUNC_TYPE_RUN_IN_COMP_CLS_INIT:
+               run_in_comp_cls_init(run_in_comp_cls_init_defer,
+                       trigger->func.run_in_comp_cls_init);
+               break;
+       default:
+               abort();
+       }
+}
+
+static
+void escape_json_string(const char *str, GString *escaped_str)
+{
+       g_string_assign(escaped_str, "");
+
+       for (const char *ch = str; *ch; ch++) {
+               if (*ch == '\\' || *ch == '"') {
+                       g_string_append_c(escaped_str, '\\');
+               }
+
+               g_string_append_c(escaped_str, *ch);
+       }
+}
+
+static
+void list_triggers(const struct cond_trigger triggers[], size_t trigger_count)
+{
+       GString *escaped_str = g_string_new(NULL);
+       size_t i;
+
+       BT_ASSERT(escaped_str);
+       printf("[");
+
+       for (i = 0; i < trigger_count; i++) {
+               const struct cond_trigger *trigger = &triggers[i];
+
+               /* Condition ID */
+               escape_json_string(trigger->cond_id, escaped_str);
+               printf("{\"cond-id\":\"%s\",", escaped_str->str);
+
+               /* Name starts with condition ID */
+               printf("\"name\":\"%s", escaped_str->str);
+
+               if (trigger->suffix) {
+                       escape_json_string(trigger->suffix, escaped_str);
+                       printf("-%s", escaped_str->str);
+               }
+
+               printf("\"}");
+
+               if (i < trigger_count - 1) {
+                       /* Comma between objects */
+                       printf(",");
+               }
+       }
+
+       printf("]");
+       g_string_free(escaped_str, TRUE);
+       fflush(stdout);
+}
+
+void cond_main(int argc, const char *argv[],
+               const struct cond_trigger triggers[], size_t trigger_count)
+{
+       BT_ASSERT(argc >= 2);
+
+       if (strcmp(argv[1], "list") == 0) {
+               list_triggers(triggers, trigger_count);
+       } else if (strcmp(argv[1], "run") == 0) {
+               int index;
+
+               BT_ASSERT(argc >= 3);
+               index = atoi(argv[2]);
+               BT_ASSERT(index >= 0 && index < trigger_count);
+               run_trigger(&triggers[index]);
+       }
+}
diff --git a/tests/lib/conds/utils.h b/tests/lib/conds/utils.h
new file mode 100644 (file)
index 0000000..1a77bde
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (C) 2020 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#ifndef TESTS_LIB_CONDS_UTILS_H
+#define TESTS_LIB_CONDS_UTILS_H
+
+enum cond_trigger_func_type {
+       COND_TRIGGER_FUNC_TYPE_BASIC,
+       COND_TRIGGER_FUNC_TYPE_RUN_IN_COMP_CLS_INIT,
+};
+
+enum cond_trigger_type {
+       COND_TRIGGER_TYPE_PRE,
+       COND_TRIGGER_TYPE_POST,
+};
+
+typedef void (* cond_trigger_basic_func)(void);
+typedef void (* cond_trigger_run_in_comp_cls_init_func)(bt_self_component *);
+
+struct cond_trigger {
+       enum cond_trigger_type type;
+       enum cond_trigger_func_type func_type;
+       const char *cond_id;
+       const char *suffix;
+       union {
+               cond_trigger_basic_func basic;
+               cond_trigger_run_in_comp_cls_init_func run_in_comp_cls_init;
+       } func;
+};
+
+#define COND_TRIGGER_PRE_BASIC(_cond_id, _suffix, _func)               \
+       {                                                               \
+               .type = COND_TRIGGER_TYPE_PRE,                          \
+               .func_type = COND_TRIGGER_FUNC_TYPE_BASIC,              \
+               .cond_id = _cond_id,                                    \
+               .suffix = _suffix,                                      \
+               .func = {                                               \
+                       .basic = _func,                                 \
+               }                                                       \
+       }
+
+#define COND_TRIGGER_POST_BASIC(_cond_id, _suffix, _func)              \
+       {                                                               \
+               .type = COND_TRIGGER_TYPE_POST,                         \
+               .func_type = COND_TRIGGER_FUNC_TYPE_BASIC,              \
+               .cond_id = _cond_id,                                    \
+               .suffix = _suffix,                                      \
+               .func = {                                               \
+                       .basic = _func,                                 \
+               }                                                       \
+       }
+
+#define COND_TRIGGER_PRE_RUN_IN_COMP_CLS_INIT(_cond_id, _suffix, _func)        \
+       {                                                               \
+               .type = COND_TRIGGER_TYPE_PRE,                          \
+               .func_type = COND_TRIGGER_FUNC_TYPE_RUN_IN_COMP_CLS_INIT, \
+               .cond_id = _cond_id,                                    \
+               .suffix = _suffix,                                      \
+               .func = {                                               \
+                       .run_in_comp_cls_init = _func,                  \
+               }                                                       \
+       }
+
+#define COND_TRIGGER_POST_RUN_IN_COMP_CLS_INIT(_cond_id, _suffix, _func) \
+       {                                                               \
+               .type = COND_TRIGGER_TYPE_POST,                         \
+               .func_type = COND_TRIGGER_FUNC_TYPE_RUN_IN_COMP_CLS_INIT, \
+               .cond_id = _cond_id,                                    \
+               .suffix = _suffix,                                      \
+               .func = {                                               \
+                       .run_in_comp_cls_init = _func,                  \
+               }                                                       \
+       }
+
+void cond_main(int argc, const char *argv[],
+               const struct cond_trigger triggers[],
+               size_t trigger_count);
+
+#endif /* TESTS_LIB_CONDS_UTILS_H */
This page took 0.036553 seconds and 4 git commands to generate.