]> git.ipfire.org Git - people/ms/strongswan.git/commitdiff
Merge branch 'unit-tests'
authorTobias Brunner <tobias@strongswan.org>
Tue, 11 Jun 2013 09:07:43 +0000 (11:07 +0200)
committerTobias Brunner <tobias@strongswan.org>
Tue, 11 Jun 2013 09:16:30 +0000 (11:16 +0200)
Adds a test runner and several test suites for libstrongswan.
Also adds an option to produce a test coverage report.

Several bugs were fixed in the process and chunk_hash() was replaced
with an improved implementation based on SipHash-2-4 (with a randomly
allocated key to prevent hash flooding attacks).

38 files changed:
.gitignore
HACKING
Makefile.am
configure.in
src/libcharon/plugins/unit_tester/Makefile.am
src/libcharon/plugins/unit_tester/tests.h
src/libcharon/plugins/unit_tester/tests/test_chunk.c [deleted file]
src/libcharon/plugins/unit_tester/tests/test_enumerator.c [deleted file]
src/libcharon/plugins/unit_tester/tests/test_hashtable.c [deleted file]
src/libcharon/plugins/unit_tester/tests/test_id.c [deleted file]
src/libstrongswan/Makefile.am
src/libstrongswan/bio/bio_writer.c
src/libstrongswan/collections/enumerator.c
src/libstrongswan/collections/linked_list.h
src/libstrongswan/tests/.gitignore [new file with mode: 0644]
src/libstrongswan/tests/Makefile.am [new file with mode: 0644]
src/libstrongswan/tests/test_bio_reader.c [new file with mode: 0644]
src/libstrongswan/tests/test_bio_writer.c [new file with mode: 0644]
src/libstrongswan/tests/test_chunk.c [new file with mode: 0644]
src/libstrongswan/tests/test_enum.c [new file with mode: 0644]
src/libstrongswan/tests/test_enumerator.c [new file with mode: 0644]
src/libstrongswan/tests/test_hashtable.c [new file with mode: 0644]
src/libstrongswan/tests/test_identification.c [new file with mode: 0644]
src/libstrongswan/tests/test_linked_list.c [new file with mode: 0644]
src/libstrongswan/tests/test_linked_list_enumerator.c [new file with mode: 0644]
src/libstrongswan/tests/test_runner.c [new file with mode: 0644]
src/libstrongswan/tests/test_runner.h [new file with mode: 0644]
src/libstrongswan/tests/test_suite.h [new file with mode: 0644]
src/libstrongswan/tests/test_threading.c [moved from src/libcharon/plugins/unit_tester/tests/test_mutex.c with 67% similarity]
src/libstrongswan/tests/test_utils.c [new file with mode: 0644]
src/libstrongswan/utils/chunk.c
src/libstrongswan/utils/chunk.h
src/libstrongswan/utils/enum.c
src/libstrongswan/utils/identification.c
src/libstrongswan/utils/leak_detective.c
src/libstrongswan/utils/leak_detective.h
src/libstrongswan/utils/utils.c
src/libstrongswan/utils/utils.h

index 024d9e45210d3f82b688c66e9a5d6112a8ca0b80..3deb9fdb5a65f96b8eb441fab494e5509ad70407 100644 (file)
@@ -34,3 +34,7 @@ apidoc/
 *.tar.bz2
 *.tar.gz
 .DS_Store
+coverage/
+*.gcno
+*.gcda
+*.gcov
\ No newline at end of file
diff --git a/HACKING b/HACKING
index dbb347e6fef579891e6754ead322a3bb78bb2de8..8755fd63f709917e343143faab0eb71e4e78ba59 100644 (file)
--- a/HACKING
+++ b/HACKING
@@ -18,8 +18,11 @@ the code, you need the following tools:
     - lex/flex
     - yacc/bison
     - gperf
+
+    Optionally:
     - check
-    - optionally Doxygen
+    - lcov/genhtml
+    - Doxygen
 
 To check out the master branch, use:
 
index 1fc1fcdb3143495274e6b5e2777e21b941b05675..d9bae5f20586e830cc37ecdbe3123887104b0390 100644 (file)
@@ -37,5 +37,37 @@ Doxyfile :   Doxyfile.in
 apidoc :       Doxyfile
                doxygen
 
