cmocka [1,2] is a testing framework for C. Adding unit test
capabilities to the openvpn repository will greatly ease the
task of writing correct code.
cmocka source code is added as git submodule in ./vendor. A
submodule approach has been chosen over a classical library
dependency because libcmocka is not available, or only
available in very old versions (e.g. on Ubuntu).
cmocka is build during 'make check' and installed in vendor/dist/.
[1] https://cmocka.org/
[2] https://lwn.net/Articles/558106/
Signed-off-by: Jens Neuhalfen <jens@neuhalfen.name>
Acked-by: Steffan Karger <steffan@karger.me>
Message-Id: <
20160525175756.56186-2-openvpn-devel@neuhalfen.name>
URL: http://article.gmane.org/gmane.network.openvpn.devel/11725
Signed-off-by: David Sommerseth <dazo@privateinternetaccess.com>
(cherry picked from commit
40cb4cfc5d011102daec61ab39583cba0eeb3077)
--- /dev/null
+[submodule "vendor/cmocka"]
+ path = vendor/cmocka
+ url = git://git.cryptomilk.org/projects/cmocka.git
+ branch = master
config-version.h
endif
-SUBDIRS = build distro include src sample doc tests
+SUBDIRS = build distro include src sample doc vendor tests
dist_doc_DATA = \
README \
AC_SUBST([plugindir])
AC_SUBST([sampledir])
+VENDOR_SRC_ROOT="\$(abs_top_srcdir)/vendor/"
+VENDOR_DIST_ROOT="\$(abs_top_builddir)/vendor/dist"
+VENDOR_BUILD_ROOT="\$(abs_top_builddir)/vendor/.build"
+AC_SUBST([VENDOR_SRC_ROOT])
+AC_SUBST([VENDOR_BUILD_ROOT])
+AC_SUBST([VENDOR_DIST_ROOT])
+
+TEST_LDFLAGS="-lcmocka -L\$(abs_top_builddir)/vendor/dist/lib -Wl,-rpath,\$(abs_top_builddir)/vendor/dist/lib"
+TEST_CFLAGS="-I\$(top_srcdir)/include -I\$(abs_top_builddir)/vendor/dist/include"
+
+AC_SUBST([TEST_LDFLAGS])
+AC_SUBST([TEST_CFLAGS])
+
AC_CONFIG_FILES([
version.sh
Makefile
src/plugins/auth-pam/Makefile
src/plugins/down-root/Makefile
tests/Makefile
+ tests/unit_tests/Makefile
+ tests/unit_tests/example_test/Makefile
+ vendor/Makefile
sample/Makefile
doc/Makefile
])
MAINTAINERCLEANFILES = \
$(srcdir)/Makefile.in
+SUBDIRS = unit_tests
+
test_scripts = t_client.sh t_lpback.sh t_cltsrv.sh
TESTS_ENVIRONMENT = top_srcdir="$(top_srcdir)"
dist_noinst_SCRIPTS = \
$(test_scripts) \
t_cltsrv-down.sh
-
--- /dev/null
+*_testdriver
--- /dev/null
+AUTOMAKE_OPTIONS = foreign
+
+SUBDIRS = example_test
--- /dev/null
+Unit Tests
+===========
+
+This directory contains unit tests for openvpn. New features/bugfixes should be written in a test friendly way and come with corresponding tests.
+
+Run tests
+----------
+
+Tests are run by `make check`. A failed tests stops test execution. To run all
+tests regardless of errors call `make -k check`.
+
+Add new tests to existing test suite
+-------------------------------------
+
+Test suites are organized in directories. [example_test/](example_test/) is an example
+for a test suite with two test executables. Feel free to use it as a template for new tests.
+
+Test suites
+--------------------
+
+Test suites live inside a subdirectory of `$ROOT/tests/unit_tests`, e.g. `$ROOT/tests/unit_tests/my_feature`.
+
+Test suites are configured by a `Makefile.am`. Tests are executed by testdrivers. One testsuite can contain more than one testdriver.
+
+### Hints
+* Name suites & testdrivers in a way that the name of the driver says something about which component/feature is tested
+* Name the testdriver executable `*_testdriver`. This way it gets picked up by the default `.gitignore`
+ * If this is not feasible: Add all output to a `.gitignore`* Use descriptive test names: `coffee_brewing__with_no_beans__fails` vs. `test34`
+* Testing a configurable feature? Wrap test execution with a conditional (see [auth_pam](plugins/auth-pam/Makefile.am) for an example)
+* Add multiple test-drivers when one testdriver looks crowded with tests
+
+### New Test Suites
+1. Organize tests in folders for features.
+2. Add the new test directory to `SUBDIRS` in `Makefile.am`
+3. Edit `configure.ac` and add the new `Makefile` to `AC_CONFIG_FILES`
+4. Run `./configure`, and *enable* the feature you'd like to test
+5. Make sure that `make check` runs your tests
+6. Check: Would a stranger be able to easily find your tests by you looking at the test output?
+7. Run `./configure`, and *disable* the feature you'd like to test
+8. Make sure that `make check` does *not run* your tests
--- /dev/null
+AUTOMAKE_OPTIONS = foreign
+
+check_PROGRAMS = example_testdriver example2_testdriver
+
+TESTS = $(check_PROGRAMS)
+
+example_testdriver_CFLAGS = @TEST_CFLAGS@
+example_testdriver_LDFLAGS = @TEST_LDFLAGS@
+example_testdriver_SOURCES = test.c
+
+example2_testdriver_CFLAGS = @TEST_CFLAGS@
+example2_testdriver_LDFLAGS = @TEST_LDFLAGS@
+example2_testdriver_SOURCES = test2.c
--- /dev/null
+This test only checks that test compilation works. This example contains two test executables.
+
+These tests can be used as template for 'real' tests.
--- /dev/null
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+static int setup(void **state) {
+ int *answer = malloc(sizeof(int));
+
+ *answer=42;
+ *state=answer;
+
+ return 0;
+}
+
+static int teardown(void **state) {
+ free(*state);
+
+ return 0;
+}
+
+static void null_test_success(void **state) {
+ (void) state;
+}
+
+static void int_test_success(void **state) {
+ int *answer = *state;
+ assert_int_equal(*answer, 42);
+}
+
+static void failing_test(void **state) {
+ // This tests fails to test that make check fails
+ assert_int_equal(0, 42);
+}
+
+int main(void) {
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(null_test_success),
+ cmocka_unit_test_setup_teardown(int_test_success, setup, teardown),
+// cmocka_unit_test(failing_test),
+ };
+
+ return cmocka_run_group_tests_name("success_test", tests, NULL, NULL);
+}
--- /dev/null
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+
+static void test_true(void **state) {
+ (void) state;
+}
+
+
+int main(void) {
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(test_true),
+ };
+
+ return cmocka_run_group_tests_name("success_test2", tests, NULL, NULL);
+}
--- /dev/null
+.build/
+dist/
--- /dev/null
+AUTOMAKE_OPTIONS = foreign
+
+cmockasrc = @VENDOR_SRC_ROOT@/cmocka # needs an absolute path bc. of the cmake invocation
+cmockabuild = @VENDOR_BUILD_ROOT@/cmocka
+cmockainstall = @VENDOR_DIST_ROOT@
+
+MAINTAINERCLEANFILES = \
+ $(srcdir)/Makefile.in \
+ $(cmockabuild) \
+ $(cmockainstall) \
+ @VENDOR_BUILD_ROOT@
+
+distdir:
+ mkdir -p $(cmockainstall)
+
+libcmocka: distdir
+ mkdir -p $(cmockabuild)
+ (cd $(cmockabuild) && cmake -DCMAKE_INSTALL_PREFIX=$(cmockainstall) $(cmockasrc) && make && make install)
+
+check: libcmocka
+
+clean:
+ rm -rf $(cmockabuild)
+ rm -rf $(cmockainstall)
--- /dev/null
+Vendor
+========
+
+Vendor source libraries are included in this directory. Libraries are included
+when there is no good way to ensure that the package is available on all
+systems.
+
+`Makefile.am` compiles these libraries and installs them into ./dist.
--- /dev/null
+Subproject commit b2732b52202ae48f866a024c633466efdbb8e85a