build/pkg/pkginfo build/config_vars.sh bsd_converted
EXTRACLEAN_TARGETS = configure include/ap_config_auto.h.in generated_lists \
httpd.spec
-PHONY_TARGETS := check check-conf check-dirs check-include
+PHONY_TARGETS := check check-conf check-dirs check-include unittest-objdir
include $(top_builddir)/build/rules.mk
include $(top_srcdir)/build/program.mk
./t/TEST -clean && \
./t/TEST -config && \
./t/TEST
+
+#
+# Unit Test Suite
+#
+
+# Make sure the object subdirectories we use exist in the build directory during
+# VPATH builds.
+unittest-objdir:
+ @mkdir -p test/unit
+
+# Normally I don't like wildcard sources, but for tests, autodiscovery is the
+# way to go.
+testcase_SOURCES := $(patsubst $(top_srcdir)/%,%,$(wildcard $(top_srcdir)/test/unit/*.c))
+testcase_OBJECTS := $(testcase_SOURCES:%.c=%.lo)
+testcase_STUBS := $(testcase_SOURCES:%.c=%.tests)
+
+# Each testcase depends on the source file as well as the autogenerated .tests
+# stub.
+$(testcase_OBJECTS): %.lo: %.c %.tests | unittest-objdir
+
+$(testcase_STUBS): %.tests: %.c
+ $(top_srcdir)/build/httpdunit_gen_stubs.pl < "$<" > "$@"
+
+test/httpdunit.cases: $(testcase_SOURCES) | unittest-objdir
+ for t in $^; do \
+ $(top_srcdir)/build/httpdunit_gen_cases.pl < "$$t"; \
+ done > $@
+
+test/httpdunit.lo: test/httpdunit.c test/httpdunit.cases | unittest-objdir
+
+# httpdunit is only added to $(other_targets) if configure detects a working
+# libcheck on the system.
+httpdunit_OBJECTS := test/httpdunit.lo $(testcase_OBJECTS)
+$(httpdunit_OBJECTS): override LTCFLAGS += $(UNITTEST_CFLAGS)
+test/httpdunit: $(httpdunit_OBJECTS) $(PROGRAM_DEPENDENCIES) $(PROGRAM_OBJECTS)
+ $(LINK) $(httpdunit_OBJECTS) $(PROGRAM_OBJECTS) $(UNITTEST_LIBS) $(PROGRAM_LDADD)
--- /dev/null
+#! /usr/bin/env perl
+
+#
+# Generates a list of test cases to be pulled into the httpdunit main test
+# suite.
+#
+# Supply all the test cases' source file contents on stdin; the resulting code
+# will be printed to stdout. Normally you will want to call this twice: once
+# with --declaration to print the function declarations of all the test cases,
+# and once without any options to produce the code that actually adds each test
+# case to the main suite.
+#
+
+use strict;
+use warnings;
+
+while (my $line = <>) {
+ if ($line =~ /^HTTPD_BEGIN_TEST_CASE(?:\w+)?\((\w+)/) {
+ my $name = "$1_test_case";
+ print "TCase *$name(void); ";
+ print "suite_add_tcase(suite, $name());\n";
+ }
+}
--- /dev/null
+#! /usr/bin/env perl
+
+#
+# Generates a code stub that adds unit tests to a Check test case.
+#
+# Supply the test case's source file contents on stdin; the resulting code will
+# be printed to stdout. This code is designed to be included as part of the
+# boilerplate at the end of each test case.
+#
+
+use strict;
+use warnings;
+
+while (my $line = <>) {
+ # FIXME: this does not correctly handle macro invocations that are split
+ # over multiple lines.
+ if ($line =~ /^HTTPD_START_LOOP_TEST\((\w+),(.*)\)/) {
+ print "tcase_add_loop_test(testcase, $1, 0, ($2));\n";
+ } elsif ($line =~ /^START_TEST\((\w+)\)/) {
+ print "tcase_add_test(testcase, $1);\n"
+ }
+}
fi ]
)
+dnl Enable the unit test executable if Check is installed.
+dnl TODO: at the moment, only pkg-config discovery is supported.
+AC_MSG_CHECKING([for Check to enable unit tests])
+if test "x$PKGCONFIG" != "x" && `$PKGCONFIG --atleast-version='0.9.12' check`; then
+ UNITTEST_CFLAGS=`$PKGCONFIG --cflags check`
+ UNITTEST_LIBS=`$PKGCONFIG --libs check`
+ other_targets="$other_targets test/httpdunit"
+
+ AC_MSG_RESULT([yes])
+else
+ AC_MSG_RESULT([no])
+fi
+APACHE_SUBST(UNITTEST_CFLAGS)
+APACHE_SUBST(UNITTEST_LIBS)
+
+
prefix="$orig_prefix"
APACHE_ENABLE_MODULES
--- /dev/null
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "check.h"
+
+#include "apr_general.h"
+
+static Suite *main_test_suite(void)
+{
+ Suite *suite = suite_create("main");
+
+ /* The list of test cases is automatically generated from the test/unit
+ * directory by the build system. */
+# include "test/httpdunit.cases"
+
+ return suite;
+}
+
+int main(int argc, const char * const argv[])
+{
+ SRunner *runner;
+ int failed;
+
+ /* Initialize APR and create our test runner. */
+ apr_app_initialize(&argc, &argv, NULL);
+ runner = srunner_create(main_test_suite());
+
+ /* Log TAP to stdout. */
+ srunner_set_tap(runner, "-");
+
+ /* Run the tests and collect failures. */
+ srunner_run_all(runner, CK_SILENT /* output only TAP */);
+ failed = srunner_ntests_failed(runner);
+
+ /* Clean up. */
+ srunner_free(runner);
+ apr_terminate();
+
+ return failed ? 1 : 0;
+}
--- /dev/null
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * httpdunit.h: a collection of test helper macros designed to reduce test
+ * boilerplate and help the build system autogenerate test case definitions.
+ *
+ * Here's how the magic works:
+ *
+ * Every test case under test/unit declares test functions using Check's
+ * START_TEST or the HTTPD_START_LOOP_TEST macro. The build system searches each
+ * source file for those macros and generates an appropriate stub, called
+ * test/unit/<filename>.tests, that will add each test function to the test
+ * case. This stub is then pulled in by three lines of boilerplate at the end of
+ * every test case's source file (see the HTTPD_BEGIN_TEST_CASE documentation,
+ * below).
+ *
+ * The build system uses that same three-line boilerplate to determine the names
+ * of all the test cases, and adds them automatically to the main test suite
+ * using a similar generate-stub-and-include process.
+ */
+
+#include "check.h"
+
+/*
+ * Boilerplate Macros
+ */
+
+/**
+ * Declares a test case. The build system uses this macro to generate a stub,
+ * which will automatically pull the new test case into the main suite.
+ *
+ * After beginning a test case, you must #include the autogenerated testcase
+ * stub ("test/unit/<filename>.tests") and then end the test case with
+ * HTTPD_END_TEST_CASE. For example, for a test case called foobar.c:
+ *
+ * HTTPD_BEGIN_TEST_CASE(foobar)
+ * #include "test/unit/foobar.tests"
+ * HTTPD_END_TEST_CASE
+ *
+ * The NAME identifying the test case must be globally unique within the test
+ * suite; if in doubt, just use the filename (minus extension).
+ */
+#define HTTPD_BEGIN_TEST_CASE(NAME) \
+TCase * NAME##_test_case(void); \
+TCase * NAME##_test_case(void) \
+{ \
+ TCase *testcase = tcase_create(#NAME);
+
+/**
+ * Like HTTPD_BEGIN_TEST_CASE, but additionally declares a pair of setup and
+ * teardown functions for the test case. The setup function is run once before
+ * every test function, and the teardown function is run directly after. Keep in
+ * mind that expensive fixtures will heavily impact test runtime.
+ *
+ * Both setup and teardown take no parameters and return void.
+ *
+ * These functions are passed to Check's tcase_add_checked_fixture(). See the
+ * Check documentation for details; the gist is that checked fixures run after
+ * Check forks the test process, and therefore they cannot influence each other
+ * even if something smashes the heap.
+ */
+#define HTTPD_BEGIN_TEST_CASE_WITH_FIXTURE(NAME, SETUP, TEARDOWN) \
+HTTPD_BEGIN_TEST_CASE(NAME) \
+ tcase_add_checked_fixture(testcase, (SETUP), (TEARDOWN));
+
+/**
+ * Bookend for HTTPD_BEGIN_TEST_CASE[_WITH_FIXTURE], which finishes the test
+ * case function.
+ */
+#define HTTPD_END_TEST_CASE \
+ return testcase; \
+}
+
+/*
+ * Test Declaration Macros
+ */
+
+/**
+ * Use this macro instead of Check's START_TEST when you want to run a test
+ * function multiple times. The build system will add the test function to the
+ * test case using Check's tcase_add_loop_test().
+ *
+ * The loop index is available to the test function as the special variable _i.
+ * _i will start at 0 and increment to a maximum of (NUM_ITERATIONS - 1).
+ */
+#define HTTPD_START_LOOP_TEST(NAME, NUM_ITERATIONS) START_TEST(NAME)