Files
mercury/tests/recompilation/test_functions
Zoltan Somogyi 7b6bace9ec Don't return dummy parse_trees for missing files.
The predicates that read a Mercury source, interface or optimization file
used to return four things:

- the name of the file (for callers who specified only a module name),
- the timestamp, if requested and available,
- a parse tree, and
- a representation of any errors (sets of error categories, and error_specs).

However, these four things were not independent. Some combinations of their
values were not allowed, but (a) there was no documentation of what these
combinations were, and (b) code that processed these four things had to be
prepared to handle illegal as well as legal combinations.

This diff makes these predicates return only one result, which contains

- all four of the above things, when the file could be opened, but
- only the file name and a representation of the error if the file
  could not be opened,
- only the file name and a representation of *no* errors, if the caller
  asked the predicate to read the file only if its timestamp did not match
  a specified value, and it does match that value.

We use a somewhat modified version of an existing type, have_read_module,
for this. It is modified both by including information that its users
now need that they did not need before, and shortening the names of its
function symbols, which now occur in many more places than before.

compiler/read_modules.m:
    Make the change to the output arguments described above.

    Making this change requires having the affected predicates deal with
    the case where we either cannot find or cannot open the relevant file.
    (The two are effectively the same thing in this module, since we search
    by attempting to open.) Passing that task off to parse_modules.m
    was always inelegant.

    Simplify the have_read_module_map type by making the key always
    be a module_name. We deleted the only use of have_read_module_maps
    with another kind of key a while ago.

    Delete some no-longer-needed predicates.

compiler/parse_module.m:
    Delete the code dealing with the absence of a file stream to parse.

    Replace code that used to construct dummy parse trees with code
    that simply returns *no* parse tree if the file could be opened
    but could not be read, or if its timestamp indicated that reading it
    would have been redundant.

    Delete some utility predicates moved to parse_error.m, so that
    read_modules.m could also use them.

    Fix comment rot.

compiler/parse_error.m:
    Add some utility predicates for use by read_modules.m and parse_module.m.

compiler/deps_map.m:
    Conform to the changes above.

    Document the dubious effects of an old design decision.

    Fix a misleading predicate name.

    Fix comment rot.

compiler/grab_modules.m:
    Conform to the changes above.

    Some predicates used to return parse trees that could be dummies.
    Change them to return just the parts of the parse tree that the
    caller was interested in, which was usually a tiny part, and which
    can be constructed trivially even when we don't have a parse tree.

    Delete an unneeded type.

compiler/recompilation.check.m:
    Conform to the changes above.

    Represent the operations we need to test version numbers in interface files
    as a typeclass, and rewrite the checking operation in terms of that
    typeclass, with one instance for each kind of interface file.

    Move some repeated code into a predicate.

    Shorten some long names.

compiler/mercury_compile_main.m:
    Conform to the changes above.

    Break up a large predicate.

compiler/generate_dep_d_files.m:
compiler/make.module_dep_file.m:
compiler/write_module_interface_files.m:
    Conform to the changes above.

compiler/prog_item.m:
    Delete the parse_tree_some_int type. The change in recompilation.check.m,
    deleted its last few uses there, and allowed the deletion of the last
    uses in read_modules.m.

tests/recompilation/two_module_debug:
    Extend this script to help deal with problems in all stages of the
    execution of a test case, since that was required while debugging
    this diff.

    Document which parts of this script correspond to which parts of
    two_module_test.

tests/recompilation/test_functions:
tests/recompilation/two_module_test:
    Simplify the logic of the main test function, by testing a condition
    once instead of three times.

    Specify whether a recompilation test is expected to succeed or fail
    directly, using alternatives that model a bespoke type, instead
    of alternatives that mimic a boolean answer to a question, which
    does not help readers who don't remember the question.

    Always put shell variable names in braces.

    Note a problem that makes one of the shell functions ineffective.

tests/recompilation/Mmakefile:
    Tell two_module_test what to do using its updated interface.
2022-04-30 14:37:22 +10:00

217 lines
6.3 KiB
Bash
Executable File

