starter: Add tests for ipsec.conf parser
authorTobias Brunner <tobias@strongswan.org>
Tue, 20 May 2014 16:16:48 +0000 (18:16 +0200)
committerTobias Brunner <tobias@strongswan.org>
Thu, 19 Jun 2014 12:00:48 +0000 (14:00 +0200)
configure.ac
src/starter/Makefile.am
src/starter/tests/.gitignore [new file with mode: 0644]
src/starter/tests/Makefile.am [new file with mode: 0644]
src/starter/tests/starter_tests.c [new file with mode: 0644]
src/starter/tests/starter_tests.h [new file with mode: 0644]
src/starter/tests/suites/test_parser.c [new file with mode: 0644]

index fbb7677..c706559 100644 (file)
@@ -1701,6 +1701,7 @@ AC_CONFIG_FILES([
        src/stroke/Makefile
        src/ipsec/Makefile
        src/starter/Makefile
        src/stroke/Makefile
        src/ipsec/Makefile
        src/starter/Makefile
+       src/starter/tests/Makefile
        src/_updown/Makefile
        src/_updown_espmark/Makefile
        src/_copyright/Makefile
        src/_updown/Makefile
        src/_updown_espmark/Makefile
        src/_copyright/Makefile
index 02c1d68..32aa9d2 100644 (file)
@@ -1,3 +1,5 @@
+SUBDIRS = . tests
+
 ipsec_PROGRAMS = starter
 starter_SOURCES = \
 starter.c files.h \
 ipsec_PROGRAMS = starter
 starter_SOURCES = \
 starter.c files.h \
diff --git a/src/starter/tests/.gitignore b/src/starter/tests/.gitignore
new file mode 100644 (file)
index 0000000..c4c6471
--- /dev/null
@@ -0,0 +1 @@
+starter_tests
\ No newline at end of file
diff --git a/src/starter/tests/Makefile.am b/src/starter/tests/Makefile.am
new file mode 100644 (file)
index 0000000..5e188c4
--- /dev/null
@@ -0,0 +1,21 @@
+TESTS = starter_tests
+
+check_PROGRAMS = $(TESTS)
+
+starter_tests_SOURCES = \
+       suites/test_parser.c \
+       ../parser/conf_parser.c \
+       ../parser/lexer.l \
+       ../parser/parser.y \
+       starter_tests.h starter_tests.c
+
+starter_tests_CFLAGS = \
+       -I$(top_srcdir)/src/libstrongswan \
+       -I$(top_srcdir)/src/libstrongswan/tests \
+       -I$(top_srcdir)/src/starter \
+       @COVERAGE_CFLAGS@
+
+starter_tests_LDFLAGS = @COVERAGE_LDFLAGS@
+starter_tests_LDADD = \
+       $(top_builddir)/src/libstrongswan/libstrongswan.la \
+       $(top_builddir)/src/libstrongswan/tests/libtest.la
diff --git a/src/starter/tests/starter_tests.c b/src/starter/tests/starter_tests.c
new file mode 100644 (file)
index 0000000..4194c52
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014 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_runner.h>
+
+/* declare test suite constructors */
+#define TEST_SUITE(x) test_suite_t* x();
+#include "starter_tests.h"
+#undef TEST_SUITE
+
+static test_configuration_t tests[] = {
+#define TEST_SUITE(x) \
+       { .suite = x, },
+#include "starter_tests.h"
+       { .suite = NULL, }
+};
+
+static bool test_runner_init(bool init)
+{
+       if (!init)
+       {
+               lib->processor->set_threads(lib->processor, 0);
+               lib->processor->cancel(lib->processor);
+       }
+       return TRUE;
+}
+
+int main(int argc, char *argv[])
+{
+       return test_runner_run("stroke", tests, test_runner_init);
+}
diff --git a/src/starter/tests/starter_tests.h b/src/starter/tests/starter_tests.h
new file mode 100644 (file)
index 0000000..3486597
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+TEST_SUITE(parser_suite_create)
diff --git a/src/starter/tests/suites/test_parser.c b/src/starter/tests/suites/test_parser.c
new file mode 100644 (file)
index 0000000..84b5eed
--- /dev/null
@@ -0,0 +1,524 @@
+/*
+ * Copyright (C) 2014 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_suite.h>
+
+#include "../../parser/conf_parser.h"
+
+static char *path = "/tmp/strongswan-starter-parser-test";
+static conf_parser_t *parser;
+
+static void create_parser(chunk_t contents)
+{
+       ck_assert(chunk_write(contents, path, 0022, TRUE));
+       parser = conf_parser_create(path);
+}
+
+START_TEARDOWN(teardown_parser)
+{
+       parser->destroy(parser);
+       unlink(path);
+}
+END_TEARDOWN
+
+START_TEST(test_get_sections_config_setup)
+{
+       enumerator_t *enumerator;
+
+       create_parser(chunk_from_str(""));
+       ck_assert(parser->parse(parser));
+       enumerator = parser->get_sections(parser, CONF_PARSER_CONFIG_SETUP);
+       ck_assert(enumerator);
+       ck_assert(!enumerator->enumerate(enumerator, NULL));
+       enumerator->destroy(enumerator);
+       parser->destroy(parser);
+
+       create_parser(chunk_from_str("config setup\n\tfoo=bar"));
+       ck_assert(parser->parse(parser));
+       enumerator = parser->get_sections(parser, CONF_PARSER_CONFIG_SETUP);
+       ck_assert(enumerator);
+       ck_assert(!enumerator->enumerate(enumerator, NULL));
+       enumerator->destroy(enumerator);
+}
+END_TEST
+
+START_TEST(test_get_sections_conn)
+{
+       enumerator_t *enumerator;
+       char *name;
+
+       create_parser(chunk_from_str(""));
+       ck_assert(parser->parse(parser));
+       enumerator = parser->get_sections(parser, CONF_PARSER_CONN);
+       ck_assert(enumerator);
+       ck_assert(!enumerator->enumerate(enumerator, NULL));
+       enumerator->destroy(enumerator);
+       parser->destroy(parser);
+
+       create_parser(chunk_from_str(
+               "conn foo\n"
+               "conn bar\n"
+               "conn foo\n"));
+       ck_assert(parser->parse(parser));
+       enumerator = parser->get_sections(parser, CONF_PARSER_CONN);
+       ck_assert(enumerator);
+       ck_assert(enumerator->enumerate(enumerator, &name));
+       ck_assert_str_eq("foo", name);
+       ck_assert(enumerator->enumerate(enumerator, &name));
+       ck_assert_str_eq("bar", name);
+       ck_assert(!enumerator->enumerate(enumerator, &name));
+       enumerator->destroy(enumerator);
+}
+END_TEST
+
+START_TEST(test_get_section_config_setup)
+{
+       dictionary_t *dict;
+
+       create_parser(chunk_from_str(""));
+       ck_assert(parser->parse(parser));
+       dict = parser->get_section(parser, CONF_PARSER_CONFIG_SETUP, "foo");
+       ck_assert(dict);
+       dict->destroy(dict);
+       parser->destroy(parser);
+
+       create_parser(chunk_from_str("config setup\n\tfoo=bar"));
+       ck_assert(parser->parse(parser));
+       dict = parser->get_section(parser, CONF_PARSER_CONFIG_SETUP, NULL);
+       ck_assert(dict);
+       dict->destroy(dict);
+       parser->destroy(parser);
+
+       create_parser(chunk_from_str("config setup\n\tfoo=bar"));
+       ck_assert(parser->parse(parser));
+       dict = parser->get_section(parser, CONF_PARSER_CONFIG_SETUP, "foo");
+       ck_assert(dict);
+       dict->destroy(dict);
+}
+END_TEST
+
+START_TEST(test_get_section_conn)
+{
+       dictionary_t *dict;
+
+       create_parser(chunk_from_str(""));
+       ck_assert(parser->parse(parser));
+       dict = parser->get_section(parser, CONF_PARSER_CONN, "foo");
+       ck_assert(!dict);
+       parser->destroy(parser);
+
+       create_parser(chunk_from_str("conn foo\n"));
+       ck_assert(parser->parse(parser));
+       dict = parser->get_section(parser, CONF_PARSER_CONN, "foo");
+       ck_assert(!parser->get_section(parser, CONF_PARSER_CONN, "bar"));
+       ck_assert(dict);
+       dict->destroy(dict);
+       parser->destroy(parser);
+
+       create_parser(chunk_from_str("conn foo\n\tfoo=bar"));
+       ck_assert(parser->parse(parser));
+       dict = parser->get_section(parser, CONF_PARSER_CONN, "foo");
+       ck_assert(dict);
+       dict->destroy(dict);
+}
+END_TEST
+
+START_TEST(test_enumerate_values)
+{
+       enumerator_t *enumerator;
+       dictionary_t *dict;
+       char *key, *value;
+       int i;
+
+       create_parser(chunk_from_str(
+               "conn foo\n"
+               "       foo=bar\n"
+               "       bar=baz"));
+       ck_assert(parser->parse(parser));
+       dict = parser->get_section(parser, CONF_PARSER_CONN, "foo");
+       ck_assert(dict);
+       ck_assert_str_eq("bar", dict->get(dict, "foo"));
+       ck_assert_str_eq("baz", dict->get(dict, "bar"));
+       enumerator = dict->create_enumerator(dict);
+       for (i = 0; enumerator->enumerate(enumerator, &key, &value); i++)
+       {
+               if ((streq(key, "foo") && !streq(value, "bar")) ||
+                       (streq(key, "bar") && !streq(value, "baz")))
+               {
+                       fail("unexpected setting %s=%s", key, value);
+               }
+       }
+       enumerator->destroy(enumerator);
+       ck_assert_int_eq(i, 2);
+       dict->destroy(dict);
+}
+END_TEST
+
+#define extensibility_config(section) \
+       section "\n" \
+       "       foo=bar\n" \
+       "       dup=one\n" \
+       "       dup=two\n" \
+       "\n" \
+       "       nope=val\n" \
+       "\n" \
+       section "\n" \
+       "       foo=baz\n" \
+       section "\n" \
+       "       answer=42\n" \
+       "       nope=\n"
+
+static struct {
+       char *conf;
+       conf_parser_section_t type;
+       char *name;
+} extensibility_data[] = {
+       { extensibility_config("config setup"), CONF_PARSER_CONFIG_SETUP, NULL },
+       { extensibility_config("ca ca-foo"), CONF_PARSER_CA, "ca-foo" },
+       { extensibility_config("conn conn-foo"), CONF_PARSER_CONN, "conn-foo" },
+};
+
+START_TEST(test_extensibility)
+{
+       dictionary_t *dict;
+
+       create_parser(chunk_from_str(extensibility_data[_i].conf));
+       ck_assert(parser->parse(parser));
+
+       dict = parser->get_section(parser, extensibility_data[_i].type,
+                                                          extensibility_data[_i].name);
+       ck_assert(dict);
+       ck_assert_str_eq("baz", dict->get(dict, "foo"));
+       ck_assert_str_eq("two", dict->get(dict, "dup"));
+       ck_assert_str_eq("42", dict->get(dict, "answer"));
+       ck_assert(!dict->get(dict, "nope"));
+       ck_assert(!dict->get(dict, "anything"));
+       dict->destroy(dict);
+}
+END_TEST
+
+static struct {
+       char *conf;
+       bool check_section;
+       char *value;
+} comments_data[] = {
+       { "# conn foo", FALSE, NULL },
+       { "# conn foo\n", FALSE, NULL },
+       { "conn foo # asdf", TRUE, NULL },
+       { "conn foo # asdf", TRUE, NULL },
+       { "conn foo# asdf\n", TRUE, NULL },
+       { "conn foo # asdf\n\tkey=val", TRUE, "val" },
+       { "conn foo # asdf\n#\tkey=val", TRUE, NULL },
+       { "conn foo # asdf\n\t#key=val", TRUE, NULL },
+       { "conn foo # asdf\n\tkey=#val", TRUE, NULL },
+       { "conn foo # asdf\n\tkey=val#asdf", TRUE, "val" },
+       { "conn foo # asdf\n\tkey=\"val#asdf\"", TRUE, "val#asdf" },
+       { "conn foo # asdf\n\tkey=val # asdf\n", TRUE, "val" },
+       { "conn foo # asdf\n# asdf\n\tkey=val\n", TRUE, "val" },
+       { "conn foo # asdf\n\t# asdf\n\tkey=val\n", TRUE, "val" },
+};
+
+START_TEST(test_comments)
+{
+       dictionary_t *dict;
+
+       create_parser(chunk_from_str(comments_data[_i].conf));
+       ck_assert(parser->parse(parser));
+       if (comments_data[_i].check_section)
+       {
+               dict = parser->get_section(parser, CONF_PARSER_CONN, "foo");
+               ck_assert(dict);
+               if (comments_data[_i].value)
+               {
+                       ck_assert_str_eq(comments_data[_i].value, dict->get(dict, "key"));
+               }
+               else
+               {
+                       ck_assert(!dict->get(dict, "key"));
+               }
+               dict->destroy(dict);
+       }
+       else
+       {
+               ck_assert(!parser->get_section(parser, CONF_PARSER_CONN, "foo"));
+       }
+}
+END_TEST
+
+static struct {
+       char *conf;
+       bool check_section;
+       char *value;
+} whitespace_data[] = {
+       { "conn foo   ", FALSE, NULL },
+       { "conn    foo", FALSE, NULL },
+       { "conn    foo\n", FALSE, NULL },
+       { "conn    foo  \n", FALSE, NULL },
+       { "conn foo\n   ", FALSE, NULL },
+       { "conn foo\n   \n", FALSE, NULL },
+       { "conn foo\nconn bar", TRUE, NULL },
+       { "conn foo\n   \nconn bar", TRUE, NULL },
+       { "conn foo\n key=val", FALSE, "val" },
+       { "conn foo\n\tkey=val", FALSE, "val" },
+       { "conn foo\n\t  \tkey=val", FALSE, "val" },
+       { "conn foo\n\tkey  =  val  ", FALSE, "val" },
+};
+
+START_TEST(test_whitespace)
+{
+       dictionary_t *dict;
+
+       create_parser(chunk_from_str(whitespace_data[_i].conf));
+       ck_assert(parser->parse(parser));
+       dict = parser->get_section(parser, CONF_PARSER_CONN, "foo");
+       ck_assert(dict);
+       if (whitespace_data[_i].value)
+       {
+               ck_assert_str_eq(whitespace_data[_i].value, dict->get(dict, "key"));
+       }
+       else
+       {
+               ck_assert(!dict->get(dict, "key"));
+       }
+       dict->destroy(dict);
+       if (whitespace_data[_i].check_section)
+       {
+               dict = parser->get_section(parser, CONF_PARSER_CONN, "bar");
+               ck_assert(dict);
+               dict->destroy(dict);
+       }
+       else
+       {
+               ck_assert(!parser->get_section(parser, CONF_PARSER_CONN, "bar"));
+       }
+}
+END_TEST
+
+static struct {
+       bool valid;
+       char *conf;
+       char *section;
+       char *value;
+} strings_data[] = {
+       { FALSE, "\"conn foo\"", NULL, NULL },
+       { TRUE, "conn \"foo\"", "foo", NULL },
+       { FALSE, "conn foo bar", NULL, NULL },
+       { TRUE, "conn \"foo bar\"", "foo bar", NULL },
+       { TRUE, "conn \"#foo\"", "#foo", NULL },
+       { FALSE, "conn foo\n\t\"key=val\"", "foo", NULL },
+       { TRUE, "conn foo\n\t\"key\"=val", "foo", "val" },
+       { TRUE, "conn foo\n\tkey=val ue", "foo", "val ue" },
+       { TRUE, "conn foo\n\tkey=val     ue", "foo", "val ue" },
+       { TRUE, "conn foo\n\tkey=\"val   ue\"", "foo", "val   ue" },
+       { TRUE, "conn foo\n\tkey=\"val\\nue\"", "foo", "val\nue" },
+};
+
+START_TEST(test_strings)
+{
+       dictionary_t *dict;
+
+       create_parser(chunk_from_str(strings_data[_i].conf));
+       ck_assert(parser->parse(parser) == strings_data[_i].valid);
+       if (strings_data[_i].section)
+       {
+               dict = parser->get_section(parser, CONF_PARSER_CONN,
+                                                                  strings_data[_i].section);
+               ck_assert(dict);
+               if (strings_data[_i].value)
+               {
+                       ck_assert_str_eq(strings_data[_i].value, dict->get(dict, "key"));
+               }
+               else
+               {
+                       ck_assert(!dict->get(dict, "key"));
+               }
+               dict->destroy(dict);
+       }
+}
+END_TEST
+
+START_TEST(test_refcounting)
+{
+       dictionary_t *dict;
+
+       create_parser(chunk_from_str(
+               "conn foo\n"
+               "       key=val"));
+
+       ck_assert(parser->parse(parser));
+       dict = parser->get_section(parser, CONF_PARSER_CONN, "foo");
+       ck_assert(dict);
+       ck_assert_str_eq("val", dict->get(dict, "key"));
+       parser->destroy(parser);
+       ck_assert_str_eq("val", dict->get(dict, "key"));
+       dict->destroy(dict);
+}
+END_TEST
+
+START_TEST(test_also)
+{
+       dictionary_t *dict;
+
+       create_parser(chunk_from_str(
+               "conn A\n"
+               "       key=vala\n"
+               "       keya=val1\n"
+               "       unset=set\n"
+               "conn B\n"
+               "       also=A\n"
+               "       key=valb\n"
+               "       keyb=val2\n"
+               "       unset=\n"
+               "conn C\n"
+               "       keyc=val3\n"
+               "       unset=set again\n"
+               "       also=B\n"
+               "conn D\n"
+               "       keyd=val4\n"
+               "       also=A\n"
+               "       also=B\n"
+               "conn E\n"
+               "       keye=val5\n"
+               "       also=B\n"
+               "       also=A\n"
+               ""));
+
+       ck_assert(parser->parse(parser));
+       dict = parser->get_section(parser, CONF_PARSER_CONN, "B");
+       ck_assert(dict);
+       ck_assert_str_eq("valb", dict->get(dict, "key"));
+       ck_assert_str_eq("val1", dict->get(dict, "keya"));
+       ck_assert_str_eq("val2", dict->get(dict, "keyb"));
+       ck_assert(!dict->get(dict, "unset"));
+       dict->destroy(dict);
+       dict = parser->get_section(parser, CONF_PARSER_CONN, "C");
+       ck_assert(dict);
+       ck_assert_str_eq("valb", dict->get(dict, "key"));
+       ck_assert_str_eq("val1", dict->get(dict, "keya"));
+       ck_assert_str_eq("val2", dict->get(dict, "keyb"));
+       ck_assert_str_eq("val3", dict->get(dict, "keyc"));
+       ck_assert_str_eq("set again", dict->get(dict, "unset"));
+       dict->destroy(dict);
+       /* since B includes A too the inclusion in D and E has no effect */
+       dict = parser->get_section(parser, CONF_PARSER_CONN, "D");
+       ck_assert(dict);
+       ck_assert_str_eq("valb", dict->get(dict, "key"));
+       ck_assert_str_eq("val1", dict->get(dict, "keya"));
+       ck_assert_str_eq("val2", dict->get(dict, "keyb"));
+       ck_assert(!dict->get(dict, "keyc"));
+       ck_assert_str_eq("val4", dict->get(dict, "keyd"));
+       ck_assert(!dict->get(dict, "unset"));
+       dict->destroy(dict);
+       dict = parser->get_section(parser, CONF_PARSER_CONN, "E");
+       ck_assert(dict);
+       ck_assert_str_eq("valb", dict->get(dict, "key"));
+       ck_assert_str_eq("val1", dict->get(dict, "keya"));
+       ck_assert_str_eq("val2", dict->get(dict, "keyb"));
+       ck_assert(!dict->get(dict, "keyc"));
+       ck_assert(!dict->get(dict, "keyd"));
+       ck_assert_str_eq("val5", dict->get(dict, "keye"));
+       ck_assert(!dict->get(dict, "unset"));
+       dict->destroy(dict);
+}
+END_TEST
+
+START_TEST(test_ambiguous)
+{
+       dictionary_t *dict;
+
+       create_parser(chunk_from_str(
+               "conn A\n"
+               "       key=vala\n"
+               "conn B\n"
+               "       key=valb\n"
+               "conn C\n"
+               "       also=A\n"
+               "       also=B\n"
+               "conn D\n"
+               "       also=B\n"
+               "       also=A\n"
+               "conn E\n"
+               "       also=C\n"
+               "       also=D\n"
+               "conn F\n"
+               "       also=D\n"
+               "       also=C\n"));
+
+       ck_assert(parser->parse(parser));
+       dict = parser->get_section(parser, CONF_PARSER_CONN, "E");
+       ck_assert(dict);
+       ck_assert_str_eq("valb", dict->get(dict, "key"));
+       dict->destroy(dict);
+       dict = parser->get_section(parser, CONF_PARSER_CONN, "F");
+       ck_assert(dict);
+       ck_assert_str_eq("vala", dict->get(dict, "key"));
+       dict->destroy(dict);
+}
+END_TEST
+
+Suite *parser_suite_create()
+{
+       Suite *s;
+       TCase *tc;
+
+       s = suite_create("ipsec.conf parser");
+
+       tc = tcase_create("get_section(s)");
+       tcase_add_checked_fixture(tc, NULL, teardown_parser);
+       tcase_add_test(tc, test_get_sections_config_setup);
+       tcase_add_test(tc, test_get_sections_conn);
+       tcase_add_test(tc, test_get_section_config_setup);
+       tcase_add_test(tc, test_get_section_conn);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("enumerate settings");
+       tcase_add_checked_fixture(tc, NULL, teardown_parser);
+       tcase_add_test(tc, test_enumerate_values);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("extensibility");
+       tcase_add_checked_fixture(tc, NULL, teardown_parser);
+       tcase_add_loop_test(tc, test_extensibility, 0, countof(extensibility_data));
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("comments");
+       tcase_add_checked_fixture(tc, NULL, teardown_parser);
+       tcase_add_loop_test(tc, test_comments, 0, countof(comments_data));
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("whitespace");
+       tcase_add_checked_fixture(tc, NULL, teardown_parser);
+       tcase_add_loop_test(tc, test_whitespace, 0, countof(whitespace_data));
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("strings");
+       tcase_add_checked_fixture(tc, NULL, teardown_parser);
+       tcase_add_loop_test(tc, test_strings, 0, countof(strings_data));
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("refcounting");
+       tcase_add_test(tc, test_refcounting);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("also=");
+       tcase_add_checked_fixture(tc, NULL, teardown_parser);
+       tcase_add_test(tc, test_also);
+       tcase_add_test(tc, test_ambiguous);
+       suite_add_tcase(s, tc);
+
+       return s;
+}