# 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 () {
    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 () {
    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 () {
    eval mmake $mmakeopts "$main_module.depend" || exit 1
}

# Build the test, then run it and compare the output.
mmake_test () {
    if test "$#" != 2
    then
        echo "** usage: mmake_test output_file_version should_fail"
        exit 1
    fi

    output_file_version="$1"
    mmake_should_fail="$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 $mmake_should_fail in
        true)
            # If the compilation is supposed to fail, then we suppress
            # the mmake output to avoid making it harder to find any
            # genuine 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
            ;;
        false)
            eval mmake $mmakeopts "$target"
            ;;
    esac

    case "$?" in
        0)
            case "$mmake_should_fail" in
                true)
                    echo "** Error: mmake $mmakeopts $target" \
                        "succeeded where it should fail"
                    exit 1
                    ;;
            esac

            $run_target > "$main_module.out"
            compare_files "$main_module.exp.$output_file_version" \
                "$main_module.out"
            ;;
        *)
            case "$mmake_should_fail" in
                false)
                    echo "** Error: mmake $mmakeopts $target failed"
                    exit 1
                    ;;
            esac
            ;;
    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 () {
    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 () {
    case "$cleanup" in
        true)
            eval mmake $mmakeopts "$main_module.realclean"

            for module in $modules
            do
                rm -f "$module.m"
            done
            ;;
    esac
}
