mirror of
https://github.com/OpenVPN/openvpn.git
synced 2025-05-09 05:31:05 +08:00
Add unit testing support via cmocka
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)
This commit is contained in:
parent
85f6a2b49d
commit
1739c102a5
4
.gitmodules
vendored
Normal file
4
.gitmodules
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
[submodule "vendor/cmocka"]
|
||||||
|
path = vendor/cmocka
|
||||||
|
url = git://git.cryptomilk.org/projects/cmocka.git
|
||||||
|
branch = master
|
@ -54,7 +54,7 @@ BUILT_SOURCES = \
|
|||||||
config-version.h
|
config-version.h
|
||||||
endif
|
endif
|
||||||
|
|
||||||
SUBDIRS = build distro include src sample doc tests
|
SUBDIRS = build distro include src sample doc vendor tests
|
||||||
|
|
||||||
dist_doc_DATA = \
|
dist_doc_DATA = \
|
||||||
README \
|
README \
|
||||||
|
16
configure.ac
16
configure.ac
@ -1119,6 +1119,19 @@ sampledir="\$(docdir)/sample"
|
|||||||
AC_SUBST([plugindir])
|
AC_SUBST([plugindir])
|
||||||
AC_SUBST([sampledir])
|
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([
|
AC_CONFIG_FILES([
|
||||||
version.sh
|
version.sh
|
||||||
Makefile
|
Makefile
|
||||||
@ -1137,6 +1150,9 @@ AC_CONFIG_FILES([
|
|||||||
src/plugins/auth-pam/Makefile
|
src/plugins/auth-pam/Makefile
|
||||||
src/plugins/down-root/Makefile
|
src/plugins/down-root/Makefile
|
||||||
tests/Makefile
|
tests/Makefile
|
||||||
|
tests/unit_tests/Makefile
|
||||||
|
tests/unit_tests/example_test/Makefile
|
||||||
|
vendor/Makefile
|
||||||
sample/Makefile
|
sample/Makefile
|
||||||
doc/Makefile
|
doc/Makefile
|
||||||
])
|
])
|
||||||
|
@ -12,6 +12,8 @@
|
|||||||
MAINTAINERCLEANFILES = \
|
MAINTAINERCLEANFILES = \
|
||||||
$(srcdir)/Makefile.in
|
$(srcdir)/Makefile.in
|
||||||
|
|
||||||
|
SUBDIRS = unit_tests
|
||||||
|
|
||||||
test_scripts = t_client.sh t_lpback.sh t_cltsrv.sh
|
test_scripts = t_client.sh t_lpback.sh t_cltsrv.sh
|
||||||
|
|
||||||
TESTS_ENVIRONMENT = top_srcdir="$(top_srcdir)"
|
TESTS_ENVIRONMENT = top_srcdir="$(top_srcdir)"
|
||||||
@ -20,4 +22,3 @@ TESTS = $(test_scripts)
|
|||||||
dist_noinst_SCRIPTS = \
|
dist_noinst_SCRIPTS = \
|
||||||
$(test_scripts) \
|
$(test_scripts) \
|
||||||
t_cltsrv-down.sh
|
t_cltsrv-down.sh
|
||||||
|
|
||||||
|
1
tests/unit_tests/.gitignore
vendored
Normal file
1
tests/unit_tests/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
*_testdriver
|
3
tests/unit_tests/Makefile.am
Normal file
3
tests/unit_tests/Makefile.am
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
AUTOMAKE_OPTIONS = foreign
|
||||||
|
|
||||||
|
SUBDIRS = example_test
|
40
tests/unit_tests/README.md
Normal file
40
tests/unit_tests/README.md
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
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
|
13
tests/unit_tests/example_test/Makefile.am
Normal file
13
tests/unit_tests/example_test/Makefile.am
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
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
|
3
tests/unit_tests/example_test/README.md
Normal file
3
tests/unit_tests/example_test/README.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
This test only checks that test compilation works. This example contains two test executables.
|
||||||
|
|
||||||
|
These tests can be used as template for 'real' tests.
|
46
tests/unit_tests/example_test/test.c
Normal file
46
tests/unit_tests/example_test/test.c
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#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);
|
||||||
|
}
|
21
tests/unit_tests/example_test/test2.c
Normal file
21
tests/unit_tests/example_test/test2.c
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#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);
|
||||||
|
}
|
2
vendor/.gitignore
vendored
Normal file
2
vendor/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
.build/
|
||||||
|
dist/
|
24
vendor/Makefile.am
vendored
Normal file
24
vendor/Makefile.am
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
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)
|
8
vendor/README.md
vendored
Normal file
8
vendor/README.md
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
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.
|
1
vendor/cmocka
vendored
Submodule
1
vendor/cmocka
vendored
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit b2732b52202ae48f866a024c633466efdbb8e85a
|
Loading…
x
Reference in New Issue
Block a user