diff options
Diffstat (limited to 'src/test/test_parseconf.sh')
-rwxr-xr-x | src/test/test_parseconf.sh | 508 |
1 files changed, 411 insertions, 97 deletions
diff --git a/src/test/test_parseconf.sh b/src/test/test_parseconf.sh index 8d41438b2e..4fe27d9f5d 100755 --- a/src/test/test_parseconf.sh +++ b/src/test/test_parseconf.sh @@ -8,15 +8,22 @@ # Valid configurations are tested with --dump-config, which parses and # validates the configuration before writing it out. We then make sure that # the result is what we expect, before parsing and dumping it again to make -# sure that there is no change. +# sure that there is no change. Optionally, we can also test the log messages +# with --verify-config. # # Invalid configurations are tested with --verify-config, which parses # and validates the configuration. We capture its output and make sure that # it contains the error message we expect. +# +# When tor is compiled with different libraries or modules, some +# configurations may have different results. We can specify these result +# variants using additional result files. # This script looks for its test cases as individual directories in # src/test/conf_examples/. Each test may have these files: # +# Configuration Files +# # torrc -- Usually needed. This file is passed to Tor on the command line # with the "-f" flag. (If you omit it, you'll test Tor's behavior when # it receives a nonexistent configuration file.) @@ -28,42 +35,53 @@ # cmdline -- Optional. If present, it contains command-line arguments that # will be passed to Tor. # -# (included torrc files or directories) -- Optional. If present, and -# configured in the torrc* or cmdline, these files or directories are -# included. Include paths should be specified relative to the test case -# directory. +# (included torrc files or directories) -- Optional. Additional files can be +# included in configuration, using the "%include" directive. Files or +# directories can be included in any of the config files listed above. +# Include paths should be specified relative to the test case directory. +# +# Result Files # # expected -- If this file is present, then it should be the expected result # of "--dump-config short" for this test case. Exactly one of # "expected" or "error" must be present, or the test will fail. # +# expected_log -- Optional. If this file is present, then it contains a regex +# that must be matched by some line in the output of "--verify-config", +# which must succeed. Only used if "expected" is also present. +# # error -- If this file is present, then it contains a regex that must be # matched by some line in the output of "--verify-config", which must # fail. Exactly one of "expected" or "error" must be present, or the # test will fail. # -# {expected,error}_${TOR_LIBS_ENABLED}* -- If this file is present, -# then the outcome is different when some optional libraries are +# {expected,expected_log,error}_${TOR_LIBS_ENABLED}* -- If this file is +# present, then the outcome is different when some optional libraries are # enabled. If there is no result file matching the exact list of enabled # libraries, the script searches for result files with one or more of # those libraries disabled. The search terminates at the standard result -# file. +# file. If expected* is present, the script also searches for +# expected_log*. # # For example: # A test that succeeds, regardless of any enabled libraries: # - expected # A test that has a different result if the nss library is enabled -# (but the same result if any other library is enabled): +# (but the same result if any other library is enabled). We also check +# the log output in this test: # - expected +# - expected_log # - expected_nss +# - expected_log_nss # A test that fails if the lzma and zstd modules are *not* enabled: # - error # - expected_lzma_zstd # -# {expected,error}*_no_${TOR_MODULES_DISABLED} -- If this file is present, -# then the outcome is different when some modules are disabled. If there -# is no result file matching the exact list of disabled modules, the -# standard result file is used. +# {expected,expected_log,error}*_no_${TOR_MODULES_DISABLED} -- If this file is +# present, then the outcome is different when some modules are disabled. +# If there is no result file matching the exact list of disabled modules, +# the standard result file is used. If expected* is present, the script +# also searches for expected_log*. # # For example: # A test that succeeds, regardless of any disabled modules: @@ -84,10 +102,12 @@ umask 077 set -e +MYNAME="$0" + # emulate realpath(), in case coreutils or equivalent is not installed. abspath() { f="$*" - if [ -d "$f" ]; then + if test -d "$f"; then dir="$f" base="" else @@ -99,8 +119,8 @@ abspath() { } # find the tor binary -if [ $# -ge 1 ]; then - TOR_BINARY="${1}" +if test $# -ge 1; then + TOR_BINARY="$1" shift else TOR_BINARY="${TESTING_TOR_BINARY:-./src/app/tor}" @@ -108,7 +128,7 @@ fi TOR_BINARY="$(abspath "$TOR_BINARY")" -echo "TOR BINARY IS ${TOR_BINARY}" +echo "Using Tor binary '$TOR_BINARY'." # make a safe space for temporary files DATA_DIR=$(mktemp -d -t tor_parseconf_tests.XXXXXX) @@ -137,8 +157,30 @@ else fi FINAL_EXIT=0 +NEXT_TEST= + +# Log a failure message to stderr, using $@ as a printf string and arguments +# Set NEXT_TEST to "yes" and FINAL_EXIT to $EXITCODE. +fail_printf() +{ + printf "FAIL: " >&2 + # The first argument is a printf string, so this warning is spurious + # shellcheck disable=SC2059 + printf "$@" >&2 + NEXT_TEST="yes" + FINAL_EXIT=$EXITCODE +} -die() { echo "$1" >&2 ; FINAL_EXIT=$EXITCODE; } +# Log a failure message to stderr, using $@ as a printf string and arguments +# Exit with status $EXITCODE. +die_printf() +{ + printf "FAIL: CRITICAL error in '%s':" "$MYNAME" >&2 + # The first argument is a printf string, so this warning is spurious + # shellcheck disable=SC2059 + printf "$@" >&2 + exit $EXITCODE +} if test "$WINDOWS" = 1; then FILTER="dos2unix" @@ -147,8 +189,12 @@ else fi EMPTY="${DATA_DIR}/EMPTY" - -touch "$EMPTY" || die "Couldn't create empty file." +touch "$EMPTY" || die_printf "Couldn't create empty file '%s'.\\n" \ + "$EMPTY" +NON_EMPTY="${DATA_DIR}/NON_EMPTY" +echo "This pattern should not match any log messages" \ + > "$NON_EMPTY" || die_printf "Couldn't create non-empty file '%s'.\\n" \ + "$NON_EMPTY" STANDARD_LIBS="libevent\\|openssl\\|zlib" # Lib names are restricted to [a-z0-9]* at the moment @@ -167,9 +213,8 @@ TOR_LIBS_ENABLED=${TOR_LIBS_ENABLED%_} # If we ever have more than 3 optional libraries, we'll need more code here TOR_LIBS_ENABLED_COUNT="$(echo "$TOR_LIBS_ENABLED_SEARCH" \ | tr ' ' '\n' | wc -l)" -if [ "$TOR_LIBS_ENABLED_COUNT" -gt 3 ]; then - echo "$0 can not handle more than 3 optional libraries" - exit 1 +if test "$TOR_LIBS_ENABLED_COUNT" -gt 3; then + die_printf "Can not handle more than 3 optional libraries.\\n" fi # Brute-force the combinations of libraries TOR_LIBS_ENABLED_SEARCH_3="$(echo "$TOR_LIBS_ENABLED" \ @@ -190,23 +235,306 @@ TOR_MODULES_DISABLED=${TOR_MODULES_DISABLED%_} echo "Tor is configured with:" echo "Optional Libraries: ${TOR_LIBS_ENABLED:-(None)}" -if [ -n "${TOR_LIBS_ENABLED}" ]; then +if test "$TOR_LIBS_ENABLED"; then echo "Optional Library Search List: $TOR_LIBS_ENABLED_SEARCH" fi echo "Disabled Modules: ${TOR_MODULES_DISABLED:-(None)}" +# Yes, unix uses "0" for a successful command +TRUE=0 +FALSE=1 + +# Run tor --verify-config on the torrc $1, and defaults torrc $2, which may +# be $EMPTY. Pass tor the extra command line arguments $3, which will be +# passed unquoted. +# Send tor's standard output to stderr. +log_verify_config() +{ + # show the command we're about to execute + # log_verify_config() is only called when we've failed + printf "Tor --verify-config said:\\n" >&2 + printf "$ %s %s %s %s %s %s %s\\n" \ + "$TOR_BINARY" --verify-config \ + -f "$1" \ + --defaults-torrc "$2" \ + "$3" \ + >&2 + # We need cmdline unquoted + # shellcheck disable=SC2086 + "$TOR_BINARY" --verify-config \ + -f "$1" \ + --defaults-torrc "$2" \ + $3 \ + >&2 \ + || true +} + +# Run "tor --dump-config short" on the torrc $1, and defaults torrc $2, which +# may be $EMPTY. Pass tor the extra command line arguments $3, which will be +# passed unquoted. Send tor's standard output to $4. +# +# Set $FULL_TOR_CMD to the tor command line that was executed. +# +# If tor fails, fail_printf() using the file name $5, and context $6, +# which may be an empty string. Then run log_verify_config(). +dump_config() +{ + if test "$6"; then + CONTEXT=" $6" + else + CONTEXT="" + fi + + # keep the command we're about to execute, and show if it we fail + FULL_TOR_CMD=$(printf "$ %s %s %s %s %s %s %s %s" \ + "$TOR_BINARY" --dump-config short \ + -f "$1" \ + --defaults-torrc "$2" \ + "$3" + ) + # We need cmdline unquoted + # shellcheck disable=SC2086 + if ! "$TOR_BINARY" --dump-config short \ + -f "$1" \ + --defaults-torrc "$2" \ + $3 \ + > "$4"; then + fail_printf "'%s': Tor --dump-config reported an error%s:\\n%s\\n" \ + "$5" \ + "$CONTEXT" \ + "$FULL_TOR_CMD" + log_verify_config "$1" \ + "$2" \ + "$3" + fi +} + +# Run "$FILTER" on the input $1. +# Send the standard output to $2. +# If tor fails, log a failure message using the file name $3, and context $4, +# which may be an empty string. +filter() +{ + if test "$4"; then + CONTEXT=" $4" + else + CONTEXT="" + fi + + "$FILTER" "$1" \ + > "$2" \ + || fail_printf "'%s': Filter '%s' reported an error%s.\\n" \ + "$3" \ + "$FILTER" \ + "$CONTEXT" +} + +# Compare the expected file $1, and output file $2. +# +# If they are different, fail. Log the differences between the files. +# Run log_verify_config() with torrc $3, defaults torrc $4, and command +# line $5, to log Tor's error messages. +# +# If the file contents are identical, returns true. Otherwise, return false. +# +# Log failure messages using fail_printf(), with the expected file name, +# context $6, which may be an empty string, and the tor command line $7. +check_diff() +{ + if test "$6"; then + CONTEXT=" $6" + else + CONTEXT="" + fi + + if cmp "$1" "$2" > /dev/null; then + return "$TRUE" + else + fail_printf "'%s': Tor --dump-config said%s:\\n%s\\n" \ + "$1" \ + "$CONTEXT" \ + "$7" + diff -u "$1" "$2" >&2 \ + || true + log_verify_config "$3" \ + "$4" \ + "$5" + return "$FALSE" + fi +} + +# Run "tor --dump-config short" on the torrc $1, and defaults torrc $2, which +# may be $EMPTY. Pass tor the extra command line arguments $3, which will be +# passed unquoted. Send tor's standard output to $4, after running $FILTER +# on it. +# +# If tor fails, run log_verify_config(). +# +# Compare the expected file $5, and output file. If they are different, fail. +# If this is the first step that failed in this test, run log_verify_config(). +# +# If the file contents are identical, returns true. Otherwise, return false, +# and log the differences between the files. +# +# Log failure messages using fail_printf(), with the expected file name, and +# context $6, which may be an empty string. +check_dump_config() +{ + OUTPUT="$4" + OUTPUT_RAW="${OUTPUT}_raw" + + FULL_TOR_CMD= + dump_config "$1" \ + "$2" \ + "$3" \ + "$OUTPUT_RAW" \ + "$5" \ + "$6" + + filter "$OUTPUT_RAW" \ + "$OUTPUT" \ + "$5" \ + "$6" + + if check_diff "$5" \ + "$OUTPUT" \ + "$1" \ + "$2" \ + "$3" \ + "$6" \ + "$FULL_TOR_CMD"; then + return "$TRUE" + else + return "$FALSE" + fi +} + +# Check if $1 is an empty file. +# If it is, fail_printf() using $2 as the type of file. +# Returns true if the file is empty, false otherwise. +check_empty_pattern() +{ + if ! test -s "$1"; then + fail_printf "%s file '%s' is empty, and will match any output.\\n" \ + "$2" \ + "$1" + return "$TRUE" + else + return "$FALSE" + fi +} + +# Run tor --verify-config on the torrc $1, and defaults torrc $2, which may +# be $EMPTY. Pass tor the extra command line arguments $3, which will be +# passed unquoted. Send tor's standard output to $4. +# +# Set $FULL_TOR_CMD to the tor command line that was executed. +# +# If tor's exit status does not match the boolean $5, fail_printf() +# using the file name $6, and context $7, which is required. +verify_config() +{ + RESULT=$TRUE + + # keep the command we're about to execute, and show if it we fail + FULL_TOR_CMD=$(printf "$ %s %s %s %s %s %s %s" \ + "$TOR_BINARY" --verify-config \ + -f "$1" \ + --defaults-torrc "$2" \ + "$3" + ) + # We need cmdline unquoted + # shellcheck disable=SC2086 + "$TOR_BINARY" --verify-config \ + -f "$1" \ + --defaults-torrc "$2" \ + $3 \ + > "$4" || RESULT=$FALSE + + # Convert the actual and expected results to boolean, and compare + if test $((! (! RESULT))) -ne $((! (! $5))); then + fail_printf "'%s': Tor --verify-config did not %s:\\n%s\\n" \ + "$6" \ + "$7" \ + "$FULL_TOR_CMD" + cat "$4" >&2 + fi +} + +# Check for the patterns in the match file $1, in the output file $2. +# Uses grep with the entire contents of the match file as the pattern. +# (Not "grep -f".) +# +# If the pattern does not match any lines in the output file, fail. +# Log the pattern, and the entire contents of the output file. +# +# Log failure messages using fail_printf(), with the match file name, +# context $3, and tor command line $4, which are required. +check_pattern() +{ + expect_log="$(cat "$1")" + if ! grep "$expect_log" "$2" > /dev/null; then + fail_printf "Expected %s '%s':\\n%s\\n" \ + "$3" \ + "$1" \ + "$expect_log" + printf "Tor --verify-config said:\\n%s\\n" \ + "$4" >&2 + cat "$2" >&2 + fi +} + +# Run tor --verify-config on the torrc $1, and defaults torrc $2, which may +# be $EMPTY. Pass tor the extra command line arguments $3, which will be +# passed unquoted. Send tor's standard output to $4. +# +# If tor's exit status does not match the boolean $5, fail. +# +# Check for the patterns in the match file $6, in the output file. +# Uses grep with the entire contents of the match file as the pattern. +# (Not "grep -f".) The match file must not be empty. +# +# If the pattern does not match any lines in the output file, fail. +# Log the pattern, and the entire contents of the output file. +# +# Log failure messages using fail_printf(), with the match file name, +# and context $7, which is required. +check_verify_config() +{ + if check_empty_pattern "$6" "$7"; then + return + fi + + FULL_TOR_CMD= + verify_config "$1" \ + "$2" \ + "$3" \ + "$4" \ + "$5" \ + "$6" \ + "$7" + + check_pattern "$6" \ + "$4" \ + "$7" \ + "$FULL_TOR_CMD" +} + for dir in "${EXAMPLEDIR}"/*; do - if ! test -d "${dir}"; then + NEXT_TEST= + + if ! test -d "$dir"; then # Only count directories. continue fi testname="$(basename "${dir}")" # We use printf since "echo -n" is not standard - printf "%s: " "$testname" + printf "%s: " \ + "$testname" PREV_DIR="$(pwd)" - cd "${dir}" + cd "$dir" if test -f "./torrc.defaults"; then DEFAULTS="./torrc.defaults" @@ -221,6 +549,7 @@ for dir in "${EXAMPLEDIR}"/*; do fi EXPECTED= + EXPECTED_LOG= ERROR= # Search for a custom result file for any combination of enabled optional # libraries @@ -235,13 +564,17 @@ for dir in "${EXAMPLEDIR}"/*; do # Check for broken configs if test -f "./error${suffix}"; then - echo "FAIL: Found both ${dir}/expected${suffix}" >&2 - echo "and ${dir}/error${suffix}." >&2 - echo "(Only one of these files should exist.)" >&2 - FINAL_EXIT=$EXITCODE + fail_printf "Found both '%s' and '%s'.%s\\n" \ + "${dir}/expected${suffix}" \ + "${dir}/error${suffix}" \ + "(Only one of these files should exist.)" + break fi EXPECTED="./expected${suffix}" + if test -f "./expected_log${suffix}"; then + EXPECTED_LOG="./expected_log${suffix}" + fi break elif test -f "./error${suffix}"; then @@ -250,92 +583,73 @@ for dir in "${EXAMPLEDIR}"/*; do fi done - # Exit as soon as the inner loop finds a file - if test -f "$EXPECTED" || test -f "$ERROR"; then + # Exit as soon as the inner loop finds a file, or fails + if test -f "$EXPECTED" || test -f "$ERROR" || test "$NEXT_TEST"; then break fi done - if test -f "$EXPECTED"; then - + if test "$NEXT_TEST"; then + # The test failed inside the file search loop: go to the next test + continue + elif test -f "$EXPECTED"; then # This case should succeed: run dump-config and see if it does. - "${TOR_BINARY}" -f "./torrc" \ - --defaults-torrc "${DEFAULTS}" \ - --dump-config short \ - ${CMDLINE} \ - | "${FILTER}" > "${DATA_DIR}/output.${testname}" \ - || die "FAIL: $EXPECTED: Tor reported an error." - - if cmp "$EXPECTED" "${DATA_DIR}/output.${testname}">/dev/null ; then + if check_dump_config "./torrc" \ + "$DEFAULTS" \ + "$CMDLINE" \ + "${DATA_DIR}/output.${testname}" \ + "$EXPECTED" \ + ""; then # Check round-trip. - "${TOR_BINARY}" -f "${DATA_DIR}/output.${testname}" \ - --defaults-torrc "${DATA_DIR}/empty" \ - --dump-config short \ - | "${FILTER}" \ - > "${DATA_DIR}/output_2.${testname}" \ - || die \ - "FAIL: $EXPECTED: Tor reported an error on round-trip." - - if ! cmp "${DATA_DIR}/output.${testname}" \ - "${DATA_DIR}/output_2.${testname}"; then - echo "FAIL: $EXPECTED did not match on round-trip." >&2 - FINAL_EXIT=$EXITCODE - fi + check_dump_config "${DATA_DIR}/output.${testname}" \ + "$EMPTY" \ + "" \ + "${DATA_DIR}/output_2.${testname}" \ + "$EXPECTED" \ + "on round-trip" || true + fi + + if test -f "$EXPECTED_LOG"; then + # This case should succeed: run verify-config and see if it does. - echo "OK" + check_verify_config "./torrc" \ + "$DEFAULTS" \ + "$CMDLINE" \ + "${DATA_DIR}/output_log.${testname}" \ + "$TRUE" \ + "$EXPECTED_LOG" \ + "log success" else - echo "FAIL" >&2 - if test "$(wc -c < "${DATA_DIR}/output.${testname}")" = 0; then - # There was no output -- probably we failed. - "${TOR_BINARY}" -f "./torrc" \ - --defaults-torrc "${DEFAULTS}" \ - --verify-config \ - ${CMDLINE} || true - fi - echo "FAIL: $EXPECTED did not match." >&2 - diff -u "$EXPECTED" "${DATA_DIR}/output.${testname}" >&2 \ - || true - FINAL_EXIT=$EXITCODE + printf "\\nNOTICE: Missing '%s_log' file:\\n" \ + "$EXPECTED" >&2 + log_verify_config "./torrc" \ + "$DEFAULTS" \ + "$CMDLINE" fi elif test -f "$ERROR"; then # This case should fail: run verify-config and see if it does. - if ! test -s "$ERROR"; then - echo "FAIL: error file '$ERROR' is empty." >&2 - echo "Empty error files match any output." >&2 - FINAL_EXIT=$EXITCODE - fi - - "${TOR_BINARY}" --verify-config \ - -f ./torrc \ - --defaults-torrc "${DEFAULTS}" \ - ${CMDLINE} \ - > "${DATA_DIR}/output.${testname}" \ - && die "FAIL: $ERROR: Tor did not report an error." - - expect_err="$(cat "$ERROR")" - if grep "${expect_err}" "${DATA_DIR}/output.${testname}" >/dev/null; then - echo "OK" - else - echo "FAIL" >&2 - echo "Expected $ERROR: ${expect_err}" >&2 - echo "Tor said:" >&2 - cat "${DATA_DIR}/output.${testname}" >&2 - FINAL_EXIT=$EXITCODE - fi - + check_verify_config "./torrc" \ + "$DEFAULTS" \ + "$CMDLINE" \ + "${DATA_DIR}/output.${testname}" \ + "$FALSE" \ + "$ERROR" \ + "log error" else # This case is not actually configured with a success or a failure. # call that an error. + fail_printf "Did not find ${dir}/*expected or ${dir}/*error.\\n" + fi - echo "FAIL: Did not find ${dir}/*expected or ${dir}/*error." >&2 - FINAL_EXIT=$EXITCODE + if test -z "$NEXT_TEST"; then + echo "OK" fi - cd "${PREV_DIR}" + cd "$PREV_DIR" done -exit $FINAL_EXIT +exit "$FINAL_EXIT" |