[ovs-dev] [RFC PATCH ovn 1/3] Add unit test framework.

Mark Michelson mmichels at redhat.com
Fri Oct 9 15:29:26 UTC 2020


This creates an API that allows for unit tests to be registered in code
by OVN. This change enables the following:

* At configure time, "--enable-unit-tests" can be specified to allow for
  unit tests to be compiled.
* This configure-time variable also enables a testsuite variable to be
  defined, allowing for unit tests to be run from the testsuite.
* Testsuite tests are defined using OVS_CONSTRUCTOR, meaning they are
  automatically registered prior to main() being run in whichever
  program the unit test is defined in.
* Unit tests can be run for a given OVN program using ovn-appctl. For
  example, `ovn-appctl -t northd unit-test my-unit-test`.

This commit does not define any unit tests. That is saved for a later
commit in this series.

Signed-off-by: Mark Michelson <mmichels at redhat.com>
---
 acinclude.m4     |  12 +++++
 configure.ac     |   1 +
 lib/automake.mk  |   4 +-
 lib/unit-test.c  | 125 +++++++++++++++++++++++++++++++++++++++++++++++
 lib/unit-test.h  |  41 ++++++++++++++++
 tests/atlocal.in |   1 +
 6 files changed, 183 insertions(+), 1 deletion(-)
 create mode 100644 lib/unit-test.c
 create mode 100644 lib/unit-test.h

diff --git a/acinclude.m4 b/acinclude.m4
index a797adc82..8b1888075 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -366,3 +366,15 @@ AC_DEFUN([OVN_CHECK_OVS], [
   AC_SUBST(OVSVERSION)
   AC_MSG_RESULT([OVS version is $OVSVERSION])
 ])
+
+AC_DEFUN([OVN_ENABLE_UNIT_TESTS],
+  [AC_ARG_ENABLE(
+     [unit_tests],
+     [AC_HELP_STRING([--enable-unit-tests], [Enable unit test framework])],
+     [], [enable_unit_tests=no])
+   AC_CONFIG_COMMANDS_PRE(
+     [if test "X$enable_unit_tests" = Xyes; then
+        OVS_CFLAGS="$OVS_CFLAGS -DENABLE_UNIT_TESTS"
+      fi])
+
+    AC_SUBST([enable_unit_tests])])
diff --git a/configure.ac b/configure.ac
index 0b17f05b9..613e2ba3d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -166,6 +166,7 @@ OVS_CONDITIONAL_CC_OPTION([-Wno-unused], [HAVE_WNO_UNUSED])
 OVS_CONDITIONAL_CC_OPTION([-Wno-unused-parameter], [HAVE_WNO_UNUSED_PARAMETER])
 OVS_ENABLE_WERROR
 OVS_ENABLE_SPARSE
+OVN_ENABLE_UNIT_TESTS
 
 OVS_CHECK_PRAGMA_MESSAGE
 OVN_CHECK_OVS
diff --git a/lib/automake.mk b/lib/automake.mk
index f3e9c8818..3e9c2a697 100644
--- a/lib/automake.mk
+++ b/lib/automake.mk
@@ -23,7 +23,9 @@ lib_libovn_la_SOURCES = \
 	lib/ovn-util.h \
 	lib/logical-fields.c \
 	lib/inc-proc-eng.c \
-	lib/inc-proc-eng.h
+	lib/inc-proc-eng.h \
+	lib/unit-test.c \
+	lib/unit-test.h
 nodist_lib_libovn_la_SOURCES = \
 	lib/ovn-dirs.c \
 	lib/ovn-nb-idl.c \
