| # |
| # This file and its contents are supplied under the terms of the |
| # Common Development and Distribution License ("CDDL"), version 1.0. |
| # You may only use this file in accordance with the terms of version |
| # 1.0 of the CDDL. |
| # |
| # A full copy of the text of the CDDL should have accompanied this |
| # source. A copy of the CDDL is also available via the Internet at |
| # http://www.illumos.org/license/CDDL. |
| # |
| |
| # |
| # Copyright (c) 2016, 2017 by Delphix. All rights reserved. |
| # |
| |
| . $STF_SUITE/include/libtest.shlib |
| |
| ZCP_ROOT=$STF_SUITE/tests/functional/channel_program |
| |
| # |
| # Note: In case of failure (log_fail) in this function |
| # we delete the file passed as <input file> so the |
| # test suite doesn't leak temp files on failures. So it |
| # is expected that <input file> is a temp file and not |
| # an installed file. |
| # |
| # <exitcode> <expected error string> <input file> <zfs program args> |
| # e.g. log_program 0 "" tmp.7a12V $POOL foo.zcp arg1 arg2 |
| function log_program |
| { |
| typeset expectexit=$1 |
| shift |
| typeset expecterror=$1 |
| shift |
| typeset tmpin=$1 |
| shift |
| typeset cmdargs=$@ tmpout=$(mktemp) tmperr=$(mktemp) |
| |
| # Expected output/error filename is the same as the .zcp name |
| typeset basename |
| if [[ $2 != "-" ]]; then |
| basename=${2%.*} |
| fi |
| |
| log_note "running: zfs program $cmdargs:" |
| |
| zfs program $cmdargs >$tmpout 2>$tmperr |
| typeset ret=$? |
| |
| log_note "input:\n$(cat $tmpin)" |
| log_note "output:\n$(cat $tmpout)" |
| log_note "error:\n$(cat $tmperr)" |
| |
| # |
| # Verify correct return value |
| # |
| if [[ $ret -ne $expectexit ]]; then |
| rm $tmpout $tmperr $tmpin |
| log_fail "return mismatch: expected $expectexit, got $ret" |
| fi |
| |
| # |
| # Check the output or reported error for successful or error returns, |
| # respectively. |
| # |
| if [[ -f "$basename.out" ]] && [[ $expectexit -eq 0 ]]; then |
| |
| outdiff=$(diff "$basename.out" "$tmpout") |
| if [[ $? -ne 0 ]]; then |
| output=$(<$tmpout) |
| rm $tmpout $tmperr $tmpin |
| log_fail "Output mismatch. Expected:\n" \ |
| "$(<$basename.out)\nBut got:\n$output\n" \ |
| "Diff:\n$outdiff" |
| fi |
| |
| elif [[ -f "$basename.err" ]] && [[ $expectexit -ne 0 ]]; then |
| |
| outdiff=$(diff "$basename.err" "$tmperr") |
| if [[ $? -ne 0 ]]; then |
| outputerror=$(<$tmperr) |
| rm $tmpout $tmperr $tmpin |
| log_fail "Error mismatch. Expected:\n" \ |
| "$(<$basename.err)\nBut got:\n$outputerror\n" \ |
| "Diff:\n$outdiff" |
| fi |
| |
| elif [[ -n $expecterror ]] && [[ $expectexit -ne 0 ]]; then |
| |
| grep -q "$expecterror" $tmperr |
| if [[ $? -ne 0 ]]; then |
| outputerror=$(<$tmperr) |
| rm $tmpout $tmperr $tmpin |
| log_fail "Error mismatch. Expected to contain:\n" \ |
| "$expecterror\nBut got:\n$outputerror\n" |
| fi |
| |
| elif [[ $expectexit -ne 0 ]]; then |
| # |
| # If there's no expected output, error reporting is allowed to |
| # vary, but ensure that we didn't fail silently. |
| # |
| if [[ -z "$(<$tmperr)" ]]; then |
| rm $tmpout $tmperr $tmpin |
| log_fail "error with no stderr output" |
| fi |
| fi |
| |
| # |
| # Clean up all temp files except $tmpin which is |
| # reused for the second invocation of log_program. |
| # |
| rm $tmpout $tmperr |
| } |
| |
| # |
| # Even though the command's arguments are passed correctly |
| # to the log_must_program family of wrappers the majority |
| # of the time, zcp scripts passed as HERE documents can |
| # make things trickier (see comment within the function |
| # below) in the ordering of the commands arguments and how |
| # they are passed. Thus, with this function we reconstruct |
| # them to ensure that they are passed properly. |
| # |
| function log_program_construct_args |
| { |
| typeset tmpin=$1 |
| shift |
| |
| args="" |
| i=0 |
| while getopts "nt:m:" opt; do |
| case $opt in |
| t) args="$args -t $OPTARG"; i=$(($i + 2)) ;; |
| m) args="$args -m $OPTARG"; i=$(($i + 2)) ;; |
| n) args="$args -n"; i=$(($i + 1)) ;; |
| esac |
| done |
| shift $i |
| |
| pool=$1 |
| shift |
| |
| infile=$1 |
| shift |
| |
| # |
| # Copy the contents of the original channel program to $tmpin. |
| # |
| # If $infile currently holds "-" (a dash) it means that we consume a |
| # HERE doc from stdin, otherwise $infile is a file path. |
| # |
| cat $infile > $tmpin |
| |
| lua_args=$@ |
| |
| echo "$args $pool $tmpin $lua_args" |
| } |
| |
| # |
| # Program should complete successfully |
| # when run in either context. |
| # |
| function log_must_program |
| { |
| typeset tmpin=$(mktemp) |
| |
| program_args=$(log_program_construct_args $tmpin $@) |
| |
| log_program 0 "" $tmpin "-n $program_args" |
| log_program 0 "" $tmpin "$program_args" |
| |
| rm $tmpin |
| } |
| # |
| # Program should error as expected in |
| # the same way in both contexts. |
| # |
| function log_mustnot_checkerror_program |
| { |
| typeset expecterror=$1 |
| shift |
| typeset tmpin=$(mktemp) |
| |
| program_args=$(log_program_construct_args $tmpin $@) |
| |
| log_program 1 "$expecterror" $tmpin "-n $program_args" |
| log_program 1 "$expecterror" $tmpin "$program_args" |
| |
| rm $tmpin |
| } |
| |
| # |
| # Program should fail when run in either |
| # context. |
| # |
| function log_mustnot_program |
| { |
| log_mustnot_checkerror_program "" $@ |
| } |
| |
| |
| # |
| # Program should error as expected in |
| # open context but complete successfully |
| # in syncing context. |
| # |
| function log_mustnot_checkerror_program_open |
| { |
| typeset expecterror=$1 |
| shift |
| typeset tmpin=$(mktemp) |
| |
| program_args=$(log_program_construct_args $tmpin $@) |
| |
| log_program 1 "$expecterror" $tmpin "-n $program_args" |
| log_program 0 "" $tmpin "$program_args" |
| |
| rm $tmpin |
| } |
| |
| # |
| # Program should complete successfully |
| # when run in syncing context but fail |
| # when attempted to run in open context. |
| # |
| function log_must_program_sync |
| { |
| log_mustnot_checkerror_program_open "requires passing sync=TRUE" $@ |
| } |