diff options
author | Martyn Russell <martyn@lanedo.com> | 2014-08-15 11:20:11 +0100 |
---|---|---|
committer | Martyn Russell <martyn@lanedo.com> | 2014-08-15 11:20:11 +0100 |
commit | 9cf1ec7a95a62a569f33134f6717d14fcb6d26c1 (patch) | |
tree | 70834dec277d2cd8b9e8266da00811087a58e52d | |
parent | 95d3be94e7672a1a449cfbd824d6fb23952684f4 (diff) | |
download | libmediaart-9cf1ec7a95a62a569f33134f6717d14fcb6d26c1.tar.gz |
tests: Use tap automake testing scripts, fixes output summaries
Also updated the unit tests so they don't fail with the new tap system
-rw-r--r-- | Makefile.am | 4 | ||||
-rw-r--r-- | Makefile.decl | 183 | ||||
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | docs/Makefile.am | 2 | ||||
-rw-r--r-- | docs/reference/Makefile.am | 2 | ||||
-rw-r--r-- | docs/reference/libmediaart/Makefile.am | 2 | ||||
-rw-r--r-- | libmediaart/Makefile.am | 2 | ||||
-rwxr-xr-x | tap-driver.sh | 652 | ||||
-rwxr-xr-x | tap-test | 5 | ||||
-rw-r--r-- | tests/Makefile.am | 8 | ||||
-rw-r--r-- | tests/mediaarttest.c | 219 |
11 files changed, 910 insertions, 171 deletions
diff --git a/Makefile.am b/Makefile.am index 7a214c4..8155f85 100644 --- a/Makefile.am +++ b/Makefile.am @@ -14,7 +14,9 @@ libmediaart-$(LIBMEDIAART_API_VERSION).pc: libmediaart.pc EXTRA_DIST = \ libmediaart.doap \ - libmediaart.pc.in + libmediaart.pc.in \ + tap-driver.sh \ + tap-test CLEANFILES = \ libmediaart-@LIBMEDIAART_API_VERSION@.pc diff --git a/Makefile.decl b/Makefile.decl index 2c5a4a2..4fe3577 100644 --- a/Makefile.decl +++ b/Makefile.decl @@ -1,64 +1,133 @@ +TESTS_ENVIRONMENT= \ + G_TEST_SRCDIR="$(abs_srcdir)" \ + G_TEST_BUILDDIR="$(abs_builddir)" \ + G_DEBUG=gc-friendly \ + MALLOC_CHECK_=2 \ + MALLOC_PERTURB_=$$(($${RANDOM:-256} % 256)) +LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) $(top_srcdir)/tap-driver.sh +LOG_COMPILER = $(top_srcdir)/tap-test + +NULL = + +# initialize variables for unconditional += appending +BUILT_SOURCES = +BUILT_EXTRA_DIST = +CLEANFILES = *.log *.trs +DISTCLEANFILES = +MAINTAINERCLEANFILES = +EXTRA_DIST = +TESTS = + +installed_test_LTLIBRARIES = +installed_test_PROGRAMS = +installed_test_SCRIPTS = +nobase_installed_test_DATA = + +noinst_LTLIBRARIES = +noinst_PROGRAMS = +noinst_SCRIPTS = +noinst_DATA = + +check_LTLIBRARIES = +check_PROGRAMS = +check_SCRIPTS = +check_DATA = + +# We support a fairly large range of possible variables. It is expected that all types of files in a test suite +# will belong in exactly one of the following variables. +# +# First, we support the usual automake suffixes, but in lowercase, with the customary meaning: +# +# test_programs, test_scripts, test_data, test_ltlibraries +# +# The above are used to list files that are involved in both uninstalled and installed testing. The +# test_programs and test_scripts are taken to be actual testcases and will be run as part of the test suite. +# Note that _data is always used with the nobase_ automake variable name to ensure that installed test data is +# installed in the same way as it appears in the package layout. # -# Testing rules for make -# Original file: http://svn.gnome.org/svn/glib/trunk/Makefile.decl +# In order to mark a particular file as being only for one type of testing, use 'installed' or 'uninstalled', +# like so: # -# test: run all tests in cwd and subdirs -# test-report: run tests in subdirs and generate report -# perf-report: run tests in subdirs with -m perf and generate report -# full-report: like test-report: with -m perf and -m slow +# installed_test_programs, uninstalled_test_programs +# installed_test_scripts, uninstalled_test_scripts +# installed_test_data, uninstalled_test_data +# installed_test_ltlibraries, uninstalled_test_ltlibraries # +# Additionally, we support 'extra' infixes for programs and scripts. This is used for support programs/scripts +# that should not themselves be run as testcases (but exist to be used from other testcases): +# +# test_extra_programs, installed_test_extra_programs, uninstalled_test_extra_programs +# test_extra_scripts, installed_test_extra_scripts, uninstalled_test_extra_scripts +# +# Additionally, for _scripts and _data, we support the customary dist_ prefix so that the named script or data +# file automatically end up in the tarball. +# +# dist_test_scripts, dist_test_data, dist_test_extra_scripts +# dist_installed_test_scripts, dist_installed_test_data, dist_installed_test_extra_scripts +# dist_uninstalled_test_scripts, dist_uninstalled_test_data, dist_uninstalled_test_extra_scripts +# +# Note that no file is automatically disted unless it appears in one of the dist_ variables. This follows the +# standard automake convention of not disting programs scripts or data by default. +# +# test_programs, test_scripts, uninstalled_test_programs and uninstalled_test_scripts (as well as their disted +# variants) will be run as part of the in-tree 'make check'. These are all assumed to be runnable under +# gtester. That's a bit strange for scripts, but it's possible. -GTESTER = gtester # for non-GLIB packages -GTESTER_REPORT = gtester-report +TESTS += $(test_programs) $(test_scripts) $(uninstalled_test_programs) $(uninstalled_test_scripts) \ + $(dist_test_scripts) $(dist_uninstalled_test_scripts) -# initialize variables for unconditional += appending -TEST_PROGS = +# Note: build even the installed-only targets during 'make check' to ensure that they still work. +# We need to do a bit of trickery here and manage disting via EXTRA_DIST instead of using dist_ prefixes to +# prevent automake from mistreating gmake functions like $(wildcard ...) and $(addprefix ...) as if they were +# filenames, including removing duplicate instances of the opening part before the space, eg. '$(addprefix'. +all_test_programs = $(test_programs) $(uninstalled_test_programs) $(installed_test_programs) \ + $(test_extra_programs) $(uninstalled_test_extra_programs) $(installed_test_extra_programs) +all_test_scripts = $(test_scripts) $(uninstalled_test_scripts) $(installed_test_scripts) \ + $(test_extra_scripts) $(uninstalled_test_extra_scripts) $(installed_test_extra_scripts) +all_dist_test_scripts = $(dist_test_scripts) $(dist_uninstalled_test_scripts) $(dist_installed_test_scripts) \ + $(dist_test_extra_scripts) $(dist_uninstalled_test_extra_scripts) $(dist_installed_test_extra_scripts) +all_test_scripts += $(all_dist_test_scripts) +EXTRA_DIST += $(all_dist_test_scripts) +all_test_data = $(test_data) $(uninstalled_test_data) $(installed_test_data) +all_dist_test_data = $(dist_test_data) $(dist_uninstalled_test_data) $(dist_installed_test_data) +all_test_data += $(all_dist_test_data) +EXTRA_DIST += $(all_dist_test_data) +all_test_ltlibs = $(test_ltlibraries) $(uninstalled_test_ltlibraries) $(installed_test_ltlibraries) + +if ENABLE_ALWAYS_BUILD_TESTS +noinst_LTLIBRARIES += $(all_test_ltlibs) +noinst_PROGRAMS += $(all_test_programs) +noinst_SCRIPTS += $(all_test_scripts) +noinst_DATA += $(all_test_data) +else +check_LTLIBRARIES += $(all_test_ltlibs) +check_PROGRAMS += $(all_test_programs) +check_SCRIPTS += $(all_test_scripts) +check_DATA += $(all_test_data) +endif + +if ENABLE_INSTALLED_TESTS +installed_test_PROGRAMS += $(test_programs) $(installed_test_programs) \ + $(test_extra_programs) $(installed_test_extra_programs) +installed_test_SCRIPTS += $(test_scripts) $(installed_test_scripts) \ + $(test_extra_scripts) $(test_installed_extra_scripts) +installed_test_SCRIPTS += $(dist_test_scripts) $(dist_test_extra_scripts) \ + $(dist_installed_test_scripts) $(dist_installed_test_extra_scripts) +nobase_installed_test_DATA += $(test_data) $(installed_test_data) +nobase_installed_test_DATA += $(dist_test_data) $(dist_installed_test_data) +installed_test_LTLIBRARIES += $(test_ltlibraries) $(installed_test_ltlibraries) +installed_testcases = $(test_programs) $(installed_test_programs) \ + $(test_scripts) $(installed_test_scripts) \ + $(dist_test_scripts) $(dist_installed_test_scripts) + +installed_test_meta_DATA = $(installed_testcases:=.test) -### testing rules +%.test: %$(EXEEXT) Makefile + $(AM_V_GEN) (echo '[Test]' > $@.tmp; \ + echo 'Type=session' >> $@.tmp; \ + echo 'Exec=$(installed_testdir)/$(notdir $<) --tap' >> $@.tmp; \ + echo 'Output=TAP' >> $@.tmp; \ + mv $@.tmp $@) -# test: run all tests in cwd and subdirs -test: ${TEST_PROGS} all - @ test -z "${TEST_PROGS}" || ${GTESTER} --verbose ${TEST_PROGS} - @ for subdir in $(SUBDIRS) . ; do \ - test "$$subdir" = "." -o "$$subdir" = "po" || \ - ( cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $? ; \ - done -# test-report: run tests in subdirs and generate report -# perf-report: run tests in subdirs with -m perf and generate report -# full-report: like test-report: with -m perf and -m slow -test-report perf-report full-report: ${TEST_PROGS} - @test -z "${TEST_PROGS}" || { \ - case $@ in \ - test-report) test_options="-k";; \ - perf-report) test_options="-k -m=perf";; \ - full-report) test_options="-k -m=perf -m=slow";; \ - esac ; \ - if test -z "$$GTESTER_LOGDIR" ; then \ - ${GTESTER} --verbose $$test_options -o test-report.xml ${TEST_PROGS} ; \ - elif test -n "${TEST_PROGS}" ; then \ - ${GTESTER} --verbose $$test_options -o `mktemp "$$GTESTER_LOGDIR/log-XXXXXX"` ${TEST_PROGS} ; \ - fi ; \ - } - @ ignore_logdir=true ; \ - if test -z "$$GTESTER_LOGDIR" ; then \ - GTESTER_LOGDIR=`mktemp -d "\`pwd\`/.testlogs-XXXXXX"`; export GTESTER_LOGDIR ; \ - ignore_logdir=false ; \ - fi ; \ - for subdir in $(SUBDIRS) . ; do \ - test "$$subdir" = "." -o "$$subdir" = "po" || \ - ( cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $? ; \ - done ; \ - $$ignore_logdir || { \ - echo '<?xml version="1.0"?>' > $@.xml ; \ - echo '<report-collection>' >> $@.xml ; \ - for lf in `ls -L "$$GTESTER_LOGDIR"/.` ; do \ - sed '1,1s/^<?xml\b[^>?]*?>//' <"$$GTESTER_LOGDIR"/"$$lf" >> $@.xml ; \ - done ; \ - echo >> $@.xml ; \ - echo '</report-collection>' >> $@.xml ; \ - rm -rf "$$GTESTER_LOGDIR"/ ; \ - ${GTESTER_REPORT} --version 2>/dev/null 1>&2 ; test "$$?" != 0 || ${GTESTER_REPORT} $@.xml >$@.html ; \ - } -.PHONY: test test-report perf-report full-report -# run make test as part of make check -check-local: test +CLEANFILES += $(installed_test_meta_DATA) +endif diff --git a/configure.ac b/configure.ac index 7f4b963..17cfb10 100644 --- a/configure.ac +++ b/configure.ac @@ -128,6 +128,8 @@ AC_CHECK_FUNCS([setenv strstr utime]) CFLAGS="$CFLAGS" +GLIB_TESTS + # Library required versions GLIB_REQUIRED=2.38.0 GDKPIXBUF_REQUIRED=2.12.0 diff --git a/docs/Makefile.am b/docs/Makefile.am index 34905f5..b407dcb 100644 --- a/docs/Makefile.am +++ b/docs/Makefile.am @@ -1,5 +1,3 @@ -include $(top_srcdir)/Makefile.decl - SUBDIRS = reference # require gtk-doc when making dist diff --git a/docs/reference/Makefile.am b/docs/reference/Makefile.am index a68f8d3..a2fe813 100644 --- a/docs/reference/Makefile.am +++ b/docs/reference/Makefile.am @@ -1,3 +1 @@ -include $(top_srcdir)/Makefile.decl - SUBDIRS = libmediaart diff --git a/docs/reference/libmediaart/Makefile.am b/docs/reference/libmediaart/Makefile.am index 6424e10..b912432 100644 --- a/docs/reference/libmediaart/Makefile.am +++ b/docs/reference/libmediaart/Makefile.am @@ -1,5 +1,3 @@ -include $(top_srcdir)/Makefile.decl - AUTOMAKE_OPTIONS = 1.6 # The name of the module. diff --git a/libmediaart/Makefile.am b/libmediaart/Makefile.am index 8403d23..a234ead 100644 --- a/libmediaart/Makefile.am +++ b/libmediaart/Makefile.am @@ -1,5 +1,3 @@ -include $(top_srcdir)/Makefile.decl - AM_CPPFLAGS = \ $(BUILD_CFLAGS) \ $(LIBMEDIAART_CFLAGS) diff --git a/tap-driver.sh b/tap-driver.sh new file mode 100755 index 0000000..19aa531 --- /dev/null +++ b/tap-driver.sh @@ -0,0 +1,652 @@ +#! /bin/sh +# Copyright (C) 2011-2013 Free Software Foundation, Inc. +# +# 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, or (at your option) +# any later version. +# +# 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. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# This file is maintained in Automake, please report +# bugs to <bug-automake@gnu.org> or send patches to +# <automake-patches@gnu.org>. + +scriptversion=2011-12-27.17; # UTC + +# Make unconditional expansion of undefined variables an error. This +# helps a lot in preventing typo-related bugs. +set -u + +me=tap-driver.sh + +fatal () +{ + echo "$me: fatal: $*" >&2 + exit 1 +} + +usage_error () +{ + echo "$me: $*" >&2 + print_usage >&2 + exit 2 +} + +print_usage () +{ + cat <<END +Usage: + tap-driver.sh --test-name=NAME --log-file=PATH --trs-file=PATH + [--expect-failure={yes|no}] [--color-tests={yes|no}] + [--enable-hard-errors={yes|no}] [--ignore-exit] + [--diagnostic-string=STRING] [--merge|--no-merge] + [--comments|--no-comments] [--] TEST-COMMAND +The \`--test-name', \`--log-file' and \`--trs-file' options are mandatory. +END +} + +# TODO: better error handling in option parsing (in particular, ensure +# TODO: $log_file, $trs_file and $test_name are defined). +test_name= # Used for reporting. +log_file= # Where to save the result and output of the test script. +trs_file= # Where to save the metadata of the test run. +expect_failure=0 +color_tests=0 +merge=0 +ignore_exit=0 +comments=0 +diag_string='#' +while test $# -gt 0; do + case $1 in + --help) print_usage; exit $?;; + --version) echo "$me $scriptversion"; exit $?;; + --test-name) test_name=$2; shift;; + --log-file) log_file=$2; shift;; + --trs-file) trs_file=$2; shift;; + --color-tests) color_tests=$2; shift;; + --expect-failure) expect_failure=$2; shift;; + --enable-hard-errors) shift;; # No-op. + --merge) merge=1;; + --no-merge) merge=0;; + --ignore-exit) ignore_exit=1;; + --comments) comments=1;; + --no-comments) comments=0;; + --diagnostic-string) diag_string=$2; shift;; + --) shift; break;; + -*) usage_error "invalid option: '$1'";; + esac + shift +done + +test $# -gt 0 || usage_error "missing test command" + +case $expect_failure in + yes) expect_failure=1;; + *) expect_failure=0;; +esac + +if test $color_tests = yes; then + init_colors=' + color_map["red"]="[0;31m" # Red. + color_map["grn"]="[0;32m" # Green. + color_map["lgn"]="[1;32m" # Light green. + color_map["blu"]="[1;34m" # Blue. + color_map["mgn"]="[0;35m" # Magenta. + color_map["std"]="[m" # No color. + color_for_result["ERROR"] = "mgn" + color_for_result["PASS"] = "grn" + color_for_result["XPASS"] = "red" + color_for_result["FAIL"] = "red" + color_for_result["XFAIL"] = "lgn" + color_for_result["SKIP"] = "blu"' +else + init_colors='' +fi + +# :; is there to work around a bug in bash 3.2 (and earlier) which +# does not always set '$?' properly on redirection failure. +# See the Autoconf manual for more details. +:;{ + ( + # Ignore common signals (in this subshell only!), to avoid potential + # problems with Korn shells. Some Korn shells are known to propagate + # to themselves signals that have killed a child process they were + # waiting for; this is done at least for SIGINT (and usually only for + # it, in truth). Without the `trap' below, such a behaviour could + # cause a premature exit in the current subshell, e.g., in case the + # test command it runs gets terminated by a SIGINT. Thus, the awk + # script we are piping into would never seen the exit status it + # expects on its last input line (which is displayed below by the + # last `echo $?' statement), and would thus die reporting an internal + # error. + # For more information, see the Autoconf manual and the threads: + # <http://lists.gnu.org/archive/html/bug-autoconf/2011-09/msg00004.html> + # <http://mail.opensolaris.org/pipermail/ksh93-integration-discuss/2009-February/004121.html> + trap : 1 3 2 13 15 + if test $merge -gt 0; then + exec 2>&1 + else + exec 2>&3 + fi + "$@" + echo $? + ) | LC_ALL=C ${AM_TAP_AWK-awk} \ + -v me="$me" \ + -v test_script_name="$test_name" \ + -v log_file="$log_file" \ + -v trs_file="$trs_file" \ + -v expect_failure="$expect_failure" \ + -v merge="$merge" \ + -v ignore_exit="$ignore_exit" \ + -v comments="$comments" \ + -v diag_string="$diag_string" \ +' +# FIXME: the usages of "cat >&3" below could be optimized when using +# FIXME: GNU awk, and/on on systems that supports /dev/fd/. + +# Implementation note: in what follows, `result_obj` will be an +# associative array that (partly) simulates a TAP result object +# from the `TAP::Parser` perl module. + +## ----------- ## +## FUNCTIONS ## +## ----------- ## + +function fatal(msg) +{ + print me ": " msg | "cat >&2" + exit 1 +} + +function abort(where) +{ + fatal("internal error " where) +} + +# Convert a boolean to a "yes"/"no" string. +function yn(bool) +{ + return bool ? "yes" : "no"; +} + +function add_test_result(result) +{ + if (!test_results_index) + test_results_index = 0 + test_results_list[test_results_index] = result + test_results_index += 1 + test_results_seen[result] = 1; +} + +# Whether the test script should be re-run by "make recheck". +function must_recheck() +{ + for (k in test_results_seen) + if (k != "XFAIL" && k != "PASS" && k != "SKIP") + return 1 + return 0 +} + +# Whether the content of the log file associated to this test should +# be copied into the "global" test-suite.log. +function copy_in_global_log() +{ + for (k in test_results_seen) + if (k != "PASS") + return 1 + return 0 +} + +# FIXME: this can certainly be improved ... +function get_global_test_result() +{ + if ("ERROR" in test_results_seen) + return "ERROR" + if ("FAIL" in test_results_seen || "XPASS" in test_results_seen) + return "FAIL" + all_skipped = 1 + for (k in test_results_seen) + if (k != "SKIP") + all_skipped = 0 + if (all_skipped) + return "SKIP" + return "PASS"; +} + +function stringify_result_obj(result_obj) +{ + if (result_obj["is_unplanned"] || result_obj["number"] != testno) + return "ERROR" + + if (plan_seen == LATE_PLAN) + return "ERROR" + + if (result_obj["directive"] == "TODO") + return result_obj["is_ok"] ? "XPASS" : "XFAIL" + + if (result_obj["directive"] == "SKIP") + return result_obj["is_ok"] ? "SKIP" : COOKED_FAIL; + + if (length(result_obj["directive"])) + abort("in function stringify_result_obj()") + + return result_obj["is_ok"] ? COOKED_PASS : COOKED_FAIL +} + +function decorate_result(result) +{ + color_name = color_for_result[result] + if (color_name) + return color_map[color_name] "" result "" color_map["std"] + # If we are not using colorized output, or if we do not know how + # to colorize the given result, we should return it unchanged. + return result +} + +function report(result, details) +{ + if (result ~ /^(X?(PASS|FAIL)|SKIP|ERROR)/) + { + msg = ": " test_script_name + add_test_result(result) + } + else if (result == "#") + { + msg = " " test_script_name ":" + } + else + { + abort("in function report()") + } + if (length(details)) + msg = msg " " details + # Output on console might be colorized. + print decorate_result(result) msg + # Log the result in the log file too, to help debugging (this is + # especially true when said result is a TAP error or "Bail out!"). + print result msg | "cat >&3"; +} + +function testsuite_error(error_message) +{ + report("ERROR", "- " error_message) +} + +function handle_tap_result() +{ + details = result_obj["number"]; + if (length(result_obj["description"])) + details = details " " result_obj["description"] + + if (plan_seen == LATE_PLAN) + { + details = details " # AFTER LATE PLAN"; + } + else if (result_obj["is_unplanned"]) + { + details = details " # UNPLANNED"; + } + else if (result_obj["number"] != testno) + { + details = sprintf("%s # OUT-OF-ORDER (expecting %d)", + details, testno); + } + else if (result_obj["directive"]) + { + details = details " # " result_obj["directive"]; + if (length(result_obj["explanation"])) + details = details " " result_obj["explanation"] + } + + report(stringify_result_obj(result_obj), details) +} + +# `skip_reason` should be empty whenever planned > 0. +function handle_tap_plan(planned, skip_reason) +{ + planned += 0 # Avoid getting confused if, say, `planned` is "00" + if (length(skip_reason) && planned > 0) + abort("in function handle_tap_plan()") + if (plan_seen) + { + # Error, only one plan per stream is acceptable. + testsuite_error("multiple test plans") + return; + } + planned_tests = planned + # The TAP plan can come before or after *all* the TAP results; we speak + # respectively of an "early" or a "late" plan. If we see the plan line + # after at least one TAP result has been seen, assume we have a late + # plan; in this case, any further test result seen after the plan will + # be flagged as an error. + plan_seen = (testno >= 1 ? LATE_PLAN : EARLY_PLAN) + # If testno > 0, we have an error ("too many tests run") that will be + # automatically dealt with later, so do not worry about it here. If + # $plan_seen is true, we have an error due to a repeated plan, and that + # has already been dealt with above. Otherwise, we have a valid "plan + # with SKIP" specification, and should report it as a particular kind + # of SKIP result. + if (planned == 0 && testno == 0) + { + if (length(skip_reason)) + skip_reason = "- " skip_reason; + report("SKIP", skip_reason); + } +} + +function extract_tap_comment(line) +{ + if (index(line, diag_string) == 1) + { + # Strip leading `diag_string` from `line`. + line = substr(line, length(diag_string) + 1) + # And strip any leading and trailing whitespace left. + sub("^[ \t]*", "", line) + sub("[ \t]*$", "", line) + # Return what is left (if any). + return line; + } + return ""; +} + +# When this function is called, we know that line is a TAP result line, +# so that it matches the (perl) RE "^(not )?ok\b". +function setup_result_obj(line) +{ + # Get the result, and remove it from the line. + result_obj["is_ok"] = (substr(line, 1, 2) == "ok" ? 1 : 0) + sub("^(not )?ok[ \t]*", "", line) + + # If the result has an explicit number, get it and strip it; otherwise, + # automatically assing the next progresive number to it. + if (line ~ /^[0-9]+$/ || line ~ /^[0-9]+[^a-zA-Z0-9_]/) + { + match(line, "^[0-9]+") + # The final `+ 0` is to normalize numbers with leading zeros. + result_obj["number"] = substr(line, 1, RLENGTH) + 0 + line = substr(line, RLENGTH + 1) + } + else + { + result_obj["number"] = testno + } + + if (plan_seen == LATE_PLAN) + # No further test results are acceptable after a "late" TAP plan + # has been seen. + result_obj["is_unplanned"] = 1 + else if (plan_seen && testno > planned_tests) + result_obj["is_unplanned"] = 1 + else + result_obj["is_unplanned"] = 0 + + # Strip trailing and leading whitespace. + sub("^[ \t]*", "", line) + sub("[ \t]*$", "", line) + + # This will have to be corrected if we have a "TODO"/"SKIP" directive. + result_obj["description"] = line + result_obj["directive"] = "" + result_obj["explanation"] = "" + + if (index(line, "#") == 0) + return # No possible directive, nothing more to do. + + # Directives are case-insensitive. + rx = "[ \t]*#[ \t]*([tT][oO][dD][oO]|[sS][kK][iI][pP])[ \t]*" + + # See whether we have the directive, and if yes, where. + pos = match(line, rx "$") + if (!pos) + pos = match(line, rx "[^a-zA-Z0-9_]") + + # If there was no TAP directive, we have nothing more to do. + if (!pos) + return + + # Let`s now see if the TAP directive has been escaped. For example: + # escaped: ok \# SKIP + # not escaped: ok \\# SKIP + # escaped: ok \\\\\# SKIP + # not escaped: ok \ # SKIP + if (substr(line, pos, 1) == "#") + { + bslash_count = 0 + for (i = pos; i > 1 && substr(line, i - 1, 1) == "\\"; i--) + bslash_count += 1 + if (bslash_count % 2) + return # Directive was escaped. + } + + # Strip the directive and its explanation (if any) from the test + # description. + result_obj["description"] = substr(line, 1, pos - 1) + # Now remove the test description from the line, that has been dealt + # with already. + line = substr(line, pos) + # Strip the directive, and save its value (normalized to upper case). + sub("^[ \t]*#[ \t]*", "", line) + result_obj["directive"] = toupper(substr(line, 1, 4)) + line = substr(line, 5) + # Now get the explanation for the directive (if any), with leading + # and trailing whitespace removed. + sub("^[ \t]*", "", line) + sub("[ \t]*$", "", line) + result_obj["explanation"] = line +} + +function get_test_exit_message(status) +{ + if (status == 0) + return "" + if (status !~ /^[1-9][0-9]*$/) + abort("getting exit status") + if (status < 127) + exit_details = "" + else if (status == 127) + exit_details = " (command not found?)" + else if (status >= 128 && status <= 255) + exit_details = sprintf(" (terminated by signal %d?)", status - 128) + else if (status > 256 && status <= 384) + # We used to report an "abnormal termination" here, but some Korn + # shells, when a child process die due to signal number n, can leave + # in $? an exit status of 256+n instead of the more standard 128+n. + # Apparently, both behaviours are allowed by POSIX (2008), so be + # prepared to handle them both. See also Austing Group report ID + # 0000051 <http://www.austingroupbugs.net/view.php?id=51> + exit_details = sprintf(" (terminated by signal %d?)", status - 256) + else + # Never seen in practice. + exit_details = " (abnormal termination)" + return sprintf("exited with status %d%s", status, exit_details) +} + +function write_test_results() +{ + print ":global-test-result: " get_global_test_result() > trs_file + print ":recheck: " yn(must_recheck()) > trs_file + print ":copy-in-global-log: " yn(copy_in_global_log()) > trs_file + for (i = 0; i < test_results_index; i += 1) + print ":test-result: " test_results_list[i] > trs_file + close(trs_file); +} + +BEGIN { + +## ------- ## +## SETUP ## +## ------- ## + +'"$init_colors"' + +# Properly initialized once the TAP plan is seen. +planned_tests = 0 + +COOKED_PASS = expect_failure ? "XPASS": "PASS"; +COOKED_FAIL = expect_failure ? "XFAIL": "FAIL"; + +# Enumeration-like constants to remember which kind of plan (if any) +# has been seen. It is important that NO_PLAN evaluates "false" as +# a boolean. +NO_PLAN = 0 +EARLY_PLAN = 1 +LATE_PLAN = 2 + +testno = 0 # Number of test results seen so far. +bailed_out = 0 # Whether a "Bail out!" directive has been seen. + +# Whether the TAP plan has been seen or not, and if yes, which kind +# it is ("early" is seen before any test result, "late" otherwise). +plan_seen = NO_PLAN + +## --------- ## +## PARSING ## +## --------- ## + +is_first_read = 1 + +while (1) + { + # Involutions required so that we are able to read the exit status + # from the last input line. + st = getline + if (st < 0) # I/O error. + fatal("I/O error while reading from input stream") + else if (st == 0) # End-of-input + { + if (is_first_read) + abort("in input loop: only one input line") + break + } + if (is_first_read) + { + is_first_read = 0 + nextline = $0 + continue + } + else + { + curline = nextline + nextline = $0 + $0 = curline + } + # Copy any input line verbatim into the log file. + print | "cat >&3" + # Parsing of TAP input should stop after a "Bail out!" directive. + if (bailed_out) + continue + + # TAP test result. + if ($0 ~ /^(not )?ok$/ || $0 ~ /^(not )?ok[^a-zA-Z0-9_]/) + { + testno += 1 + setup_result_obj($0) + handle_tap_result() + } + # TAP plan (normal or "SKIP" without explanation). + else if ($0 ~ /^1\.\.[0-9]+[ \t]*$/) + { + # The next two lines will put the number of planned tests in $0. + sub("^1\\.\\.", "") + sub("[^0-9]*$", "") + handle_tap_plan($0, "") + continue + } + # TAP "SKIP" plan, with an explanation. + else if ($0 ~ /^1\.\.0+[ \t]*#/) + { + # The next lines will put the skip explanation in $0, stripping + # any leading and trailing whitespace. This is a little more + # tricky in truth, since we want to also strip a potential leading + # "SKIP" string from the message. + sub("^[^#]*#[ \t]*(SKIP[: \t][ \t]*)?", "") + sub("[ \t]*$", ""); + handle_tap_plan(0, $0) + } + # "Bail out!" magic. + # Older versions of prove and TAP::Harness (e.g., 3.17) did not + # recognize a "Bail out!" directive when preceded by leading + # whitespace, but more modern versions (e.g., 3.23) do. So we + # emulate the latter, "more modern" behaviour. + else if ($0 ~ /^[ \t]*Bail out!/) + { + bailed_out = 1 + # Get the bailout message (if any), with leading and trailing + # whitespace stripped. The message remains stored in `$0`. + sub("^[ \t]*Bail out![ \t]*", ""); + sub("[ \t]*$", ""); + # Format the error message for the + bailout_message = "Bail out!" + if (length($0)) + bailout_message = bailout_message " " $0 + testsuite_error(bailout_message) + } + # Maybe we have too look for dianogtic comments too. + else if (comments != 0) + { + comment = extract_tap_comment($0); + if (length(comment)) + report("#", comment); + } + } + +## -------- ## +## FINISH ## +## -------- ## + +# A "Bail out!" directive should cause us to ignore any following TAP +# error, as well as a non-zero exit status from the TAP producer. +if (!bailed_out) + { + if (!plan_seen) + { + testsuite_error("missing test plan") + } + else if (planned_tests != testno) + { + bad_amount = testno > planned_tests ? "many" : "few" + testsuite_error(sprintf("too %s tests run (expected %d, got %d)", + bad_amount, planned_tests, testno)) + } + if (!ignore_exit) + { + # Fetch exit status from the last line. + exit_message = get_test_exit_message(nextline) + if (exit_message) + testsuite_error(exit_message) + } + } + +write_test_results() + +exit 0 + +} # End of "BEGIN" block. +' + +# TODO: document that we consume the file descriptor 3 :-( +} 3>"$log_file" + +test $? -eq 0 || fatal "I/O or internal error" + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/tap-test b/tap-test new file mode 100755 index 0000000..481e333 --- /dev/null +++ b/tap-test @@ -0,0 +1,5 @@ +#! /bin/sh + +# run a GTest in tap mode. The test binary is passed as $1 + +$1 -k --tap diff --git a/tests/Makefile.am b/tests/Makefile.am index dedca14..152887a 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,8 +1,8 @@ include $(top_srcdir)/Makefile.decl -noinst_PROGRAMS = $(TEST_PROGS) +noinst_PROGRAMS += $(test_programs) -TEST_PROGS += mediaarttest +test_programs = mediaarttest AM_CPPFLAGS = \ -DTOP_SRCDIR=\"$(abs_top_srcdir)\" \ @@ -16,8 +16,8 @@ LDADD = \ $(BUILD_LIBS) \ $(LIBMEDIAART_LIBS) -mediaarttest = mediaarttest.c +mediaarttest_SOURCES = mediaarttest.c -EXTRA_DIST = \ +EXTRA_DIST += \ 543249_King-Kilo---Radium.mp3 \ cover.png diff --git a/tests/mediaarttest.c b/tests/mediaarttest.c index dc1806b..4abb08b 100644 --- a/tests/mediaarttest.c +++ b/tests/mediaarttest.c @@ -26,57 +26,74 @@ #include <libmediaart/mediaart.h> -struct { - const gchar *input; - const gchar *expected_output; -} strip_test_cases [] = { - { "nothing to strip here", "nothing to strip here" }, - { "Upper Case gOEs dOwN", "upper case goes down"}, - { "o", "o"}, - { "A", "a"}, - { "cool album (CD1)", "cool album"}, - { "cool album [CD1]", "cool album"}, - { "cool album {CD1}", "cool album"}, - { "cool album <CD1>", "cool album"}, - { " ", ""}, - { " a ", "a"}, - { "messy #title & stuff?", "messy title stuff"}, - { "Unbalanced [brackets", "unbalanced brackets" }, - { "Unbalanced (brackets", "unbalanced brackets" }, - { "Unbalanced <brackets", "unbalanced brackets" }, - { "Unbalanced brackets)", "unbalanced brackets" }, - { "Unbalanced brackets]", "unbalanced brackets" }, - { "Unbalanced brackets>", "unbalanced brackets" }, - { "Live at *WEMBLEY* dude!", "live at wembley dude" }, - { "met[xX[x]alli]ca", "metallica" }, - { NULL, NULL} +typedef struct { + const gchar *test_name; + const gchar *input1; + const gchar *input2; + const gchar *expected; +} TestInfo; + +static TestInfo strip_test_cases [] = { + { "nothing-to-strip", "nothing to strip here", NULL, "nothing to strip here" }, + { "case-strip", "Upper Case gOEs dOwN", NULL, "upper case goes down" }, + { "single-char", "o", NULL, "o" }, + { "single-char-case", "A", NULL, "a" }, + { "remove-parenthesis-round", "cool album (CD1)", NULL, "cool album" }, + { "remove-parenthesis-square", "cool album [CD1]", NULL, "cool album" }, + { "remove-parenthesis-squirly", "cool album {CD1}", NULL, "cool album" }, + { "remove-parenthesis-gt-lt", "cool album <CD1>", NULL, "cool album" }, + { "whitespace", " ", NULL, "" }, + { "whitespace-with-content", " a ", NULL, "a" }, + { "messy-title", "messy #title & stuff?", NULL, "messy title stuff" }, + { "unbalanced-brackets-square-start", "Unbalanced [brackets", NULL, "unbalanced brackets" }, + { "unbalanced-brackets-round-start", "Unbalanced (brackets", NULL, "unbalanced brackets" }, + { "unbalanced-brackets-gt-lt-start", "Unbalanced <brackets", NULL, "unbalanced brackets" }, + { "unbalanced-brackets-round-end", "Unbalanced brackets)", NULL, "unbalanced brackets" }, + { "unbalanced-brackets-square-end", "Unbalanced brackets]", NULL, "unbalanced brackets" }, + { "unbalanced-brackets-gt-lt-end", "Unbalanced brackets>", NULL, "unbalanced brackets" }, + { "messy-title-punctuation", "Live at *WEMBLEY* dude!", NULL, "live at wembley dude" }, + { "crap-brackets-everywhere", "met[xX[x]alli]ca", NULL, "metallica" }, + { NULL, NULL, NULL, NULL } }; -struct { - const gchar *artist; - const gchar *album; - const gchar *filename; -} mediaart_test_cases [] = { - {"Beatles", "Sgt. Pepper", - "album-2a9ea35253dbec60e76166ec8420fbda-cfba4326a32b44b8760b3a2fc827a634.jpeg"}, - - { "", "sgt. pepper", - "album-d41d8cd98f00b204e9800998ecf8427e-cfba4326a32b44b8760b3a2fc827a634.jpeg"}, - - { " ", "sgt. pepper", - "album-d41d8cd98f00b204e9800998ecf8427e-cfba4326a32b44b8760b3a2fc827a634.jpeg"}, - - { NULL, "sgt. pepper", - "album-cfba4326a32b44b8760b3a2fc827a634-7215ee9c7d9dc229d2921a40e899ec5f.jpeg"}, +static TestInfo location_test_cases [] = { + { "normal-case", + "Beatles", "Sgt. Pepper", + "album-2a9ea35253dbec60e76166ec8420fbda-cfba4326a32b44b8760b3a2fc827a634.jpeg" }, + { "empty-artist", + "", "sgt. pepper", + "album-d41d8cd98f00b204e9800998ecf8427e-cfba4326a32b44b8760b3a2fc827a634.jpeg" }, + { "whitespace-artist", + " ", "sgt. pepper", + "album-d41d8cd98f00b204e9800998ecf8427e-cfba4326a32b44b8760b3a2fc827a634.jpeg" }, + { "null-artist", + NULL, "sgt. pepper", + "album-cfba4326a32b44b8760b3a2fc827a634-7215ee9c7d9dc229d2921a40e899ec5f.jpeg" }, + { "null-title", + "Beatles", NULL, + "album-2a9ea35253dbec60e76166ec8420fbda-7215ee9c7d9dc229d2921a40e899ec5f.jpeg" }, + { NULL, NULL, NULL, NULL } +}; - { "Beatles", NULL, - "album-2a9ea35253dbec60e76166ec8420fbda-7215ee9c7d9dc229d2921a40e899ec5f.jpeg"}, +static void +setup (TestInfo *info, + gconstpointer context) +{ + TestInfo *this_test = (gpointer) context; + if (this_test) { + *info = *this_test; + } +} - { NULL, NULL, NULL } -}; +static void +teardown (TestInfo *info, + gconstpointer context) +{ +} static void -test_mediaart_new (void) +test_mediaart_new (TestInfo *test_info, + gconstpointer context) { MediaArtProcess *process; GError *error = NULL; @@ -90,18 +107,14 @@ test_mediaart_new (void) } static void -test_mediaart_stripping (void) +test_mediaart_stripping (TestInfo *test_info, + gconstpointer context) { - gint i; gchar *result; - for (i = 0; strip_test_cases[i].input != NULL; i++) { - result = media_art_strip_invalid_entities (strip_test_cases[i].input); - g_assert_cmpstr (result, ==, strip_test_cases[i].expected_output); - g_free (result); - } - - g_print ("(%d test cases) ", i); + result = media_art_strip_invalid_entities (test_info->input1); + g_assert_cmpstr (result, ==, test_info->expected); + g_free (result); } static void @@ -127,32 +140,30 @@ test_mediaart_stripping_failures (void) g_test_trap_assert_stderr ("*assertion 'original != NULL' failed*"); } + static void -test_mediaart_location (void) +test_mediaart_location (TestInfo *test_info, + gconstpointer context) { gchar *path = NULL, *local_uri = NULL; gchar *expected; - gint i; - - for (i = 0; mediaart_test_cases[i].filename != NULL; i++) { - media_art_get_path (mediaart_test_cases[i].artist, - mediaart_test_cases[i].album, - "album", - "file:///home/test/a.mp3", - &path, - &local_uri); - expected = g_build_path (G_DIR_SEPARATOR_S, - g_get_user_cache_dir (), - "media-art", - mediaart_test_cases[i].filename, - NULL); - g_assert_cmpstr (path, ==, expected); + + media_art_get_path (test_info->input1, + test_info->input2, + "album", + "file:///home/test/a.mp3", + &path, + &local_uri); + expected = g_build_path (G_DIR_SEPARATOR_S, + g_get_user_cache_dir (), + "media-art", + test_info->expected, + NULL); + g_assert_cmpstr (path, ==, expected); - g_free (expected); - g_free (path); - g_free (local_uri); - } - g_print ("(%d test cases) ", i); + g_free (expected); + g_free (path); + g_free (local_uri); } static void @@ -177,8 +188,8 @@ test_mediaart_location_path (void) gchar *expected; /* Use path instead of URI */ - media_art_get_path (mediaart_test_cases[0].artist, - mediaart_test_cases[0].album, + media_art_get_path (location_test_cases[0].input1, + location_test_cases[0].input2, "album", "/home/test/a.mp3", &path, @@ -186,7 +197,7 @@ test_mediaart_location_path (void) expected = g_build_path (G_DIR_SEPARATOR_S, g_get_user_cache_dir (), "media-art", - mediaart_test_cases[0].filename, + location_test_cases[0].expected, NULL); g_assert_cmpstr (path, ==, expected); @@ -368,32 +379,38 @@ test_mediaart_process_failures_subprocess (void) int main (int argc, char **argv) { - int success = EXIT_SUCCESS; + gint success = EXIT_SUCCESS; + gint i; g_test_init (&argc, &argv, NULL); - g_test_add_func ("/mediaart/new", - test_mediaart_new); - g_test_add_func ("/mediaart/stripping", - test_mediaart_stripping); - g_test_add_func ("/mediaart/stripping_failures", - test_mediaart_stripping_failures); - g_test_add_func ("/mediaart/stripping_failures/subprocess", - test_mediaart_stripping_failures_subprocess); - g_test_add_func ("/mediaart/location", - test_mediaart_location); - g_test_add_func ("/mediaart/location_null", - test_mediaart_location_null); - g_test_add_func ("/mediaart/location_path", - test_mediaart_location_path); - g_test_add_func ("/mediaart/embedded_mp3", - test_mediaart_embedded_mp3); - g_test_add_func ("/mediaart/process_buffer", - test_mediaart_process_buffer); - g_test_add_func ("/mediaart/process_failures", - test_mediaart_process_failures); - g_test_add_func ("/mediaart/process_failures/subprocess", - test_mediaart_process_failures_subprocess); + g_test_add ("/mediaart/new", TestInfo, NULL, setup, test_mediaart_new, teardown); + + for (i = 0; strip_test_cases[i].test_name; i++) { + gchar *testpath; + + testpath = g_strconcat ("/mediaart/stripping/", strip_test_cases[i].test_name, NULL); + g_test_add (testpath, TestInfo, &strip_test_cases[i], setup, test_mediaart_stripping, teardown); + g_free (testpath); + } + + g_test_add_func ("/mediaart/stripping_failures", test_mediaart_stripping_failures); + g_test_add_func ("/mediaart/stripping_failures/subprocess", test_mediaart_stripping_failures_subprocess); + + for (i = 0; location_test_cases[i].test_name; i++) { + gchar *testpath; + + testpath = g_strconcat ("/mediaart/location/", location_test_cases[i].test_name, NULL); + g_test_add (testpath, TestInfo, &location_test_cases[i], setup, test_mediaart_location, teardown); + g_free (testpath); + } + + g_test_add_func ("/mediaart/location_null", test_mediaart_location_null); + g_test_add_func ("/mediaart/location_path", test_mediaart_location_path); + g_test_add_func ("/mediaart/embedded_mp3", test_mediaart_embedded_mp3); + g_test_add_func ("/mediaart/process_buffer", test_mediaart_process_buffer); + g_test_add_func ("/mediaart/process_failures", test_mediaart_process_failures); + g_test_add_func ("/mediaart/process_failures/subprocess", test_mediaart_process_failures_subprocess); success = g_test_run (); |