# vim: ts=4 sw=4 expandtab ft=sh
#
# Shell functions used to run the recompilation tests.
#
# Set a limit of ten minutes for each command. If a command takes that long,
# it is almost certainly in an infinite loop.
ulimit -t 600
# Set variables `main_module' and `modules' describing the test being run.
# Copy in the initial versions of the test files.
test_module () {
echo "executing shell function test_module $@"
if test "$#" = 0
then
echo "usage: test_module main_module other_modules"
exit 1
fi
main_module="$1"
modules="$*"
echo Testing ${main_module}
for module in ${modules}
do
rm -f "${module}.m"
cp "${module}.m.1" "${module}.m"
# The module.m.<n> files are the only ones that should be edited.
chmod -w ${module}.m
done
rm -f "${main_module}.res"
touch "${main_module}.res"
# XXX Avoid producing output files with the same timestamp as
# the input source file. The up-to-date check for the output file
# in recompilation_check.m checks that the output file's timestamp
# is strictly greater than the input file's, so it will recompile
# if they are the same.
#
# This won't be a problem in practice, unless for some reason a user
# decides to have mmake invoked by their editor automatically after
# each edit. If a file takes less than a second to compile, recompiling it
# all the time won't be noticeable. However, the recompilation will affect
# the `--verbose-recompilation' messages, which we compare to the expected
# output, so we need to avoid it here.
sleep 1
}
# Simulate a user editing the file.
update_module () {
# echo "executing shell function update_module $@"
if test "$#" != 2
then
echo "usage: update_module module_name version"
exit 1
fi
module="$1"
module_version="$2"
# GNU make only considers a file out of date if a dependency's timestamp
# is strictly greater than the file's timestamp. We need to make sure
# that the `.c' file is out of date with respect to the `.m' file.
sleep 1
rm -f "${module}.m"
cp "${module}.m.${module_version}" "${module}.m"
chmod -w "${module}.m"
# See comment in test_module().
sleep 1
}
mmake_depend () {
# echo "executing shell function mmake_depend $@"
eval mmake ${mmakeopts} "${main_module}.depend" || exit 1
}
# Build the test, then run it and compare the output.
mmake_test () {
# echo "executing shell function mmake_test $@"
if test "$#" != 2
then
echo "** usage: mmake_test output_file_version should_{fail,succeed}"
exit 1
fi
output_file_version="$1"
should="$2"
# XXX This is a hacky way of testing for Java grades.
# echo "mmakeopts=${mmakeopts}"
case "${mmakeopts}" in
*java*)
target=${main_module}.classes
run_target="java ${main_module}"
;;
*)
target=${main_module}
run_target="./${main_module}"
;;
esac
case "${should}" in
should_succeed)
eval mmake ${mmakeopts} "${target}"
case "$?" in
0)
${run_target} > "${main_module}.out"
compare_files "${main_module}.exp.${output_file_version}" \
"${main_module}.out"
;;
*)
echo "** Error: mmake ${mmakeopts} ${target} failed" \
"(it was expected to succeed)"
exit 1
;;
esac
;;
should_fail)
# If the compilation is supposed to fail, then we suppress
# the mmake output, to avoid burying genuine failures in a sea
# of expected failures in the nightly test logs. The `-k' option
# to mmake avoids differences in the output when using
# parallel mmakes.
eval mmake ${mmakeopts} -k "${target}" \
> "${main_module}.failing_make_output" 2>&1
case "$?" in
0)
echo "** Error: mmake ${mmakeopts} ${target} succeeded" \
"(it was expected to fail)"
exit 1
;;
*)
;;
esac
;;
*)
echo "** Error: bad should ${should}"
exit 1
;;
esac
}
# Compare the output file with the expected output file, generating
# the expected output file if (a) it doesn't exist and (b) runtests was given
# the --generate-missing-exp-files option.
compare_files () {
if test "$#" != 2
then
echo "** usage: compare_files expected_file result_file"
exit 1
fi
exp_file="$1"
res_file="$2"
if test -f "${exp_file}"
then
if diff ${DIFF_OPTS-"-c"} "${exp_file}" "${res_file}" \
>> "${main_module}.res"
then
:
else
echo "** Error: ${exp_file} and ${res_file} differ."
exit 1
fi
else
if test "${generate_missing_exp_files}" = true
then
echo "** WARNING: generating ${exp_file}"
cp "${res_file}" "${exp_file}"
else
echo "** Error: ${exp_file} not found"
exit 1
fi
fi
}
check_err_file () {
# echo "executing shell function check_err_file $@"
if test "$#" != 2
then
echo "** usage: check_err_file module message_file_version"
fi
module="$1"
error_file_version="$2"
# In `.hl*' grades, the compiler sometimes puts an extra line in
# the `.err' file ("foo.h has CHANGED"). Filter it out here.
# Also filter out any references to the directories created by the
# `--use-subdirs' option.
sed -e '/has CHANGED/d' -e 's/Mercury\/.*\///g' "${module}.err" \
> "${module}.err2"
mv "${module}.err2" "${module}.err"
compare_files "${module}.err_exp.${error_file_version}" "${module}.err"
}
cleanup_test () {
# echo "executing shell function cleanup_test $@"
# XXX "${cleanup}" does not seem to be set to "true" *anywhere*.
case "${cleanup}" in
true)
eval mmake ${mmakeopts} "${main_module}.realclean"
for module in ${modules}
do
rm -f "${module}.m"
done
;;
esac
}