-clean-local:
-               rm -rf apidoc
+cov-reset-common:
+               @rm -rf $(top_builddir)/coverage
+               @find $(top_builddir) -name "*.gcda" -delete
+
+if COVERAGE
+cov-reset: cov-reset-common
+               @lcov --zerocounters --directory $(top_builddir)
+
+cov-report:
+               @mkdir $(top_builddir)/coverage
+               lcov -c -o $(top_builddir)/coverage/coverage.info -d $(top_builddir)
+               lcov -r $(top_builddir)/coverage/coverage.info '*/tests/*' \
+                        -o $(top_builddir)/coverage/coverage.cleaned.info
+               genhtml --num-spaces 4 --legend \
+                               -t "$(PACKAGE_STRING)" \
+                               -o $(top_builddir)/coverage/html \
+                               -p `readlink -m $(abs_top_srcdir)`/src \
+                               $(top_builddir)/coverage/coverage.cleaned.info
+               @echo "Coverage Report at $(top_builddir)/coverage/html" >&2
+
+coverage:
+               @$(MAKE) cov-reset
+               @$(MAKE) check
+               @$(MAKE) cov-report
+else
+coverage:
+               @echo "reconfigure with --enable-coverage"
+endif
+
+clean-local: cov-reset-common
+               @find $(top_builddir) -name "*.gcno" -delete
+               @rm -rf apidoc
+
+.PHONY: cov-reset-common cov-reset cov-report coverage
\ No newline at end of file
index 8df0c2a0b01c6aefb23182fc7e111bbf8e54b8d9..6a7f9ffb50cd5bf1d9a1afa3cae02d9fe60c02be 100644 (file)
@@ -243,6 +243,7 @@ ARG_ENABL_SET([monolithic],     [build monolithic version of libstrongswan that
 ARG_ENABL_SET([bfd-backtraces], [use binutils libbfd to resolve backtraces for memory leaks and segfaults.])
 ARG_ENABL_SET([unwind-backtraces],[use libunwind to create backtraces for memory leaks and segfaults.])
 ARG_ENABL_SET([unit-tests],     [enable unit tests using the check test framework.])
+ARG_ENABL_SET([coverage],       [enable lcov coverage report generation.])
 ARG_ENABL_SET([tkm],            [enable Trusted Key Manager support.])
 ARG_ENABL_SET([cmd],            [enable the command line IKE client charon-cmd.])
 
@@ -362,6 +363,10 @@ if test x$medcli = xtrue; then
        mediation=true
 fi
 
+if test x$coverage = xtrue; then
+       unit_tests=true
+fi
+
 # ===========================================
 #  check required libraries and header files
 # ===========================================
@@ -917,6 +922,25 @@ if test x$unit_tests = xtrue; then
        AC_SUBST(CHECK_LIBS)
 fi
 
+if test x$coverage = xtrue; then
+       AC_PATH_PROG([LCOV], [lcov], [], [$PATH:/bin:/usr/bin:/usr/local/bin])
+       if test x$LCOV = x; then
+               AC_MSG_ERROR([lcov not found])
+       fi
+       AC_PATH_PROG([GENHTML], [genhtml], [], [$PATH:/bin:/usr/bin:/usr/local/bin])
+       if test x$GENHTML = x; then
+               AC_MSG_ERROR([genhtml not found])
+       fi
+
+       COVERAGE_CFLAGS="-fprofile-arcs -ftest-coverage"
+       COVERAGE_LDFLAGS="-fprofile-arcs"
+       AC_SUBST(COVERAGE_CFLAGS)
+       AC_SUBST(COVERAGE_LDFLAGS)
+
+       AC_MSG_NOTICE([coverage enabled, adding "-g -O0" to CFLAGS])
+       CFLAGS="${CFLAGS} -g -O0"
+fi
+
 # ===============================================
 #  collect plugin list for strongSwan components
 # ===============================================
@@ -1241,6 +1265,7 @@ AM_CONDITIONAL(USE_TROUSERS, test x$tss = xtrousers)
 AM_CONDITIONAL(MONOLITHIC, test x$monolithic = xtrue)
 AM_CONDITIONAL(USE_SILENT_RULES, test x$enable_silent_rules = xyes)
 AM_CONDITIONAL(UNITTESTS, test x$unit_tests = xtrue)
+AM_CONDITIONAL(COVERAGE, test x$coverage = xtrue)
 AM_CONDITIONAL(USE_TKM, test x$tkm = xtrue)
 AM_CONDITIONAL(USE_CMD, test x$cmd = xtrue)
 
@@ -1320,6 +1345,7 @@ AC_CONFIG_FILES([
        src/libstrongswan/plugins/gcm/Makefile
        src/libstrongswan/plugins/af_alg/Makefile
        src/libstrongswan/plugins/test_vectors/Makefile
+       src/libstrongswan/tests/Makefile
        src/libhydra/Makefile
        src/libhydra/plugins/attr/Makefile
        src/libhydra/plugins/attr_sql/Makefile
index c46d2b85d954ae10615f63b040ca070c6ebd043c..84628b507e46ba621ac54c0657549931c841bec4 100644 (file)
@@ -12,19 +12,14 @@ endif
 
 libstrongswan_unit_tester_la_SOURCES = \
        unit_tester.c unit_tester.h tests.h \
-       tests/test_enumerator.c \
        tests/test_auth_info.c \
        tests/test_curl.c \
        tests/test_mysql.c \
        tests/test_sqlite.c \
-       tests/test_mutex.c \
        tests/test_rsa_gen.c \
        tests/test_cert.c \
        tests/test_med_db.c \
-       tests/test_chunk.c \
        tests/test_pool.c \
-       tests/test_agent.c \
-       tests/test_id.c \
-       tests/test_hashtable.c
+       tests/test_agent.c
 
 libstrongswan_unit_tester_la_LDFLAGS = -module -avoid-version
index cd38c8a9965f6b5d8889825977a669de19b5054d..bcb82c3bd87d127342e819ad5f61889d67149f59 100644 (file)
  * @{ @ingroup unit_tester
  */
 
-DEFINE_TEST("linked_list_t->remove()", test_list_remove, FALSE)
-DEFINE_TEST("hashtable_t->remove_at()", test_hashtable_remove_at, FALSE)
-DEFINE_TEST("simple enumerator", test_enumerate, FALSE)
-DEFINE_TEST("nested enumerator", test_enumerate_nested, FALSE)
-DEFINE_TEST("filtered enumerator", test_enumerate_filtered, FALSE)
-DEFINE_TEST("token enumerator", test_enumerate_token, FALSE)
 DEFINE_TEST("auth cfg", test_auth_cfg, FALSE)
 DEFINE_TEST("CURL get", test_curl_get, FALSE)
 DEFINE_TEST("MySQL operations", test_mysql, FALSE)
 DEFINE_TEST("SQLite operations", test_sqlite, FALSE)
-DEFINE_TEST("mutex primitive", test_mutex, FALSE)
 DEFINE_TEST("RSA key generation", test_rsa_gen, FALSE)
 DEFINE_TEST("RSA subjectPublicKeyInfo loading", test_rsa_load_any, FALSE)
 DEFINE_TEST("X509 certificate", test_cert_x509, FALSE)
 DEFINE_TEST("Mediation database key fetch", test_med_db, FALSE)
-DEFINE_TEST("Base64 converter", test_chunk_base64, FALSE)
 DEFINE_TEST("IP pool", test_pool, FALSE)
 DEFINE_TEST("SSH agent", test_agent, FALSE)
-DEFINE_TEST("ID parts", test_id_parts, FALSE)
-DEFINE_TEST("ID wildcards", test_id_wildcards, FALSE)
-DEFINE_TEST("ID equals", test_id_equals, FALSE)
-DEFINE_TEST("ID matches", test_id_matches, FALSE)
 
 /** @}*/
diff --git a/src/libcharon/plugins/unit_tester/tests/test_chunk.c b/src/libcharon/plugins/unit_tester/tests/test_chunk.c
deleted file mode 100644 (file)
index 2e0905b..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2008 Martin Willi
- * Hochschule fuer Technik Rapperswil
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * for more details.
- */
-
-#include <library.h>
-#include <daemon.h>
-
-/*******************************************************************************
- * Base64 encoding/decoding test
- ******************************************************************************/
-bool test_chunk_base64()
-{
-       /* test vectors from RFC4648:
-        *
-        * BASE64("") = ""
-        * BASE64("f") = "Zg=="
-        * BASE64("fo") = "Zm8="
-        * BASE64("foo") = "Zm9v"
-        * BASE64("foob") = "Zm9vYg=="
-        * BASE64("fooba") = "Zm9vYmE="
-        * BASE64("foobar") = "Zm9vYmFy"
-        */
-
-       typedef struct {
-               char *in;
-               char *out;
-       } testdata_t;
-
-       testdata_t test[] = {
-               {"", ""},
-               {"f", "Zg=="},
-               {"fo", "Zm8="},
-               {"foo", "Zm9v"},
-               {"foob", "Zm9vYg=="},
-               {"fooba", "Zm9vYmE="},
-               {"foobar", "Zm9vYmFy"},
-       };
-       int i;
-
-       for (i = 0; i < countof(test); i++)
-       {
-               chunk_t out;
-
-               out = chunk_to_base64(chunk_create(test[i].in, strlen(test[i].in)), NULL);
-
-               if (!streq(out.ptr, test[i].out))
-               {
-                       DBG1(DBG_CFG, "base64 conversion error - should %s, is %s",
-                               test[i].out, out.ptr);
-                       return FALSE;
-               }
-               free(out.ptr);
-       }
-
-       for (i = 0; i < countof(test); i++)
-       {
-               chunk_t out;
-
-               out = chunk_from_base64(chunk_create(test[i].out, strlen(test[i].out)), NULL);
-
-               if (!strneq(out.ptr, test[i].in, out.len))
-               {
-                       DBG1(DBG_CFG, "base64 conversion error - should %s, is %#B",
-                               test[i].in, &out);
-                       return FALSE;
-               }
-               free(out.ptr);
-       }
-       return TRUE;
-}
-
diff --git a/src/libcharon/plugins/unit_tester/tests/test_enumerator.c b/src/libcharon/plugins/unit_tester/tests/test_enumerator.c
deleted file mode 100644 (file)
index 83b78c0..0000000
+++ /dev/null
@@ -1,306 +0,0 @@
-/*
- * Copyright (C) 2007 Martin Willi
- * Hochschule fuer Technik Rapperswil
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * for more details.
- */
-
-#include <collections/linked_list.h>
-
-
-/*******************************************************************************
- * linked list remove test
- ******************************************************************************/
-bool test_list_remove()
-{
-       void *a = (void*)1, *b = (void*)2;
-       linked_list_t *list;
-
-       list = linked_list_create();
-       list->insert_last(list, a);
-       if (list->remove(list, a, NULL) != 1)
-       {
-               return FALSE;
-       }
-       list->insert_last(list, a);
-       list->insert_first(list, a);
-       list->insert_last(list, a);
-       list->insert_last(list, b);
-       if (list->remove(list, a, NULL) != 3)
-       {
-               return FALSE;
-       }
-       if (list->remove(list, a, NULL) != 0)
-       {
-               return FALSE;
-       }
-       if (list->get_count(list) != 1)
-       {
-               return FALSE;
-       }
-       if (list->remove(list, b, NULL) != 1)
-       {
-               return FALSE;
-       }
-       if (list->remove(list, b, NULL) != 0)
-       {
-               return FALSE;
-       }
-       list->destroy(list);
-       return TRUE;
-}
-
-/*******************************************************************************
- * Simple insert first/last and enumerate test
- ******************************************************************************/
-bool test_enumerate()
-{
-       int round, x;
-       void *a = (void*)4, *b = (void*)3, *c = (void*)2, *d = (void*)5, *e = (void*)1;
-       linked_list_t *list;
-       enumerator_t *enumerator;
-
-       list = linked_list_create();
-
-       list->insert_last(list, a);
-       list->insert_first(list, b);
-       list->insert_first(list, c);
-       list->insert_last(list, d);
-       list->insert_first(list, e);
-
-       round = 1;
-       enumerator = list->create_enumerator(list);
-       while (enumerator->enumerate(enumerator, &x))
-       {
-               if (round != x)
-               {
-                       return FALSE;
-               }
-               round++;
-       }
-       enumerator->destroy(enumerator);
-
-       list->destroy(list);
-       return TRUE;
-}
-
-/*******************************************************************************
- * nested enumerator test
- ******************************************************************************/
-
-static bool bad_data;
-
-static enumerator_t* create_inner(linked_list_t *outer, void *data)
-{
-       if (data != (void*)101)
-       {
-               bad_data = TRUE;
-       }
-       return outer->create_enumerator(outer);
-}
-
-
-static void destroy_data(void *data)
-{
-       if (data != (void*)101)
-       {
-               bad_data = TRUE;
-       }
-}
-
-bool test_enumerate_nested()
-{
-       int round, x;
-       void *a = (void*)1, *b = (void*)2, *c = (void*)3, *d = (void*)4, *e = (void*)5;
-       linked_list_t *list, *l1, *l2, *l3;
-       enumerator_t *enumerator;
-
-       bad_data = FALSE;
-       list = linked_list_create();
-       l1 = linked_list_create();
-       l2 = linked_list_create();
-       l3 = linked_list_create();
-       list->insert_last(list, l1);
-       list->insert_last(list, l2);
-       list->insert_last(list, l3);
-
-       l1->insert_last(l1, a);
-       l1->insert_last(l1, b);
-       l3->insert_last(l3, c);
-       l3->insert_last(l3, d);
-       l3->insert_last(l3, e);
-
-       round = 1;
-       enumerator = enumerator_create_nested(list->create_enumerator(list),
-                                       (void*)create_inner, (void*)101, destroy_data);
-       while (enumerator->enumerate(enumerator, &x))
-       {
-               if (round != x)
-               {
-                       return FALSE;
-               }
-               round++;
-       }
-       enumerator->destroy(enumerator);
-
-       list->destroy(list);
-       l1->destroy(l1);
-       l2->destroy(l2);
-       l3->destroy(l3);
-       return !bad_data;
-}
-
-
-/*******************************************************************************
- * filtered enumerator test
- ******************************************************************************/
-static bool filter(void *data, int *v, int *vo, int *w, int *wo,
-                                  int *x, int *xo, int *y, int *yo, int *z, int *zo)
-{
-       int val = *v;
-
-       *vo = val++;
-       *wo = val++;
-       *xo = val++;
-       *yo = val++;
-       *zo = val++;
-       if (data != (void*)101)
-       {
-               return FALSE;
-       }
-       return TRUE;
-}
-
-bool test_enumerate_filtered()
-{
-       int round, v, w, x, y, z;
-       void *a = (void*)1, *b = (void*)2, *c = (void*)3, *d = (void*)4, *e = (void*)5;
-       linked_list_t *list;
-       enumerator_t *enumerator;
-
-       bad_data = FALSE;
-       list = linked_list_create();
-
-       list->insert_last(list, a);
-       list->insert_last(list, b);
-       list->insert_last(list, c);
-       list->insert_last(list, d);
-       list->insert_last(list, e);
-
-       round = 1;
-       enumerator = enumerator_create_filter(list->create_enumerator(list),
-                                                                       (void*)filter, (void*)101, destroy_data);
-       while (enumerator->enumerate(enumerator, &v, &w, &x, &y, &z))
-       {
-               if (v != round || w != round + 1 || x != round + 2 ||
-                       y != round + 3 || z != round + 4)
-               {
-                       return FALSE;
-               }
-               round++;
-       }
-       enumerator->destroy(enumerator);
-
-       list->destroy(list);
-       return !bad_data;
-}
-
-/*******************************************************************************
- * token parser test
- ******************************************************************************/
-
-bool test_enumerate_token()
-{
-       enumerator_t *enumerator;
-       char *token;
-       int i, num;
-       struct {
-               char *string;
-               char *sep;
-               char *trim;
-       } tests1[] = {
-               {"abc, cde, efg", ",", " "},
-               {" abc 1:2 cde;3  4efg5.  ", ":;.,", " 12345"},
-               {"abc.cde,efg", ",.", ""},
-               {"  abc   cde  efg  ", " ", " "},
-               {"a'abc' c 'cde' cefg", " ", " abcd"},
-               {"'abc' abc 'cde'd 'efg'", " ", " abcd"},
-       }, tests2[] = {
-               {"a, b, c", ",", " "},
-               {"a,b,c", ",", " "},
-               {" a 1:2 b;3  4c5.  ", ":;.,", " 12345"},
-               {"a.b,c", ",.", ""},
-               {"  a   b  c  ", " ", " "},
-       };
-
-       for (num = 0; num < countof(tests1); num++)
-       {
-               i = 0;
-               enumerator = enumerator_create_token(tests1[num].string,
-                                                                                        tests1[num].sep, tests1[num].trim);
-               while (enumerator->enumerate(enumerator, &token))
-               {
-                       switch (i)
-                       {
-                               case 0:
-                                       if (!streq(token, "abc")) return FALSE;
-                                       break;
-                               case 1:
-                                       if (!streq(token, "cde")) return FALSE;
-                                       break;
-                               case 2:
-                                       if (!streq(token, "efg")) return FALSE;
-                                       break;
-                               default:
-                                       return FALSE;
-                       }
-                       i++;
-               }
-               if (i != 3)
-               {
-                       return FALSE;
-               }
-               enumerator->destroy(enumerator);
-       }
-
-       for (num = 0; num < countof(tests2); num++)
-       {
-               i = 0;
-               enumerator = enumerator_create_token(tests2[num].string,
-                                                                                        tests2[num].sep, tests2[num].trim);
-               while (enumerator->enumerate(enumerator, &token))
-               {
-                       switch (i)
-                       {
-                               case 0:
-                                       if (!streq(token, "a")) return FALSE;
-                                       break;
-                               case 1:
-                                       if (!streq(token, "b")) return FALSE;
-                                       break;
-                               case 2:
-                                       if (!streq(token, "c")) return FALSE;
-                                       break;
-                               default:
-                                       return FALSE;
-                       }
-                       i++;
-               }
-               if (i != 3)
-               {
-                       return FALSE;
-               }
-               enumerator->destroy(enumerator);
-       }
-
-       return TRUE;
-}
-
diff --git a/src/libcharon/plugins/unit_tester/tests/test_hashtable.c b/src/libcharon/plugins/unit_tester/tests/test_hashtable.c
deleted file mode 100644 (file)
index 5513f67..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2010 Tobias Brunner
- * Hochschule fuer Technik Rapperswil
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * for more details.
- */
-
-#include <library.h>
-#include <collections/hashtable.h>
-
-static u_int hash(char *key)
-{
-       return chunk_hash(chunk_create(key, strlen(key)));
-}
-
-static u_int equals(char *key1, char *key2)
-{
-       return streq(key1, key2);
-}
-
-/**
- * Test the remove_at method
- */
-bool test_hashtable_remove_at()
-{
-       char *k1 = "key1", *k2 = "key2", *k3 = "key3", *key;
-       char *v1 = "val1", *v2 = "val2", *v3 = "val3", *value;
-       enumerator_t *enumerator;
-       hashtable_t *ht = hashtable_create((hashtable_hash_t)hash,
-                                                                          (hashtable_equals_t)equals, 0);
-
-       ht->put(ht, k1, v1);
-       ht->put(ht, k2, v2);
-       ht->put(ht, k3, v3);
-
-       if (ht->get_count(ht) != 3)
-       {
-               return FALSE;
-       }
-
-       enumerator = ht->create_enumerator(ht);
-       while (enumerator->enumerate(enumerator, &key, &value))
-       {
-               if (streq(key, k2))
-               {
-                       ht->remove_at(ht, enumerator);
-               }
-       }
-       enumerator->destroy(enumerator);
-
-       if (ht->get_count(ht) != 2)
-       {
-               return FALSE;
-       }
-
-       if (ht->get(ht, k1) == NULL ||
-               ht->get(ht, k3) == NULL)
-       {
-               return FALSE;
-       }
-
-       if (ht->get(ht, k2) != NULL)
-       {
-               return FALSE;
-       }
-
-       ht->put(ht, k2, v2);
-
-       if (ht->get_count(ht) != 3)
-       {
-               return FALSE;
-       }
-
-       if (ht->get(ht, k1) == NULL ||
-               ht->get(ht, k2) == NULL ||
-               ht->get(ht, k3) == NULL)
-       {
-               return FALSE;
-       }
-
-       enumerator = ht->create_enumerator(ht);
-       while (enumerator->enumerate(enumerator, &key, &value))
-       {
-               ht->remove_at(ht, enumerator);
-       }
-       enumerator->destroy(enumerator);
-
-       if (ht->get_count(ht) != 0)
-       {
-               return FALSE;
-       }
-
-       if (ht->get(ht, k1) != NULL ||
-               ht->get(ht, k2) != NULL ||
-               ht->get(ht, k3) != NULL)
-       {
-               return FALSE;
-       }
-
-       ht->destroy(ht);
-
-       return TRUE;
-}
diff --git a/src/libcharon/plugins/unit_tester/tests/test_id.c b/src/libcharon/plugins/unit_tester/tests/test_id.c
deleted file mode 100644 (file)
index 868a2ca..0000000
+++ /dev/null
@@ -1,249 +0,0 @@
-/*
- * Copyright (C) 2009 Martin Willi
- * Hochschule fuer Technik Rapperswil
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * for more details.
- */
-
-#include <daemon.h>
-
-/*******************************************************************************
- * identification part enumeration test
- ******************************************************************************/
-bool test_id_parts()
-{
-       identification_t *id;
-       enumerator_t *enumerator;
-       id_part_t part;
-       chunk_t data;
-       int i = 0;
-
-       id = identification_create_from_string("C=CH, O=strongSwan, CN=tester");
-
-       enumerator = id->create_part_enumerator(id);
-       while (enumerator->enumerate(enumerator, &part, &data))
-       {
-               switch (i++)
-               {
-                       case 0:
-                               if (part != ID_PART_RDN_C ||
-                                       !chunk_equals(data, chunk_create("CH", 2)))
-                               {
-                                       return FALSE;
-                               }
-                               break;
-                       case 1:
-                               if (part != ID_PART_RDN_O ||
-                                       !chunk_equals(data, chunk_create("strongSwan", 10)))
-                               {
-                                       return FALSE;
-                               }
-                               break;
-                       case 2:
-                               if (part != ID_PART_RDN_CN ||
-                                       !chunk_equals(data, chunk_create("tester", 6)))
-                               {
-                                       return FALSE;
-                               }
-                               break;
-                       default:
-                               return FALSE;
-               }
-       }
-       if (i < 3)
-       {
-               return FALSE;
-       }
-       enumerator->destroy(enumerator);
-       id->destroy(id);
-       return TRUE;
-}
-
-/*******************************************************************************
- * identification contains_wildcards() test
- ******************************************************************************/
-
-static bool test_id_wildcards_has(char *string)
-{
-       identification_t *id;
-       bool contains;
-
-       id = identification_create_from_string(string);
-       contains = id->contains_wildcards(id);
-       id->destroy(id);
-       return contains;
-}
-
-bool test_id_wildcards()
-{
-       if (!test_id_wildcards_has("C=*, O=strongSwan, CN=gw"))
-       {
-               return FALSE;
-       }
-       if (!test_id_wildcards_has("C=CH, O=strongSwan, CN=*"))
-       {
-               return FALSE;
-       }
-       if (test_id_wildcards_has("C=**, O=a*, CN=*a"))
-       {
-               return FALSE;
-       }
-       if (!test_id_wildcards_has("*@strongswan.org"))
-       {
-               return FALSE;
-       }
-       if (!test_id_wildcards_has("*.strongswan.org"))
-       {
-               return FALSE;
-       }
-       return TRUE;
-}
-
-/*******************************************************************************
- * identification equals test
- ******************************************************************************/
-
-static bool test_id_equals_one(identification_t *a, char *b_str)
-{
-       identification_t *b;
-       bool equals;
-
-       b = identification_create_from_string(b_str);
-       equals = a->equals(a, b);
-       b->destroy(b);
-       return equals;
-}
-
-bool test_id_equals()
-{
-       identification_t *a;
-       chunk_t encoding, fuzzed;
-       int i;
-
-       a = identification_create_from_string(
-                                                          "C=CH, E=martin@strongswan.org, CN=martin");
-
-       if (!test_id_equals_one(a, "C=CH, E=martin@strongswan.org, CN=martin"))
-       {
-               return FALSE;
-       }
-       if (!test_id_equals_one(a, "C=ch, E=martin@STRONGSWAN.ORG, CN=Martin"))
-       {
-               return FALSE;
-       }
-       if (test_id_equals_one(a, "C=CN, E=martin@strongswan.org, CN=martin"))
-       {
-               return FALSE;
-       }
-       if (test_id_equals_one(a, "E=martin@strongswan.org, C=CH, CN=martin"))
-       {
-               return FALSE;
-       }
-       if (test_id_equals_one(a, "E=martin@strongswan.org, C=CH, CN=martin"))
-       {
-               return FALSE;
-       }
-       encoding = chunk_clone(a->get_encoding(a));
-       a->destroy(a);
-
-       /* simple fuzzing, increment each byte of encoding */
-       for (i = 0; i < encoding.len; i++)
-       {
-               if (i == 11 || i == 30 || i == 62)
-               {       /* skip ASN.1 type fields, as equals() handles them graceful */
-                       continue;
-               }
-               fuzzed = chunk_clone(encoding);
-               fuzzed.ptr[i]++;
-               a = identification_create_from_encoding(ID_DER_ASN1_DN, fuzzed);
-               if (test_id_equals_one(a, "C=CH, E=martin@strongswan.org, CN=martin"))
-               {
-                       return FALSE;
-               }
-               a->destroy(a);
-               free(fuzzed.ptr);
-       }
-
-       /* and decrement each byte of encoding */
-       for (i = 0; i < encoding.len; i++)
-       {
-               if (i == 11 || i == 30 || i == 62)
-               {
-                       continue;
-               }
-               fuzzed = chunk_clone(encoding);
-               fuzzed.ptr[i]--;
-               a = identification_create_from_encoding(ID_DER_ASN1_DN, fuzzed);
-               if (test_id_equals_one(a, "C=CH, E=martin@strongswan.org, CN=martin"))
-               {
-                       return FALSE;
-               }
-               a->destroy(a);
-               free(fuzzed.ptr);
-       }
-       free(encoding.ptr);
-       return TRUE;
-}
-
-/*******************************************************************************
- * identification matches test
- ******************************************************************************/
-
-static id_match_t test_id_matches_one(identification_t *a, char *b_str)
-{
-       identification_t *b;
-       id_match_t match;
-
-       b = identification_create_from_string(b_str);
-       match = a->matches(a, b);
-       b->destroy(b);
-       return match;
-}
-
-bool test_id_matches()
-{
-       identification_t *a;
-
-       a = identification_create_from_string(
-                                                          "C=CH, E=martin@strongswan.org, CN=martin");
-
-       if (test_id_matches_one(a, "C=CH, E=martin@strongswan.org, CN=martin")
-                                                                                                                       != ID_MATCH_PERFECT)
-       {
-               return FALSE;
-       }
-       if (test_id_matches_one(a, "C=CH, E=*, CN=martin") != ID_MATCH_ONE_WILDCARD)
-       {
-               return FALSE;
-       }
-       if (test_id_matches_one(a, "C=CH, E=*, CN=*") != ID_MATCH_ONE_WILDCARD - 1)
-       {
-               return FALSE;
-       }
-       if (test_id_matches_one(a, "C=*, E=*, CN=*") != ID_MATCH_ONE_WILDCARD - 2)
-       {
-               return FALSE;
-       }
-       if (test_id_matches_one(a, "C=*, E=*, CN=*, O=BADInc") != ID_MATCH_NONE)
-       {
-               return FALSE;
-       }
-       if (test_id_matches_one(a, "C=*, E=*") != ID_MATCH_NONE)
-       {
-               return FALSE;
-       }
-       if (test_id_matches_one(a, "C=*, E=a@b.c, CN=*") != ID_MATCH_NONE)
-       {
-               return FALSE;
-       }
-       a->destroy(a);
-       return TRUE;
-}
index 2465fb09dbb848c6f68e30fc3b7c061b44c76a3f..567bdfe6f16747b02aba992f462bc2aac733869f 100644 (file)
@@ -87,7 +87,8 @@ AM_CFLAGS = \
 -DIPSEC_DIR=\"${ipsecdir}\" \
 -DIPSEC_LIB_DIR=\"${ipseclibdir}\" \
 -DPLUGINDIR=\"${plugindir}\" \
--DSTRONGSWAN_CONF=\"${strongswan_conf}\"
+-DSTRONGSWAN_CONF=\"${strongswan_conf}\" \
+@COVERAGE_CFLAGS@
 
 if USE_LEAK_DETECTIVE
   AM_CFLAGS += -DLEAK_DETECTIVE
@@ -455,3 +456,10 @@ if MONOLITHIC
   libstrongswan_la_LIBADD += plugins/test_vectors/libstrongswan-test-vectors.la
 endif
 endif
+
+if UNITTESTS
+if MONOLITHIC
+  SUBDIRS += .
+endif
+  SUBDIRS += tests
+endif
index 8576843ee6cf7a21245981c40ba152a84014b555..152d9ce22e8459faeb7dc359513e48a75d6ebb14 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 Tobias Brunner
+ * Copyright (C) 2012-2013 Tobias Brunner
  * Hochschule fuer Technik Rapperswil
  *
  * Copyright (C) 2010 Martin Willi
@@ -47,21 +47,27 @@ struct private_bio_writer_t {
 };
 
 /**
- * Increase buffer size
+ * Increase buffer size, if required
  */
-static void increase(private_bio_writer_t *this)
+static inline void increase(private_bio_writer_t *this, size_t required)
 {
-       this->buf.len += this->increase;
-       this->buf.ptr = realloc(this->buf.ptr, this->buf.len);
+       bool inc = FALSE;
+
+       while (this->used + required > this->buf.len)
+       {
+               this->buf.len += this->increase;
+               inc = TRUE;
+       }
+       if (inc)
+       {
+               this->buf.ptr = realloc(this->buf.ptr, this->buf.len);
+       }
 }
 
 METHOD(bio_writer_t, write_uint8, void,
        private_bio_writer_t *this, u_int8_t value)
 {
-       if (this->used + 1 > this->buf.len)
-       {
-               increase(this);
-       }
+       increase(this, 1);
        this->buf.ptr[this->used] = value;
        this->used += 1;
 }
@@ -69,10 +75,7 @@ METHOD(bio_writer_t, write_uint8, void,
 METHOD(bio_writer_t, write_uint16, void,
        private_bio_writer_t *this, u_int16_t value)
 {
-       if (this->used + 2 > this->buf.len)
-       {
-               increase(this);
-       }
+       increase(this, 2);
        htoun16(this->buf.ptr + this->used, value);
        this->used += 2;
 }
@@ -80,10 +83,7 @@ METHOD(bio_writer_t, write_uint16, void,
 METHOD(bio_writer_t, write_uint24, void,
        private_bio_writer_t *this, u_int32_t value)
 {
-       if (this->used + 3 > this->buf.len)
-       {
-               increase(this);
-       }
+       increase(this, 3);
        value = htonl(value);
        memcpy(this->buf.ptr + this->used, ((char*)&value) + 1, 3);
        this->used += 3;
@@ -92,10 +92,7 @@ METHOD(bio_writer_t, write_uint24, void,
 METHOD(bio_writer_t, write_uint32, void,
        private_bio_writer_t *this, u_int32_t value)
 {
-       if (this->used + 4 > this->buf.len)
-       {
-               increase(this);
-       }
+       increase(this, 4);
        htoun32(this->buf.ptr + this->used, value);
        this->used += 4;
 }
@@ -103,10 +100,7 @@ METHOD(bio_writer_t, write_uint32, void,
 METHOD(bio_writer_t, write_uint64, void,
        private_bio_writer_t *this, u_int64_t value)
 {
-       if (this->used + 8 > this->buf.len)
-       {
-               increase(this);
-       }
+       increase(this, 8);
        htoun64(this->buf.ptr + this->used, value);
        this->used += 8;
 }
@@ -114,10 +108,7 @@ METHOD(bio_writer_t, write_uint64, void,
 METHOD(bio_writer_t, write_data, void,
        private_bio_writer_t *this, chunk_t value)
 {
-       while (this->used + value.len > this->buf.len)
-       {
-               increase(this);
-       }
+       increase(this, value.len);
        memcpy(this->buf.ptr + this->used, value.ptr, value.len);
        this->used += value.len;
 }
@@ -125,6 +116,7 @@ METHOD(bio_writer_t, write_data, void,
 METHOD(bio_writer_t, write_data8, void,
        private_bio_writer_t *this, chunk_t value)
 {
+       increase(this, 1 + value.len);
        write_uint8(this, value.len);
        write_data(this, value);
 }
@@ -132,6 +124,7 @@ METHOD(bio_writer_t, write_data8, void,
 METHOD(bio_writer_t, write_data16, void,
        private_bio_writer_t *this, chunk_t value)
 {
+       increase(this, 2 + value.len);
        write_uint16(this, value.len);
        write_data(this, value);
 }
@@ -139,6 +132,7 @@ METHOD(bio_writer_t, write_data16, void,
 METHOD(bio_writer_t, write_data24, void,
        private_bio_writer_t *this, chunk_t value)
 {
+       increase(this, 3 + value.len);
        write_uint24(this, value.len);
        write_data(this, value);
 }
@@ -146,6 +140,7 @@ METHOD(bio_writer_t, write_data24, void,
 METHOD(bio_writer_t, write_data32, void,
        private_bio_writer_t *this, chunk_t value)
 {
+       increase(this, 4 + value.len);
        write_uint32(this, value.len);
        write_data(this, value);
 }
@@ -153,10 +148,7 @@ METHOD(bio_writer_t, write_data32, void,
 METHOD(bio_writer_t, wrap8, void,
        private_bio_writer_t *this)
 {
-       if (this->used + 1 > this->buf.len)
-       {
-               increase(this);
-       }
+       increase(this, 1);
        memmove(this->buf.ptr + 1, this->buf.ptr, this->used);
        this->buf.ptr[0] = this->used;
        this->used += 1;
@@ -165,10 +157,7 @@ METHOD(bio_writer_t, wrap8, void,
 METHOD(bio_writer_t, wrap16, void,
        private_bio_writer_t *this)
 {
-       if (this->used + 2 > this->buf.len)
-       {
-               increase(this);
-       }
+       increase(this, 2);
        memmove(this->buf.ptr + 2, this->buf.ptr, this->used);
        htoun16(this->buf.ptr, this->used);
        this->used += 2;
@@ -179,10 +168,7 @@ METHOD(bio_writer_t, wrap24, void,
 {
        u_int32_t len;
 
-       if (this->used + 3 > this->buf.len)
-       {
-               increase(this);
-       }
+       increase(this, 3);
        memmove(this->buf.ptr + 3, this->buf.ptr, this->used);
 
        len = htonl(this->used);
@@ -193,10 +179,7 @@ METHOD(bio_writer_t, wrap24, void,
 METHOD(bio_writer_t, wrap32, void,
        private_bio_writer_t *this)
 {
-       if (this->used + 4 > this->buf.len)
-       {
-               increase(this);
-       }
+       increase(this, 4);
        memmove(this->buf.ptr + 4, this->buf.ptr, this->used);
        htoun32(this->buf.ptr, this->used);
        this->used += 4;
@@ -207,10 +190,7 @@ METHOD(bio_writer_t, skip, chunk_t,
 {
        chunk_t skipped;
 
-       while (this->used + len > this->buf.len)
-       {
-               increase(this);
-       }
+       increase(this, len);
        skipped = chunk_create(this->buf.ptr + this->used, len);
        this->used += len;
        return skipped;
index f80cdabd2ddd8ac045b9a3449bf69d4fd3ab73b0..8049ac016e15340a7edeacbd893fd8c143e8d584 100644 (file)
@@ -264,7 +264,7 @@ static bool enumerate_token_enum(token_enum_t *this, char **token)
                }
        }
 
-       /* trim trailing characters/separators */
+       /* trim trailing characters */
        pos--;
        while (pos >= *token)
        {
@@ -278,17 +278,7 @@ static bool enumerate_token_enum(token_enum_t *this, char **token)
                        }
                        trim++;
                }
-               sep = this->sep;
-               while (*sep)
-               {
-                       if (*sep == *pos)
-                       {
-                               *(pos--) = '\0';
-                               break;
-                       }
-                       sep++;
-               }
-               if (!*trim && !*sep)
+               if (!*trim)
                {
                        break;
                }
index da539a231b1a90125e8f761cb057e5f9e717afb2..81eca8945620b45efd31b10de1ee1739313f1c08 100644 (file)
@@ -82,6 +82,7 @@ struct linked_list_t {
         * current position.
         *
         * @param enumerator    enumerator to check
+        * @return                              TRUE if more elements follow after the current item
         */
        bool (*has_more)(linked_list_t *this, enumerator_t *enumerator);
 
@@ -180,7 +181,8 @@ struct linked_list_t {
         */
        status_t (*get_last) (linked_list_t *this, void **item);
 
-       /** Find the first matching element in the list.
+       /**
+        * Find the first matching element in the list.
         *
         * The first object passed to the match function is the current list item,
         * followed by the user supplied data.
@@ -200,7 +202,8 @@ struct linked_list_t {
        status_t (*find_first) (linked_list_t *this, linked_list_match_t match,
                                                        void **item, ...);
 
-       /** Find the last matching element in the list.
+       /**
+        * Find the last matching element in the list.
         *
         * The first object passed to the match function is the current list item,
         * followed by the user supplied data.
diff --git a/src/libstrongswan/tests/.gitignore b/src/libstrongswan/tests/.gitignore
new file mode 100644 (file)
index 0000000..35429f6
--- /dev/null
@@ -0,0 +1 @@
+test_runner
diff --git a/src/libstrongswan/tests/Makefile.am b/src/libstrongswan/tests/Makefile.am
new file mode 100644 (file)
index 0000000..6b4ba2c
--- /dev/null
@@ -0,0 +1,19 @@
+TESTS = test_runner
+
+check_PROGRAMS = $(TESTS)
+
+test_runner_SOURCES = \
+  test_runner.c test_runner.h test_suite.h \
+  test_linked_list.c test_enumerator.c test_linked_list_enumerator.c \
+  test_bio_reader.c test_bio_writer.c test_chunk.c test_enum.c test_hashtable.c \
+  test_identification.c test_threading.c test_utils.c
+
+test_runner_CFLAGS = \
+  -I$(top_srcdir)/src/libstrongswan \
+  @COVERAGE_CFLAGS@ \
+  @CHECK_CFLAGS@
+
+test_runner_LDFLAGS = @COVERAGE_LDFLAGS@
+test_runner_LDADD = \
+  $(top_builddir)/src/libstrongswan/libstrongswan.la \
+  @CHECK_LIBS@
diff --git a/src/libstrongswan/tests/test_bio_reader.c b/src/libstrongswan/tests/test_bio_reader.c
new file mode 100644 (file)
index 0000000..45b20db
--- /dev/null
@@ -0,0 +1,450 @@
+/*
+ * Copyright (C) 2013 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#include "test_suite.h"
+
+#include <bio/bio_reader.h>
+
+/*******************************************************************************
+ * different integer reads
+ */
+
+#define assert_integer_read(data, bits, val) ({ \
+       bio_reader_t *reader = bio_reader_create(data); \
+       typeof(val) i; \
+       for (i = 0; reader->remaining(reader) >= (bits / 8); i++) \
+       { \
+               ck_assert(reader->read_uint##bits(reader, &val)); \
+               ck_assert_int_eq(i, val); \
+       } \
+       ck_assert_int_eq(i, data.len / (bits / 8)); \
+       ck_assert_int_eq(reader->remaining(reader), data.len % (bits / 8)); \
+       ck_assert(!reader->read_uint##bits(reader, &val)); \
+       reader->destroy(reader); \
+})
+
+#define assert_integer_read_uneven(data, bits, val) ({ \
+       int i; \
+       for (i = 0; i <= bits / 8; i++, data.len++) \
+       { \
+               assert_integer_read(data, bits, val); \
+       } \
+})
+
+#define assert_basic_read(bits, val) ({ \
+       chunk_t data; \
+       data = chunk_empty; \
+       assert_integer_read(data, bits, val); \
+       data = chunk_alloca(bits / 8); \
+       memset(data.ptr, 0, data.len); \
+       data.len = 0; \
+       assert_integer_read_uneven(data, bits, val); \
+})
+
+#define assert_extended_read(data, bits, val) ({ \
+       chunk_t extended = chunk_alloca(data.len + bits / 8); \
+       memset(extended.ptr, 0, extended.len); \
+       extended.ptr[extended.len - 1] = data.len / (bits / 8); \
+       memcpy(extended.ptr, data.ptr, data.len); \
+       extended.len = data.len; \
+       assert_integer_read_uneven(extended, bits, val); \
+})
+
+START_TEST(test_read_uint8)
+{
+       chunk_t data = chunk_from_chars(0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07);
+       u_int8_t val;
+
+       assert_integer_read(data, 8, val);
+       assert_basic_read(8, val);
+       assert_extended_read(data, 8, val);
+}
+END_TEST
+
+START_TEST(test_read_uint16)
+{
+       chunk_t data = chunk_from_chars(0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03);
+       u_int16_t val;
+
+       assert_integer_read(data, 16, val);
+       assert_basic_read(16, val);
+       assert_extended_read(data, 16, val);
+}
+END_TEST
+
+START_TEST(test_read_uint24)
+{
+       chunk_t data = chunk_from_chars(0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03);
+       u_int32_t val;
+
+       assert_integer_read(data, 24, val);
+       assert_basic_read(24, val);
+       assert_extended_read(data, 24, val);
+}
+END_TEST
+
+START_TEST(test_read_uint32)
+{
+       chunk_t data = chunk_from_chars(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+                                                                       0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03);
+       u_int32_t val;
+
+       assert_integer_read(data, 32, val);
+       assert_basic_read(32, val);
+       assert_extended_read(data, 32, val);
+}
+END_TEST
+
+START_TEST(test_read_uint64)
+{
+       chunk_t data = chunk_from_chars(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                                                                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+                                                                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+                                                                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03);
+       u_int64_t val;
+
+       assert_integer_read(data, 64, val);
+       assert_basic_read(64, val);
+       assert_extended_read(data, 64, val);
+}
+END_TEST
+
+/*******************************************************************************
+ * different integer reads from the end of a buffer
+ */
+
+#define assert_integer_read_end(data, bits, val) ({ \
+       bio_reader_t *reader = bio_reader_create(data); \
+       typeof(val) i; \
+       for (i = 0; reader->remaining(reader) >= (bits / 8); i++) \
+       { \
+               ck_assert(reader->read_uint##bits##_end(reader, &val)); \
+               ck_assert_int_eq(i, val); \
+       } \
+       ck_assert_int_eq(i, data.len / (bits / 8)); \
+       ck_assert_int_eq(reader->remaining(reader), data.len % (bits / 8)); \
+       ck_assert(!reader->read_uint##bits##_end(reader, &val)); \
+       reader->destroy(reader); \
+})
+
+#define assert_integer_read_end_uneven(data, bits, val) ({ \
+       int i; \
+       data.ptr += bits / 8; \
+       for (i = 0; i <= bits / 8; i++, data.ptr--, data.len++) \
+       { \
+               assert_integer_read_end(data, bits, val); \
+       } \
+})
+
+#define assert_basic_read_end(bits, val) ({ \
+       chunk_t data; \
+       data = chunk_empty; \
+       assert_integer_read_end(data, bits, val); \
+       data = chunk_alloca(bits / 8); \
+       memset(data.ptr, 0, data.len); \
+       data.len = 0; \
+       assert_integer_read_end_uneven(data, bits, val); \
+})
+
+#define assert_extended_read_end(data, bits, val) ({ \
+       chunk_t extended = chunk_alloca(data.len + bits / 8); \
+       memset(extended.ptr, 0, extended.len); \
+       extended.ptr[bits / 8 - 1] = data.len / (bits / 8); \
+       memcpy(extended.ptr + bits / 8, data.ptr, data.len); \
+       extended.len = data.len; \
+       assert_integer_read_end_uneven(extended, bits, val); \
+})
+
+START_TEST(test_read_uint8_end)
+{
+       chunk_t data = chunk_from_chars(0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00);
+       u_int8_t val;
+
+       assert_integer_read_end(data, 8, val);
+       assert_basic_read_end(8, val);
+       assert_extended_read_end(data, 8, val);
+}
+END_TEST
+
+START_TEST(test_read_uint16_end)
+{
+       chunk_t data = chunk_from_chars(0x00, 0x03, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00);
+       u_int16_t val;
+
+       assert_integer_read_end(data, 16, val);
+       assert_basic_read_end(16, val);
+       assert_extended_read_end(data, 16, val);
+}
+END_TEST
+
+START_TEST(test_read_uint24_end)
+{
+       chunk_t data = chunk_from_chars(0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00);
+       u_int32_t val;
+
+       assert_integer_read_end(data, 24, val);
+       assert_basic_read_end(24, val);
+       assert_extended_read_end(data, 24, val);
+}
+END_TEST
+
+START_TEST(test_read_uint32_end)
+{
+       chunk_t data = chunk_from_chars(0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02,
+                                                                       0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00);
+       u_int32_t val;
+
+       assert_integer_read_end(data, 32, val);
+       assert_basic_read_end(32, val);
+       assert_extended_read_end(data, 32, val);
+}
+END_TEST
+
+START_TEST(test_read_uint64_end)
+{
+       chunk_t data = chunk_from_chars(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
+                                                                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+                                                                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+                                                                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
+       u_int64_t val;
+
+       assert_integer_read_end(data, 64, val);
+       assert_basic_read_end(64, val);
+       assert_extended_read_end(data, 64, val);
+}
+END_TEST
+
+/*******************************************************************************
+ * read data
+ */
+
+static inline void assert_reader_after_read(bio_reader_t *reader, chunk_t data)
+{
+       chunk_t peek;
+
+       ck_assert_int_eq(reader->remaining(reader), data.len);
+       peek = reader->peek(reader);
+       ck_assert_int_eq(reader->remaining(reader), data.len);
+       ck_assert(peek.ptr == data.ptr);
+       data.ptr != NULL ? ck_assert(chunk_equals(peek, data))
+                                        : ck_assert(peek.ptr == NULL);
+}
+
+START_TEST(test_read_data)
+{
+       chunk_t read, data = chunk_from_chars(0x00, 0x00, 0x00, 0x00);
+       bio_reader_t *reader;
+
+       reader = bio_reader_create(chunk_empty);
+       ck_assert_int_eq(reader->remaining(reader), 0);
+       ck_assert(reader->read_data(reader, 0, &read));
+       ck_assert(!reader->read_data(reader, 1, &read));
+       reader->destroy(reader);
+
+       reader = bio_reader_create(data);
+       ck_assert(reader->read_data(reader, 0, &read));
+       ck_assert_int_eq(read.len, 0);
+       ck_assert(read.ptr == data.ptr);
+       assert_reader_after_read(reader, data);
+
+       ck_assert(reader->read_data(reader, 1, &read));
+       ck_assert_int_eq(read.len, 1);
+       ck_assert(read.ptr == data.ptr);
+       assert_reader_after_read(reader, chunk_skip(data, 1));
+
+       ck_assert(reader->read_data(reader, 2, &read));
+       ck_assert_int_eq(read.len, 2);
+       ck_assert(read.ptr == data.ptr + 1);
+       assert_reader_after_read(reader, chunk_skip(data, 3));
+
+       ck_assert(!reader->read_data(reader, 2, &read));
+       ck_assert(reader->read_data(reader, 1, &read));
+       ck_assert_int_eq(read.len, 1);
+       ck_assert(read.ptr == data.ptr + 3);
+       assert_reader_after_read(reader, chunk_skip(data, 4));
+
+       ck_assert_int_eq(reader->remaining(reader), 0);
+       ck_assert(reader->read_data(reader, 0, &read));
+       ck_assert(!reader->read_data(reader, 1, &read));
+       reader->destroy(reader);
+}
+END_TEST
+
+START_TEST(test_read_data_end)
+{
+       chunk_t read, data = chunk_from_chars(0x00, 0x00, 0x00, 0x00);
+       bio_reader_t *reader;
+
+       reader = bio_reader_create(chunk_empty);
+       ck_assert_int_eq(reader->remaining(reader), 0);
+       ck_assert(reader->read_data_end(reader, 0, &read));
+       ck_assert(!reader->read_data_end(reader, 1, &read));
+       reader->destroy(reader);
+
+       reader = bio_reader_create(data);
+       ck_assert(reader->read_data_end(reader, 0, &read));
+       ck_assert_int_eq(read.len, 0);
+       ck_assert(read.ptr == data.ptr + data.len);
+       assert_reader_after_read(reader, data);
+
+       ck_assert(reader->read_data_end(reader, 1, &read));
+       ck_assert_int_eq(read.len, 1);
+       data.len--;
+       ck_assert(read.ptr == data.ptr + data.len);
+       assert_reader_after_read(reader, data);
+
+       ck_assert(reader->read_data_end(reader, 2, &read));
+       ck_assert_int_eq(read.len, 2);
+       data.len -= 2;
+       ck_assert(read.ptr == data.ptr + data.len);
+       assert_reader_after_read(reader, data);
+
+       ck_assert(!reader->read_data(reader, 2, &read));
+       ck_assert(reader->read_data(reader, 1, &read));
+       ck_assert_int_eq(read.len, 1);
+       ck_assert(read.ptr == data.ptr);
+       assert_reader_after_read(reader, chunk_empty);
+
+       ck_assert_int_eq(reader->remaining(reader), 0);
+       ck_assert(reader->read_data(reader, 0, &read));
+       ck_assert(!reader->read_data(reader, 1, &read));
+       reader->destroy(reader);
+}
+END_TEST
+
+/*******************************************************************************
+ * read length followed by data
+ */
+
+#define assert_read_data_len(bits) ({ \
+       bio_reader_t *reader; \
+       chunk_t read, data; \
+       int i, len = bits / 8; \
+       data = chunk_empty; \
+       reader = bio_reader_create(data); \
+       ck_assert(!reader->read_data##bits(reader, &read)); \
+       reader->destroy(reader); \
+       data = chunk_alloca(len + 8); \
+       memset(data.ptr, 0, data.len); \
+       for (i = 0; i <= 8; i++) \
+       { \
+               data.ptr[len - 1] = i; \
+               data.len = len + i; \
+               reader = bio_reader_create(data); \
+               ck_assert(reader->read_data##bits(reader, &read)); \
+               ck_assert_int_eq(reader->remaining(reader), 0); \
+               ck_assert_int_eq(read.len, i); \
+               ck_assert((!read.ptr && !read.len) || (read.ptr == data.ptr + len)); \
+               reader->destroy(reader); \
+       } \
+       data.ptr[len - 1] = i; \
+       reader = bio_reader_create(data); \
+       ck_assert(!reader->read_data##bits(reader, &read)); \
+       reader->destroy(reader); \
+})
+
+START_TEST(test_read_data8)
+{
+       assert_read_data_len(8);
+}
+END_TEST
+
+START_TEST(test_read_data16)
+{
+       assert_read_data_len(16);
+}
+END_TEST
+
+START_TEST(test_read_data24)
+{
+       assert_read_data_len(24);
+}
+END_TEST
+
+START_TEST(test_read_data32)
+{
+       assert_read_data_len(32);
+}
+END_TEST
+
+/*******************************************************************************
+ * test constructors
+ */
+
+START_TEST(test_create)
+{
+       chunk_t data = chunk_from_str("foobar");
+       bio_reader_t *reader;
+
+       data = chunk_clone(data);
+       reader = bio_reader_create(data);
+       reader->destroy(reader);
+       chunk_free(&data);
+}
+END_TEST
+
+START_TEST(test_create_own)
+{
+       chunk_t data = chunk_from_str("foobar");
+       bio_reader_t *reader;
+
+       data = chunk_clone(data);
+       reader = bio_reader_create_own(data);
+       reader->destroy(reader);
+}
+END_TEST
+
+Suite *bio_reader_suite_create()
+{
+       Suite *s;
+       TCase *tc;
+
+       s = suite_create("bio_reader");
+
+       tc = tcase_create("integer reads");
+       tcase_add_test(tc, test_read_uint8);
+       tcase_add_test(tc, test_read_uint16);
+       tcase_add_test(tc, test_read_uint24);
+       tcase_add_test(tc, test_read_uint32);
+       tcase_add_test(tc, test_read_uint64);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("integer reads from end");
+       tcase_add_test(tc, test_read_uint8_end);
+       tcase_add_test(tc, test_read_uint16_end);
+       tcase_add_test(tc, test_read_uint24_end);
+       tcase_add_test(tc, test_read_uint32_end);
+       tcase_add_test(tc, test_read_uint64_end);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("data reads and peek");
+       tcase_add_test(tc, test_read_data);
+       tcase_add_test(tc, test_read_data_end);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("data length reads");
+       tcase_add_test(tc, test_read_data8);
+       tcase_add_test(tc, test_read_data16);
+       tcase_add_test(tc, test_read_data24);
+       tcase_add_test(tc, test_read_data32);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("constructors");
+       tcase_add_test(tc, test_create);
+       tcase_add_test(tc, test_create_own);
+       suite_add_tcase(s, tc);
+
+       return s;
+}
diff --git a/src/libstrongswan/tests/test_bio_writer.c b/src/libstrongswan/tests/test_bio_writer.c
new file mode 100644 (file)
index 0000000..767f179
--- /dev/null
@@ -0,0 +1,386 @@
+/*
+ * Copyright (C) 2013 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#include "test_suite.h"
+
+#include <bio/bio_writer.h>
+
+/*******************************************************************************
+ * different integer writes
+ */
+
+static inline void verify_int_buffer(chunk_t data, int bits, int val)
+{
+       size_t i;
+       int len = bits / 8;
+
+       ck_assert_int_eq(data.len, (val + 1) * len);
+       for (i = 0; i < data.len; i++)
+       {
+               (i + 1) % len ? ck_assert_int_eq(data.ptr[i], 0)
+                                         : ck_assert_int_eq(data.ptr[i], i / len);
+       }
+}
+
+#define assert_integer_write(init, bits) ({ \
+       int i; \
+       bio_writer_t *writer = bio_writer_create(init); \
+       for (i = 0; i < 16; i++) \
+       { \
+               writer->write_uint##bits(writer, i); \
+               verify_int_buffer(writer->get_buf(writer), bits, i); \
+       } \
+       writer->destroy(writer); \
+})
+
+START_TEST(test_write_uint8)
+{
+       /* use default buffer (and increase) size */
+       assert_integer_write(0, 8);
+       /* force a resize by the given size */
+       assert_integer_write(1, 8);
+}
+END_TEST
+
+START_TEST(test_write_uint16)
+{
+       assert_integer_write(0, 16);
+       assert_integer_write(1, 16);
+}
+END_TEST
+
+START_TEST(test_write_uint24)
+{
+       assert_integer_write(0, 24);
+       assert_integer_write(1, 24);
+}
+END_TEST
+
+START_TEST(test_write_uint32)
+{
+       assert_integer_write(0, 32);
+       assert_integer_write(1, 32);
+}
+END_TEST
+
+START_TEST(test_write_uint64)
+{
+       assert_integer_write(0, 64);
+       assert_integer_write(1, 64);
+}
+END_TEST
+
+/*******************************************************************************
+ * write data / skip
+ */
+
+static inline void assert_writer_after_write(bio_writer_t *writer, int count)
+{
+       chunk_t buf;
+       size_t i;
+
+       buf = writer->get_buf(writer);
+       ck_assert_int_eq(buf.len, count * 3);
+       for (i = 0; i < buf.len; i++)
+       {
+               ck_assert(buf.ptr[i] == i % 3);
+       }
+}
+
+START_TEST(test_write_data)
+{
+       chunk_t buf, data = chunk_from_chars(0x00, 0x01, 0x02);
+       bio_writer_t *writer;
+
+       /* no allocation, but default buffer size */
+       writer = bio_writer_create(0);
+       buf = writer->get_buf(writer);
+       ck_assert_int_eq(buf.len, 0);
+       ck_assert(buf.ptr == NULL);
+
+       writer->write_data(writer, chunk_empty);
+       buf = writer->get_buf(writer);
+       ck_assert_int_eq(buf.len, 0);
+       ck_assert(buf.ptr == NULL);
+       writer->destroy(writer);
+
+       /* custom buffer size, initial buffer allocated */
+       writer = bio_writer_create(1);
+       buf = writer->get_buf(writer);
+       ck_assert_int_eq(buf.len, 0);
+       ck_assert(buf.ptr != NULL);
+
+       writer->write_data(writer, chunk_empty);
+       buf = writer->get_buf(writer);
+       ck_assert_int_eq(buf.len, 0);
+       ck_assert(buf.ptr != NULL);
+       writer->destroy(writer);
+
+       writer = bio_writer_create(0);
+
+       writer->write_data(writer, data);
+       assert_writer_after_write(writer, 1);
+
+       writer->write_data(writer, data);
+       assert_writer_after_write(writer, 2);
+
+       writer->write_data(writer, data);
+       assert_writer_after_write(writer, 3);
+
+       writer->destroy(writer);
+}
+END_TEST
+
+START_TEST(test_skip)
+{
+       chunk_t skipped, buf, data = chunk_from_chars(0x00, 0x01, 0x02);
+       bio_writer_t *writer;
+
+       writer = bio_writer_create(4);
+       skipped = writer->skip(writer, 3);
+       ck_assert_int_eq(skipped.len, 3);
+       buf = writer->get_buf(writer);
+       ck_assert(skipped.ptr == buf.ptr);
+       memset(skipped.ptr, 0, skipped.len);
+
+       writer->write_data(writer, data);
+       buf = writer->get_buf(writer);
+       ck_assert(chunk_equals(buf, chunk_from_chars(0x00, 0x00, 0x00, 0x00, 0x01, 0x02)));
+       writer->destroy(writer);
+
+       writer = bio_writer_create(1);
+       skipped = writer->skip(writer, 3);
+       memcpy(skipped.ptr, data.ptr, data.len);
+
+       writer->write_data(writer, data);
+       assert_writer_after_write(writer, 2);
+       writer->destroy(writer);
+}
+END_TEST
+
+/*******************************************************************************
+ * write length followed by data
+ */
+
+#define assert_write_data_len(init, bits) ({ \
+       bio_writer_t *writer; \
+       chunk_t buf, data; \
+       int i, len = bits / 8; \
+       writer = bio_writer_create(init); \
+       writer->write_data##bits(writer, chunk_empty); \
+       buf = writer->get_buf(writer); \
+       ck_assert_int_eq(buf.len, len); \
+       ck_assert_int_eq(buf.ptr[len - 1], 0); \
+       writer->destroy(writer); \
+       data = chunk_alloca(32); \
+       memset(data.ptr, 0, data.len); \
+       for (i = 0; i < 32; i++) \
+       { \
+               data.ptr[i] = i; \
+               data.len = i; \
+               writer = bio_writer_create(init); \
+               writer->write_data##bits(writer, data); \
+               buf = writer->get_buf(writer); \
+               ck_assert_int_eq(buf.len, len + i); \
+               ck_assert_int_eq(buf.ptr[len - 1], i); \
+               ck_assert(chunk_equals(chunk_create(buf.ptr + len, buf.len - len), data)); \
+               writer->destroy(writer); \
+       } \
+})
+
+START_TEST(test_write_data8)
+{
+       assert_write_data_len(0, 8);
+       assert_write_data_len(1, 8);
+}
+END_TEST
+
+START_TEST(test_write_data16)
+{
+       assert_write_data_len(0, 16);
+       assert_write_data_len(1, 16);
+}
+END_TEST
+
+START_TEST(test_write_data24)
+{
+       assert_write_data_len(0, 24);
+       assert_write_data_len(1, 24);
+}
+END_TEST
+
+START_TEST(test_write_data32)
+{
+       assert_write_data_len(0, 32);
+       assert_write_data_len(1, 32);
+}
+END_TEST
+
+
+/*******************************************************************************
+ * add length header before current data
+ */
+
+#define assert_wrap_data(init, bits) ({ \
+       bio_writer_t *writer; \
+       chunk_t buf, data; \
+       int i, len = bits / 8; \
+       writer = bio_writer_create(init); \
+       writer->wrap##bits(writer); \
+       buf = writer->get_buf(writer); \
+       ck_assert_int_eq(buf.len, len); \
+       ck_assert_int_eq(buf.ptr[len - 1], 0); \
+       writer->destroy(writer); \
+       data = chunk_alloca(32); \
+       memset(data.ptr, 0, data.len); \
+       for (i = 0; i < 32; i++) \
+       { \
+               data.ptr[i] = i; \
+               data.len = i; \
+               writer = bio_writer_create(init); \
+               writer->write_data(writer, data); \
+               writer->wrap##bits(writer); \
+               buf = writer->get_buf(writer); \
+               ck_assert_int_eq(buf.len, len + i); \
+               ck_assert_int_eq(buf.ptr[len - 1], i); \
+               ck_assert(chunk_equals(chunk_create(buf.ptr + len, buf.len - len), data)); \
+               writer->wrap##bits(writer); \
+               buf = writer->get_buf(writer); \
+               ck_assert_int_eq(buf.len, 2 * len + i); \
+               ck_assert_int_eq(buf.ptr[len - 1], len + i); \
+               ck_assert(chunk_equals(chunk_create(buf.ptr + 2 * len, buf.len - 2 * len), data)); \
+               writer->destroy(writer); \
+       } \
+})
+
+START_TEST(test_wrap8)
+{
+       assert_wrap_data(0, 8);
+       assert_wrap_data(1, 8);
+}
+END_TEST
+
+START_TEST(test_wrap16)
+{
+       assert_wrap_data(0, 16);
+       assert_wrap_data(1, 16);
+}
+END_TEST
+
+START_TEST(test_wrap24)
+{
+       assert_wrap_data(0, 24);
+       assert_wrap_data(1, 24);
+}
+END_TEST
+
+START_TEST(test_wrap32)
+{
+       assert_wrap_data(0, 32);
+       assert_wrap_data(1, 32);
+}
+END_TEST
+
+/*******************************************************************************
+ * test data extraction
+ */
+
+START_TEST(test_get_buf)
+{
+       bio_writer_t *writer;
+       chunk_t data1, data2;
+
+       writer = bio_writer_create(0);
+       writer->write_uint8(writer, 1);
+       data1 = writer->get_buf(writer);
+       ck_assert_int_eq(data1.len, 1);
+       ck_assert(data1.ptr[0] == 1);
+
+       data2 = writer->get_buf(writer);
+       ck_assert(chunk_equals(data1, data2));
+       ck_assert(data1.ptr == data2.ptr);
+       writer->destroy(writer);
+}
+END_TEST
+
+START_TEST(test_extract_buf)
+{
+       bio_writer_t *writer;
+       chunk_t data1, data2;
+
+       writer = bio_writer_create(0);
+       writer->write_uint8(writer, 1);
+       data1 = writer->extract_buf(writer);
+       ck_assert_int_eq(data1.len, 1);
+       ck_assert(data1.ptr[0] == 1);
+
+       data2 = writer->get_buf(writer);
+       ck_assert_int_eq(data2.len, 0);
+       ck_assert(data2.ptr == NULL);
+       data2 = writer->extract_buf(writer);
+       ck_assert_int_eq(data2.len, 0);
+       ck_assert(data2.ptr == NULL);
+
+       writer->write_uint8(writer, 1);
+       data2 = writer->get_buf(writer);
+       ck_assert(chunk_equals(data1, data2));
+       ck_assert(data1.ptr != data2.ptr);
+
+       writer->destroy(writer);
+       chunk_free(&data1);
+}
+END_TEST
+
+Suite *bio_writer_suite_create()
+{
+       Suite *s;
+       TCase *tc;
+
+       s = suite_create("bio_writer");
+
+       tc = tcase_create("integer writes");
+       tcase_add_test(tc, test_write_uint8);
+       tcase_add_test(tc, test_write_uint16);
+       tcase_add_test(tc, test_write_uint24);
+       tcase_add_test(tc, test_write_uint32);
+       tcase_add_test(tc, test_write_uint64);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("data writes/skip");
+       tcase_add_test(tc, test_write_data);
+       tcase_add_test(tc, test_skip);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("data length writes");
+       tcase_add_test(tc, test_write_data8);
+       tcase_add_test(tc, test_write_data16);
+       tcase_add_test(tc, test_write_data24);
+       tcase_add_test(tc, test_write_data32);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("wrap writes");
+       tcase_add_test(tc, test_wrap8);
+       tcase_add_test(tc, test_wrap16);
+       tcase_add_test(tc, test_wrap24);
+       tcase_add_test(tc, test_wrap32);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("get/extract");
+       tcase_add_test(tc, test_get_buf);
+       tcase_add_test(tc, test_extract_buf);
+       suite_add_tcase(s, tc);
+
+       return s;
+}
diff --git a/src/libstrongswan/tests/test_chunk.c b/src/libstrongswan/tests/test_chunk.c
new file mode 100644 (file)
index 0000000..5fa1c0b
--- /dev/null
@@ -0,0 +1,824 @@
+/*
+ * Copyright (C) 2013 Tobias Brunner
+ * Copyright (C) 2008 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+
+#include "test_suite.h"
+
+#include <utils/chunk.h>
+
+/*******************************************************************************
+ * utilities
+ */
+
+static void assert_chunk_empty(chunk_t chunk)
+{
+       ck_assert(chunk.len == 0 && chunk.ptr == NULL);
+}
+
+/*******************************************************************************
+ * equals
+ */
+
+START_TEST(test_chunk_equals)
+{
+       chunk_t chunk = chunk_from_str("chunk");
+       chunk_t chunk_a, chunk_b;
+
+       chunk_a = chunk_empty;
+       chunk_b = chunk_empty;
+       ck_assert(!chunk_equals(chunk_a, chunk_b));
+
+       chunk_a = chunk;
+       ck_assert(!chunk_equals(chunk_a, chunk_b));
+       chunk_b = chunk;
+       ck_assert(chunk_equals(chunk_a, chunk_b));
+
+       chunk_b = chunk_from_str("asdf");
+       ck_assert(!chunk_equals(chunk_a, chunk_b));
+
+       chunk_b = chunk_from_str("chunk");
+       ck_assert(chunk_equals(chunk_a, chunk_b));
+}
+END_TEST
+
+/*******************************************************************************
+ * chunk_compare test
+ */
+
+static struct {
+       int result;
+       chunk_t a;
+       chunk_t b;
+} compare_data[] = {
+       { 0, { NULL, 0 }, { NULL, 0 }},
+       { 0, chunk_from_chars(0x00), chunk_from_chars(0x00)},
+       {-1, chunk_from_chars(0x00), chunk_from_chars(0x01)},
+       { 1, chunk_from_chars(0x01), chunk_from_chars(0x00)},
+       { 0, chunk_from_chars(0x00, 0x00), chunk_from_chars(0x00, 0x00)},
+       {-1, chunk_from_chars(0x00, 0x00), chunk_from_chars(0x00, 0x01)},
+       { 1, chunk_from_chars(0x00, 0x01), chunk_from_chars(0x00, 0x00)},
+       {-1, chunk_from_chars(0x00, 0x00), chunk_from_chars(0x01, 0x00)},
+       { 1, chunk_from_chars(0x01, 0x00), chunk_from_chars(0x00, 0x00)},
+       {-1, chunk_from_chars(0xff), chunk_from_chars(0x00, 0x00)},
+       { 1, chunk_from_chars(0x00, 0x00), chunk_from_chars(0xff)},
+};
+
+START_TEST(test_compare)
+{
+       int result, expected;
+
+       result = chunk_compare(compare_data[_i].a, compare_data[_i].b);
+       expected = compare_data[_i].result;
+       ck_assert((result == 0 && expected == 0) ||
+                         (result < 0 && expected < 0) ||
+                         (result > 0 && expected > 0));
+}
+END_TEST
+
+/*******************************************************************************
+ * clear
+ */
+
+START_TEST(test_chunk_clear)
+{
+       chunk_t chunk;
+       u_char *ptr;
+       int i;
+
+       chunk = chunk_empty;
+       chunk_clear(&chunk);
+       chunk_free(&chunk);
+
+       chunk = chunk_alloc(64);
+       ptr = chunk.ptr;
+       for (i = 0; i < 64; i++)
+       {
+               chunk.ptr[i] = i;
+       }
+       chunk_clear(&chunk);
+       assert_chunk_empty(chunk);
+       /* check memory area of freed chunk */
+       for (i = 0; i < 64; i++)
+       {
+               ck_assert(ptr[i] == 0 || ptr[i] != i);
+       }
+}
+END_TEST
+
+/*******************************************************************************
+ * chunk_length
+ */
+
+START_TEST(test_chunk_length)
+{
+       chunk_t a, b, c;
+       size_t len;
+
+       a = chunk_empty;
+       b = chunk_empty;
+       c = chunk_empty;
+       len = chunk_length("ccc", a, b, c);
+       ck_assert_int_eq(len, 0);
+
+       a = chunk_from_str("foo");
+       b = chunk_from_str("bar");
+       len = chunk_length("ccc", a, b, c);
+       ck_assert_int_eq(len, 6);
+
+       len = chunk_length("zcc", a, b, c);
+       ck_assert_int_eq(len, 0);
+
+       len = chunk_length("czc", a, b, c);
+       ck_assert_int_eq(len, 3);
+
+       a = chunk_from_str("foo");
+       b = chunk_from_str("bar");
+       c = chunk_from_str("baz");
+       len = chunk_length("ccc", a, b, c);
+       ck_assert_int_eq(len, 9);
+}
+END_TEST
+
+/*******************************************************************************
+ * chunk_create_cat
+ */
+
+START_TEST(test_chunk_create_cat)
+{
+       chunk_t foo, bar;
+       chunk_t a, b, c;
+       u_char *ptra, *ptrb;
+
+       foo = chunk_from_str("foo");
+       bar = chunk_from_str("bar");
+
+       /* to simplify things we use the chunk_cata macro */
+
+       a = chunk_empty;
+       b = chunk_empty;
+       c = chunk_cata("cc", a, b);
+       ck_assert_int_eq(c.len, 0);
+       ck_assert(c.ptr != NULL);
+
+       a = foo;
+       b = bar;
+       c = chunk_cata("cc", a, b);
+       ck_assert_int_eq(c.len, 6);
+       ck_assert(chunk_equals(c, chunk_from_str("foobar")));
+
+       a = chunk_clone(foo);
+       b = chunk_clone(bar);
+       c = chunk_cata("mm", a, b);
+       ck_assert_int_eq(c.len, 6);
+       ck_assert(chunk_equals(c, chunk_from_str("foobar")));
+
+       a = chunk_clone(foo);
+       b = chunk_clone(bar);
+       ptra = a.ptr;
+       ptrb = b.ptr;
+       c = chunk_cata("ss", a, b);
+       ck_assert_int_eq(c.len, 6);
+       ck_assert(chunk_equals(c, chunk_from_str("foobar")));
+       /* check memory area of cleared chunk */
+       ck_assert(!chunk_equals(foo, chunk_create(ptra, 3)));
+       ck_assert(!chunk_equals(bar, chunk_create(ptrb, 3)));
+}
+END_TEST
+
+/*******************************************************************************
+ * chunk_split
+ */
+
+static bool mem_in_chunk(u_char *ptr, chunk_t chunk)
+{
+       return ptr >= chunk.ptr && ptr < (chunk.ptr + chunk.len);
+}
+
+START_TEST(test_chunk_split)
+{
+       chunk_t foo, bar, foobar;
+       chunk_t a, b, c;
+       u_char *ptra, *ptrb;
+
+       foo = chunk_from_str("foo");
+       bar = chunk_from_str("bar");
+       foobar = chunk_from_str("foobar");
+
+       chunk_split(foobar, "aa", 3, &a, 3, &b);
+       ck_assert(chunk_equals(a, foo));
+       ck_assert(chunk_equals(b, bar));
+       ck_assert(!mem_in_chunk(a.ptr, foobar));
+       ck_assert(!mem_in_chunk(b.ptr, foobar));
+       chunk_free(&a);
+       chunk_free(&b);
+
+       chunk_split(foobar, "mm", 3, &a, 3, &b);
+       ck_assert(chunk_equals(a, foo));
+       ck_assert(chunk_equals(b, bar));
+       ck_assert(mem_in_chunk(a.ptr, foobar));
+       ck_assert(mem_in_chunk(b.ptr, foobar));
+
+       chunk_split(foobar, "am", 3, &a, 3, &b);
+       ck_assert(chunk_equals(a, foo));
+       ck_assert(chunk_equals(b, bar));
+       ck_assert(!mem_in_chunk(a.ptr, foobar));
+       ck_assert(mem_in_chunk(b.ptr, foobar));
+       chunk_free(&a);
+
+       a = chunk_alloca(3);
+       ptra = a.ptr;
+       b = chunk_alloca(3);
+       ptrb = b.ptr;
+       chunk_split(foobar, "cc", 3, &a, 3, &b);
+       ck_assert(chunk_equals(a, foo));
+       ck_assert(chunk_equals(b, bar));
+       ck_assert(a.ptr == ptra);
+       ck_assert(b.ptr == ptrb);
+
+       chunk_split(foobar, "mm", 1, NULL, 2, &a, 2, NULL, 1, &b);
+       ck_assert(chunk_equals(a, chunk_from_str("oo")));
+       ck_assert(chunk_equals(b, chunk_from_str("r")));
+
+       chunk_split(foobar, "mm", 6, &a, 6, &b);
+       ck_assert(chunk_equals(a, foobar));
+       assert_chunk_empty(b);
+
+       chunk_split(foobar, "mac", 12, &a, 12, &b, 12, &c);
+       ck_assert(chunk_equals(a, foobar));
+       assert_chunk_empty(b);
+       assert_chunk_empty(c);
+}
+END_TEST
+
+/*******************************************************************************
+ * chunk_skip[_zero]
+ */
+
+START_TEST(test_chunk_skip)
+{
+       chunk_t foobar, a;
+
+       foobar = chunk_from_str("foobar");
+       a = foobar;
+       a = chunk_skip(a, 0);
+       ck_assert(chunk_equals(a, foobar));
+       a = chunk_skip(a, 1);
+       ck_assert(chunk_equals(a, chunk_from_str("oobar")));
+       a = chunk_skip(a, 2);
+       ck_assert(chunk_equals(a, chunk_from_str("bar")));
+       a = chunk_skip(a, 3);
+       assert_chunk_empty(a);
+
+       a = foobar;
+       a = chunk_skip(a, 6);
+       assert_chunk_empty(a);
+
+       a = foobar;
+       a = chunk_skip(a, 10);
+       assert_chunk_empty(a);
+}
+END_TEST
+
+START_TEST(test_chunk_skip_zero)
+{
+       chunk_t foobar, a;
+
+       a = chunk_empty;
+       a = chunk_skip_zero(a);
+       assert_chunk_empty(a);
+
+       foobar = chunk_from_str("foobar");
+       a = foobar;
+       a = chunk_skip_zero(a);
+       ck_assert(chunk_equals(a, foobar));
+
+       a = chunk_from_chars(0x00, 0xaa, 0xbb, 0xcc);
+       a = chunk_skip_zero(a);
+       ck_assert(chunk_equals(a, chunk_from_chars(0xaa, 0xbb, 0xcc)));
+       a = chunk_skip_zero(a);
+       ck_assert(chunk_equals(a, chunk_from_chars(0xaa, 0xbb, 0xcc)));
+}
+END_TEST
+
+/*******************************************************************************
+ * BASE16 encoding test
+ */
+
+START_TEST(test_base16)
+{
+       /* test vectors from RFC 4648:
+        *
+        * BASE16("") = ""
+        * BASE16("f") = "66"
+        * BASE16("fo") = "666F"
+        * BASE16("foo") = "666F6F"
+        * BASE16("foob") = "666F6F62"
+        * BASE16("fooba") = "666F6F6261"
+        * BASE16("foobar") = "666F6F626172"
+        */
+       typedef struct {
+               bool upper;
+               char *in;
+               char *out;
+       } testdata_t;
+
+       testdata_t test[] = {
+               {TRUE,  "", ""},
+               {TRUE,  "f", "66"},
+               {TRUE,  "fo", "666F"},
+               {TRUE,  "foo", "666F6F"},
+               {TRUE,  "foob", "666F6F62"},
+               {TRUE,  "fooba", "666F6F6261"},
+               {TRUE,  "foobar", "666F6F626172"},
+               {FALSE, "", ""},
+               {FALSE, "f", "66"},
+               {FALSE, "fo", "666f"},
+               {FALSE, "foo", "666f6f"},
+               {FALSE, "foob", "666f6f62"},
+               {FALSE, "fooba", "666f6f6261"},
+               {FALSE, "foobar", "666f6f626172"},
+       };
+       testdata_t test_colon[] = {
+               {TRUE,  "", ""},
+               {TRUE,  "f", "66"},
+               {TRUE,  "fo", "66:6F"},
+               {TRUE,  "foo", "66:6F:6F"},
+               {FALSE, "foob", "66:6f:6f:62"},
+               {FALSE, "fooba", "66:6f:6f:62:61"},
+               {FALSE, "foobar", "66:6f:6f:62:61:72"},
+               {FALSE, "foobar", "66:6f6f:6261:72"},
+       };
+       int i;
+
+       for (i = 0; i < countof(test); i++)
+       {
+               chunk_t out;
+
+               out = chunk_to_hex(chunk_create(test[i].in, strlen(test[i].in)), NULL,
+                                                  test[i].upper);
+               ck_assert_str_eq(out.ptr, test[i].out);
+               free(out.ptr);
+       }
+
+       for (i = 0; i < countof(test); i++)
+       {
+               chunk_t out;
+
+               out = chunk_from_hex(chunk_create(test[i].out, strlen(test[i].out)), NULL);
+               fail_unless(strneq(out.ptr, test[i].in, out.len),
+                                       "base16 conversion error - should '%s', is %#B",
+                                       test[i].in, &out);
+               free(out.ptr);
+       }
+
+       for (i = 0; i < countof(test_colon); i++)
+       {
+               chunk_t out;
+
+               out = chunk_from_hex(chunk_create(test_colon[i].out, strlen(test_colon[i].out)), NULL);
+               fail_unless(strneq(out.ptr, test_colon[i].in, out.len),
+                                       "base16 conversion error - should '%s', is %#B",
+                                       test_colon[i].in, &out);
+               free(out.ptr);
+       }
+}
+END_TEST
+
+/*******************************************************************************
+ * BASE64 encoding test
+ */
+
+START_TEST(test_base64)
+{
+       /* test vectors from RFC 4648:
+        *
+        * BASE64("") = ""
+        * BASE64("f") = "Zg=="
+        * BASE64("fo") = "Zm8="
+        * BASE64("foo") = "Zm9v"
+        * BASE64("foob") = "Zm9vYg=="
+        * BASE64("fooba") = "Zm9vYmE="
+        * BASE64("foobar") = "Zm9vYmFy"
+        */
+       typedef struct {
+               char *in;
+               char *out;
+       } testdata_t;
+
+       testdata_t test[] = {
+               {"", ""},
+               {"f", "Zg=="},
+               {"fo", "Zm8="},
+               {"foo", "Zm9v"},
+               {"foob", "Zm9vYg=="},
+               {"fooba", "Zm9vYmE="},
+               {"foobar", "Zm9vYmFy"},
+       };
+       int i;
+
+       for (i = 0; i < countof(test); i++)
+       {
+               chunk_t out;
+
+               out = chunk_to_base64(chunk_create(test[i].in, strlen(test[i].in)), NULL);
+               ck_assert_str_eq(out.ptr, test[i].out);
+               free(out.ptr);
+       }
+
+       for (i = 0; i < countof(test); i++)
+       {
+               chunk_t out;
+
+               out = chunk_from_base64(chunk_create(test[i].out, strlen(test[i].out)), NULL);
+               fail_unless(strneq(out.ptr, test[i].in, out.len),
+                                       "base64 conversion error - should '%s', is %#B",
+                                       test[i].in, &out);
+               free(out.ptr);
+       }
+}
+END_TEST
+
+/*******************************************************************************
+ * BASE32 encoding test
+ */
+
+START_TEST(test_base32)
+{
+       /* test vectors from RFC 4648:
+        *
+        * BASE32("") = ""
+        * BASE32("f") = "MY======"
+        * BASE32("fo") = "MZXQ===="
+        * BASE32("foo") = "MZXW6==="
+        * BASE32("foob") = "MZXW6YQ="
+        * BASE32("fooba") = "MZXW6YTB"
+        * BASE32("foobar") = "MZXW6YTBOI======"
+        */
+       typedef struct {
+               char *in;
+               char *out;
+       } testdata_t;
+
+       testdata_t test[] = {
+               {"", ""},
+               {"f", "MY======"},
+               {"fo", "MZXQ===="},
+               {"foo", "MZXW6==="},
+               {"foob", "MZXW6YQ="},
+               {"fooba", "MZXW6YTB"},
+               {"foobar", "MZXW6YTBOI======"},
+       };
+       int i;
+
+       for (i = 0; i < countof(test); i++)
+       {
+               chunk_t out;
+
+               out = chunk_to_base32(chunk_create(test[i].in, strlen(test[i].in)), NULL);
+               ck_assert_str_eq(out.ptr, test[i].out);
+               free(out.ptr);
+       }
+}
+END_TEST
+
+/*******************************************************************************
+ * chunk_increment test
+ */
+
+static struct {
+       bool overflow;
+       chunk_t in;
+       chunk_t out;
+} increment_data[] = {
+       {TRUE,  { NULL, 0 }, { NULL, 0 }},
+       {FALSE, chunk_from_chars(0x00), chunk_from_chars(0x01)},
+       {FALSE, chunk_from_chars(0xfe), chunk_from_chars(0xff)},
+       {TRUE,  chunk_from_chars(0xff), chunk_from_chars(0x00)},
+       {FALSE, chunk_from_chars(0x00, 0x00), chunk_from_chars(0x00, 0x01)},
+       {FALSE, chunk_from_chars(0x00, 0xff), chunk_from_chars(0x01, 0x00)},
+       {FALSE, chunk_from_chars(0xfe, 0xff), chunk_from_chars(0xff, 0x00)},
+       {TRUE,  chunk_from_chars(0xff, 0xff), chunk_from_chars(0x00, 0x00)},
+};
+
+START_TEST(test_increment)
+{
+       chunk_t chunk;
+       bool overflow;
+
+       chunk = chunk_clonea(increment_data[_i].in);
+       overflow = chunk_increment(chunk);
+       ck_assert(overflow == increment_data[_i].overflow);
+       ck_assert(!increment_data[_i].out.ptr ||
+                         chunk_equals(chunk, increment_data[_i].out));
+}
+END_TEST
+
+/*******************************************************************************
+ * chunk_printable tests
+ */
+
+static struct {
+       bool printable;
+       chunk_t in;
+       char *out;
+} printable_data[] = {
+       {TRUE,  chunk_from_chars(0x31), "1"},
+       {FALSE, chunk_from_chars(0x00), "?"},
+       {FALSE, chunk_from_chars(0x31, 0x00), "1?"},
+       {FALSE, chunk_from_chars(0x00, 0x31), "?1"},
+       {TRUE,  chunk_from_chars(0x3f, 0x31), "?1"},
+       {FALSE, chunk_from_chars(0x00, 0x31, 0x00), "?1?"},
+       {FALSE, chunk_from_chars(0x00, 0x31, 0x00, 0x32), "?1?2"},
+};
+
+START_TEST(test_printable)
+{
+       bool printable;
+
+       printable = chunk_printable(printable_data[_i].in, NULL, ' ');
+       ck_assert(printable == printable_data[_i].printable);
+}
+END_TEST
+
+START_TEST(test_printable_sanitize)
+{
+       chunk_t sane, expected;
+       bool printable;
+
+       printable = chunk_printable(printable_data[_i].in, &sane, '?');
+       ck_assert(printable == printable_data[_i].printable);
+       expected = chunk_from_str(printable_data[_i].out);
+       ck_assert(chunk_equals(sane, expected));
+       chunk_free(&sane);
+}
+END_TEST
+
+START_TEST(test_printable_empty)
+{
+       chunk_t sane;
+       bool printable;
+
+       printable = chunk_printable(chunk_empty, NULL, ' ');
+       ck_assert(printable);
+
+       sane.ptr = (void*)1;
+       sane.len = 1;
+       printable = chunk_printable(chunk_empty, &sane, ' ');
+       ck_assert(printable);
+       assert_chunk_empty(sane);
+}
+END_TEST
+
+/*******************************************************************************
+ * test for chunk_mac(), i.e. SipHash-2-4
+ */
+
+/**
+ * SipHash-2-4 output with
+ * k = 00 01 02 ...
+ * and
+ * in = (empty string)
+ * in = 00 (1 byte)
+ * in = 00 01 (2 bytes)
+ * in = 00 01 02 (3 bytes)
+ * ...
+ * in = 00 01 02 ... 3e (63 bytes)
+ */
+static const u_char sip_vectors[64][8] =
+{
+       { 0x31, 0x0e, 0x0e, 0xdd, 0x47, 0xdb, 0x6f, 0x72, },
+       { 0xfd, 0x67, 0xdc, 0x93, 0xc5, 0x39, 0xf8, 0x74, },
+       { 0x5a, 0x4f, 0xa9, 0xd9, 0x09, 0x80, 0x6c, 0x0d, },
+       { 0x2d, 0x7e, 0xfb, 0xd7, 0x96, 0x66, 0x67, 0x85, },
+       { 0xb7, 0x87, 0x71, 0x27, 0xe0, 0x94, 0x27, 0xcf, },
+       { 0x8d, 0xa6, 0x99, 0xcd, 0x64, 0x55, 0x76, 0x18, },
+       { 0xce, 0xe3, 0xfe, 0x58, 0x6e, 0x46, 0xc9, 0xcb, },
+       { 0x37, 0xd1, 0x01, 0x8b, 0xf5, 0x00, 0x02, 0xab, },
+       { 0x62, 0x24, 0x93, 0x9a, 0x79, 0xf5, 0xf5, 0x93, },
+       { 0xb0, 0xe4, 0xa9, 0x0b, 0xdf, 0x82, 0x00, 0x9e, },
+       { 0xf3, 0xb9, 0xdd, 0x94, 0xc5, 0xbb, 0x5d, 0x7a, },
+       { 0xa7, 0xad, 0x6b, 0x22, 0x46, 0x2f, 0xb3, 0xf4, },
+       { 0xfb, 0xe5, 0x0e, 0x86, 0xbc, 0x8f, 0x1e, 0x75, },
+       { 0x90, 0x3d, 0x84, 0xc0, 0x27, 0x56, 0xea, 0x14, },
+       { 0xee, 0xf2, 0x7a, 0x8e, 0x90, 0xca, 0x23, 0xf7, },
+       { 0xe5, 0x45, 0xbe, 0x49, 0x61, 0xca, 0x29, 0xa1, },
+       { 0xdb, 0x9b, 0xc2, 0x57, 0x7f, 0xcc, 0x2a, 0x3f, },
+       { 0x94, 0x47, 0xbe, 0x2c, 0xf5, 0xe9, 0x9a, 0x69, },
+       { 0x9c, 0xd3, 0x8d, 0x96, 0xf0, 0xb3, 0xc1, 0x4b, },
+       { 0xbd, 0x61, 0x79, 0xa7, 0x1d, 0xc9, 0x6d, 0xbb, },
+       { 0x98, 0xee, 0xa2, 0x1a, 0xf2, 0x5c, 0xd6, 0xbe, },
+       { 0xc7, 0x67, 0x3b, 0x2e, 0xb0, 0xcb, 0xf2, 0xd0, },
+       { 0x88, 0x3e, 0xa3, 0xe3, 0x95, 0x67, 0x53, 0x93, },
+       { 0xc8, 0xce, 0x5c, 0xcd, 0x8c, 0x03, 0x0c, 0xa8, },
+       { 0x94, 0xaf, 0x49, 0xf6, 0xc6, 0x50, 0xad, 0xb8, },
+       { 0xea, 0xb8, 0x85, 0x8a, 0xde, 0x92, 0xe1, 0xbc, },
+       { 0xf3, 0x15, 0xbb, 0x5b, 0xb8, 0x35, 0xd8, 0x17, },
+       { 0xad, 0xcf, 0x6b, 0x07, 0x63, 0x61, 0x2e, 0x2f, },
+       { 0xa5, 0xc9, 0x1d, 0xa7, 0xac, 0xaa, 0x4d, 0xde, },
+       { 0x71, 0x65, 0x95, 0x87, 0x66, 0x50, 0xa2, 0xa6, },
+       { 0x28, 0xef, 0x49, 0x5c, 0x53, 0xa3, 0x87, 0xad, },
+       { 0x42, 0xc3, 0x41, 0xd8, 0xfa, 0x92, 0xd8, 0x32, },
+       { 0xce, 0x7c, 0xf2, 0x72, 0x2f, 0x51, 0x27, 0x71, },
+       { 0xe3, 0x78, 0x59, 0xf9, 0x46, 0x23, 0xf3, 0xa7, },
+       { 0x38, 0x12, 0x05, 0xbb, 0x1a, 0xb0, 0xe0, 0x12, },
+       { 0xae, 0x97, 0xa1, 0x0f, 0xd4, 0x34, 0xe0, 0x15, },
+       { 0xb4, 0xa3, 0x15, 0x08, 0xbe, 0xff, 0x4d, 0x31, },
+       { 0x81, 0x39, 0x62, 0x29, 0xf0, 0x90, 0x79, 0x02, },
+       { 0x4d, 0x0c, 0xf4, 0x9e, 0xe5, 0xd4, 0xdc, 0xca, },
+       { 0x5c, 0x73, 0x33, 0x6a, 0x76, 0xd8, 0xbf, 0x9a, },
+       { 0xd0, 0xa7, 0x04, 0x53, 0x6b, 0xa9, 0x3e, 0x0e, },
+       { 0x92, 0x59, 0x58, 0xfc, 0xd6, 0x42, 0x0c, 0xad, },
+       { 0xa9, 0x15, 0xc2, 0x9b, 0xc8, 0x06, 0x73, 0x18, },
+       { 0x95, 0x2b, 0x79, 0xf3, 0xbc, 0x0a, 0xa6, 0xd4, },
+       { 0xf2, 0x1d, 0xf2, 0xe4, 0x1d, 0x45, 0x35, 0xf9, },
+       { 0x87, 0x57, 0x75, 0x19, 0x04, 0x8f, 0x53, 0xa9, },
+       { 0x10, 0xa5, 0x6c, 0xf5, 0xdf, 0xcd, 0x9a, 0xdb, },
+       { 0xeb, 0x75, 0x09, 0x5c, 0xcd, 0x98, 0x6c, 0xd0, },
+       { 0x51, 0xa9, 0xcb, 0x9e, 0xcb, 0xa3, 0x12, 0xe6, },
+       { 0x96, 0xaf, 0xad, 0xfc, 0x2c, 0xe6, 0x66, 0xc7, },
+       { 0x72, 0xfe, 0x52, 0x97, 0x5a, 0x43, 0x64, 0xee, },
+       { 0x5a, 0x16, 0x45, 0xb2, 0x76, 0xd5, 0x92, 0xa1, },
+       { 0xb2, 0x74, 0xcb, 0x8e, 0xbf, 0x87, 0x87, 0x0a, },
+       { 0x6f, 0x9b, 0xb4, 0x20, 0x3d, 0xe7, 0xb3, 0x81, },
+       { 0xea, 0xec, 0xb2, 0xa3, 0x0b, 0x22, 0xa8, 0x7f, },
+       { 0x99, 0x24, 0xa4, 0x3c, 0xc1, 0x31, 0x57, 0x24, },
+       { 0xbd, 0x83, 0x8d, 0x3a, 0xaf, 0xbf, 0x8d, 0xb7, },
+       { 0x0b, 0x1a, 0x2a, 0x32, 0x65, 0xd5, 0x1a, 0xea, },
+       { 0x13, 0x50, 0x79, 0xa3, 0x23, 0x1c, 0xe6, 0x60, },
+       { 0x93, 0x2b, 0x28, 0x46, 0xe4, 0xd7, 0x06, 0x66, },
+       { 0xe1, 0x91, 0x5f, 0x5c, 0xb1, 0xec, 0xa4, 0x6c, },
+       { 0xf3, 0x25, 0x96, 0x5c, 0xa1, 0x6d, 0x62, 0x9f, },
+       { 0x57, 0x5f, 0xf2, 0x8e, 0x60, 0x38, 0x1b, 0xe5, },
+       { 0x72, 0x45, 0x06, 0xeb, 0x4c, 0x32, 0x8a, 0x95, }
+};
+
+START_TEST(test_chunk_mac)
+{
+       chunk_t in;
+       u_char key[16];
+       u_int64_t out;
+       int i, count;
+
+       count = countof(sip_vectors);
+       in = chunk_alloca(count);
+
+       for (i = 0; i < 16; ++i)
+       {
+               key[i] = i;
+       }
+
+       for (i = 0; i < count; ++i)
+       {
+               in.ptr[i] = i;
+               in.len = i;
+               out = chunk_mac(in, key);
+               fail_unless(memeq(&out, sip_vectors[i], 8),
+                                       "test vector failed for %d bytes", i);
+       }
+}
+END_TEST
+
+
+/*******************************************************************************
+ * test for chunk_hash[_inc]()
+ */
+
+START_TEST(test_chunk_hash)
+{
+       chunk_t chunk;
+       u_int32_t hash_a, hash_b, hash_c;
+
+       chunk = chunk_from_str("asdf");
+
+       /* output is randomized, so there are no test-vectors we could use */
+       hash_a = chunk_hash(chunk);
+       hash_b = chunk_hash(chunk);
+       ck_assert(hash_a == hash_b);
+       hash_b = chunk_hash_inc(chunk, hash_a);
+       ck_assert(hash_a != hash_b);
+       hash_c = chunk_hash_inc(chunk, hash_a);
+       ck_assert(hash_b == hash_c);
+}
+END_TEST
+
+/*******************************************************************************
+ * printf_hook tests
+ */
+
+static struct {
+       chunk_t in;
+       char *out;
+} printf_hook_data[] = {
+       {chunk_from_chars(), ""},
+       {chunk_from_chars(0x00), "00"},
+       {chunk_from_chars(0x00, 0x01), "00:01"},
+       {chunk_from_chars(0x00, 0x01, 0x02), "00:01:02"},
+};
+
+START_TEST(test_printf_hook_hash)
+{
+       char buf[16];
+       int len;
+
+       len = snprintf(buf, sizeof(buf), "%#B", &printf_hook_data[_i].in);
+       ck_assert(len >= 0 && len < sizeof(buf));
+       ck_assert_str_eq(buf, printf_hook_data[_i].out);
+}
+END_TEST
+
+START_TEST(test_printf_hook)
+{
+       char buf[128], mem[128];
+       int len;
+
+       /* %B should be the same as %b, which is what we check, comparing the
+        * acutal result could be tricky as %b prints the chunk's memory address */
+       len = snprintf(buf, sizeof(buf), "%B", &printf_hook_data[_i].in);
+       ck_assert(len >= 0 && len < sizeof(buf));
+       len = snprintf(mem, sizeof(mem), "%b", printf_hook_data[_i].in.ptr,
+                                 (u_int)printf_hook_data[_i].in.len);
+       ck_assert(len >= 0 && len < sizeof(mem));
+       ck_assert_str_eq(buf, mem);
+}
+END_TEST
+
+Suite *chunk_suite_create()
+{
+       Suite *s;
+       TCase *tc;
+
+       s = suite_create("chunk");
+
+       tc = tcase_create("equals");
+       tcase_add_test(tc, test_chunk_equals);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("chunk_compare");
+       tcase_add_loop_test(tc, test_compare, 0, countof(compare_data));
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("clear");
+       tcase_add_test(tc, test_chunk_clear);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("chunk_length");
+       tcase_add_test(tc, test_chunk_length);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("chunk_create_cat");
+       tcase_add_test(tc, test_chunk_create_cat);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("chunk_split");
+       tcase_add_test(tc, test_chunk_split);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("chunk_skip");
+       tcase_add_test(tc, test_chunk_skip);
+       tcase_add_test(tc, test_chunk_skip_zero);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("chunk_increment");
+       tcase_add_loop_test(tc, test_increment, 0, countof(increment_data));
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("chunk_printable");
+       tcase_add_loop_test(tc, test_printable, 0, countof(printable_data));
+       tcase_add_loop_test(tc, test_printable_sanitize, 0, countof(printable_data));
+       tcase_add_test(tc, test_printable_empty);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("baseXX");
+       tcase_add_test(tc, test_base64);
+       tcase_add_test(tc, test_base32);
+       tcase_add_test(tc, test_base16);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("chunk_mac");
+       tcase_add_test(tc, test_chunk_mac);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("chunk_hash");
+       tcase_add_test(tc, test_chunk_hash);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("printf_hook");
+       tcase_add_loop_test(tc, test_printf_hook_hash, 0, countof(printf_hook_data));
+       tcase_add_loop_test(tc, test_printf_hook, 0, countof(printf_hook_data));
+       suite_add_tcase(s, tc);
+
+       return s;
+}
diff --git a/src/libstrongswan/tests/test_enum.c b/src/libstrongswan/tests/test_enum.c
new file mode 100644 (file)
index 0000000..990d9cf
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2013 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#include "test_suite.h"
+
+#include <utils/enum.h>
+#include <utils/utils.h>
+
+/*******************************************************************************
+ * continuous enum
+ */
+enum {
+       CONT1,
+       CONT2,
+       CONT3,
+       CONT4,
+       CONT5,
+} test_enum_cont;
+
+/* can't be static */
+enum_name_t *test_enum_cont_names;
+
+ENUM_BEGIN(test_enum_cont_names, CONT1, CONT5,
+       "CONT1", "CONT2", "CONT3", "CONT4", "CONT5");
+ENUM_END(test_enum_cont_names, CONT5);
+
+/*******************************************************************************
+ * split enum
+ */
+enum {
+       SPLIT1 = 1,
+       SPLIT2,
+       SPLIT3 = 5,
+       SPLIT4,
+       SPLIT5 = 255,
+} test_enum_split;
+
+/* can't be static */
+enum_name_t *test_enum_split_names;
+
+ENUM_BEGIN(test_enum_split_names, SPLIT1, SPLIT2,
+       "SPLIT1", "SPLIT2");
+ENUM_NEXT(test_enum_split_names, SPLIT3, SPLIT4, SPLIT2,
+       "SPLIT3", "SPLIT4");
+ENUM_NEXT(test_enum_split_names, SPLIT5, SPLIT5, SPLIT4,
+       "SPLIT5");
+ENUM_END(test_enum_split_names, SPLIT5);
+
+/*******************************************************************************
+ * enum_to_name
+ */
+
+static struct {
+       int val;
+       char *str;
+} name_tests_cont[] = {
+       {-1, NULL},
+       {CONT1, "CONT1"},
+       {CONT2, "CONT2"},
+       {CONT3, "CONT3"},
+       {CONT4, "CONT4"},
+       {CONT5, "CONT5"},
+       {5, NULL},
+}, name_tests_split[] = {
+       {-1, NULL},
+       {0, NULL},
+       {SPLIT1, "SPLIT1"},
+       {SPLIT2, "SPLIT2"},
+       {3, NULL},
+       {4, NULL},
+       {SPLIT3, "SPLIT3"},
+       {SPLIT4, "SPLIT4"},
+       {7, NULL},
+       {254, NULL},
+       {SPLIT5, "SPLIT5"},
+       {256, NULL},
+};
+
+START_TEST(test_enum_to_name_cont)
+{
+       char *str = enum_to_name(test_enum_cont_names, name_tests_cont[_i].val);
+       if (str)
+       {
+               ck_assert_str_eq(str, name_tests_cont[_i].str);
+       }
+       else
+       {
+               ck_assert(str == name_tests_cont[_i].str);
+       }
+}
+END_TEST
+
+START_TEST(test_enum_to_name_split)
+{
+       char *str = enum_to_name(test_enum_split_names, name_tests_split[_i].val);
+       if (str)
+       {
+               ck_assert_str_eq(str, name_tests_split[_i].str);
+       }
+       else
+       {
+               ck_assert(str == name_tests_split[_i].str);
+       }
+}
+END_TEST
+
+/*******************************************************************************
+ * enum_from_name
+ */
+
+static struct {
+       int val;
+       char *str;
+} enum_tests_cont[] = {
+       {CONT1, "CONT1"},
+       {CONT2, "CONT2"},
+       {CONT2, "CoNt2"},
+       {CONT3, "CONT3"},
+       {CONT4, "CONT4"},
+       {CONT5, "CONT5"},
+       {-1, "asdf"},
+       {-1, ""},
+       {-1, NULL},
+}, enum_tests_split[] = {
+       {SPLIT1, "SPLIT1"},
+       {SPLIT1, "split1"},
+       {SPLIT2, "SPLIT2"},
+       {SPLIT2, "SpLiT2"},
+       {SPLIT3, "SPLIT3"},
+       {SPLIT4, "SPLIT4"},
+       {SPLIT5, "SPLIT5"},
+       {-1, "asdf"},
+       {-1, ""},
+       {-1, NULL},
+};
+
+START_TEST(test_enum_from_name_cont)
+{
+       int val = enum_from_name(test_enum_cont_names, enum_tests_cont[_i].str);
+       ck_assert_int_eq(val, enum_tests_cont[_i].val);
+}
+END_TEST
+
+START_TEST(test_enum_from_name_split)
+{
+       int val = enum_from_name(test_enum_split_names, enum_tests_split[_i].str);
+       ck_assert_int_eq(val, enum_tests_split[_i].val);
+}
+END_TEST
+
+/*******************************************************************************
+ * enum_printf_hook
+ */
+
+static struct {
+       int val;
+       char *str;
+} printf_tests_cont[] = {
+       {-1, "(-1)"},
+       {CONT1, "CONT1"},
+       {CONT2, "CONT2"},
+       {CONT3, "CONT3"},
+       {CONT4, "CONT4"},
+       {CONT5, "CONT5"},
+       {5, "(5)"},
+}, printf_tests_split[] = {
+       {-1, "(-1)"},
+       {0, "(0)"},
+       {SPLIT1, "SPLIT1"},
+       {SPLIT2, "SPLIT2"},
+       {3, "(3)"},
+       {4, "(4)"},
+       {SPLIT3, "SPLIT3"},
+       {SPLIT4, "SPLIT4"},
+       {7, "(7)"},
+       {254, "(254)"},
+       {SPLIT5, "SPLIT5"},
+       {256, "(256)"},
+};
+
+START_TEST(test_enum_printf_hook_cont)
+{
+       char buf[128];
+
+       snprintf(buf, sizeof(buf), "%N", test_enum_cont_names, printf_tests_cont[_i].val);
+       ck_assert_str_eq(printf_tests_cont[_i].str, buf);
+}
+END_TEST
+
+START_TEST(test_enum_printf_hook_split)
+{
+       char buf[128];
+
+       snprintf(buf, sizeof(buf), "%N", test_enum_split_names, printf_tests_split[_i].val);
+       ck_assert_str_eq(printf_tests_split[_i].str, buf);
+}
+END_TEST
+
+START_TEST(test_enum_printf_hook_width)
+{
+       char buf[128];
+
+       snprintf(buf, sizeof(buf), "%10N", test_enum_cont_names, CONT1);
+       ck_assert_str_eq("     CONT1", buf);
+       snprintf(buf, sizeof(buf), "%-*N", 10, test_enum_cont_names, CONT2);
+       ck_assert_str_eq("CONT2     ", buf);
+       snprintf(buf, sizeof(buf), "%3N", test_enum_cont_names, CONT3);
+       ck_assert_str_eq("CONT3", buf);
+}
+END_TEST
+
+Suite *enum_suite_create()
+{
+       Suite *s;
+       TCase *tc;
+
+       s = suite_create("enum");
+
+       tc = tcase_create("enum_to_name");
+       tcase_add_loop_test(tc, test_enum_to_name_cont, 0, countof(name_tests_cont));
+       tcase_add_loop_test(tc, test_enum_to_name_split, 0, countof(name_tests_split));
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("enum_from_name");
+       tcase_add_loop_test(tc, test_enum_from_name_cont, 0, countof(enum_tests_cont));
+       tcase_add_loop_test(tc, test_enum_from_name_split, 0, countof(enum_tests_split));
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("enum_printf_hook");
+       tcase_add_loop_test(tc, test_enum_printf_hook_cont, 0, countof(printf_tests_cont));
+       tcase_add_loop_test(tc, test_enum_printf_hook_split, 0, countof(printf_tests_split));
+       tcase_add_test(tc, test_enum_printf_hook_width);
+       suite_add_tcase(s, tc);
+
+       return s;
+}
diff --git a/src/libstrongswan/tests/test_enumerator.c b/src/libstrongswan/tests/test_enumerator.c
new file mode 100644 (file)
index 0000000..b5dde46
--- /dev/null
@@ -0,0 +1,409 @@
+/*
+ * Copyright (C) 2013 Tobias Brunner
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#include "test_suite.h"
+
+#include <collections/enumerator.h>
+#include <collections/linked_list.h>
+
+/*******************************************************************************
+ * token test
+ */
+
+static const char *token_results1[] = { "abc", "cde", "efg" };
+static const char *token_results2[] = { "a", "b", "c" };
+
+static struct {
+       char *string;
+       char *sep;
+       char *trim;
+       const char **results;
+} token_tests[] = {
+       {"abc, cde, efg", ",", " ", token_results1},
+       {" abc 1:2 cde;3  4efg5.  ", ":;.,", " 12345", token_results1},
+       {"abc.cde,efg", ",.", "", token_results1},
+       {"  abc   cde  efg  ", " ", " ", token_results1},
+       {"a'abc' c 'cde' cefg", " ", " abcd", token_results1},
+       {"'abc' abc 'cde'd 'efg'", " ", " abcd", token_results1},
+
+       {"a, b, c", ",", " ", token_results2},
+       {"a,b,c", ",", " ", token_results2},
+       {" a 1:2 b;3  4c5.  ", ":;.,", " 12345", token_results2},
+       {"a.b,c", ",.", "", token_results2},
+       {"  a   b  c  ", " ", " ", token_results2},
+};
+
+START_TEST(test_token)
+{
+       enumerator_t *enumerator;
+       const char **results;
+       char *token;
+       int tok = 0;
+
+       enumerator = enumerator_create_token(token_tests[_i].string,
+                                                                       token_tests[_i].sep, token_tests[_i].trim);
+       results = token_tests[_i].results;
+       while (enumerator->enumerate(enumerator, &token))
+       {
+               switch (tok)
+               {
+                       case 0:
+                       case 1:
+                       case 2:
+                               ck_assert_str_eq(token, results[tok]);
+                               break;
+                       default:
+                               fail("unexpected token '%s'", token);
+               }
+               tok++;
+       }
+       fail_if(tok != 3, "not enough tokens (%d) extracted from '%s'",
+                       tok, token_tests[_i].string);
+       enumerator->destroy(enumerator);
+}
+END_TEST
+
+/*******************************************************************************
+ * utilities for filtered, nested and cleaner tests
+ */
+
+static int destroy_data_called;
+
+START_SETUP(setup_destroy_data)
+{
+       destroy_data_called = 0;
+}
+END_SETUP
+
+START_TEARDOWN(teardown_destroy_data)
+{
+       ck_assert_int_eq(destroy_data_called, 1);
+}
+END_TEARDOWN
+
+static void destroy_data(void *data)
+{
+       fail_if(data != (void*)101, "data does not match '101' in destructor");
+       destroy_data_called++;
+}
+
+/*******************************************************************************
+ * filtered test
+ */
+
+static bool filter(void *data, int *v, int *vo, int *w, int *wo,
+                                  int *x, int *xo, int *y, int *yo, int *z, int *zo)
+{
+       int val = *v;
+
+       *vo = val++;
+       *wo = val++;
+       *xo = val++;
+       *yo = val++;
+       *zo = val++;
+       fail_if(data != (void*)101, "data does not match '101' in filter function");
+       return TRUE;
+}
+
+static bool filter_odd(void *data, int *item, int *out)
+{
+       fail_if(data != (void*)101, "data does not match '101' in filter function");
+       *out = *item;
+       return *item % 2 == 0;
+}
+
+START_TEST(test_filtered)
+{
+       int round, v, w, x, y, z;
+       linked_list_t *list;
+       enumerator_t *enumerator;
+
+       list = linked_list_create_with_items((void*)1, (void*)2, (void*)3, (void*)4,
+                                                                                (void*)5, NULL);
+
+       round = 1;
+       enumerator = enumerator_create_filter(list->create_enumerator(list),
+                                                                       (void*)filter, (void*)101, destroy_data);
+       while (enumerator->enumerate(enumerator, &v, &w, &x, &y, &z))
+       {
+               ck_assert_int_eq(v, round);
+               ck_assert_int_eq(w, round + 1);
+               ck_assert_int_eq(x, round + 2);
+               ck_assert_int_eq(y, round + 3);
+               ck_assert_int_eq(z, round + 4);
+               round++;
+       }
+       enumerator->destroy(enumerator);
+       ck_assert_int_eq(round, 6);
+
+       list->destroy(list);
+}
+END_TEST
+
+START_TEST(test_filtered_filter)
+{
+       int count, x;
+       linked_list_t *list;
+       enumerator_t *enumerator;
+
+       list = linked_list_create_with_items((void*)1, (void*)2, (void*)3, (void*)4,
+                                                                                (void*)5, NULL);
+
+       count = 0;
+       /* should also work without destructor, so set this manually */
+       destroy_data_called = 1;
+       enumerator = enumerator_create_filter(list->create_enumerator(list),
+                                                                                (void*)filter_odd, (void*)101, NULL);
+       while (enumerator->enumerate(enumerator, &x))
+       {
+               ck_assert(x % 2 == 0);
+               count++;
+       }
+       enumerator->destroy(enumerator);
+       ck_assert_int_eq(count, 2);
+
+       list->destroy(list);
+}
+END_TEST
+
+/*******************************************************************************
+ * nested test
+ */
+
+static enumerator_t* create_inner(linked_list_t *outer, void *data)
+{
+       fail_if(data != (void*)101, "data does not match '101' in nested constr.");
+       return outer->create_enumerator(outer);
+}
+
+static enumerator_t* create_inner_null(void *outer, void *data)
+{
+       ck_assert(outer == (void*)1);
+       fail_if(data != (void*)101, "data does not match '101' in nested constr.");
+       return NULL;
+}
+
+START_TEST(test_nested)
+{
+       linked_list_t *list, *l1, *l2, *l3;
+       enumerator_t *enumerator;
+       intptr_t x;
+       int round;
+
+       l1 = linked_list_create_with_items((void*)1, (void*)2, NULL);
+       l2 = linked_list_create();
+       l3 = linked_list_create_with_items((void*)3, (void*)4, (void*)5, NULL);
+       list = linked_list_create_with_items(l1, l2, l3, NULL);
+
+       round = 1;
+       enumerator = enumerator_create_nested(list->create_enumerator(list),
+                                                               (void*)create_inner, (void*)101, destroy_data);
+       while (enumerator->enumerate(enumerator, &x))
+       {
+               ck_assert_int_eq(round, x);
+               round++;
+       }
+       enumerator->destroy(enumerator);
+       ck_assert_int_eq(round, 6);
+
+       list->destroy(list);
+       l1->destroy(l1);
+       l2->destroy(l2);
+       l3->destroy(l3);
+}
+END_TEST
+
+START_TEST(test_nested_reset)
+{
+       linked_list_t *list, *l1, *l2, *l3;
+       enumerator_t *outer, *enumerator;
+       intptr_t x;
+       int count = 0;
+
+       l1 = linked_list_create_with_items((void*)1, (void*)2, NULL);
+       l2 = linked_list_create();
+       l3 = linked_list_create_with_items((void*)3, (void*)4, (void*)5, NULL);
+       list = linked_list_create_with_items(l1, l2, l3, NULL);
+
+       outer = list->create_enumerator(list);
+       enumerator = enumerator_create_nested(outer, (void*)create_inner,
+                                                                                (void*)101, destroy_data);
+       while (enumerator->enumerate(enumerator, &x))
+       {
+               count++;
+       }
+       ck_assert_int_eq(count, 5);
+
+       list->reset_enumerator(list, outer);
+       ck_assert(enumerator->enumerate(enumerator, &x));
+       ck_assert_int_eq(x, 1);
+       enumerator->destroy(enumerator);
+
+       list->destroy(list);
+       l1->destroy(l1);
+       l2->destroy(l2);
+       l3->destroy(l3);
+}
+END_TEST
+
+START_TEST(test_nested_empty)
+{
+       linked_list_t *list;
+       enumerator_t *enumerator;
+       intptr_t x;
+       int count;
+
+       list = linked_list_create();
+       count = 0;
+       enumerator = enumerator_create_nested(list->create_enumerator(list),
+                                                               (void*)create_inner, (void*)101, destroy_data);
+       while (enumerator->enumerate(enumerator, &x))
+       {
+               count++;
+       }
+       enumerator->destroy(enumerator);
+       ck_assert_int_eq(count, 0);
+
+       list->destroy(list);
+}
+END_TEST
+
+START_TEST(test_nested_null)
+{
+       linked_list_t *list;
+       enumerator_t *enumerator;
+       intptr_t x;
+       int count;
+
+       list = linked_list_create_with_items((void*)1, NULL);
+
+       count = 0;
+       /* should also work without destructor, so set this manually */
+       destroy_data_called = 1;
+       enumerator = enumerator_create_nested(list->create_enumerator(list),
+                                                                       (void*)create_inner_null, (void*)101, NULL);
+       while (enumerator->enumerate(enumerator, &x))
+       {
+               count++;
+       }
+       enumerator->destroy(enumerator);
+       ck_assert_int_eq(count, 0);
+
+       list->destroy(list);
+}
+END_TEST
+
+/*******************************************************************************
+ * cleaner test
+ */
+
+START_TEST(test_cleaner)
+{
+       enumerator_t *enumerator;
+       linked_list_t *list;
+       intptr_t x;
+       int round;
+
+       list = linked_list_create_with_items((void*)1, (void*)2, NULL);
+
+       round = 1;
+       enumerator = enumerator_create_cleaner(list->create_enumerator(list),
+                                                                                  destroy_data, (void*)101);
+       while (enumerator->enumerate(enumerator, &x))
+       {
+               ck_assert_int_eq(round, x);
+               round++;
+       }
+       ck_assert_int_eq(round, 3);
+       enumerator->destroy(enumerator);
+       list->destroy(list);
+}
+END_TEST
+
+/*******************************************************************************
+ * single test
+ */
+
+static void single_cleanup(void *data)
+{
+       ck_assert_int_eq((intptr_t)data, 1);
+}
+
+static void do_test_single(enumerator_t *enumerator)
+{
+       intptr_t x;
+
+       ck_assert(enumerator->enumerate(enumerator, &x));
+       ck_assert_int_eq(x, 1);
+       ck_assert(!enumerator->enumerate(enumerator, &x));
+       enumerator->destroy(enumerator);
+}
+
+START_TEST(test_single)
+{
+       enumerator_t *enumerator;
+
+       enumerator = enumerator_create_single((void*)1, NULL);
+       do_test_single(enumerator);
+}
+END_TEST
+
+START_TEST(test_single_cleanup)
+{
+       enumerator_t *enumerator;
+
+       enumerator = enumerator_create_single((void*)1, single_cleanup);
+       do_test_single(enumerator);
+}
+END_TEST
+
+Suite *enumerator_suite_create()
+{
+       Suite *s;
+       TCase *tc;
+
+       s = suite_create("enumerator");
+
+       tc = tcase_create("tokens");
+       tcase_add_loop_test(tc, test_token, 0, countof(token_tests));
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("filtered");
+       tcase_add_checked_fixture(tc, setup_destroy_data, teardown_destroy_data);
+       tcase_add_test(tc, test_filtered);
+       tcase_add_test(tc, test_filtered_filter);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("nested");
+       tcase_add_checked_fixture(tc, setup_destroy_data, teardown_destroy_data);
+       tcase_add_test(tc, test_nested);
+       tcase_add_test(tc, test_nested_reset);
+       tcase_add_test(tc, test_nested_empty);
+       tcase_add_test(tc, test_nested_null);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("cleaner");
+       tcase_add_checked_fixture(tc, setup_destroy_data, teardown_destroy_data);
+       tcase_add_test(tc, test_cleaner);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("single");
+       tcase_add_test(tc, test_single);
+       tcase_add_test(tc, test_single_cleanup);
+       suite_add_tcase(s, tc);
+
+       return s;
+}
diff --git a/src/libstrongswan/tests/test_hashtable.c b/src/libstrongswan/tests/test_hashtable.c
new file mode 100644 (file)
index 0000000..8cc7bfe
--- /dev/null
@@ -0,0 +1,346 @@
+/*
+ * Copyright (C) 2010-2013 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#include "test_suite.h"
+
+#include <collections/hashtable.h>
+#include <utils/chunk.h>
+
+/*******************************************************************************
+ * string hash table functions
+ */
+
+static u_int hash(char *key)
+{
+       return chunk_hash(chunk_from_str(key));
+}
+
+static bool equals(char *key1, char *key2)
+{
+       return streq(key1, key2);
+}
+
+/*******************************************************************************
+ * test fixture
+ */
+
+static hashtable_t *ht;
+
+START_SETUP(setup_ht)
+{
+       ht = hashtable_create((hashtable_hash_t)hash,
+                                                 (hashtable_equals_t)equals, 0);
+       ck_assert_int_eq(ht->get_count(ht), 0);
+}
+END_SETUP
+
+START_TEARDOWN(teardown_ht)
+{
+       ht->destroy(ht);
+}
+END_TEARDOWN
+
+/*******************************************************************************
+ * put/get
+ */
+
+START_TEST(test_put_get)
+{
+       char *k1 = "key1", *k2 = "key2", *k3 = "key3";
+       char *v1 = "val1", *v2 = "val2", *v3 = "val3", *value;
+
+       value = ht->put(ht, k1, v1);
+       ck_assert_int_eq(ht->get_count(ht), 1);
+       ck_assert(streq(ht->get(ht, k1), v1));
+       ck_assert(ht->get(ht, k2) == NULL);
+       ck_assert(ht->get(ht, k3) == NULL);
+       ck_assert(value == NULL);
+
+       ht->put(ht, k2, v2);
+       ht->put(ht, k3, v3);
+       ck_assert_int_eq(ht->get_count(ht), 3);
+       ck_assert(streq(ht->get(ht, k1), v1));
+       ck_assert(streq(ht->get(ht, k2), v2));
+       ck_assert(streq(ht->get(ht, k3), v3));
+
+       value = ht->put(ht, k2, v1);
+       ck_assert_int_eq(ht->get_count(ht), 3);
+       ck_assert(streq(value, v2));
+       ck_assert(streq(ht->get(ht, k2), v1));
+}
+END_TEST
+
+/*******************************************************************************
+ * get_match
+ */
+
+static u_int hash_match(char *key)
+{
+       return chunk_hash(chunk_create(key, 4));
+}
+
+static bool equal_match(char *key1, char *key2)
+{
+       if (!strneq(key1, key2, 4))
+       {
+               return FALSE;
+       }
+       /* look for an item with a key < than what we look for */
+       return strcmp(key1, key2) >= 0;
+}
+
+START_TEST(test_get_match)
+{
+       char *k1 = "key1_a", *k2 = "key2", *k3 = "key1_b", *k4 = "key1_c";
+       char *v1 = "val1", *v2 = "val2", *v3 = "val3", *value;
+
+       ht = hashtable_create((hashtable_hash_t)hash_match,
+                                                 (hashtable_equals_t)equals, 0);
+
+       ht->put(ht, k1, v1);
+       ht->put(ht, k2, v2);
+       value = ht->put(ht, k3, v3);
+       ck_assert_int_eq(ht->get_count(ht), 3);
+       ck_assert(streq(ht->get(ht, k1), v1));
+       ck_assert(streq(ht->get(ht, k2), v2));
+       ck_assert(streq(ht->get(ht, k3), v3));
+       ck_assert(value == NULL);
+
+       value = ht->get_match(ht, k1, (hashtable_equals_t)equal_match);
+       ck_assert(value != NULL);
+       ck_assert(streq(value, v1));
+       value = ht->get_match(ht, k2, (hashtable_equals_t)equal_match);
+       ck_assert(value != NULL);
+       ck_assert(streq(value, v2));
+       value = ht->get_match(ht, k3, (hashtable_equals_t)equal_match);
+       ck_assert(value != NULL);
+       ck_assert(streq(value, v1));
+       value = ht->get_match(ht, k4, (hashtable_equals_t)equal_match);
+       ck_assert(value != NULL);
+       ck_assert(streq(value, v1));
+
+       ht->destroy(ht);
+}
+END_TEST
+
+/*******************************************************************************
+ * remove
+ */
+
+static void do_remove(char *k1, char *k2, char *k3)
+{
+       char *v1 = "val1", *v2 = "val2", *v3 = "val3", *value;
+
+       ht->put(ht, k1, v1);
+       ht->put(ht, k2, v2);
+       ht->put(ht, k3, v3);
+
+       value = ht->remove(ht, k2);
+       ck_assert_int_eq(ht->get_count(ht), 2);
+       ck_assert(streq(ht->get(ht, k1), v1));
+       ck_assert(streq(ht->get(ht, k3), v3));
+       ck_assert(streq(value, v2));
+       ck_assert(ht->get(ht, k2) == NULL);
+
+       value = ht->remove(ht, k2);
+       ck_assert_int_eq(ht->get_count(ht), 2);
+       ck_assert(value == NULL);
+
+       value = ht->remove(ht, k1);
+       value = ht->remove(ht, k3);
+       ck_assert_int_eq(ht->get_count(ht), 0);
+       ck_assert(ht->get(ht, k1) == NULL);
+       ck_assert(ht->get(ht, k2) == NULL);
+       ck_assert(ht->get(ht, k3) == NULL);
+}
+
+START_TEST(test_remove)
+{
+       char *k1 = "key1", *k2 = "key2", *k3 = "key3";
+
+       do_remove(k1, k2, k3);
+}
+END_TEST
+
+START_TEST(test_remove_one_bucket)
+{
+       char *k1 = "key1_a", *k2 = "key1_b", *k3 = "key1_c";
+
+       ht->destroy(ht);
+       /* set a capacity to avoid rehashing, which would change the items' order */
+       ht = hashtable_create((hashtable_hash_t)hash_match,
+                                                 (hashtable_equals_t)equals, 8);
+
+       do_remove(k1, k2, k3);
+}
+END_TEST
+
+/*******************************************************************************
+ * enumerator
+ */
+
+START_TEST(test_enumerator)
+{
+       char *k1 = "key1", *k2 = "key2", *k3 = "key3", *key;
+       char *v1 = "val1", *v2 = "val2", *v3 = "val3", *value;
+       enumerator_t *enumerator;
+       int count;
+
+       ht->put(ht, k1, v1);
+       ht->put(ht, k2, v2);
+       ht->put(ht, k3, v3);
+
+       count = 0;
+       enumerator = ht->create_enumerator(ht);
+       while (enumerator->enumerate(enumerator, &key, &value))
+       {
+               ck_assert(streq(key, k1) || streq(key, k2) || streq(key, k3));
+               ck_assert(streq(value, v1) || streq(value, v2) || streq(value, v3));
+               ck_assert(!streq(key, k1) || streq(value, v1));
+               ck_assert(!streq(key, k2) || streq(value, v2));
+               ck_assert(!streq(key, k3) || streq(value, v3));
+               count++;
+       }
+       enumerator->destroy(enumerator);
+       ck_assert_int_eq(count, 3);
+
+       count = 0;
+       enumerator = ht->create_enumerator(ht);
+       while (enumerator->enumerate(enumerator, NULL, NULL))
+       {
+               count++;
+       }
+       enumerator->destroy(enumerator);
+       ck_assert_int_eq(count, 3);
+
+       value = ht->remove(ht, k1);
+       value = ht->remove(ht, k2);
+       value = ht->remove(ht, k3);
+
+       count = 0;
+       enumerator = ht->create_enumerator(ht);
+       while (enumerator->enumerate(enumerator, &key, &value))
+       {
+               count++;
+       }
+       enumerator->destroy(enumerator);
+       ck_assert_int_eq(count, 0);
+}
+END_TEST
+
+/*******************************************************************************
+ * remove_at
+ */
+
+static void do_remove_at(char *k1, char *k2, char *k3)
+{
+       char *v1 = "val1", *v2 = "val2", *v3 = "val3", *value, *key;
+       enumerator_t *enumerator;
+
+       ht->put(ht, k1, v1);
+       ht->put(ht, k2, v2);
+       ht->put(ht, k3, v3);
+
+       enumerator = ht->create_enumerator(ht);
+       ht->remove_at(ht, enumerator);
+       while (enumerator->enumerate(enumerator, &key, &value))
+       {
+               if (streq(key, k2))
+               {
+                       ht->remove_at(ht, enumerator);
+               }
+       }
+       enumerator->destroy(enumerator);
+
+       ck_assert_int_eq(ht->get_count(ht), 2);
+       ck_assert(ht->get(ht, k1) != NULL);
+       ck_assert(ht->get(ht, k3) != NULL);
+       ck_assert(ht->get(ht, k2) == NULL);
+
+       ht->put(ht, k2, v2);
+
+       ck_assert_int_eq(ht->get_count(ht), 3);
+       ck_assert(ht->get(ht, k1) != NULL);
+       ck_assert(ht->get(ht, k2) != NULL);
+       ck_assert(ht->get(ht, k3) != NULL);
+
+       enumerator = ht->create_enumerator(ht);
+       while (enumerator->enumerate(enumerator, &key, &value))
+       {
+               ht->remove_at(ht, enumerator);
+       }
+       enumerator->destroy(enumerator);
+
+       ck_assert_int_eq(ht->get_count(ht), 0);
+       ck_assert(ht->get(ht, k1) == NULL);
+       ck_assert(ht->get(ht, k2) == NULL);
+       ck_assert(ht->get(ht, k3) == NULL);
+}
+
+START_TEST(test_remove_at)
+{
+       char *k1 = "key1", *k2 = "key2", *k3 = "key3";
+
+       do_remove_at(k1, k2, k3);
+}
+END_TEST
+
+START_TEST(test_remove_at_one_bucket)
+{
+       char *k1 = "key1_a", *k2 = "key1_b", *k3 = "key1_c";
+
+       ht->destroy(ht);
+       /* set a capacity to avoid rehashing, which would change the items' order */
+       ht = hashtable_create((hashtable_hash_t)hash_match,
+                                                 (hashtable_equals_t)equals, 8);
+       do_remove_at(k1, k2, k3);
+}
+END_TEST
+
+Suite *hashtable_suite_create()
+{
+       Suite *s;
+       TCase *tc;
+
+       s = suite_create("hashtable");
+
+       tc = tcase_create("put/get");
+       tcase_add_checked_fixture(tc, setup_ht, teardown_ht);
+       tcase_add_test(tc, test_put_get);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("get_match");
+       tcase_add_test(tc, test_get_match);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("remove");
+       tcase_add_checked_fixture(tc, setup_ht, teardown_ht);
+       tcase_add_test(tc, test_remove);
+       tcase_add_test(tc, test_remove_one_bucket);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("enumerator");
+       tcase_add_checked_fixture(tc, setup_ht, teardown_ht);
+       tcase_add_test(tc, test_enumerator);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("remove_at");
+       tcase_add_checked_fixture(tc, setup_ht, teardown_ht);
+       tcase_add_test(tc, test_remove_at);
+       tcase_add_test(tc, test_remove_at_one_bucket);
+       suite_add_tcase(s, tc);
+
+       return s;
+}
diff --git a/src/libstrongswan/tests/test_identification.c b/src/libstrongswan/tests/test_identification.c
new file mode 100644 (file)
index 0000000..b0b3ce8
--- /dev/null
@@ -0,0 +1,715 @@
+/*
+ * Copyright (C) 2013 Tobias Brunner
+ * Copyright (C) 2009 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#include "test_suite.h"
+
+#include <utils/identification.h>
+
+/*******************************************************************************
+ * create (_from_encoding, _from_data, _from_string, _from_sockaddr)
+ */
+
+START_TEST(test_from_encoding)
+{
+       identification_t *a;
+       chunk_t expected, encoding;
+
+       /* only ID_ANY is handled differently, for all other types the following
+        * applies.  should we perhaps test that this is in fact the case? */
+       expected = chunk_from_str("moon@strongswan.org");
+       a = identification_create_from_encoding(ID_RFC822_ADDR, expected);
+       ck_assert(ID_RFC822_ADDR == a->get_type(a));
+       encoding = a->get_encoding(a);
+       ck_assert(expected.ptr != encoding.ptr);
+       ck_assert(chunk_equals(expected, encoding));
+       a->destroy(a);
+
+       a = identification_create_from_encoding(ID_ANY, expected);
+       ck_assert(ID_ANY == a->get_type(a));
+       encoding = a->get_encoding(a);
+       ck_assert(encoding.ptr == NULL);
+       ck_assert(encoding.len == 0);
+       a->destroy(a);
+}
+END_TEST
+
+START_TEST(test_from_data)
+{
+       identification_t *a;
+       chunk_t expected, encoding;
+
+       /* this uses the DN parser (C=CH) */
+       expected = chunk_from_chars(0x30, 0x0d, 0x31, 0x0b, 0x30, 0x09, 0x06,
+                                                               0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x43, 0x48);
+       a = identification_create_from_data(expected);
+       ck_assert(ID_DER_ASN1_DN == a->get_type(a));
+       encoding = a->get_encoding(a);
+       ck_assert(expected.ptr != encoding.ptr);
+       ck_assert(chunk_equals(expected, encoding));
+       a->destroy(a);
+
+       /* everything else is handled by the string parser */
+       expected = chunk_from_str("moon@strongswan.org");
+       a = identification_create_from_data(expected);
+       ck_assert(ID_RFC822_ADDR == a->get_type(a));
+       encoding = a->get_encoding(a);
+       ck_assert(expected.ptr != encoding.ptr);
+       ck_assert(chunk_equals(expected, encoding));
+       a->destroy(a);
+}
+END_TEST
+
+START_TEST(test_from_sockaddr)
+{
+       identification_t *a;
+       chunk_t expected, encoding;
+       struct sockaddr_in in = {
+               .sin_family = AF_INET,
+       };
+       struct sockaddr_in6 in6 = {
+               .sin6_family = AF_INET6,
+       };
+
+       expected = chunk_from_chars(0xc0, 0xa8, 0x01, 0x01);
+       memcpy(&in.sin_addr, expected.ptr, sizeof(in.sin_addr));
+       a = identification_create_from_sockaddr((sockaddr_t*)&in);
+       ck_assert(ID_IPV4_ADDR == a->get_type(a));
+       encoding = a->get_encoding(a);
+       ck_assert(chunk_equals(expected, encoding));
+       a->destroy(a);
+
+       expected = chunk_from_chars(0xfe, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                                                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01);
+       memcpy(&in6.sin6_addr, expected.ptr, sizeof(in6.sin6_addr));
+       a = identification_create_from_sockaddr((sockaddr_t*)&in6);
+       ck_assert(ID_IPV6_ADDR == a->get_type(a));
+       encoding = a->get_encoding(a);
+       ck_assert(chunk_equals(expected, encoding));
+       a->destroy(a);
+
+       in6.sin6_family = AF_UNSPEC;
+       a = identification_create_from_sockaddr((sockaddr_t*)&in6);
+       ck_assert(ID_ANY == a->get_type(a));
+       a->destroy(a);
+}
+END_TEST
+
+static struct {
+       char *id;
+       id_type_t type;
+       struct {
+               enum {
+                       ENC_CHUNK,
+                       ENC_STRING,
+                       ENC_SIMPLE,
+               } type;
+               union {
+                       chunk_t c;
+                       char *s;
+               } data;
+       } result;
+} string_data[] = {
+       {NULL,      ID_ANY,  { .type = ENC_CHUNK }},
+       {"",        ID_ANY,  { .type = ENC_CHUNK }},
+       {"%any",    ID_ANY,  { .type = ENC_CHUNK }},
+       {"%any6",   ID_ANY,  { .type = ENC_CHUNK }},
+       {"0.0.0.0", ID_ANY,  { .type = ENC_CHUNK }},
+       {"0::0",    ID_ANY,  { .type = ENC_CHUNK }},
+       {"::",      ID_ANY,  { .type = ENC_CHUNK }},
+       {"*",       ID_ANY,  { .type = ENC_CHUNK }},
+       {"any",     ID_FQDN, { .type = ENC_SIMPLE }},
+       {"any6",    ID_FQDN, { .type = ENC_SIMPLE }},
+       {"0",       ID_FQDN, { .type = ENC_SIMPLE }},
+       {"**",      ID_FQDN, { .type = ENC_SIMPLE }},
+       {"192.168.1.1", ID_IPV4_ADDR, { .type = ENC_CHUNK,
+                                                                       .data.c = chunk_from_chars(0xc0, 0xa8, 0x01, 0x01) }},
+       {"192.168.",ID_FQDN, { .type = ENC_SIMPLE }},
+       {".",       ID_FQDN, { .type = ENC_SIMPLE }},
+       {"fec0::1", ID_IPV6_ADDR, { .type = ENC_CHUNK,
+                                                               .data.c = chunk_from_chars(0xfe, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                                                                                                                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01) }},
+       {"fec0::",  ID_IPV6_ADDR, { .type = ENC_CHUNK,
+                                                               .data.c = chunk_from_chars(0xfe, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                                                                                                                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) }},
+       {"fec0:",   ID_KEY_ID,    { .type = ENC_SIMPLE }},
+       {":",       ID_KEY_ID,    { .type = ENC_SIMPLE }},
+       {"alice@strongswan.org", ID_RFC822_ADDR, { .type = ENC_SIMPLE }},
+       {"alice@strongswan", ID_RFC822_ADDR, { .type = ENC_SIMPLE }},
+       {"alice@",  ID_RFC822_ADDR, { .type = ENC_SIMPLE }},
+       {"alice",   ID_FQDN, { .type = ENC_SIMPLE }},
+       {"@",       ID_FQDN, { .type = ENC_CHUNK }},
+       {" @",      ID_RFC822_ADDR, { .type = ENC_SIMPLE }},
+       {"@strongswan.org",  ID_FQDN, { .type = ENC_STRING,
+                                                                       .data.s = "strongswan.org" }},
+       {"@#deadbeef", ID_KEY_ID, { .type = ENC_CHUNK,
+                                                               .data.c = chunk_from_chars(0xde, 0xad, 0xbe, 0xef) }},
+       {"@#deadbee",  ID_KEY_ID, { .type = ENC_CHUNK,
+                                                               .data.c = chunk_from_chars(0x0d, 0xea, 0xdb, 0xee) }},
+       {"foo=bar",    ID_KEY_ID, { .type = ENC_SIMPLE }},
+       {"foo=",           ID_KEY_ID, { .type = ENC_SIMPLE }},
+       {"=bar",           ID_KEY_ID, { .type = ENC_SIMPLE }},
+       {"C=",             ID_DER_ASN1_DN, { .type = ENC_CHUNK,
+                                                                        .data.c = chunk_from_chars(0x30, 0x0b, 0x31, 0x09, 0x30, 0x07, 0x06,
+                                                                                                                               0x03, 0x55, 0x04, 0x06, 0x13, 0x00)}},
+       {"C=CH",           ID_DER_ASN1_DN, { .type = ENC_CHUNK,
+                                                                        .data.c = chunk_from_chars(0x30, 0x0d, 0x31, 0x0b, 0x30, 0x09, 0x06,
+                                                                                                                               0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x43, 0x48)}},
+       {"C=CH,",          ID_DER_ASN1_DN, { .type = ENC_CHUNK,
+                                                                        .data.c = chunk_from_chars(0x30, 0x0d, 0x31, 0x0b, 0x30, 0x09, 0x06,
+                                                                                                                               0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x43, 0x48)}},
+       {"C=CH, ",         ID_DER_ASN1_DN, { .type = ENC_CHUNK,
+                                                                        .data.c = chunk_from_chars(0x30, 0x0d, 0x31, 0x0b, 0x30, 0x09, 0x06,
+                                                                                                                               0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x43, 0x48)}},
+       {"C=CH, O",        ID_KEY_ID, { .type = ENC_SIMPLE }},
+};
+
+START_TEST(test_from_string)
+{
+       identification_t *a;
+       chunk_t encoding, expected;
+       char *id;
+
+       id = string_data[_i].id;
+       a = identification_create_from_string(id);
+       fail_unless(a->get_type(a) == string_data[_i].type,
+                               "type of id '%s' is %N, %N expected", id,
+                               id_type_names, a->get_type(a),
+                               id_type_names, string_data[_i].type);
+
+       encoding = a->get_encoding(a);
+       switch (string_data[_i].result.type)
+       {
+               case ENC_SIMPLE:
+                       expected = chunk_from_str(string_data[_i].id);
+                       break;
+               case ENC_STRING:
+                       expected = chunk_from_str(string_data[_i].result.data.s);
+                       break;
+               case ENC_CHUNK:
+                       expected = string_data[_i].result.data.c;
+                       break;
+               default:
+                       fail("unexpected result type");
+       }
+
+       ck_assert(!id || (char*)encoding.ptr != id);
+       if (expected.ptr)
+       {
+               fail_unless(chunk_equals(encoding, expected),
+                                       "parsing '%s' failed\nencoding %B\nexpected %B\n",
+                                       id, &encoding, &expected);
+       }
+       else
+       {
+               ck_assert(encoding.ptr == NULL);
+               ck_assert(encoding.len == 0);
+       }
+       a->destroy(a);
+}
+END_TEST
+
+/*******************************************************************************
+ * printf_hook
+ */
+
+static void string_equals(char *a_str, char *b_str)
+{
+       identification_t *b;
+       char buf[128];
+
+       b = b_str ? identification_create_from_string(b_str) : NULL;
+       snprintf(buf, sizeof(buf), "%Y", b);
+       DESTROY_IF(b);
+       ck_assert_str_eq(a_str, buf);
+}
+
+static void string_equals_id(char *a_str, identification_t *b)
+{
+       char buf[128];
+
+       snprintf(buf, sizeof(buf), "%Y", b);
+       DESTROY_IF(b);
+       ck_assert_str_eq(a_str, buf);
+}
+
+START_TEST(test_printf_hook)
+{
+       string_equals("(null)", NULL);
+       string_equals("%any", "");
+       string_equals("%any", "%any");
+       string_equals("%any", "*");
+
+       string_equals("192.168.1.1", "192.168.1.1");
+       string_equals_id("(invalid ID_IPV4_ADDR)",
+                               identification_create_from_encoding(ID_IPV4_ADDR, chunk_empty));
+       string_equals("fec0::1", "fec0::1");
+       string_equals("fec0::1", "fec0:0:0::1");
+       string_equals_id("(invalid ID_IPV6_ADDR)",
+                               identification_create_from_encoding(ID_IPV6_ADDR, chunk_empty));
+
+       string_equals_id("(unknown ID type: 255)",
+                               identification_create_from_encoding(255, chunk_empty));
+
+       string_equals("moon@strongswan.org", "moon@strongswan.org");
+       string_equals("MOON@STRONGSWAN.ORG", "MOON@STRONGSWAN.ORG");
+       /* non-printable characters */
+       string_equals_id("????@strongswan.org", identification_create_from_encoding(ID_RFC822_ADDR,
+                       chunk_from_chars(0xfa, 0xfb, 0xfc, 0xfd, 0x40, 0x73, 0x74, 0x72,
+                                                        0x6f, 0x6e, 0x67, 0x73, 0x77, 0x61, 0x6e, 0x2e,
+                                                        0x6f, 0x72, 0x67)));
+
+       /* not a DN => ID_KEY_ID => no normalization */
+       string_equals("C=CH, AsdF=asdf", "C=CH, AsdF=asdf");
+       string_equals_id("moon@strongswan.org", identification_create_from_encoding(ID_KEY_ID,
+                       chunk_from_str("moon@strongswan.org")));
+       /* non-printable characters */
+       string_equals_id("de:ad:be:ef", identification_create_from_encoding(ID_KEY_ID,
+                       chunk_from_chars(0xde, 0xad, 0xbe, 0xef)));
+       /* printable characters */
+       string_equals_id("ABCDEFGHIJKLMNOPQRS",
+               identification_create_from_encoding(ID_KEY_ID,
+                       chunk_from_chars(0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
+                                                        0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50,
+                                                        0x51, 0x52, 0x53)));
+       /* ABCDEFGHIJKLMNOPQRST is printable but has the length of a SHA1 hash */
+       string_equals_id("41:42:43:44:45:46:47:48:49:4a:4b:4c:4d:4e:4f:50:51:52:53:54",
+               identification_create_from_encoding(ID_KEY_ID,
+                       chunk_from_chars(0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
+                                                        0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50,
+                                                        0x51, 0x52, 0x53, 0x54)));
+
+       string_equals_id("", identification_create_from_encoding(ID_DER_ASN1_DN, chunk_empty));
+       string_equals("C=", "C=");
+       string_equals("C=", "C=,");
+       string_equals("C=", "C=, ");
+       string_equals("C=", "C= , ");
+       string_equals("C=, O=strongSwan", "C=, O=strongSwan");
+       string_equals("C=CH, O=", "C=CH, O=");
+       string_equals("C=CH, O=strongSwan, CN=strongswan.org",
+                                 "C=CH, O=strongSwan, CN=strongswan.org");
+       string_equals("CN=strongswan.org, O=strongSwan, C=CH",
+                                 "cn=strongswan.org, o=strongSwan, c=CH");
+       string_equals("C=CH, O=strongSwan, CN=strongswan.org",
+                                 "C=CH,O=strongSwan,CN=strongswan.org");
+       string_equals("C=CH, O=strongSwan, CN=strongswan.org",
+                                 "/C=CH/O=strongSwan/CN=strongswan.org");
+       string_equals("CN=strongswan.org, O=strongSwan, C=CH",
+                                 "CN=strongswan.org,O=strongSwan,C=CH");
+
+       string_equals("C=CH, E=moon@strongswan.org, CN=moon",
+                                 "C=CH, email=moon@strongswan.org, CN=moon");
+       string_equals("C=CH, E=moon@strongswan.org, CN=moon",
+                                 "C=CH, emailAddress=moon@strongswan.org, CN=moon");
+
+       /* C=CH, pseudonym=ANO (pseudonym is currently not recognized) */
+       string_equals_id("C=CH, 55:04:41=ANO", identification_create_from_encoding(ID_DER_ASN1_DN,
+               chunk_from_chars(0x30, 0x19, 0x31, 0x17, 0x30, 0x09, 0x06, 0x03, 0x55,
+                                                0x04, 0x06, 0x13, 0x02, 0x43, 0x48, 0x30, 0x0a, 0x06,
+                                                0x03, 0x55, 0x04, 0x41, 0x13, 0x03, 0x41, 0x4e, 0x4f)));
+       /* C=CH, O=strongSwan (but instead of a 2nd OID -0x06- we got NULL -0x05) */
+       string_equals_id("C=CH, (invalid ID_DER_ASN1_DN)", identification_create_from_encoding(ID_DER_ASN1_DN,
+               chunk_from_chars(0x30, 0x20, 0x31, 0x1e, 0x30, 0x09, 0x06, 0x03, 0x55,
+                                                0x04, 0x06, 0x13, 0x02, 0x43, 0x48, 0x30, 0x11, 0x05,
+                                                0x03, 0x55, 0x04, 0x0a, 0x13, 0x0a, 0x73, 0x74, 0x72,
+                                                0x6f, 0x6e, 0x67, 0x53, 0x77, 0x61, 0x6e)));
+       /* moon@strongswan.org as GN */
+       string_equals_id("(ASN.1 general name)", identification_create_from_encoding(ID_DER_ASN1_GN,
+               chunk_from_chars(0x81, 0x14, 0x6d, 0x6f, 0x6f, 0x6e, 0x40, 0x73, 0x74,
+                                                0x72, 0x6f, 0x6e, 0x67, 0x73, 0x77, 0x61, 0x6e, 0x2e,
+                                                0x6f, 0x72, 0x67)));
+}
+END_TEST
+
+START_TEST(test_printf_hook_width)
+{
+       identification_t *a;
+       char buf[128];
+
+       a = identification_create_from_string("moon@strongswan.org");
+       snprintf(buf, sizeof(buf), "%25Y", a);
+       ck_assert_str_eq("      moon@strongswan.org", buf);
+       snprintf(buf, sizeof(buf), "%-*Y", 25, a);
+       ck_assert_str_eq("moon@strongswan.org      ", buf);
+       snprintf(buf, sizeof(buf), "%5Y", a);
+       ck_assert_str_eq("moon@strongswan.org", buf);
+       DESTROY_IF(a);
+}
+END_TEST
+
+/*******************************************************************************
+ * equals
+ */
+
+static bool id_equals(identification_t *a, char *b_str)
+{
+       identification_t *b;
+       bool equals;
+
+       b = identification_create_from_string(b_str);
+       equals = a->equals(a, b);
+       equals = equals && b->equals(b, a);
+       b->destroy(b);
+       return equals;
+}
+
+START_TEST(test_equals)
+{
+       identification_t *a;
+       chunk_t encoding, fuzzed;
+       int i;
+
+       /* this test also tests identification_create_from_string with DNs */
+       a = identification_create_from_string(
+                                                        "C=CH, E=moon@strongswan.org, CN=moon");
+
+       ck_assert(id_equals(a, "C=CH, E=moon@strongswan.org, CN=moon"));
+       ck_assert(id_equals(a, "C==CH, E==moon@strongswan.org,,, CN==moon"));
+       ck_assert(id_equals(a, "  C=CH, E=moon@strongswan.org, CN=moon  "));
+       ck_assert(id_equals(a, "C=ch, E=moon@STRONGSWAN.ORG, CN=Moon"));
+       ck_assert(id_equals(a, "/C=CH/E=moon@strongswan.org/CN=moon"));
+       ck_assert(id_equals(a, "C=CH/E=moon@strongswan.org/CN=moon"));
+       ck_assert(id_equals(a, "C=CH/E=moon@strongswan.org,CN=moon"));
+       ck_assert(id_equals(a, "C=CH / E=moon@strongswan.org , CN=moon"));
+
+       ck_assert(!id_equals(a, "C=CH E=moon@strongswan.org CN=moon"));
+       ck_assert(!id_equals(a, "C=CN, E=moon@strongswan.org, CN=moon"));
+       ck_assert(!id_equals(a, "E=moon@strongswan.org, C=CH, CN=moon"));
+       ck_assert(!id_equals(a, "E=moon@strongswan.org, C=CH, CN=moon"));
+
+       encoding = chunk_clone(a->get_encoding(a));
+       a->destroy(a);
+
+       /* simple fuzzing, increment each byte of encoding */
+       for (i = 0; i < encoding.len; i++)
+       {
+               if (i == 11 || i == 30 || i == 60)
+               {       /* skip ASN.1 type fields, as equals() handles them graceful */
+                       continue;
+               }
+               fuzzed = chunk_clone(encoding);
+               fuzzed.ptr[i]++;
+               a = identification_create_from_encoding(ID_DER_ASN1_DN, fuzzed);
+               if (id_equals(a, "C=CH, E=moon@strongswan.org, CN=moon"))
+               {
+                       printf("%d %B\n%B\n", i, &fuzzed, &encoding);
+               }
+               ck_assert(!id_equals(a, "C=CH, E=moon@strongswan.org, CN=moon"));
+               a->destroy(a);
+               free(fuzzed.ptr);
+       }
+
+       /* and decrement each byte of encoding */
+       for (i = 0; i < encoding.len; i++)
+       {
+               if (i == 11 || i == 30 || i == 60)
+               {
+                       continue;
+               }
+               fuzzed = chunk_clone(encoding);
+               fuzzed.ptr[i]--;
+               a = identification_create_from_encoding(ID_DER_ASN1_DN, fuzzed);
+               ck_assert(!id_equals(a, "C=CH, E=moon@strongswan.org, CN=moon"));
+               a->destroy(a);
+               free(fuzzed.ptr);
+       }
+       free(encoding.ptr);
+}
+END_TEST
+
+START_TEST(test_equals_any)
+{
+       identification_t *a, *b;
+
+       a = identification_create_from_string("%any");
+       b = identification_create_from_encoding(ID_ANY, chunk_empty);
+       ck_assert(a->equals(a, b));
+       ck_assert(b->equals(b, a));
+       b->destroy(b);
+
+       b = identification_create_from_string("C=CH, O=strongSwan, CN=strongswan.org");
+       ck_assert(!a->equals(a, b));
+       ck_assert(!b->equals(b, a));
+       a->destroy(a);
+       b->destroy(b);
+}
+END_TEST
+
+START_TEST(test_equals_binary)
+{
+       identification_t *a, *b;
+       chunk_t encoding;
+
+       encoding = chunk_from_str("foobar=");
+       /* strings containing = are parsed as KEY_ID if they aren't valid ASN.1 DNs */
+       a = identification_create_from_string("foobar=");
+       ck_assert(a->get_type(a) == ID_KEY_ID);
+       b = identification_create_from_encoding(ID_KEY_ID, encoding);
+       ck_assert(a->equals(a, b));
+       a->destroy(a);
+       b->destroy(b);
+}
+END_TEST
+
+START_TEST(test_equals_fqdn)
+{
+       identification_t *a;
+
+       a = identification_create_from_string("ipsec.strongswan.org");
+       ck_assert(id_equals(a, "IPSEC.strongswan.org"));
+       ck_assert(id_equals(a, "ipsec.strongSwan.org"));
+       ck_assert(id_equals(a, "ipsec.strongSwan.ORG"));
+       ck_assert(!id_equals(a, "strongswan.org"));
+       a->destroy(a);
+}
+END_TEST
+
+/*******************************************************************************
+ * matches
+ */
+
+static bool id_matches(identification_t *a, char *b_str, id_match_t expected)
+{
+       identification_t *b;
+       id_match_t match;
+
+       b = identification_create_from_string(b_str);
+       match = a->matches(a, b);
+       b->destroy(b);
+       return match == expected;
+}
+
+START_TEST(test_matches)
+{
+       identification_t *a;
+
+       a = identification_create_from_string("C=CH, E=moon@strongswan.org, CN=moon");
+
+       ck_assert(id_matches(a, "C=CH, E=moon@strongswan.org, CN=moon", ID_MATCH_PERFECT));
+       ck_assert(id_matches(a, "C=CH, E=*, CN=moon", ID_MATCH_ONE_WILDCARD));
+       ck_assert(id_matches(a, "C=CH, E=*, CN=*", ID_MATCH_ONE_WILDCARD - 1));
+       ck_assert(id_matches(a, "C=*, E=*, CN=*", ID_MATCH_ONE_WILDCARD - 2));
+       ck_assert(id_matches(a, "C=*, E=*, CN=*, O=BADInc", ID_MATCH_NONE));
+       ck_assert(id_matches(a, "C=*, E=*", ID_MATCH_NONE));
+       ck_assert(id_matches(a, "C=*, E=a@b.c, CN=*", ID_MATCH_NONE));
+       ck_assert(id_matches(a, "%any", ID_MATCH_ANY));
+
+       a->destroy(a);
+}
+END_TEST
+
+START_TEST(test_matches_any)
+{
+       identification_t *a;
+
+       a = identification_create_from_string("%any");
+
+       ck_assert(id_matches(a, "%any", ID_MATCH_ANY));
+       ck_assert(id_matches(a, "", ID_MATCH_ANY));
+       ck_assert(id_matches(a, "*", ID_MATCH_ANY));
+       ck_assert(id_matches(a, "moon@strongswan.org", ID_MATCH_NONE));
+       ck_assert(id_matches(a, "vpn.strongswan.org", ID_MATCH_NONE));
+       a->destroy(a);
+}
+END_TEST
+
+START_TEST(test_matches_binary)
+{
+       identification_t *a;
+
+       /* strings containing = are parsed as KEY_ID if they aren't valid ASN.1 DNs */
+       a = identification_create_from_string("foo=bar");
+       ck_assert(a->get_type(a) == ID_KEY_ID);
+       ck_assert(id_matches(a, "%any", ID_MATCH_ANY));
+       ck_assert(id_matches(a, "foo=bar", ID_MATCH_PERFECT));
+       ck_assert(id_matches(a, "bar=foo", ID_MATCH_NONE));
+       ck_assert(id_matches(a, "*=bar", ID_MATCH_NONE));
+       ck_assert(id_matches(a, "foo=*", ID_MATCH_NONE));
+       ck_assert(id_matches(a, "foo@bar", ID_MATCH_NONE));
+       a->destroy(a);
+}
+END_TEST
+
+START_TEST(test_matches_string)
+{
+       identification_t *a;
+
+       a = identification_create_from_string("moon@strongswan.org");
+
+       ck_assert(id_matches(a, "moon@strongswan.org", ID_MATCH_PERFECT));
+       ck_assert(id_matches(a, "*@strongswan.org", ID_MATCH_ONE_WILDCARD));
+       ck_assert(id_matches(a, "*@*.org", ID_MATCH_NONE));
+       ck_assert(id_matches(a, "*@*", ID_MATCH_NONE));
+       /* the following two are parsed as ID_FQDN, so no match */
+       ck_assert(id_matches(a, "*strongswan.org", ID_MATCH_NONE));
+       ck_assert(id_matches(a, "*.org", ID_MATCH_NONE));
+       ck_assert(id_matches(a, "moon@*", ID_MATCH_NONE));
+       ck_assert(id_matches(a, "**", ID_MATCH_NONE));
+       ck_assert(id_matches(a, "*", ID_MATCH_ANY));
+       ck_assert(id_matches(a, "%any", ID_MATCH_ANY));
+       a->destroy(a);
+
+       a = identification_create_from_string("vpn.strongswan.org");
+
+       ck_assert(id_matches(a, "vpn.strongswan.org", ID_MATCH_PERFECT));
+       ck_assert(id_matches(a, "*.strongswan.org", ID_MATCH_ONE_WILDCARD));
+       ck_assert(id_matches(a, "*strongswan.org", ID_MATCH_ONE_WILDCARD));
+       ck_assert(id_matches(a, "*.org", ID_MATCH_ONE_WILDCARD));
+       ck_assert(id_matches(a, "*.strongswan.*", ID_MATCH_NONE));
+       ck_assert(id_matches(a, "*vpn.strongswan.org", ID_MATCH_NONE));
+       ck_assert(id_matches(a, "vpn.strongswan.*", ID_MATCH_NONE));
+       ck_assert(id_matches(a, "**", ID_MATCH_NONE));
+       ck_assert(id_matches(a, "*", ID_MATCH_ANY));
+       ck_assert(id_matches(a, "%any", ID_MATCH_ANY));
+       a->destroy(a);
+}
+END_TEST
+
+/*******************************************************************************
+ * identification part enumeration
+ */
+
+START_TEST(test_parts)
+{
+       identification_t *id;
+       enumerator_t *enumerator;
+       id_part_t part;
+       chunk_t data;
+       int i = 0;
+
+       id = identification_create_from_string("C=CH, O=strongSwan, CN=tester");
+
+       enumerator = id->create_part_enumerator(id);
+       while (enumerator->enumerate(enumerator, &part, &data))
+       {
+               switch (i++)
+               {
+                       case 0:
+                               ck_assert(part == ID_PART_RDN_C &&
+                                                 chunk_equals(data, chunk_create("CH", 2)));
+                               break;
+                       case 1:
+                               ck_assert(part == ID_PART_RDN_O &&
+                                                 chunk_equals(data, chunk_from_str("strongSwan")));
+                               break;
+                       case 2:
+                               ck_assert(part == ID_PART_RDN_CN &&
+                                                 chunk_equals(data, chunk_from_str("tester")));
+                               break;
+                       default:
+                               fail("unexpected identification part %d", part);
+               }
+       }
+       ck_assert_int_eq(i, 3);
+       enumerator->destroy(enumerator);
+       id->destroy(id);
+}
+END_TEST
+
+/*******************************************************************************
+ * wildcards
+ */
+
+static bool id_contains_wildcards(char *string)
+{
+       identification_t *id;
+       bool contains;
+
+       id = identification_create_from_string(string);
+       contains = id->contains_wildcards(id);
+       id->destroy(id);
+       return contains;
+}
+
+START_TEST(test_contains_wildcards)
+{
+       ck_assert(id_contains_wildcards("%any"));
+       ck_assert(id_contains_wildcards("C=*, O=strongSwan, CN=gw"));
+       ck_assert(id_contains_wildcards("C=CH, O=strongSwan, CN=*"));
+       ck_assert(id_contains_wildcards("*@strongswan.org"));
+       ck_assert(id_contains_wildcards("*.strongswan.org"));
+       ck_assert(!id_contains_wildcards("C=**, O=a*, CN=*a"));
+}
+END_TEST
+
+/*******************************************************************************
+ * clone
+ */
+
+START_TEST(test_clone)
+{
+       identification_t *a, *b;
+       chunk_t a_enc, b_enc;
+
+       a = identification_create_from_string("moon@strongswan.org");
+       a_enc = a->get_encoding(a);
+       b = a->clone(a);
+       ck_assert(b != NULL);
+       ck_assert(a != b);
+       b_enc = b->get_encoding(b);
+       ck_assert(a_enc.ptr != b_enc.ptr);
+       ck_assert(chunk_equals(a_enc, b_enc));
+       a->destroy(a);
+       b->destroy(b);
+}
+END_TEST
+
+Suite *identification_suite_create()
+{
+       Suite *s;
+       TCase *tc;
+
+       s = suite_create("identification");
+
+       tc = tcase_create("create");
+       tcase_add_test(tc, test_from_encoding);
+       tcase_add_test(tc, test_from_data);
+       tcase_add_test(tc, test_from_sockaddr);
+       tcase_add_loop_test(tc, test_from_string, 0, countof(string_data));
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("printf_hook");
+       tcase_add_test(tc, test_printf_hook);
+       tcase_add_test(tc, test_printf_hook_width);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("equals");
+       tcase_add_test(tc, test_equals);
+       tcase_add_test(tc, test_equals_any);
+       tcase_add_test(tc, test_equals_binary);
+       tcase_add_test(tc, test_equals_fqdn);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("matches");
+       tcase_add_test(tc, test_matches);
+       tcase_add_test(tc, test_matches_any);
+       tcase_add_test(tc, test_matches_binary);
+       tcase_add_test(tc, test_matches_string);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("part enumeration");
+       tcase_add_test(tc, test_parts);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("wildcards");
+       tcase_add_test(tc, test_contains_wildcards);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("clone");
+       tcase_add_test(tc, test_clone);
+       suite_add_tcase(s, tc);
+
+       return s;
+}
diff --git a/src/libstrongswan/tests/test_linked_list.c b/src/libstrongswan/tests/test_linked_list.c
new file mode 100644 (file)
index 0000000..fc055bb
--- /dev/null
@@ -0,0 +1,431 @@
+/*
+ * Copyright (C) 2013 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#include "test_suite.h"
+
+#include <collections/linked_list.h>
+
+/*******************************************************************************
+ * test fixture
+ */
+
+static linked_list_t *list;
+
+START_SETUP(setup_list)
+{
+       void *x = NULL;
+
+       list = linked_list_create();
+       ck_assert_int_eq(list->get_count(list), 0);
+       ck_assert(list->get_first(list, &x) == NOT_FOUND);
+       ck_assert(list->get_last(list, &x) == NOT_FOUND);
+}
+END_SETUP
+
+START_TEARDOWN(teardown_list)
+{
+       list->destroy(list);
+}
+END_TEARDOWN
+
+/*******************************************************************************
+ * insert first/last
+ */
+
+START_TEST(test_insert_first)
+{
+       void *a = (void*)1, *b = (void*)2, *x = NULL;
+
+       list->insert_first(list, a);
+       ck_assert_int_eq(list->get_count(list), 1);
+       ck_assert(list->get_first(list, &x) == SUCCESS);
+       ck_assert(x == a);
+       ck_assert(list->get_last(list, &x) == SUCCESS);
+       ck_assert(x == a);
+
+       list->insert_first(list, b);
+       ck_assert_int_eq(list->get_count(list), 2);
+       ck_assert(list->get_first(list, &x) == SUCCESS);
+       ck_assert(x == b);
+       ck_assert(list->get_last(list, &x) == SUCCESS);
+       ck_assert(x == a);
+}
+END_TEST
+
+START_TEST(test_insert_last)
+{
+       void *a = (void*)1, *b = (void*)2, *x = NULL;
+
+       list->insert_last(list, a);
+       ck_assert_int_eq(list->get_count(list), 1);
+       ck_assert(list->get_first(list, &x) == SUCCESS);
+       ck_assert(x == a);
+       ck_assert(list->get_last(list, &x) == SUCCESS);
+       ck_assert(x == a);
+
+       list->insert_last(list, b);
+       ck_assert_int_eq(list->get_count(list), 2);
+       ck_assert(list->get_first(list, &x) == SUCCESS);
+       ck_assert(x == a);
+       ck_assert(list->get_last(list, &x) == SUCCESS);
+       ck_assert(x == b);
+}
+END_TEST
+
+/*******************************************************************************
+ * remove first/last
+ */
+
+START_TEST(test_remove_first)
+{
+       void *a = (void*)1, *b = (void*)2, *x = NULL;
+
+       list->insert_first(list, a);
+       list->insert_first(list, b);
+       ck_assert(list->remove_first(list, &x) == SUCCESS);
+       ck_assert_int_eq(list->get_count(list), 1);
+       ck_assert(x == b);
+       ck_assert(list->remove_first(list, &x) == SUCCESS);
+       ck_assert_int_eq(list->get_count(list), 0);
+       ck_assert(x == a);
+       ck_assert(list->remove_first(list, &x) == NOT_FOUND);
+       ck_assert(list->remove_last(list, &x) == NOT_FOUND);
+}
+END_TEST
+
+START_TEST(test_remove_last)
+{
+       void *a = (void*)1, *b = (void*)2, *x = NULL;
+
+       list->insert_first(list, a);
+       list->insert_first(list, b);
+       ck_assert(list->remove_last(list, &x) == SUCCESS);
+       ck_assert_int_eq(list->get_count(list), 1);
+       ck_assert(x == a);
+       ck_assert(list->remove_last(list, &x) == SUCCESS);
+       ck_assert_int_eq(list->get_count(list), 0);
+       ck_assert(x == b);
+       ck_assert(list->remove_first(list, &x) == NOT_FOUND);
+       ck_assert(list->remove_last(list, &x) == NOT_FOUND);
+}
+END_TEST
+
+/*******************************************************************************
+ * helper function for remove and find tests
+ */
+
+static bool match_a(void *item, void *a)
+{
+       ck_assert(a == (void*)1);
+       return item == a;
+}
+
+static bool match_b(void *item, void *b)
+{
+       ck_assert(b == (void*)2);
+       return item == b;
+}
+
+/*******************************************************************************
+ * remove
+ */
+
+START_TEST(test_remove)
+{
+       void *a = (void*)1, *b = (void*)2;
+
+       list->insert_first(list, a);
+       ck_assert(list->remove(list, a, NULL) == 1);
+       ck_assert_int_eq(list->get_count(list), 0);
+
+       list->insert_last(list, a);
+       list->insert_last(list, a);
+       list->insert_last(list, a);
+       list->insert_last(list, b);
+       ck_assert(list->remove(list, a, NULL) == 3);
+       ck_assert(list->remove(list, a, NULL) == 0);
+       ck_assert_int_eq(list->get_count(list), 1);
+       ck_assert(list->remove(list, b, NULL) == 1);
+       ck_assert(list->remove(list, b, NULL) == 0);
+}
+END_TEST
+
+START_TEST(test_remove_callback)
+{
+       void *a = (void*)1, *b = (void*)2;
+
+       list->insert_last(list, a);
+       list->insert_last(list, b);
+       list->insert_last(list, a);
+       list->insert_last(list, b);
+       ck_assert(list->remove(list, a, match_a) == 2);
+       ck_assert(list->remove(list, a, match_a) == 0);
+       ck_assert_int_eq(list->get_count(list), 2);
+       ck_assert(list->remove(list, b, match_b) == 2);
+       ck_assert(list->remove(list, b, match_b) == 0);
+       ck_assert_int_eq(list->get_count(list), 0);
+}
+END_TEST
+
+/*******************************************************************************
+ * find
+ */
+
+static bool match_a_b(void *item, void *a, void *b)
+{
+       ck_assert(a == (void*)1);
+       ck_assert(b == (void*)2);
+       return item == a || item == b;
+}
+
+START_TEST(test_find)
+{
+       void *a = (void*)1, *b = (void*)2;
+
+       ck_assert(list->find_first(list, NULL, &a) == NOT_FOUND);
+       ck_assert(list->find_last(list, NULL, &a) == NOT_FOUND);
+       list->insert_last(list, a);
+       ck_assert(list->find_first(list, NULL, &a) == SUCCESS);
+       ck_assert(list->find_first(list, NULL, &b) == NOT_FOUND);
+       ck_assert(list->find_last(list, NULL, &a) == SUCCESS);
+       ck_assert(list->find_last(list, NULL, &b) == NOT_FOUND);
+       list->insert_last(list, b);
+       ck_assert(list->find_first(list, NULL, &a) == SUCCESS);
+       ck_assert(list->find_first(list, NULL, &b) == SUCCESS);
+       ck_assert(list->find_last(list, NULL, &a) == SUCCESS);
+       ck_assert(list->find_last(list, NULL, &b) == SUCCESS);
+
+       ck_assert(list->find_first(list, NULL, NULL) == NOT_FOUND);
+       ck_assert(list->find_last(list, NULL, NULL) == NOT_FOUND);
+}
+END_TEST
+
+START_TEST(test_find_callback)
+{
+       void *a = (void*)1, *b = (void*)2, *x = NULL;
+
+       ck_assert(list->find_first(list, (linked_list_match_t)match_a_b, &x, a, b) == NOT_FOUND);
+       list->insert_last(list, a);
+       ck_assert(list->find_first(list, (linked_list_match_t)match_a, NULL, a) == SUCCESS);
+       x = NULL;
+       ck_assert(list->find_first(list, (linked_list_match_t)match_a, &x, a) == SUCCESS);
+       ck_assert(a == x);
+       ck_assert(list->find_first(list, (linked_list_match_t)match_b, &x, b) == NOT_FOUND);
+       ck_assert(a == x);
+       x = NULL;
+       ck_assert(list->find_first(list, (linked_list_match_t)match_a_b, &x, a, b) == SUCCESS);
+       ck_assert(a == x);
+
+       ck_assert(list->find_last(list, (linked_list_match_t)match_a, NULL, a) == SUCCESS);
+       x = NULL;
+       ck_assert(list->find_last(list, (linked_list_match_t)match_a, &x, a) == SUCCESS);
+       ck_assert(a == x);
+       ck_assert(list->find_last(list, (linked_list_match_t)match_b, &x, b) == NOT_FOUND);
+       ck_assert(a == x);
+       x = NULL;
+       ck_assert(list->find_last(list, (linked_list_match_t)match_a_b, &x, a, b) == SUCCESS);
+       ck_assert(a == x);
+
+       list->insert_last(list, b);
+       ck_assert(list->find_first(list, (linked_list_match_t)match_a, &x, a) == SUCCESS);
+       ck_assert(a == x);
+       ck_assert(list->find_first(list, (linked_list_match_t)match_b, &x, b) == SUCCESS);
+       ck_assert(b == x);
+       ck_assert(list->find_last(list, (linked_list_match_t)match_a, &x, a) == SUCCESS);
+       ck_assert(a == x);
+       ck_assert(list->find_last(list, (linked_list_match_t)match_b, &x, b) == SUCCESS);
+       ck_assert(b == x);
+       x = NULL;
+       ck_assert(list->find_first(list, (linked_list_match_t)match_a_b, &x, a, b) == SUCCESS);
+       ck_assert(a == x);
+       x = NULL;
+       ck_assert(list->find_last(list, (linked_list_match_t)match_a_b, &x, a, b) == SUCCESS);
+       ck_assert(b == x);
+}
+END_TEST
+
+/*******************************************************************************
+ * invoke
+ */
+
+typedef struct invoke_t invoke_t;
+
+struct invoke_t {
+       int val;
+       void (*invoke)(invoke_t *item, void *a, void *b, void *c, void *d, int *sum);
+};
+
+static void invoke(intptr_t item, void *a, void *b, void *c, void *d, int *sum)
+{
+       ck_assert(a == (void*)1);
+       ck_assert(b == (void*)2);
+       ck_assert(c == (void*)3);
+       ck_assert(d == (void*)4);
+       *sum += item;
+}
+
+static void invoke_offset(invoke_t *item, void *a, void *b, void *c, void *d, int *sum)
+{
+       invoke(item->val, a, b, c, d, sum);
+}
+
+START_TEST(test_invoke_function)
+{
+       int sum = 0;
+
+       list->insert_last(list, (void*)1);
+       list->insert_last(list, (void*)2);
+       list->insert_last(list, (void*)3);
+       list->insert_last(list, (void*)4);
+       list->insert_last(list, (void*)5);
+       list->invoke_function(list, (linked_list_invoke_t)invoke, 1, 2, 3, 4, &sum);
+       ck_assert_int_eq(sum, 15);
+}
+END_TEST
+
+START_TEST(test_invoke_offset)
+{
+       invoke_t items[] = {
+               { .val = 1, .invoke = invoke_offset, },
+               { .val = 2, .invoke = invoke_offset, },
+               { .val = 3, .invoke = invoke_offset, },
+               { .val = 4, .invoke = invoke_offset, },
+               { .val = 5, .invoke = invoke_offset, },
+       };
+       int i, sum = 0;
+
+       for (i = 0; i < countof(items); i++)
+       {
+               list->insert_last(list, &items[i]);
+       }
+       list->invoke_offset(list, offsetof(invoke_t, invoke), 1, 2, 3, 4, &sum);
+       ck_assert_int_eq(sum, 15);
+}
+END_TEST
+
+/*******************************************************************************
+ * clone
+ */
+
+typedef struct clone_t clone_t;
+
+struct clone_t {
+       void *val;
+       void *(*clone)(clone_t *item);
+};
+
+static void *clone(void *item)
+{
+       return item;
+}
+
+static void *clone_offset(clone_t *item)
+{
+       return clone(item->val);
+}
+
+static void test_clone(linked_list_t *list)
+{
+       intptr_t x;
+       int round = 1;
+
+       ck_assert_int_eq(list->get_count(list), 5);
+       while (list->remove_first(list, (void*)&x) == SUCCESS)
+       {
+               ck_assert_int_eq(round, x);
+               round++;
+       }
+       ck_assert_int_eq(round, 6);
+}
+
+START_TEST(test_clone_function)
+{
+       linked_list_t *other;
+
+       list->insert_last(list, (void*)1);
+       list->insert_last(list, (void*)2);
+       list->insert_last(list, (void*)3);
+       list->insert_last(list, (void*)4);
+       list->insert_last(list, (void*)5);
+
+       other = list->clone_function(list, clone);
+       test_clone(other);
+       other->destroy(other);
+}
+END_TEST
+
+START_TEST(test_clone_offset)
+{
+       linked_list_t *other;
+       clone_t items[] = {
+               { .val = (void*)1, .clone = clone_offset, },
+               { .val = (void*)2, .clone = clone_offset, },
+               { .val = (void*)3, .clone = clone_offset, },
+               { .val = (void*)4, .clone = clone_offset, },
+               { .val = (void*)5, .clone = clone_offset, },
+       };
+       int i;
+
+       for (i = 0; i < countof(items); i++)
+       {
+               list->insert_last(list, &items[i]);
+       }
+       other = list->clone_offset(list, offsetof(clone_t, clone));
+       test_clone(other);
+       other->destroy(other);
+}
+END_TEST
+
+Suite *linked_list_suite_create()
+{
+       Suite *s;
+       TCase *tc;
+
+       s = suite_create("linked list");
+
+       tc = tcase_create("insert/get");
+       tcase_add_checked_fixture(tc, setup_list, teardown_list);
+       tcase_add_test(tc, test_insert_first);
+       tcase_add_test(tc, test_insert_last);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("remove");
+       tcase_add_checked_fixture(tc, setup_list, teardown_list);
+       tcase_add_test(tc, test_remove_first);
+       tcase_add_test(tc, test_remove_last);
+       tcase_add_test(tc, test_remove);
+       tcase_add_test(tc, test_remove_callback);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("find");
+       tcase_add_checked_fixture(tc, setup_list, teardown_list);
+       tcase_add_test(tc, test_find);
+       tcase_add_test(tc, test_find_callback);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("invoke");
+       tcase_add_checked_fixture(tc, setup_list, teardown_list);
+       tcase_add_test(tc, test_invoke_function);
+       tcase_add_test(tc, test_invoke_offset);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("clone");
+       tcase_add_checked_fixture(tc, setup_list, teardown_list);
+       tcase_add_test(tc, test_clone_function);
+       tcase_add_test(tc, test_clone_offset);
+       suite_add_tcase(s, tc);
+
+       return s;
+}
diff --git a/src/libstrongswan/tests/test_linked_list_enumerator.c b/src/libstrongswan/tests/test_linked_list_enumerator.c
new file mode 100644 (file)
index 0000000..338a382
--- /dev/null
@@ -0,0 +1,381 @@
+/*
+ * Copyright (C) 2013 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#include "test_suite.h"
+
+#include <collections/linked_list.h>
+
+/*******************************************************************************
+ * test fixture
+ */
+
+static linked_list_t *list;
+
+START_SETUP(setup_list)
+{
+       list = linked_list_create_with_items((void*)1, (void*)2, (void*)3, (void*)4,
+                                                                        (void*)5, NULL);
+       ck_assert_int_eq(list->get_count(list), 5);
+}
+END_SETUP
+
+START_TEARDOWN(teardown_list)
+{
+       list->destroy(list);
+}
+END_TEARDOWN
+
+/*******************************************************************************
+ * enumeration
+ */
+
+START_TEST(test_enumerate)
+{
+       enumerator_t *enumerator;
+       intptr_t x;
+       int round;
+
+       round = 1;
+       enumerator = list->create_enumerator(list);
+       while (enumerator->enumerate(enumerator, &x))
+       {
+               ck_assert_int_eq(round, x);
+               round++;
+       }
+       ck_assert_int_eq(round, 6);
+       enumerator->destroy(enumerator);
+}
+END_TEST
+
+START_TEST(test_reset_enumerator)
+{
+       enumerator_t *enumerator;
+       intptr_t x;
+       int round;
+
+       enumerator = list->create_enumerator(list);
+       while (enumerator->enumerate(enumerator, &x))
+       {
+       }
+       list->reset_enumerator(list, enumerator);
+       round = 1;
+       while (enumerator->enumerate(enumerator, &x))
+       {
+               ck_assert_int_eq(round, x);
+               round++;
+       }
+       ck_assert_int_eq(round, 6);
+       enumerator->destroy(enumerator);
+}
+END_TEST
+
+START_TEST(test_has_more_empty)
+{
+       enumerator_t *enumerator;
+       intptr_t x;
+
+       list->destroy(list);
+       list = linked_list_create();
+       enumerator = list->create_enumerator(list);
+       ck_assert(!list->has_more(list, enumerator));
+       ck_assert(!enumerator->enumerate(enumerator, &x));
+       ck_assert(!list->has_more(list, enumerator));
+       enumerator->destroy(enumerator);
+}
+END_TEST
+
+START_TEST(test_has_more)
+{
+       enumerator_t *enumerator;
+       intptr_t x;
+       int round;
+
+       round = 1;
+       enumerator = list->create_enumerator(list);
+       while (enumerator->enumerate(enumerator, &x))
+       {
+               ck_assert_int_eq(round, x);
+               round++;
+               if (x == 2)
+               {
+                       break;
+               }
+       }
+       ck_assert(list->has_more(list, enumerator));
+       while (enumerator->enumerate(enumerator, &x))
+       {
+               ck_assert_int_eq(round, x);
+               round++;
+       }
+       ck_assert(!list->has_more(list, enumerator));
+       ck_assert_int_eq(round, 6);
+       enumerator->destroy(enumerator);
+}
+END_TEST
+
+/*******************************************************************************
+ * insert before
+ */
+
+START_TEST(test_insert_before)
+{
+       enumerator_t *enumerator;
+       intptr_t x;
+       int round;
+
+       round = 1;
+       enumerator = list->create_enumerator(list);
+       while (enumerator->enumerate(enumerator, &x))
+       {
+               ck_assert_int_eq(round, x);
+               round++;
+               if (x == _i)
+               {
+                       list->insert_before(list, enumerator, (void*)6);
+               }
+       }
+       ck_assert_int_eq(list->get_count(list), 6);
+       list->reset_enumerator(list, enumerator);
+       round = 1;
+       while (enumerator->enumerate(enumerator, &x))
+       {
+               if (round == _i && x != _i)
+               {
+                       ck_assert_int_eq(6, x);
+               }
+               else
+               {
+                       ck_assert_int_eq(round, x);
+                       round++;
+               }
+       }
+       enumerator->destroy(enumerator);
+}
+END_TEST
+
+START_TEST(test_insert_before_ends)
+{
+       enumerator_t *enumerator;
+       intptr_t x;
+       int round;
+
+       enumerator = list->create_enumerator(list);
+       list->insert_before(list, enumerator, (void*)0);
+       ck_assert_int_eq(list->get_count(list), 6);
+       ck_assert(list->get_first(list, (void*)&x) == SUCCESS);
+       ck_assert_int_eq(x, 0);
+       round = 0;
+       while (enumerator->enumerate(enumerator, &x))
+       {
+               ck_assert_int_eq(round, x);
+               round++;
+       }
+       list->insert_before(list, enumerator, (void*)6);
+       ck_assert_int_eq(list->get_count(list), 7);
+       ck_assert(list->get_last(list, (void*)&x) == SUCCESS);
+       ck_assert_int_eq(x, 6);
+       ck_assert(!list->has_more(list, enumerator));
+       ck_assert(!enumerator->enumerate(enumerator, &x));
+       enumerator->destroy(enumerator);
+}
+END_TEST
+
+START_TEST(test_insert_before_empty)
+{
+       enumerator_t *enumerator;
+       intptr_t x;
+
+       list->destroy(list);
+       list = linked_list_create();
+       enumerator = list->create_enumerator(list);
+       list->insert_before(list, enumerator, (void*)1);
+       ck_assert_int_eq(list->get_count(list), 1);
+       ck_assert(list->get_first(list, (void*)&x) == SUCCESS);
+       ck_assert_int_eq(x, 1);
+       ck_assert(list->get_last(list, (void*)&x) == SUCCESS);
+       ck_assert_int_eq(x, 1);
+       ck_assert(list->has_more(list, enumerator));
+       ck_assert(enumerator->enumerate(enumerator, &x));
+       ck_assert_int_eq(x, 1);
+       ck_assert(!list->has_more(list, enumerator));
+       enumerator->destroy(enumerator);
+}
+END_TEST
+
+/*******************************************************************************
+ * replace / remove_at
+ */
+
+START_TEST(test_replace)
+{
+       enumerator_t *enumerator;
+       intptr_t x, y;
+       int round;
+
+       round = 1;
+       enumerator = list->create_enumerator(list);
+       while (enumerator->enumerate(enumerator, &x))
+       {
+               ck_assert_int_eq(round, x);
+               y = (intptr_t)list->replace(list, enumerator, (void*)(intptr_t)(6 - round));
+               ck_assert_int_eq(round, y);
+               round++;
+       }
+       list->reset_enumerator(list, enumerator);
+       round = 5;
+       while (enumerator->enumerate(enumerator, &x))
+       {
+               ck_assert_int_eq(round, x);
+               round--;
+       }
+       enumerator->destroy(enumerator);
+}
+END_TEST
+
+START_TEST(test_replace_first)
+{
+       enumerator_t *enumerator;
+       intptr_t x;
+
+       enumerator = list->create_enumerator(list);
+       x = (intptr_t)list->replace(list, enumerator, (void*)6);
+       ck_assert_int_eq(x, 0);
+       ck_assert(enumerator->enumerate(enumerator, &x));
+       ck_assert_int_eq(x, 1);
+       enumerator->destroy(enumerator);
+}
+END_TEST
+
+START_TEST(test_remove_at)
+{
+       enumerator_t *enumerator;
+       intptr_t x;
+       int round;
+
+       round = 1;
+       enumerator = list->create_enumerator(list);
+       while (enumerator->enumerate(enumerator, &x))
+       {
+               ck_assert_int_eq(round, x);
+               if (round == 2)
+               {
+                       list->remove_at(list, enumerator);
+               }
+               round++;
+       }
+       ck_assert_int_eq(list->get_count(list), 4);
+       list->reset_enumerator(list, enumerator);
+       round = 1;
+       while (enumerator->enumerate(enumerator, &x))
+       {
+               if (round == 2)
+               {       /* skip removed item */
+                       round++;
+               }
+               ck_assert_int_eq(round, x);
+               round++;
+       }
+       enumerator->destroy(enumerator);
+}
+END_TEST
+
+START_TEST(test_remove_at_ends)
+{
+       enumerator_t *enumerator;
+       intptr_t x;
+
+       enumerator = list->create_enumerator(list);
+       list->remove_at(list, enumerator);
+       ck_assert_int_eq(list->get_count(list), 5);
+       ck_assert(list->get_first(list, (void*)&x) == SUCCESS);
+       ck_assert_int_eq(x, 1);
+       while (enumerator->enumerate(enumerator, &x))
+       {
+       }
+       list->remove_at(list, enumerator);
+       ck_assert_int_eq(list->get_count(list), 5);
+       ck_assert(list->get_last(list, (void*)&x) == SUCCESS);
+       ck_assert_int_eq(x, 5);
+       enumerator->destroy(enumerator);
+}
+END_TEST
+
+/*******************************************************************************
+ * create list from enumerator
+ */
+
+START_TEST(test_create_from_enumerator)
+{
+       enumerator_t *enumerator, *enumerator_other;
+       linked_list_t *other;
+       intptr_t x, y;
+       int count = 0;
+
+       enumerator = list->create_enumerator(list);
+       other = linked_list_create_from_enumerator(enumerator);
+       ck_assert_int_eq(other->get_count(list), 5);
+
+       enumerator = list->create_enumerator(list);
+       enumerator_other = other->create_enumerator(other);
+       while (enumerator->enumerate(enumerator, &x) &&
+                  enumerator_other->enumerate(enumerator_other, &y))
+       {
+               ck_assert_int_eq(x, y);
+               count++;
+       }
+       ck_assert_int_eq(count, 5);
+       enumerator_other->destroy(enumerator_other);
+       enumerator->destroy(enumerator);
+       other->destroy(other);
+}
+END_TEST
+
+Suite *linked_list_enumerator_suite_create()
+{
+       Suite *s;
+       TCase *tc;
+
+       s = suite_create("linked list and enumerators");
+
+       tc = tcase_create("enumerate");
+       tcase_add_checked_fixture(tc, setup_list, teardown_list);
+       tcase_add_test(tc, test_enumerate);
+       tcase_add_test(tc, test_reset_enumerator);
+       tcase_add_test(tc, test_has_more_empty);
+       tcase_add_test(tc, test_has_more);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("insert_before()");
+       tcase_add_checked_fixture(tc, setup_list, teardown_list);
+       tcase_add_loop_test(tc, test_insert_before, 1, 5);
+       tcase_add_test(tc, test_insert_before_ends);
+       tcase_add_test(tc, test_insert_before_empty);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("modify");
+       tcase_add_checked_fixture(tc, setup_list, teardown_list);
+       tcase_add_test(tc, test_replace);
+       tcase_add_test(tc, test_replace_first);
+       tcase_add_test(tc, test_remove_at);
+       tcase_add_test(tc, test_remove_at_ends);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("create_from_enumerator");
+       tcase_add_checked_fixture(tc, setup_list, teardown_list);
+       tcase_add_test(tc, test_create_from_enumerator);
+       suite_add_tcase(s, tc);
+
+       return s;
+}
diff --git a/src/libstrongswan/tests/test_runner.c b/src/libstrongswan/tests/test_runner.c
new file mode 100644 (file)
index 0000000..2cce42b
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2013 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#include <unistd.h>
+
+#include "test_runner.h"
+
+#include <library.h>
+
+int main()
+{
+       SRunner *sr;
+       int nf;
+
+       /* test cases are forked and there is no cleanup, so disable leak detective.
+        * if test_suite.h is included leak detective is enabled in test cases */
+       setenv("LEAK_DETECTIVE_DISABLE", "1", 1);
+       /* redirect all output to stderr (to redirect make's stdout to /dev/null) */
+       dup2(2, 1);
+
+       library_init(NULL);
+
+       sr = srunner_create(NULL);
+       srunner_add_suite(sr, bio_reader_suite_create());
+       srunner_add_suite(sr, bio_writer_suite_create());
+       srunner_add_suite(sr, chunk_suite_create());
+       srunner_add_suite(sr, enum_suite_create());
+       srunner_add_suite(sr, enumerator_suite_create());
+       srunner_add_suite(sr, linked_list_suite_create());
+       srunner_add_suite(sr, linked_list_enumerator_suite_create());
+       srunner_add_suite(sr, hashtable_suite_create());
+       srunner_add_suite(sr, identification_suite_create());
+       srunner_add_suite(sr, threading_suite_create());
+       srunner_add_suite(sr, utils_suite_create());
+
+       srunner_run_all(sr, CK_NORMAL);
+       nf = srunner_ntests_failed(sr);
+
+       srunner_free(sr);
+       library_deinit();
+
+       return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/libstrongswan/tests/test_runner.h b/src/libstrongswan/tests/test_runner.h
new file mode 100644 (file)
index 0000000..1fc9851
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2013 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef TEST_RUNNER_H_
+#define TEST_RUNNER_H_
+
+#include <check.h>
+
+Suite *bio_reader_suite_create();
+Suite *bio_writer_suite_create();
+Suite *chunk_suite_create();
+Suite *enum_suite_create();
+Suite *enumerator_suite_create();
+Suite *linked_list_suite_create();
+Suite *linked_list_enumerator_suite_create();
+Suite *hashtable_suite_create();
+Suite *identification_suite_create();
+Suite *threading_suite_create();
+Suite *utils_suite_create();
+
+#endif /** TEST_RUNNER_H_ */
diff --git a/src/libstrongswan/tests/test_suite.h b/src/libstrongswan/tests/test_suite.h
new file mode 100644 (file)
index 0000000..edf16f1
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2013 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef TEST_UTILS_H_
+#define TEST_UTILS_H_
+
+#include <check.h>
+#include <library.h>
+#include <utils/debug.h>
+
+/**
+ * Used to mark test cases that use test fixtures.
+ */
+#define UNIT_TEST_FIXTURE_USED "UNIT_TEST_FIXTURE_USED"
+
+/**
+ * Check for memory leaks and fail if any are encountered.
+ */
+#define CHECK_FOR_LEAKS() do \
+{ \
+       if (lib->leak_detective->leaks(lib->leak_detective)) { \
+               lib->leak_detective->report(lib->leak_detective, TRUE); \
+       } \
+       ck_assert_int_eq(lib->leak_detective->leaks(lib->leak_detective), 0); \
+} \
+while(0)
+
+/**
+ * Extended versions of the START|END_TEST macros that use leak detective.
+ *
+ * Since each test case runs in its own fork of the test runner the stuff
+ * allocated before the test starts is not freed, so leak detective is disabled
+ * by default to prevent false positives.  By enabling it right when the test
+ * starts we at least capture leaks created by the tested objects/functions and
+ * the test case itself.  This allows writing test cases for cleanup functions.
+ *
+ * To define test fixture with possibly allocated/destroyed memory that is
+ * allocated/freed in a test case use the START|END_SETUP|TEARDOWN macros.
+ */
+#undef START_TEST
+#define START_TEST(name) \
+static void name (int _i CK_ATTRIBUTE_UNUSED) \
+{ \
+       tcase_fn_start(""#name, __FILE__, __LINE__); \
+       dbg_default_set_level(LEVEL_SILENT); \
+       lib->leak_detective->set_state(lib->leak_detective, TRUE);
+
+#undef END_TEST
+#define END_TEST \
+       if (!lib->get(lib, UNIT_TEST_FIXTURE_USED)) \
+       { \
+               CHECK_FOR_LEAKS(); \
+       } \
+}
+
+/**
+ * Define a function to setup a test fixture that can be used with the above
+ * macros.
+ */
+#define START_SETUP(name) \
+static void name() \
+{ \
+       lib->set(lib, UNIT_TEST_FIXTURE_USED, (void*)TRUE); \
+       lib->leak_detective->set_state(lib->leak_detective, TRUE);
+
+/**
+ * End a setup function
+ */
+#define END_SETUP }
+
+/**
+ * Define a function to teardown a test fixture that can be used with the above
+ * macros.
+ */
+#define START_TEARDOWN(name) \
+static void name() \
+{
+
+/**
+ * End a teardown function
+ */
+#define END_TEARDOWN \
+       if (lib->get(lib, UNIT_TEST_FIXTURE_USED)) \
+       { \
+               CHECK_FOR_LEAKS(); \
+       } \
+}
+
+#endif /** TEST_UTILS_H_ */
similarity index 67%
rename from src/libcharon/plugins/unit_tester/tests/test_mutex.c
rename to src/libstrongswan/tests/test_threading.c
index 77085cb2f1108871f6a911723a9dcd817383512c..0c768b3e2ac6409557dcf696a2e5003952b0fc5d 100644 (file)
@@ -1,4 +1,5 @@
 /*
+ * Copyright (C) 2013 Tobias Brunner
  * Copyright (C) 2008 Martin Willi
  * Hochschule fuer Technik Rapperswil
  *
  * for more details.
  */
 
-#include <library.h>
-#include <threading/mutex.h>
-
-#include <unistd.h>
 #include <sched.h>
 #include <pthread.h>
 
+#include "test_suite.h"
 
-static mutex_t *mutex;
+#include <threading/mutex.h>
 
-static int locked = 0;
+/*******************************************************************************
+ * recursive mutex test
+ */
 
-static bool failed = FALSE;
+#define THREADS 20
+
+static mutex_t *mutex;
 
-static pthread_barrier_t barrier;
+static pthread_barrier_t mutex_barrier;
 
-static void* run(void* null)
+static int mutex_locked = 0;
+
+static void *mutex_run(void *data)
 {
        int i;
 
        /* wait for all threads before getting in action */
-       pthread_barrier_wait(&barrier);
+       pthread_barrier_wait(&mutex_barrier);
 
        for (i = 0; i < 100; i++)
        {
                mutex->lock(mutex);
                mutex->lock(mutex);
                mutex->lock(mutex);
-               locked++;
+               mutex_locked++;
                sched_yield();
-               if (locked > 1)
+               if (mutex_locked > 1)
                {
-                       failed = TRUE;
+                       fail("two threads locked the mutex concurrently");
                }
-               locked--;
+               mutex_locked--;
                mutex->unlock(mutex);
                mutex->unlock(mutex);
                mutex->unlock(mutex);
@@ -55,15 +59,10 @@ static void* run(void* null)
        return NULL;
 }
 
-#define THREADS 20
-
-/*******************************************************************************
- * mutex test
- ******************************************************************************/
-bool test_mutex()
+START_TEST(test_mutex)
 {
-       int i;
        pthread_t threads[THREADS];
+       int i;
 
        mutex = mutex_create(MUTEX_TYPE_RECURSIVE);
 
@@ -81,20 +80,31 @@ bool test_mutex()
                mutex->unlock(mutex);
        }
 
-       pthread_barrier_init(&barrier, NULL, THREADS);
-
+       pthread_barrier_init(&mutex_barrier, NULL, THREADS);
        for (i = 0; i < THREADS; i++)
        {
-               pthread_create(&threads[i], NULL, run, NULL);
+               pthread_create(&threads[i], NULL, mutex_run, NULL);
        }
        for (i = 0; i < THREADS; i++)
        {
                pthread_join(threads[i], NULL);
        }
-       pthread_barrier_destroy(&barrier);
+       pthread_barrier_destroy(&mutex_barrier);
 
        mutex->destroy(mutex);
-
-       return !failed;
 }
+END_TEST
 
+Suite *threading_suite_create()
+{
+       Suite *s;
+       TCase *tc;
+
+       s = suite_create("threading");
+
+       tc = tcase_create("recursive mutex");
+       tcase_add_test(tc, test_mutex);
+       suite_add_tcase(s, tc);
+
+       return s;
+}
diff --git a/src/libstrongswan/tests/test_utils.c b/src/libstrongswan/tests/test_utils.c
new file mode 100644 (file)
index 0000000..f7cb605
--- /dev/null
@@ -0,0 +1,432 @@
+/*
+ * Copyright (C) 2013 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#include "test_suite.h"
+
+#include <library.h>
+#include <utils/utils.h>
+
+/*******************************************************************************
+ * object storage on lib
+ */
+
+START_TEST(test_objects)
+{
+       char *k1 = "key1", *k2 = "key2";
+       char *v1 = "val1", *val;
+
+       ck_assert(lib->get(lib, k1) == NULL);
+
+       ck_assert(lib->set(lib, k1, v1));
+       ck_assert(!lib->set(lib, k1, v1));
+
+       val = lib->get(lib, k1);
+       ck_assert(val != NULL);
+       ck_assert(streq(val, v1));
+
+       ck_assert(lib->set(lib, k1, NULL));
+       ck_assert(!lib->set(lib, k2, NULL));
+
+       ck_assert(lib->get(lib, k1) == NULL);
+}
+END_TEST
+
+/*******************************************************************************
+ * test return_... functions
+ */
+
+START_TEST(test_return_functions)
+{
+       ck_assert(return_null() == NULL);
+       ck_assert(return_null("asdf", 5, NULL, 1, "qwer") == NULL);
+
+       ck_assert(return_true() == TRUE);
+       ck_assert(return_true("asdf", 5, NULL, 1, "qwer") == TRUE);
+
+       ck_assert(return_false() == FALSE);
+       ck_assert(return_false("asdf", 5, NULL, 1, "qwer") == FALSE);
+
+       ck_assert(return_failed() == FAILED);
+       ck_assert(return_failed("asdf", 5, NULL, 1, "qwer") == FAILED);
+
+       ck_assert(return_success() == SUCCESS);
+       ck_assert(return_success("asdf", 5, NULL, 1, "qwer") == SUCCESS);
+
+       /* just make sure this works */
+       nop();
+       nop("asdf", 5, NULL, 1, "qwer");
+}
+END_TEST
+
+/*******************************************************************************
+ * timeval_add_ms
+ */
+
+START_TEST(test_timeval_add_ms)
+{
+       timeval_t tv;
+
+       tv.tv_sec = 0;
+       tv.tv_usec = 0;
+       timeval_add_ms(&tv, 0);
+       ck_assert_int_eq(tv.tv_sec, 0);
+       ck_assert_int_eq(tv.tv_usec, 0);
+
+       timeval_add_ms(&tv, 1);
+       ck_assert_int_eq(tv.tv_sec, 0);
+       ck_assert_int_eq(tv.tv_usec, 1000);
+
+       timeval_add_ms(&tv, 0);
+       ck_assert_int_eq(tv.tv_sec, 0);
+       ck_assert_int_eq(tv.tv_usec, 1000);
+
+       timeval_add_ms(&tv, 999);
+       ck_assert_int_eq(tv.tv_sec, 1);
+       ck_assert_int_eq(tv.tv_usec, 0);
+
+       timeval_add_ms(&tv, 0);
+       ck_assert_int_eq(tv.tv_sec, 1);
+       ck_assert_int_eq(tv.tv_usec, 0);
+
+       timeval_add_ms(&tv, 1000);
+       ck_assert_int_eq(tv.tv_sec, 2);
+       ck_assert_int_eq(tv.tv_usec, 0);
+
+       timeval_add_ms(&tv, 1500);
+       ck_assert_int_eq(tv.tv_sec, 3);
+       ck_assert_int_eq(tv.tv_usec, 500000);
+}
+END_TEST
+
+/*******************************************************************************
+ * htoun/untoh
+ */
+
+START_TEST(test_htoun)
+{
+       chunk_t net64, expected;
+       u_int16_t host16 = 513;
+       u_int32_t net16 = 0, host32 = 67305985;
+       u_int64_t net32 = 0, host64 = 578437695752307201;
+
+       net64 = chunk_alloca(16);
+       memset(net64.ptr, 0, net64.len);
+
+       expected = chunk_from_chars(0x00, 0x02, 0x01, 0x00);
+       htoun16((char*)&net16 + 1, host16);
+       ck_assert(chunk_equals(expected, chunk_from_thing(net16)));
+
+       expected = chunk_from_chars(0x00, 0x00, 0x04, 0x03, 0x02, 0x01, 0x00, 0x00);
+       htoun32((u_int16_t*)&net32 + 1, host32);
+       ck_assert(chunk_equals(expected, chunk_from_thing(net32)));
+
+       expected = chunk_from_chars(0x00, 0x00, 0x00, 0x00,
+                                                               0x08, 0x07, 0x06, 0x05,
+                                                               0x04, 0x03, 0x02, 0x01,
+                                                               0x00, 0x00, 0x00, 0x00);
+       htoun64((u_int32_t*)net64.ptr + 1, host64);
+       ck_assert(chunk_equals(expected, net64));
+}
+END_TEST
+
+START_TEST(test_untoh)
+{
+       chunk_t net;
+       u_int16_t host16;
+       u_int32_t host32;
+       u_int64_t host64;
+
+       net = chunk_from_chars(0x00, 0x02, 0x01, 0x00);
+       host16 = untoh16(net.ptr + 1);
+       ck_assert(host16 == 513);
+
+       net = chunk_from_chars(0x00, 0x00, 0x04, 0x03, 0x02, 0x01, 0x00, 0x00);
+       host32 = untoh32(net.ptr + 2);
+       ck_assert(host32 == 67305985);
+
+       net = chunk_from_chars(0x00, 0x00, 0x00, 0x00, 0x08, 0x07, 0x06, 0x05,
+                                                  0x04, 0x03, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00);
+       host64 = untoh64(net.ptr + 4);
+       ck_assert(host64 == 578437695752307201);
+}
+END_TEST
+
+/*******************************************************************************
+ * memxor
+ */
+
+static void do_memxor(chunk_t a, chunk_t b, chunk_t exp)
+{
+       chunk_t dst;
+
+       dst = chunk_clonea(a);
+       dst.len = b.len;
+       memxor(dst.ptr, b.ptr, b.len);
+       ck_assert(chunk_equals(dst, exp));
+}
+
+START_TEST(test_memxor)
+{
+       chunk_t a, b, dst;
+       int i;
+
+       a = chunk_alloca(64);
+       memset(a.ptr, 0, a.len);
+       b = chunk_alloca(64);
+       for (i = 0; i < 64; i++)
+       {
+               b.ptr[i] = i;
+               b.len = i;
+               do_memxor(a, b, b);
+       }
+       b.len = 64;
+       do_memxor(a, b, b);
+
+       dst = chunk_clonea(a);
+       memxor(dst.ptr, b.ptr, b.len);
+       ck_assert(chunk_equals(dst, b));
+
+       memxor(dst.ptr, b.ptr, 0);
+       memxor(dst.ptr, b.ptr, 1);
+       memxor(dst.ptr + 1, b.ptr + 1, 1);
+       memxor(dst.ptr + 2, b.ptr + 2, b.len - 2);
+       ck_assert(chunk_equals(dst, a));
+}
+END_TEST
+
+START_TEST(test_memxor_aligned)
+{
+       u_int64_t a = 0, b = 0;
+       chunk_t ca, cb;
+       int i;
+
+       ca = chunk_from_thing(a);
+       cb = chunk_from_thing(b);
+
+       for (i = 0; i < 8; i++)
+       {
+               cb.ptr[i] = i + 1;
+       }
+
+       /* 64-bit aligned */
+       memxor(ca.ptr, cb.ptr, 8);
+       ck_assert(a == b);
+       /* 32-bit aligned source */
+       a = 0;
+       memxor(ca.ptr, cb.ptr + 4, 4);
+       ck_assert(chunk_equals(ca, chunk_from_chars(0x05, 0x06, 0x07, 0x08,
+                                                                                               0x00, 0x00, 0x00, 0x00)));
+       /* 16-bit aligned source */
+       a = 0;
+       memxor(ca.ptr, cb.ptr + 2, 6);
+       ck_assert(chunk_equals(ca, chunk_from_chars(0x03, 0x04, 0x05, 0x06,
+                                                                                               0x07, 0x08, 0x00, 0x00)));
+       /* 8-bit aligned source */
+       a = 0;
+       memxor(ca.ptr, cb.ptr + 1, 7);
+       ck_assert(chunk_equals(ca, chunk_from_chars(0x02, 0x03, 0x04, 0x05,
+                                                                                               0x06, 0x07, 0x08, 0x00)));
+}
+END_TEST
+
+/*******************************************************************************
+ * memstr
+ */
+
+static struct {
+       char *haystack;
+       char *needle;
+       size_t n;
+       int offset;
+} memstr_data[] = {
+       {NULL, NULL, 0, -1},
+       {NULL, NULL, 3, -1},
+       {NULL, "abc", 0, -1},
+       {NULL, "abc", 3, -1},
+       {"", "", 0, -1},
+       {"abc", NULL, 3, -1},
+       {"abc", "", 3, -1},
+       {"abc", "abc", 3, 0},
+       {" abc", "abc", 4, 1},
+       {" abc", "abc", 3, -1},
+       {"abcabc", "abc", 6, 0},
+       {" abc ", "abc", 5, 1},
+};
+
+START_TEST(test_memstr)
+{
+       char *ret;
+
+       ret = memstr(memstr_data[_i].haystack, memstr_data[_i].needle, memstr_data[_i].n);
+       if (memstr_data[_i].offset >= 0)
+       {
+               ck_assert(ret == memstr_data[_i].haystack + memstr_data[_i].offset);
+       }
+       else
+       {
+               ck_assert(ret == NULL);
+       }
+}
+END_TEST
+
+/*******************************************************************************
+ * translate
+ */
+
+static struct {
+       char *in;
+       char *from;
+       char *to;
+       char *out;
+} translate_data[] = {
+       {NULL, "", "", NULL},
+       {"abc", "", "", "abc"},
+       {"abc", "", "x", "abc"},
+       {"abc", "x", "", "abc"},
+       {"abc", "abc", "xyz", "xyz"},
+       {"aabbcc", "abc", "xyz", "xxyyzz"},
+       {"abbaccb", "abc", "xyz", "xyyxzzy"},
+       {"abxyzc", "abc", "xyz", "xyxyzz"},
+       {"abcdef", "abc", "xyz", "xyzdef"},
+       {"aaa", "abc", "xyz", "xxx"},
+       {"abc", "aaa", "xyz", "xbc"},
+       {"abc", "abc", "xxx", "xxx"},
+};
+
+START_TEST(test_translate)
+{
+       char *str, *ret;
+
+       str = strdupnull(translate_data[_i].in);
+       ret = translate(str, translate_data[_i].from, translate_data[_i].to);
+       ck_assert(ret == str);
+       if (ret != translate_data[_i].out)
+       {
+               ck_assert_str_eq(str, translate_data[_i].out);
+       }
+       free(str);
+}
+END_TEST
+
+/*******************************************************************************
+ * time_printf_hook
+ */
+
+static struct {
+       time_t in;
+       bool utc;
+       char *out;
+} time_data[] = {
+       {UNDEFINED_TIME, FALSE, "--- -- --:--:-- ----"},
+       {UNDEFINED_TIME, TRUE , "--- -- --:--:-- UTC ----"},
+       {1, FALSE, "Jan 01 01:00:01 1970"},
+       {1, TRUE , "Jan 01 00:00:01 UTC 1970"},
+       {1341150196, FALSE, "Jul 01 15:43:16 2012"},
+       {1341150196, TRUE , "Jul 01 13:43:16 UTC 2012"},
+};
+
+START_TEST(test_time_printf_hook)
+{
+       char buf[32];
+       int len;
+
+       len = snprintf(buf, sizeof(buf), "%T", &time_data[_i].in, time_data[_i].utc);
+       ck_assert(len >= 0 && len < sizeof(buf));
+       ck_assert_str_eq(buf, time_data[_i].out);
+}
+END_TEST
+
+/*******************************************************************************
+ * time_delta_printf_hook
+ */
+
+static struct {
+       time_t a;
+       time_t b;
+       char *out;
+} time_delta_data[] = {
+       {0, 0, "0 seconds"},
+       {0, 1, "1 second"},
+       {0, -1, "1 second"},
+       {1, 0, "1 second"},
+       {0, 2, "2 seconds"},
+       {2, 0, "2 seconds"},
+       {0, 60, "60 seconds"},
+       {0, 120, "120 seconds"},
+       {0, 121, "2 minutes"},
+       {0, 3600, "60 minutes"},
+       {0, 7200, "120 minutes"},
+       {0, 7201, "2 hours"},
+       {0, 86400, "24 hours"},
+       {0, 172800, "48 hours"},
+       {0, 172801, "2 days"},
+       {172801, 86400, "24 hours"},
+};
+
+START_TEST(test_time_delta_printf_hook)
+{
+       char buf[16];
+       int len;
+
+       len = snprintf(buf, sizeof(buf), "%V", &time_delta_data[_i].a, &time_delta_data[_i].b);
+       ck_assert(len >= 0 && len < sizeof(buf));
+       ck_assert_str_eq(buf, time_delta_data[_i].out);
+}
+END_TEST
+
+Suite *utils_suite_create()
+{
+       Suite *s;
+       TCase *tc;
+
+       s = suite_create("utils");
+
+       tc = tcase_create("objects");
+       tcase_add_test(tc, test_objects);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("return functions");
+       tcase_add_test(tc, test_return_functions);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("timeval_add_ms");
+       tcase_add_test(tc, test_timeval_add_ms);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("htoun,untoh");
+       tcase_add_test(tc, test_htoun);
+       tcase_add_test(tc, test_untoh);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("memxor");
+       tcase_add_test(tc, test_memxor);
+       tcase_add_test(tc, test_memxor_aligned);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("memstr");
+       tcase_add_loop_test(tc, test_memstr, 0, countof(memstr_data));
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("translate");
+       tcase_add_loop_test(tc, test_translate, 0, countof(translate_data));
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("printf_hooks");
+       tcase_add_loop_test(tc, test_time_printf_hook, 0, countof(time_data));
+       tcase_add_loop_test(tc, test_time_delta_printf_hook, 0, countof(time_delta_data));
+       suite_add_tcase(s, tc);
+
+       return s;
+}
index d7f1c31d984b972596f0b2e014789739601f45f7..61c7bd5a515c0926d05ab173053c5bb7d5cce887 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008-2009 Tobias Brunner
+ * Copyright (C) 2008-2013 Tobias Brunner
  * Copyright (C) 2005-2006 Martin Willi
  * Copyright (C) 2005 Jan Hutter
  * Hochschule fuer Technik Rapperswil
  */
 
 #include <stdio.h>
+#include <sys/types.h>
 #include <sys/stat.h>
+#include <fcntl.h>
 #include <unistd.h>
 #include <errno.h>
+#include <pthread.h>
 #include <ctype.h>
 
 #include "chunk.h"
 #include "debug.h"
 
-/* required for chunk_hash */
-#undef get16bits
-#if (defined(__GNUC__) && defined(__i386__))
-#define get16bits(d) (*((const u_int16_t*)(d)))
-#endif
-#if !defined (get16bits)
-#define get16bits(d) ((((u_int32_t)(((const u_int8_t*)(d))[1])) << 8)\
-                      + (u_int32_t)(((const u_int8_t*)(d))[0]) )
-#endif
-
 /**
  * Empty chunk.
  */
@@ -579,72 +572,187 @@ bool chunk_printable(chunk_t chunk, chunk_t *sane, char replace)
 }
 
 /**
- * Described in header.
- *
- * The implementation is based on Paul Hsieh's SuperFastHash:
- *      http://www.azillionmonkeys.com/qed/hash.html
+ * Helper functions for chunk_mac()
  */
-u_int32_t chunk_hash_inc(chunk_t chunk, u_int32_t hash)
+static inline u_int64_t sipget(u_char *in)
 {
-       u_char *data = chunk.ptr;
-       size_t len = chunk.len;
-       u_int32_t tmp;
-       int rem;
+       u_int64_t v = 0;
+       int i;
 
-       if (!len || data == NULL)
+       for (i = 0; i < 64; i += 8, ++in)
        {
-               return 0;
+               v |= ((u_int64_t)*in) << i;
        }
+       return v;
+}
 
-       rem = len & 3;
-       len >>= 2;
+static inline u_int64_t siprotate(u_int64_t v, int shift)
+{
+        return (v << shift) | (v >> (64 - shift));
+}
 
-       /* Main loop */
-       for (; len > 0; --len)
-       {
-               hash += get16bits(data);
-               tmp   = (get16bits(data + 2) << 11) ^ hash;
-               hash  = (hash << 16) ^ tmp;
-               data += 2 * sizeof(u_int16_t);
-               hash += hash >> 11;
-       }
+static inline void sipround(u_int64_t *v0, u_int64_t *v1, u_int64_t *v2,
+                                                       u_int64_t *v3)
+{
+       *v0 += *v1;
+       *v1 = siprotate(*v1, 13);
+       *v1 ^= *v0;
+       *v0 = siprotate(*v0, 32);
+
+       *v2 += *v3;
+       *v3 = siprotate(*v3, 16);
+       *v3 ^= *v2;
+
+       *v2 += *v1;
+       *v1 = siprotate(*v1, 17);
+       *v1 ^= *v2;
+       *v2 = siprotate(*v2, 32);
+
+       *v0 += *v3;
+       *v3 = siprotate(*v3, 21);
+       *v3 ^= *v0;
+}
 
-       /* Handle end cases */
+static inline void sipcompress(u_int64_t *v0, u_int64_t *v1, u_int64_t *v2,
+                                                          u_int64_t *v3, u_int64_t m)
+{
+       *v3 ^= m;
+       sipround(v0, v1, v2, v3);
+       sipround(v0, v1, v2, v3);
+       *v0 ^= m;
+}
+
+static inline u_int64_t siplast(size_t len, u_char *pos)
+{
+       u_int64_t b;
+       int rem = len & 7;
+
+       b = ((u_int64_t)len) << 56;
        switch (rem)
        {
+               case 7:
+                       b |= ((u_int64_t)pos[6]) << 48;
+               case 6:
+                       b |= ((u_int64_t)pos[5]) << 40;
+               case 5:
+                       b |= ((u_int64_t)pos[4]) << 32;
+               case 4:
+                       b |= ((u_int64_t)pos[3]) << 24;
                case 3:
-               {
-                       hash += get16bits(data);
-                       hash ^= hash << 16;
-                       hash ^= data[sizeof(u_int16_t)] << 18;
-                       hash += hash >> 11;
-                       break;
-               }
+                       b |= ((u_int64_t)pos[2]) << 16;
                case 2:
-               {
-                       hash += get16bits(data);
-                       hash ^= hash << 11;
-                       hash += hash >> 17;
+                       b |= ((u_int64_t)pos[1]) <<  8;
+               case 1:
+                       b |= ((u_int64_t)pos[0]);
+                       break;
+               case 0:
                        break;
+       }
+       return b;
+}
+
+/**
+ * Caculate SipHash-2-4 with an optional first block given as argument.
+ */
+static u_int64_t chunk_mac_inc(chunk_t chunk, u_char *key, u_int64_t m)
+{
+       u_int64_t v0, v1, v2, v3, k0, k1;
+       size_t len = chunk.len;
+       u_char *pos = chunk.ptr, *end;
+
+       end = chunk.ptr + len - (len % 8);
+
+       k0 = sipget(key);
+       k1 = sipget(key + 8);
+
+       v0 = k0 ^ 0x736f6d6570736575ULL;
+       v1 = k1 ^ 0x646f72616e646f6dULL;
+       v2 = k0 ^ 0x6c7967656e657261ULL;
+       v3 = k1 ^ 0x7465646279746573ULL;
+
+       if (m)
+       {
+               sipcompress(&v0, &v1, &v2, &v3, m);
+       }
+
+       /* compression with c = 2 */
+       for (; pos != end; pos += 8)
+       {
+               m = sipget(pos);
+               sipcompress(&v0, &v1, &v2, &v3, m);
+       }
+       sipcompress(&v0, &v1, &v2, &v3, siplast(len, pos));
+
+       /* finalization with d = 4 */
+       v2 ^= 0xff;
+       sipround(&v0, &v1, &v2, &v3);
+       sipround(&v0, &v1, &v2, &v3);
+       sipround(&v0, &v1, &v2, &v3);
+       sipround(&v0, &v1, &v2, &v3);
+       return v0 ^ v1 ^ v2  ^ v3;
+}
+
+/**
+ * Described in header.
+ */
+u_int64_t chunk_mac(chunk_t chunk, u_char *key)
+{
+       return chunk_mac_inc(chunk, key, 0);
+}
+
+/**
+ * Secret key allocated randomly during first use.
+ */
+static u_char key[16];
+
+/**
+ * Only allocate the key once
+ */
+static pthread_once_t key_allocated = PTHREAD_ONCE_INIT;
+
+/**
+ * Allocate a key on first use, we do this manually to avoid dependencies on
+ * plugins.
+ */
+static void allocate_key()
+{
+       ssize_t len;
+       size_t done = 0;
+       int fd;
+
+       fd = open("/dev/urandom", O_RDONLY);
+       if (fd >= 0)
+       {
+               while (done < sizeof(key))
+               {
+                       len = read(fd, key + done, sizeof(key) - done);
+                       if (len < 0)
+                       {
+                               break;
+                       }
+                       done += len;
                }
-               case 1:
+               close(fd);
+       }
+       /* on error we use random() to generate the key (better than nothing) */
+       if (done < sizeof(key))
+       {
+               srandom(time(NULL) + getpid());
+               for (; done < sizeof(key); done++)
                {
-                       hash += *data;
-                       hash ^= hash << 10;
-                       hash += hash >> 1;
-                       break;
+                       key[done] = (u_char)random();
                }
        }
+}
 
-       /* Force "avalanching" of final 127 bits */
-       hash ^= hash << 3;
-       hash += hash >> 5;
-       hash ^= hash << 4;
-       hash += hash >> 17;
-       hash ^= hash << 25;
-       hash += hash >> 6;
-
-       return hash;
+/**
+ * Described in header.
+ */
+u_int32_t chunk_hash_inc(chunk_t chunk, u_int32_t hash)
+{
+       pthread_once(&key_allocated, allocate_key);
+       /* we could use a mac of the previous hash, but this is faster */
+       return chunk_mac_inc(chunk, key, ((u_int64_t)hash) << 32 | hash);
 }
 
 /**
@@ -652,7 +760,8 @@ u_int32_t chunk_hash_inc(chunk_t chunk, u_int32_t hash)
  */
 u_int32_t chunk_hash(chunk_t chunk)
 {
-       return chunk_hash_inc(chunk, chunk.len);
+       pthread_once(&key_allocated, allocate_key);
+       return chunk_mac(chunk, key);
 }
 
 /**
index bc14b739497a144c2e7cf021341d010a962701a7..b9f2bf266e27565cb3b8f84ade8c4dc34197be93 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008-2009 Tobias Brunner
+ * Copyright (C) 2008-2013 Tobias Brunner
  * Copyright (C) 2005-2008 Martin Willi
  * Copyright (C) 2005 Jan Hutter
  * Hochschule fuer Technik Rapperswil
@@ -191,9 +191,9 @@ static inline void chunk_clear(chunk_t *chunk)
 #define chunk_from_thing(thing) chunk_create((char*)&(thing), sizeof(thing))
 
 /**
- * Initialize a chunk from a static string, not containing 0-terminator
+ * Initialize a chunk from a string, not containing 0-terminator
  */
-#define chunk_from_str(str) chunk_create(str, strlen(str))
+#define chunk_from_str(str) ({char *x = (str); chunk_create(x, strlen(x));})
 
 /**
  * Allocate a chunk on the heap
@@ -301,15 +301,37 @@ bool chunk_printable(chunk_t chunk, chunk_t *sane, char replace);
 
 /**
  * Computes a 32 bit hash of the given chunk.
- * Note: This hash is only intended for hash tables not for cryptographic purposes.
+ *
+ * @note This hash is only intended for hash tables not for cryptographic purposes.
+ *
+ * @param chunk                        data to hash
+ * @return                             hash value
  */
 u_int32_t chunk_hash(chunk_t chunk);
 
 /**
  * Incremental version of chunk_hash. Use this to hash two or more chunks.
+ *
+ * @param chunk                        data to hash
+ * @param hash                 previous hash value
+ * @return                             hash value
  */
 u_int32_t chunk_hash_inc(chunk_t chunk, u_int32_t hash);
 
+/**
+ * Computes a quick MAC from the given chunk and key using SipHash.
+ *
+ * The key must have a length of 128-bit (16 bytes).
+ *
+ * @note While SipHash has strong features using it for cryptographic purposes
+ * is not recommended (in particular because of the rather short output size).
+ *
+ * @param chunk                        data to process
+ * @param key                  key to use
+ * @return                             MAC for given input and key
+ */
+u_int64_t chunk_mac(chunk_t chunk, u_char *key);
+
 /**
  * printf hook function for chunk_t.
  *
index 9b3c4d56623ffcbb9385abd18e5b90cf61473593..3db9a34e057fd6d3e1e295d2dc57ab30b4d8d026 100644 (file)
@@ -47,7 +47,7 @@ int enum_from_name(enum_name_t *e, char *name)
 
                for (i = 0; i < count; i++)
                {
-                       if (strcaseeq(name, e->names[i]))
+                       if (name && strcaseeq(name, e->names[i]))
                        {
                                return e->first + i;
                        }
index 4176320dca6dc6e0427e2eab091f0cc366a2c72d..06ec533ea77f087aef5c5a7bdbb51c7304630abb 100644 (file)
@@ -276,6 +276,23 @@ METHOD(identification_t, create_part_enumerator, enumerator_t*,
        }
 }
 
+/**
+ * Print a separator between two RDNs
+ */
+static inline bool print_separator(char **buf, size_t *len)
+{
+       int written;
+
+       written = snprintf(*buf, *len, ", ");
+       if (written < 0 || written >= *len)
+       {
+               return FALSE;
+       }
+       *buf += written;
+       *len -= written;
+       return TRUE;
+}
+
 /**
  * Print a DN with all its RDN in a buffer to present it to the user
  */
@@ -292,8 +309,14 @@ static void dntoa(chunk_t dn, char *buf, size_t len)
        {
                empty = FALSE;
 
-               oid = asn1_known_oid(oid_data);
+               /* previous RDN was empty but it wasn't the last one */
+               if (finished && !print_separator(&buf, &len))
+               {
+                       break;
+               }
+               finished = FALSE;
 
+               oid = asn1_known_oid(oid_data);
                if (oid == OID_UNKNOWN)
                {
                        written = snprintf(buf, len, "%#B=", &oid_data);
@@ -319,21 +342,19 @@ static void dntoa(chunk_t dn, char *buf, size_t len)
                buf += written;
                len -= written;
 
-               if (data.ptr + data.len != dn.ptr + dn.len)
-               {
-                       written = snprintf(buf, len, ", ");
-                       if (written < 0 || written >= len)
-                       {
-                               break;
-                       }
-                       buf += written;
-                       len -= written;
+               if (!data.ptr)
+               {       /* we can't calculate if we're finished, assume we are */
+                       finished = TRUE;
                }
-               else
+               else if (data.ptr + data.len == dn.ptr + dn.len)
                {
                        finished = TRUE;
                        break;
                }
+               else if (!print_separator(&buf, &len))
+               {
+                       break;
+               }
        }
        if (empty)
        {
@@ -377,7 +398,7 @@ static status_t atodn(char *src, chunk_t *dn)
                switch (state)
                {
                        case SEARCH_OID:
-                               if (*src != ' ' && *src != '/' && *src !=  ',')
+                               if (*src != ' ' && *src != '/' && *src !=  ',' && *src != '\0')
                                {
                                        oid.ptr = src;
                                        oid.len = 1;
@@ -418,7 +439,7 @@ static status_t atodn(char *src, chunk_t *dn)
                                {
                                        break;
                                }
-                               else if (*src != ',' && *src != '/')
+                               else if (*src != ',' && *src != '/' && *src != '\0')
                                {
                                        name.ptr = src;
                                        name.len = 1;
@@ -481,6 +502,11 @@ static status_t atodn(char *src, chunk_t *dn)
                }
        } while (*src++ != '\0');
 
+       if (state == READ_OID)
+       {       /* unterminated OID */
+               status = INVALID_ARG;
+       }
+
        /* build the distinguished name sequence */
        {
                int i;
@@ -493,7 +519,6 @@ static status_t atodn(char *src, chunk_t *dn)
                        free(rdns[i].ptr);
                }
        }
-
        if (status != SUCCESS)
        {
                free(dn->ptr);
@@ -799,7 +824,7 @@ int identification_printf_hook(printf_hook_data_t *data,
                        dntoa(this->encoded, buf, sizeof(buf));
                        break;
                case ID_DER_ASN1_GN:
-                       snprintf(buf, sizeof(buf), "(ASN.1 general Name");
+                       snprintf(buf, sizeof(buf), "(ASN.1 general name)");
                        break;
                case ID_KEY_ID:
                        if (chunk_printable(this->encoded, NULL, '?') &&
@@ -915,14 +940,15 @@ identification_t *identification_create_from_string(char *string)
                else
                {
                        this = identification_create(ID_KEY_ID);
-                       this->encoded = chunk_clone(chunk_create(string, strlen(string)));
+                       this->encoded = chunk_from_str(strdup(string));
                }
                return &this->public;
        }
        else if (strchr(string, '@') == NULL)
        {
-               if (streq(string, "%any")
-               ||  streq(string, "%any6")
+               if (streq(string, "")
+               ||      streq(string, "%any")
+               ||      streq(string, "%any6")
                ||      streq(string, "0.0.0.0")
                ||      streq(string, "*")
                ||      streq(string, "::")
@@ -947,11 +973,7 @@ identification_t *identification_create_from_string(char *string)
                                else
                                {       /* not IPv4, mostly FQDN */
                                        this = identification_create(ID_FQDN);
-                                       this->encoded.len = strlen(string);
-                                       if (this->encoded.len)
-                                       {
-                                               this->encoded.ptr = strdup(string);
-                                       }
+                                       this->encoded = chunk_from_str(strdup(string));
                                }
                                return &this->public;
                        }
@@ -968,11 +990,7 @@ identification_t *identification_create_from_string(char *string)
                                else
                                {       /* not IPv4/6 fallback to KEY_ID */
                                        this = identification_create(ID_KEY_ID);
-                                       this->encoded.len = strlen(string);
-                                       if (this->encoded.len)
-                                       {
-                                               this->encoded.ptr = strdup(string);
-                                       }
+                                       this->encoded = chunk_from_str(strdup(string));
                                }
                                return &this->public;
                        }
@@ -996,7 +1014,7 @@ identification_t *identification_create_from_string(char *string)
                                string += 1;
                                this->encoded.len = strlen(string);
                                if (this->encoded.len)
-                               {
+                               {       /* if we only got an @ */
                                        this->encoded.ptr = strdup(string);
                                }
                                return &this->public;
@@ -1005,11 +1023,7 @@ identification_t *identification_create_from_string(char *string)
                else
                {
                        this = identification_create(ID_RFC822_ADDR);
-                       this->encoded.len = strlen(string);
-                       if (this->encoded.len)
-                       {
-                               this->encoded.ptr = strdup(string);
-                       }
+                       this->encoded = chunk_from_str(strdup(string));
                        return &this->public;
                }
        }
index 4f3c9f78b202cb59548d6c50af1c313c43a0aa9b..9d9062203dcff050634763501d379f32255ba1f6 100644 (file)
@@ -589,7 +589,7 @@ static int print_traces(private_leak_detective_t *this,
        enumerator = entries->create_enumerator(entries);
        while (enumerator->enumerate(enumerator, NULL, &entry))
        {
-               if (!thresh || entry->bytes >= thresh)
+               if (out && (!thresh || entry->bytes >= thresh))
                {
                        fprintf(out, "%d bytes total, %d allocations, %d bytes average:\n",
                                        entry->bytes, entry->count, entry->bytes / entry->count);
@@ -609,7 +609,7 @@ METHOD(leak_detective_t, report, void,
 {
        if (lib->leak_detective)
        {
-               int leaks = 0, whitelisted = 0;
+               int leaks, whitelisted = 0;
 
                leaks = print_traces(this, stderr, 0, detailed, &whitelisted);
                switch (leaks)
@@ -632,6 +632,19 @@ METHOD(leak_detective_t, report, void,
        }
 }
 
+METHOD(leak_detective_t, leaks, int,
+       private_leak_detective_t *this)
+{
+       if (lib->leak_detective)
+       {
+               int leaks, whitelisted = 0;
+
+               leaks = print_traces(this, NULL, 0, FALSE, &whitelisted);
+               return leaks;
+       }
+       return 0;
+}
+
 METHOD(leak_detective_t, set_state, bool,
        private_leak_detective_t *this, bool enable)
 {
@@ -885,6 +898,7 @@ leak_detective_t *leak_detective_create()
        INIT(this,
                .public = {
                        .report = _report,
+                       .leaks = _leaks,
                        .usage = _usage,
                        .set_state = _set_state,
                        .destroy = _destroy,
index 55d7e44d9dabe71fe26f8567bfb48d93195694ad..6a39aef067f188d62b881f07c814aff2b1c7c1ca 100644 (file)
@@ -42,6 +42,13 @@ struct leak_detective_t {
         */
        void (*report)(leak_detective_t *this, bool detailed);
 
+       /**
+        * Number of detected leaks.
+        *
+        * @return                              number of leaks
+        */
+       int (*leaks)(leak_detective_t *this);
+
        /**
         * Report current memory usage to out.
         *
index 2f38d8a9333104f322b9cdc43eeb35a1da0f3d27..aa59f4a4dc19de9b8810527995e0641ecfea03eb 100644 (file)
@@ -45,19 +45,6 @@ ENUM(status_names, SUCCESS, NEED_MORE,
        "NEED_MORE",
 );
 
-/**
- * Described in header.
- */
-void *clalloc(void * pointer, size_t size)
-{
-       void *data;
-       data = malloc(size);
-
-       memcpy(data, pointer, size);
-
-       return (data);
-}
-
 /**
  * Described in header.
  */
@@ -115,7 +102,12 @@ void memwipe_noinline(void *ptr, size_t n)
 void *memstr(const void *haystack, const char *needle, size_t n)
 {
        unsigned const char *pos = haystack;
-       size_t l = strlen(needle);
+       size_t l;
+
+       if (!haystack || !needle || (l = strlen(needle)) == 0)
+       {
+               return NULL;
+       }
        for (; n >= l; ++pos, --n)
        {
                if (memeq(pos, needle, l))
index 46eaf7b647c3d4421d3e83a197b9a3fdd18ea34a..ff1a007c11976adee1bb292cea197921a5ea23bb 100644 (file)
@@ -375,11 +375,6 @@ typedef struct timespec timespec_t;
  */
 typedef struct sockaddr sockaddr_t;
 
-/**
- * Clone a data to a newly allocated buffer
- */
-void *clalloc(void *pointer, size_t size);
-
 /**
  * Same as memcpy, but XORs src into dst instead of copy
  */
@@ -507,7 +502,7 @@ time_t time_monotonic(timeval_t *tv);
 static inline void timeval_add_ms(timeval_t *tv, u_int ms)
 {
        tv->tv_usec += ms * 1000;
-       while (tv->tv_usec > 1000000 /* 1s */)
+       while (tv->tv_usec >= 1000000 /* 1s */)
        {
                tv->tv_usec -= 1000000;
                tv->tv_sec++;