mirror of https://github.com/MidnightCommander/mc
extfs: introduce a tester.
This tester tests that helpers implement the "list" command correctly. Signed-off-by: Andrew Borodin <aborodin@vmail.ru>
This commit is contained in:
parent
13a805bd79
commit
7a3891eb90
|
@ -10,11 +10,39 @@ LIBS = $(top_builddir)/lib/libmc.la
|
||||||
|
|
||||||
# Programs/scripts to build on 'make check'.
|
# Programs/scripts to build on 'make check'.
|
||||||
check_PROGRAMS = mc_parse_ls_l
|
check_PROGRAMS = mc_parse_ls_l
|
||||||
|
check_SCRIPTS = run
|
||||||
|
|
||||||
# Tests to run on 'make check'
|
# Tests to run on 'make check'
|
||||||
TESTS = run
|
TESTS = run
|
||||||
|
|
||||||
|
# On 'make clean', delete 'run' as well.
|
||||||
|
CLEANFILES = run
|
||||||
|
|
||||||
mc_parse_ls_l_SOURCES = \
|
mc_parse_ls_l_SOURCES = \
|
||||||
mc_parse_ls_l.c
|
mc_parse_ls_l.c
|
||||||
|
|
||||||
EXTRA_DIST = run
|
data_files_to_distribute = \
|
||||||
|
data/dummy
|
||||||
|
|
||||||
|
EXTRA_DIST = mc_xcat test_all $(data_files_to_distribute)
|
||||||
|
|
||||||
|
run:
|
||||||
|
echo '#!/bin/sh' > $@
|
||||||
|
echo >> $@
|
||||||
|
echo '# This script is an easy way to launch the "test_all" script' >> $@
|
||||||
|
echo '# with all the required arguments.' >> $@
|
||||||
|
echo '#' >> $@
|
||||||
|
echo '# Run this script with "--help" to learn more.' >> $@
|
||||||
|
echo >> $@
|
||||||
|
echo '# Where to find mc_parse_ls_l and mc_xcat, respectively.' >> $@
|
||||||
|
echo 'PATH="$(abs_builddir):$(abs_srcdir):$$PATH"' >> $@
|
||||||
|
echo >> $@
|
||||||
|
# The 'abs_' isn't mandatory. It lets you move this script out of the build tree.
|
||||||
|
echo '"$(abs_srcdir)"/test_all "$$@" \' >> $@
|
||||||
|
echo ' --data-dir "$(abs_srcdir)/data" \' >> $@
|
||||||
|
# Before installation, some helpers are in the build tree, some in the src tree.
|
||||||
|
echo ' --helpers-dir "$(abs_top_builddir)/src/vfs/extfs/helpers" \' >> $@
|
||||||
|
echo ' --helpers-dir "$(abs_top_srcdir)/src/vfs/extfs/helpers"' >> $@
|
||||||
|
chmod +x $@
|
||||||
|
# (We can alternatively create run from a run.in template
|
||||||
|
# with 'AC_CONFIG_FILES[run, chmod +x run]'.)
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
This is a dummy file meant to ensure this directory isn't empty so that git always creates it.
|
||||||
|
|
||||||
|
Otherwise our 'test_all' script will fail complaining the directory doesn't exist.
|
|
@ -0,0 +1,16 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# This program is intended to be identical to 'cat' with the exception that
|
||||||
|
# it ignores any arguments past the 1st one.
|
||||||
|
#
|
||||||
|
# To understand why it's needed, first read the section about
|
||||||
|
# MC_TEST_EXTFS_LIST_CMD in the README. As explained there, the command in
|
||||||
|
# MC_TEST_EXTFS_LIST_CMD has to ignore any extra arguments passed to it.
|
||||||
|
# The tester achieves this by invoking a helper thus (roughly):
|
||||||
|
#
|
||||||
|
# export MC_TEST_EXTFS_LIST_CMD="mc_xcat /path/to/fake/input"
|
||||||
|
# sh /path/to/helper list /dev/null
|
||||||
|
#
|
||||||
|
|
||||||
|
exec cat "$1"
|
|
@ -1,15 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo "This is a placeholder."
|
|
||||||
echo "It will be replaced in the future with a script that does the real"
|
|
||||||
echo "work of testing the helpers."
|
|
||||||
echo
|
|
||||||
echo "Here's a silly test to check whether 'ls', had it been an extfs helper,"
|
|
||||||
echo "works properly:"
|
|
||||||
echo
|
|
||||||
|
|
||||||
LC_ALL=C ls -l | ./mc_parse_ls_l
|
|
||||||
|
|
||||||
# (LC_ALL=C is meant to prevent date formats MC can't parse. This might
|
|
||||||
# still not be foolproof, but that's just a demonstration.)
|
|
|
@ -0,0 +1,393 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# A tester for extfs helpers.
|
||||||
|
#
|
||||||
|
# Copyright (C) 2016
|
||||||
|
# The Free Software Foundation, Inc.
|
||||||
|
#
|
||||||
|
# This file is part of the Midnight Commander.
|
||||||
|
#
|
||||||
|
# The Midnight Commander 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 3 of the License,
|
||||||
|
# or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# The Midnight Commander 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/>.
|
||||||
|
|
||||||
|
help() {
|
||||||
|
cat << EOS
|
||||||
|
|
||||||
|
NAME
|
||||||
|
|
||||||
|
$(basename "$0") - Tests the 'list' command of extfs helpers.
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
|
||||||
|
$(basename "$0") \\
|
||||||
|
--data-dir /path/to/where/data/files/are/stored \\
|
||||||
|
--helpers-dir /path/to/where/helpers/are/stored
|
||||||
|
|
||||||
|
(But you're more likely to invoke this program with the 'run' script
|
||||||
|
created by 'make check'; or by 'make check' itself.)
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
|
||||||
|
This program tests extfs helpers by feeding them input and comparing
|
||||||
|
their output to the expected output.
|
||||||
|
|
||||||
|
See README for full details.
|
||||||
|
|
||||||
|
You need to tell this program two things: where the helpers are stored,
|
||||||
|
and where the "data files" are stored. The data files are *.input files
|
||||||
|
that are fed to the helpers and *.output files that are the correct
|
||||||
|
output expected from these helpers.
|
||||||
|
|
||||||
|
EOS
|
||||||
|
}
|
||||||
|
|
||||||
|
#"'
|
||||||
|
|
||||||
|
############################ Global variables ##############################
|
||||||
|
|
||||||
|
# The directories used.
|
||||||
|
DATA_DIR=
|
||||||
|
HELPERS_DIR1=
|
||||||
|
HELPERS_DIR2=
|
||||||
|
|
||||||
|
opt_create_output=no # "yes" if '--create-output' provided.
|
||||||
|
opt_run_mcdiff_on_error=no # "yes" if '--mcdiff' provided.
|
||||||
|
|
||||||
|
############################ Coding guidance ###############################
|
||||||
|
|
||||||
|
#
|
||||||
|
# Portability notes:
|
||||||
|
#
|
||||||
|
# - We do `local var="$whatever"` instead of `local var=$whatever` for
|
||||||
|
# compatibility with Dash. See http://unix.stackexchange.com/questions/97560.
|
||||||
|
#
|
||||||
|
# - The 'local' keyword used in this file isn't mandatory. Feel free to
|
||||||
|
# remove it if it isn't supported by your archaic shell.
|
||||||
|
#
|
||||||
|
|
||||||
|
############################ Utility functions #############################
|
||||||
|
|
||||||
|
#
|
||||||
|
# Does $1 contain $2?
|
||||||
|
#
|
||||||
|
# Accepts basic regex.
|
||||||
|
#
|
||||||
|
has_string() {
|
||||||
|
local haystack="$1" # quotes needed for Dash, as may contain spaces (see notes above).
|
||||||
|
local needle="$2"
|
||||||
|
echo "$haystack" | grep "$needle" > /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Given "/path/to/basename.and.some.ext", returns "basename"
|
||||||
|
#
|
||||||
|
basename_sans_extensions() {
|
||||||
|
local base="$(basename "$1")"
|
||||||
|
echo "${base%%.*}"
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Does an executable exist?
|
||||||
|
#
|
||||||
|
has_prog() {
|
||||||
|
# see http://stackoverflow.com/questions/592620
|
||||||
|
command -v "$1" >/dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Can we use colors?
|
||||||
|
#
|
||||||
|
has_colors() {
|
||||||
|
[ -t 1 ] && has_string "$TERM" 'linux\|xterm\|screen\|tmux\|putty'
|
||||||
|
}
|
||||||
|
|
||||||
|
init_colors() {
|
||||||
|
if has_colors; then
|
||||||
|
ESC=$(printf '\033') # for portability
|
||||||
|
C_bold="$ESC[1m"
|
||||||
|
C_green="$ESC[1;32m"
|
||||||
|
C_red="$ESC[1;31m"
|
||||||
|
C_magenta="$ESC[1;35m"
|
||||||
|
C_norm="$ESC[0m"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# A few colorful alternatives to 'echo'.
|
||||||
|
#
|
||||||
|
header() { echo $C_bold"$@"$C_norm; }
|
||||||
|
err() { echo $C_red"$@"$C_norm; }
|
||||||
|
notice() { echo $C_magenta"$@"$C_norm; }
|
||||||
|
success() { echo $C_green"$@"$C_norm; }
|
||||||
|
|
||||||
|
die() {
|
||||||
|
err "Error: $@"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_dir_exists() {
|
||||||
|
[ -d "$1" ] || die "The directory '$1' doesn't exist, or is not a directory."
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Creates a temporary file.
|
||||||
|
#
|
||||||
|
temp_file() {
|
||||||
|
local template="$1"
|
||||||
|
# BSD's doesn't support -t.
|
||||||
|
mktemp "${TMPDIR:-/tmp}/$template"
|
||||||
|
}
|
||||||
|
|
||||||
|
################################ Main code #################################
|
||||||
|
|
||||||
|
#
|
||||||
|
# Prints out the command to run a helper, if it can find it.
|
||||||
|
#
|
||||||
|
# For example,
|
||||||
|
#
|
||||||
|
# find_helper uzip /path/to/helpers/dir
|
||||||
|
#
|
||||||
|
# prints:
|
||||||
|
#
|
||||||
|
# /usr/bin/perl -w /path/to/helpers/dir/uzip
|
||||||
|
#
|
||||||
|
# Since helpers in the build tree don't yet have executable bit set, we
|
||||||
|
# need to extract the shebang line.
|
||||||
|
#
|
||||||
|
find_helper() {
|
||||||
|
local helper_name="$1"
|
||||||
|
local dir="$2"
|
||||||
|
|
||||||
|
local try="$dir/$helper_name"
|
||||||
|
if [ -f "$try" ]; then
|
||||||
|
helper_CMD="$(head -1 $try | cut -c 3-) $try" # reason #1 we don't allow spaces in pathnames.
|
||||||
|
true
|
||||||
|
else
|
||||||
|
false
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# The crux of this program.
|
||||||
|
#
|
||||||
|
run() {
|
||||||
|
|
||||||
|
local error_count=0
|
||||||
|
local pass_count=0
|
||||||
|
|
||||||
|
for INPUT in "$DATA_DIR"/*.input; do
|
||||||
|
|
||||||
|
has_string "$INPUT" '\*' && break # we can't use 'shopt -s nullglob' as it's bash-specific.
|
||||||
|
|
||||||
|
header "Testing $INPUT"
|
||||||
|
|
||||||
|
has_string "$INPUT" " " && die "Error: filename contains spaces."
|
||||||
|
|
||||||
|
#
|
||||||
|
# Set up variables:
|
||||||
|
#
|
||||||
|
|
||||||
|
local helper_name="$(basename_sans_extensions "$INPUT")"
|
||||||
|
local expected_parsed_output="${INPUT%.input}.output"
|
||||||
|
local env_vars_file="${INPUT%.input}.env_vars"
|
||||||
|
local args_file="${INPUT%.input}.args"
|
||||||
|
|
||||||
|
local do_create_output=no
|
||||||
|
|
||||||
|
if [ ! -f "$expected_parsed_output" ]; then
|
||||||
|
# Corresponding *.output file doesn't exist. We either create it, later, or exit with error.
|
||||||
|
if [ $opt_create_output = "yes" ]; then
|
||||||
|
do_create_output=yes
|
||||||
|
else
|
||||||
|
err
|
||||||
|
err "Missing file: '$expected_parsed_output'."
|
||||||
|
err "You have to create an '.output' file for each '.input' one."
|
||||||
|
err
|
||||||
|
notice "Tip: invoke this program with '--create-output' to"
|
||||||
|
notice "automatically create missing '.output' files."
|
||||||
|
notice
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
find_helper "$helper_name" "$HELPERS_DIR1" ||
|
||||||
|
find_helper "$helper_name" "$HELPERS_DIR2" ||
|
||||||
|
die "I can't find helper '$helper_name' in either $HELPERS_DIR1 or $HELPERS_DIR2"
|
||||||
|
|
||||||
|
local extra_parser_args=""
|
||||||
|
[ -f "$args_file" ] && extra_parser_args="$(cat "$args_file")"
|
||||||
|
|
||||||
|
local actual_output="$(temp_file $helper_name.actual-output.XXXXXXXX)"
|
||||||
|
local actual_parsed_output="$(temp_file $helper_name.actual-parsed-output.XXXXXXXX)"
|
||||||
|
|
||||||
|
#
|
||||||
|
# Variables are all set. Now do the actual stuff:
|
||||||
|
#
|
||||||
|
|
||||||
|
(
|
||||||
|
MC_TEST_EXTFS_LIST_CMD="mc_xcat $INPUT" # reason #2 we don't allow spaces in pathnames.
|
||||||
|
export MC_TEST_EXTFS_LIST_CMD
|
||||||
|
if [ -f "$env_vars_file" ]; then
|
||||||
|
set -a # "allexport: Export all variables assigned to."
|
||||||
|
. "$env_vars_file"
|
||||||
|
set +a
|
||||||
|
fi
|
||||||
|
$helper_CMD list /dev/null > "$actual_output"
|
||||||
|
)
|
||||||
|
|
||||||
|
error_count=$((error_count + 1)) # we'll decrement it later.
|
||||||
|
|
||||||
|
if [ ! -s "$actual_output" ]; then
|
||||||
|
err
|
||||||
|
err "The helper '$helper_name' produced no output for this input. Something is wrong."
|
||||||
|
err
|
||||||
|
err "Make sure this helper supports testability: that it uses \$MC_TEST_EXTFS_LIST_CMD."
|
||||||
|
err
|
||||||
|
err "You may try running the helper yourself with:"
|
||||||
|
err
|
||||||
|
err " \$ MC_TEST_EXTFS_LIST_CMD=\"mc_xcat $INPUT\" \\"
|
||||||
|
err " $helper_CMD list /dev/null"
|
||||||
|
err
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# '--symbolic-ids': uid/gid aren't portable between computers,
|
||||||
|
# of course, so we always represent them symbolically when possible.
|
||||||
|
if ! mc_parse_ls_l --symbolic-ids $extra_parser_args "$actual_output" > "$actual_parsed_output"; then
|
||||||
|
err
|
||||||
|
err "ERROR: Parsing of the output of the helper '$helper_name' has failed."
|
||||||
|
err "This means that $helper_name has produced output that MC won't be able to parse."
|
||||||
|
err "Run the parsing command yourself ('mc_parse_ls_l $extra_parser_args $actual_output')"
|
||||||
|
err "to figure out the problem."
|
||||||
|
err
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $do_create_output = "yes" ]; then
|
||||||
|
# We arrive here if we were invoked with '--create-output' and
|
||||||
|
# the .output file doesn't exist. We create it and move to the next iteration.
|
||||||
|
cp "$actual_parsed_output" "$expected_parsed_output"
|
||||||
|
notice "The output file has been created in $expected_parsed_output"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! cmp "$expected_parsed_output" "$actual_parsed_output"; then
|
||||||
|
err
|
||||||
|
err "ERROR: $helper_name has produced output that's different than the expected output."
|
||||||
|
err
|
||||||
|
err " Expected output (after parsing): $expected_parsed_output"
|
||||||
|
err " Actual output (after parsing): $actual_parsed_output"
|
||||||
|
err
|
||||||
|
err "This might mean that a bug was introduced into $helper_name. Or that a bug was fixed."
|
||||||
|
err "Please compare the files."
|
||||||
|
err
|
||||||
|
err "If the actual output is the correct one, just copy the latter file"
|
||||||
|
err "onto the former (and commit to the git repository)."
|
||||||
|
err
|
||||||
|
if [ $opt_run_mcdiff_on_error = "yes" ]; then
|
||||||
|
notice "Hit ENTER to launch mcdiff ..."
|
||||||
|
read DUMMY_VAR # dash needs this.
|
||||||
|
${MCDIFF:-mcdiff} "$expected_parsed_output" "$actual_parsed_output"
|
||||||
|
else
|
||||||
|
notice "Tip: invoke this program with '--mcdiff' to automatically launch"
|
||||||
|
notice "mcdiff to visually inspect the diff."
|
||||||
|
notice
|
||||||
|
fi
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm "$actual_output" "$actual_parsed_output"
|
||||||
|
|
||||||
|
error_count=$((error_count - 1)) # cancel the earlier "+1".
|
||||||
|
pass_count=$((pass_count + 1))
|
||||||
|
|
||||||
|
success "PASSED."
|
||||||
|
|
||||||
|
done
|
||||||
|
|
||||||
|
[ $pass_count = "0" -a $error_count = "0" ] && notice "Note: The data directory contains no *.input files."
|
||||||
|
|
||||||
|
[ $error_count = "0" ] # exit status of function.
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_command_line_arguments() {
|
||||||
|
# We want --long-options, so we don't use 'getopts'.
|
||||||
|
while [ -n "$1" ]; do
|
||||||
|
case "$1" in
|
||||||
|
--data-dir)
|
||||||
|
DATA_DIR=$2
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--helpers-dir)
|
||||||
|
if [ -z "$HELPERS_DIR1" ]; then
|
||||||
|
HELPERS_DIR1=$2
|
||||||
|
else
|
||||||
|
HELPERS_DIR2=$2
|
||||||
|
fi
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--create-output)
|
||||||
|
opt_create_output=yes
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--mcdiff)
|
||||||
|
opt_run_mcdiff_on_error=yes
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--help|-h)
|
||||||
|
help
|
||||||
|
exit
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
die "Unknown command-line option $1"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Check that everything is set up correctly.
|
||||||
|
#
|
||||||
|
verify_setup() {
|
||||||
|
[ -n "$DATA_DIR" ] || die "You didn't specify the data dir (--data-dir). Run me with --help for info."
|
||||||
|
[ -n "$HELPERS_DIR1" ] || die "You didn't specify the helpers dir (--helpers-dir). Run me with --help for info."
|
||||||
|
[ -z "$HELPERS_DIR2" ] && HELPERS_DIR2=$HELPERS_DIR1 # we're being lazy.
|
||||||
|
|
||||||
|
local dir
|
||||||
|
for dir in "$DATA_DIR" "$HELPERS_DIR1" "$HELPERS_DIR2"; do
|
||||||
|
assert_dir_exists "$dir"
|
||||||
|
has_string "$dir" " " && die "$dir: Sorry, spaces aren't allowed in pathnames." # search "reason", twice, above.
|
||||||
|
done
|
||||||
|
|
||||||
|
local missing_progs=""
|
||||||
|
check_prog() {
|
||||||
|
if ! has_prog "$1"; then
|
||||||
|
err "I can't see the program '$1'."
|
||||||
|
missing_progs="${missing_progs}${missing_progs:+ and }'$1'"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
check_prog "mc_parse_ls_l"
|
||||||
|
check_prog "mc_xcat"
|
||||||
|
check_prog "mktemp" # non-POSIX
|
||||||
|
[ -z "$missing_progs" ] || die "You need to add to your PATH the directories containing the executables $missing_progs."
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
init_colors
|
||||||
|
parse_command_line_arguments "$@"
|
||||||
|
verify_setup
|
||||||
|
run # being the last command executed, its exit status is that of this whole script.
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
Loading…
Reference in New Issue