diff --git a/Makefile.in b/Makefile.in index 7166c1887..2a64ad00c 100644 --- a/Makefile.in +++ b/Makefile.in @@ -350,14 +350,14 @@ test: test-prep install-force test_low_level test_high_level # We want the various tests to run serially so their output doesn't mix # We can do that by adding ordering dependencies based on what goals are being used. # -test_goals := test_low_level test_fishscript test_interactive +test_goals := test_low_level test_invocation test_fishscript test_interactive # # The following variables define targets that depend on the tests. If any more targets # are added that depend, directly or indirectly, on tests, they need to be recorded here. # test_test_deps = test_low_level $(test_high_level_test_deps) -test_high_level_test_deps = test_fishscript test_interactive +test_high_level_test_deps = test_invocation test_fishscript test_interactive active_test_goals = $(filter $(test_goals),$(foreach a,$(or $(MAKECMDGOALS),$(.DEFAULT_GOAL)),$(a) $($(a)_test_deps))) filter_up_to = $(eval b:=1)$(foreach a,$(2),$(and $(b),$(if $(subst $(1),,$(a)),$(a),$(eval b:=)))) @@ -371,9 +371,13 @@ test_low_level: fish_tests $(call filter_up_to,test_low_level,$(active_test_goal test_high_level: DESTDIR = $(PWD)/test/root/ test_high_level: prefix = . -test_high_level: test-prep install-force test_fishscript test_interactive +test_high_level: test-prep install-force test_invocation test_fishscript test_interactive .PHONY: test_high_level +test_invocation: $(call filter_up_to,test_invocation,$(active_test_goals)) + cd tests; ./invocation.sh +.PHONY: test_invocation + test_fishscript: $(call filter_up_to,test_fishscript,$(active_test_goals)) cd tests; ../test/root/bin/fish test.fish .PHONY: test_fishscript @@ -382,6 +386,7 @@ test_interactive: $(call filter_up_to,test_interactive,$(active_test_goals)) cd tests; ../test/root/bin/fish interactive.fish .PHONY: test_interactive + # # commands.hdr collects documentation on all commands, functions and # builtins diff --git a/tests/invocation.sh b/tests/invocation.sh new file mode 100755 index 000000000..645031e97 --- /dev/null +++ b/tests/invocation.sh @@ -0,0 +1,242 @@ +#!/bin/bash +## +# Test that the invocation of the fish executable works as we hope. +# +# We try to run the 'fish' binary with different command line switches. +# Each time we check against an output that we expect. +# +# We are testing fish's invocation itself, so this is not written in +# fish itself - if the invocation wasn't working, we'd never even +# be able to this test to check that the invocation wasn't working. +# + +# Errors will be fatal +set -e + +# If any command in the pipeline fails report the rc of the first fail. +set -o pipefail + +# If nothing matches a glob expansion, return nothing (not the glob +# itself) +shopt -s nullglob + +# The directory this script is in (as everything is relative to here) +here="$(cd "$(dirname "$0")" && pwd -P)" +cd "$here" + +# The temporary directory to use +temp_dir="$here/../test" + +# The files we're going to execute are in the 'invocation' directory. +files_to_test=($(echo invocation/*.invoke)) + +# The fish binary we are testing - for manual testing, may be overridden +fish_exe="${fish_exe:-../test/root/bin/fish}" +fish_dir="$(dirname "${fish_exe}")" +fish_leaf="$(basename "${fish_exe}")" + + +# Terminal colouring +term_red="$(tput setaf 1)" +term_green="$(tput setaf 2)" +term_yellow="$(tput setaf 3)" +term_blue="$(tput setaf 4)" +term_magenta="$(tput setaf 5)" +term_cyan="$(tput setaf 6)" +term_white="$(tput setaf 7)" +term_reset="$(tput sgr0)" + + +## +# Set variables to known values so that they will not affect the +# execution of the test. +function clean_environment() { + + # Reset the terminal variables to a known type. + export TERM=xterm + unset ITERM_PROFILE + + # And the language as well, so that we do not see differences in + # output dur to the user's locale + export LANGUAGE=en_US:en + + # Ensure that the fish environment we use is in a clean state + rm -rf "${temp_dir}/data" "${temp_dir}/home" + mkdir -p "${temp_dir}/data" "${temp_dir}/home" "${temp_dir}/home/fish" + export XDG_DATA_HOME="${temp_dir}/data" + export XDG_CONFIG_HOME="${temp_dir}/home" +} + + +## +# Fail completely :-( +function fail() { + say red "FAIL: $@" >&2 + exit 1 +} + + +## +# Coloured output +function say() { + local color_name="$1" + local msg="$2" + local color_var="term_${color_name}" + local color="${!color_var}" + + echo "$color$msg$term_reset" +} + + +## +# Actual testing of a .invocation file. +function test_file() { + local file="$*" + local dir="$(dirname "$file")" + local base="$(basename "$file" .invoke)" + local test_config="${dir}/${base}.config" + local test_stdout="${dir}/${base}.tmp.out" + local test_stderr="${dir}/${base}.tmp.err" + local want_stdout="${dir}/${base}.out" + local grep_stdout="${dir}/${base}.grep" + local want_stderr="${dir}/${base}.err" + local empty="${dir}/${base}.empty" + local -a filter + local rc=0 + local test_args_literal + local test_args + local out_status=0 + local err_status=0 + + # Literal arguments, for printing + test_args_literal="$(cat "$file")" + # Read the test arguments, escaping things that might be processed by us + test_args="$(sed 's/\$/\$/' "$file" | tr '\n' ' ')" + + # Create an empty file so that we can compare against it if needed + echo -n > "${empty}" + + # If they supplied a configuration file, we create it here + if [ -f "$test_config" ] ; then + cat "$test_config" > "${temp_dir}/home/fish/config.fish" + else + rm -f "${temp_dir}/home/fish/config.fish" + fi + + # In some cases we want to check only a part of the output. + # For those we filter the output through grep'd matches. + if [ -f "$grep_stdout" ] ; then + # grep '-o', '-E' and '-f' are supported by the tools in modern GNU + # environments, and on OS X. + filter=('grep' '-o' '-E' '-f' "$grep_stdout") + else + filter=('cat') + fi + + echo -n "Testing file $file ... " + + # The hoops we are jumping through here, with changing directory are + # so that we always execute fish as './fish', which means that any + # error messages will appear the same, even if the tested binary + # is not one that we built here. + # We disable the exit-on-error here, so that we can catch the return + # code. + set +e + eval "cd \"$fish_dir\" && \"./$fish_leaf\" $test_args" \ + 2> "$test_stderr" \ + < /dev/null \ + | ${filter[*]} \ + > "$test_stdout" + rc="$?" + set -e + + if [ "$rc" != '0' ] ; then + # Write the return code on to the end of the stderr, so that it can be + # checked like anything else. + echo "RC: $rc" >> "${test_stderr}" + fi + + # If the wanted output files are not present, they are assumed empty. + if [ ! -f "$want_stdout" ] ; then + want_stdout="$empty" + fi + if [ ! -f "$want_stderr" ] ; then + want_stderr="$empty" + fi + + # The standard error that we get will report errors using non-relative + # filenames, so we try to replace these with the variable names. + # + # However, fish will also have helpfully translated the home directory + # into '~/' in the error report. Consequently, we need to perform a + # small fix-up so that we can replace the string sanely. + xdg_config_in_home="$XDG_CONFIG_HOME" + if [ "${xdg_config_in_home:0:${#HOME}}" = "${HOME}" ] ; then + xdg_config_in_home="~/${xdg_config_in_home:${#HOME}+1}" + fi + # 'sed -i' (inplace) has different syntax on BSD and GNU versions of + # the tool, so cannot be used here, hence we write to a separate file, + # and then move back. + sed "s,$xdg_config_in_home,\$XDG_CONFIG_HOME,g" "${test_stderr}" > "${test_stderr}.new" + mv -f "${test_stderr}.new" "${test_stderr}" + + # Check the results + if ! diff "${test_stdout}" "${want_stdout}" >/dev/null 2>/dev/null ; then + out_status=1 + fi + if ! diff "${test_stderr}" "${want_stderr}" >/dev/null 2>/dev/null ; then + err_status=1 + fi + + if [ "$out_status" = '0' ] && \ + [ "$err_status" = '0' ] ; then + say green "ok" + # clean up tmp files + rm -f "${test_stdout}" "${test_stderr}" "${empty}" + rc=0 + else + say red "fail" + say blue "$test_args_literal" | sed 's/^/ /' + + if [ "$out_status" != '0' ] ; then + say yellow "Output differs for file $file. Diff follows:" + colordiff -u "${test_stdout}" "${want_stdout}" + fi + if [ "$err_status" != '0' ] ; then + say yellow "Error output differs for file $file. Diff follows:" + colordiff -u "${test_stderr}" "${want_stderr}" + fi + rc=1 + fi + + return $rc +} + + +######################################################################## +# Main harness + +if [ ! -x "${fish_exe}" ] ; then + fail "Fish executable not found at '${fish_exe}'" +fi + +clean_environment + +say cyan "Testing shell invocation funtionality" + +passed=0 +failed=0 +for file in ${files_to_test[*]} ; do + if ! test_file "$file" ; then + failed=$(( failed + 1 )) + else + passed=$(( passed + 1 )) + fi +done + +echo "Encountered $failed errors in the invocation tests (out of $(( failed + passed )))." + +if [ "$failed" != 0 ] ; then + exit 1 +fi +exit 0 diff --git a/tests/invocation/init-command-2.invoke b/tests/invocation/init-command-2.invoke new file mode 100644 index 000000000..667c6273f --- /dev/null +++ b/tests/invocation/init-command-2.invoke @@ -0,0 +1 @@ +-C 'echo init-command' -C 'echo 2nd init-command' diff --git a/tests/invocation/init-command-2.out b/tests/invocation/init-command-2.out new file mode 100644 index 000000000..00962a99b --- /dev/null +++ b/tests/invocation/init-command-2.out @@ -0,0 +1,2 @@ +init-command +2nd init-command diff --git a/tests/invocation/init-command-mix-ordering.invoke b/tests/invocation/init-command-mix-ordering.invoke new file mode 100644 index 000000000..1718e5c03 --- /dev/null +++ b/tests/invocation/init-command-mix-ordering.invoke @@ -0,0 +1 @@ +-c 'echo command' -C 'echo init-command' diff --git a/tests/invocation/init-command-mix-ordering.out b/tests/invocation/init-command-mix-ordering.out new file mode 100644 index 000000000..8ca70f9a2 --- /dev/null +++ b/tests/invocation/init-command-mix-ordering.out @@ -0,0 +1,2 @@ +init-command +command diff --git a/tests/invocation/init-command-mix.invoke b/tests/invocation/init-command-mix.invoke new file mode 100644 index 000000000..e40eb4088 --- /dev/null +++ b/tests/invocation/init-command-mix.invoke @@ -0,0 +1 @@ +-C 'echo init-command' -c 'echo command' diff --git a/tests/invocation/init-command-mix.out b/tests/invocation/init-command-mix.out new file mode 100644 index 000000000..8ca70f9a2 --- /dev/null +++ b/tests/invocation/init-command-mix.out @@ -0,0 +1,2 @@ +init-command +command diff --git a/tests/invocation/init-command.invoke b/tests/invocation/init-command.invoke new file mode 100644 index 000000000..deb1f1061 --- /dev/null +++ b/tests/invocation/init-command.invoke @@ -0,0 +1 @@ +-C 'echo init-command' diff --git a/tests/invocation/init-command.out b/tests/invocation/init-command.out new file mode 100644 index 000000000..35db3ef38 --- /dev/null +++ b/tests/invocation/init-command.out @@ -0,0 +1 @@ +init-command