diff --git a/lib/unit-test.c b/lib/unit-test.c
new file mode 100644
index 000000000..a9eb79e73
--- /dev/null
+++ b/lib/unit-test.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2020 Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+
+#include "openvswitch/shash.h"
+#include "openvswitch/dynamic-string.h"
+#include "util.h"
+#include "unixctl.h"
+
+#include "lib/unit-test.h"
+
+#ifdef ENABLE_UNIT_TESTS
+static struct shash unit_tests = SHASH_INITIALIZER(&unit_tests);
+
+struct unit_test_data {
+    unit_test_cb cb;
+    void *data;
+};
+
+void
+register_unit_test(const char *name, unit_test_cb cb, void *data)
+{
+    struct unit_test_data *test_data = xmalloc(sizeof *test_data);
+    test_data->cb = cb;
+    test_data->data = data;
+    shash_add_once(&unit_tests, name, test_data);
+}
+
+enum test_result
+run_unit_test(const char *name)
+{
+    struct unit_test_data *test_data = shash_find_data(&unit_tests, name);
+    if (!test_data) {
+        return OVN_TEST_NOT_FOUND;
+    }
+
+    return test_data->cb(test_data->data);
+}
+
+static void
+unixctl_run_unit_test(struct unixctl_conn *conn, int argc,
+                      const char *argv[], void *ignore OVS_UNUSED)
+{
+    if (argc < 2) {
+        unixctl_command_reply_error(conn, "No unit test specified");
+        return;
+    }
+
+    enum test_result result;
+    bool error;
+    struct ds reply = DS_EMPTY_INITIALIZER;
+
+    result = run_unit_test(argv[1]);
+    switch (result) {
+    case OVN_TEST_NOT_FOUND:
+        error = true;
+        ds_put_format(&reply, "Unit test %s not found", argv[1]);
+        break;
+    case OVN_TEST_FAIL:
+        error = true;
+        ds_put_format(&reply, "Unit test %s failed", argv[1]);
+        break;
+    case OVN_TEST_PASS:
+        error = false;
+        ds_put_format(&reply, "Unit test %s passed", argv[1]);
+        break;
+    case OVN_TEST_SKIP:
+        error = false;
+        ds_put_format(&reply, "Unit test %s skipped", argv[1]);
+        break;
+    default:
+        OVS_NOT_REACHED();
+    }
+
+    if (error) {
+        unixctl_command_reply_error(conn, ds_cstr(&reply));
+    } else {
+        unixctl_command_reply(conn, ds_cstr(&reply));
+    }
+
+    ds_destroy(&reply);
+}
+
+void
+register_unixctl_unit_test(void)
+{
+    unixctl_command_register("unit-test", "", 1, 1,
+                             unixctl_run_unit_test, NULL);
+}
+
+#else /* ENABLE_UNIT_TESTS */
+
+void
+register_unit_test(const char *name OVS_UNUSED, unit_test_cb cb OVS_UNUSED,
+                   void *data OVS_UNUSED)
+{
+    return;
+}
+
+void
+enum test_result run_unit_test(const char *name OVS_UNUSED)
+{
+    return OVN_TEST_NOT_FOUND;
+}
+
+void register_unixctl_unit_test(void)
+{
+    return;
+}
+
+#endif /* ENABLE_UNIT_TESTS */
diff --git a/lib/unit-test.h b/lib/unit-test.h
new file mode 100644
index 000000000..e89a4d8f6
--- /dev/null
+++ b/lib/unit-test.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020 Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef OVN_UNIT_TEST_H
+#define OVN_UNIT_TEST_H 1
+
+#include "openvswitch/compiler.h"
+
+enum test_result {
+    OVN_TEST_FAIL,
+    OVN_TEST_PASS,
+    OVN_TEST_SKIP,
+    OVN_TEST_NOT_FOUND,
+};
+
+typedef enum test_result (*unit_test_cb)(void *data);
+
+void register_unit_test(const char *name, unit_test_cb cb, void *data);
+
+enum test_result run_unit_test(const char *name);
+
+void register_unixctl_unit_test(void);
+
+#define UNIT_TEST_DEFINE(NAME, CB, DATA)       \
+    OVS_CONSTRUCTOR(unit_test_##NAME) {        \
+        register_unit_test(#NAME, CB, DATA);    \
+    }
+#endif
diff --git a/tests/atlocal.in b/tests/atlocal.in
index 26681f02d..ca48ee93d 100644
--- a/tests/atlocal.in
+++ b/tests/atlocal.in
@@ -3,6 +3,7 @@ HAVE_OPENSSL='@HAVE_OPENSSL@'
 OPENSSL_SUPPORTS_SNI='@OPENSSL_SUPPORTS_SNI@'
 HAVE_UNBOUND='@HAVE_UNBOUND@'
 EGREP='@EGREP@'
+ENABLE_UNIT_TESTS='@enable_unit_tests@'
 
 if test x"$PYTHON3" = x; then
     PYTHON3='@PYTHON3@'
-- 
2.25.4



More information about the dev mailing list