Commit f049f810 authored by Jacob Champion's avatar Jacob Champion
Browse files

httpdunit: a Check-based unit test suite

Add a unit test suite based on Check:

    https://libcheck.github.io/check/

The suite depends on the build system to automatically generate the code
stubs that call every test case.

httpdunit is automatically enabled in the build if configure is able to
find Check via pkg-config. At the moment pkg-config is the only official
(non-deprecated) way to build and link against Check with an autoconf
system, since platforms may distribute Check as a static library.

Note that Check is an LGPL'd library, so we can't distribute test
objects and binaries. Building and running the suite remains optional
and is not required to run the server.

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/httpdunit@1796202 13f79535-47bb-0310-9956-ffa450edef68
parent fddba1fb
Loading
Loading
Loading
Loading
+37 −1
Original line number Diff line number Diff line
@@ -28,7 +28,7 @@ DISTCLEAN_TARGETS = include/ap_config_auto.h include/ap_config_layout.h \
	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
@@ -431,3 +431,39 @@ check: check-include check-dirs check-conf check/build/config_vars.mk check/apxs
	    ./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)
+23 −0
Original line number Diff line number Diff line
#! /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";
    }
}
+22 −0
Original line number Diff line number Diff line
#! /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"
    }
}
+16 −0
Original line number Diff line number Diff line
@@ -706,6 +706,22 @@ AC_ARG_WITH(valgrind,
    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

test/httpdunit.c

0 → 100644
+53 −0
Original line number Diff line number Diff line
/* 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;
}
Loading