From 9aad8d683dc6feca0487ccbf74d129906f262739 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 14 Oct 2021 16:25:07 +0200 Subject: [PATCH 1/4] common.sh: printStackTrace() and assertFail() --- scripts/common.sh | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/scripts/common.sh b/scripts/common.sh index b3a5f82e4..1ace4d5de 100644 --- a/scripts/common.sh +++ b/scripts/common.sh @@ -33,12 +33,45 @@ else function printLog() { echo "$(tput setaf 3)$1$(tput sgr0)"; } fi +function printStackTrace +{ + printWarning "" + printWarning "Stack trace:" + + local frame=1 + while caller "$frame" > /dev/null + do + local lineNumber file function + + # `caller` returns something that could already be printed as a stacktrace but we can make + # it more readable by rearranging the components. + # NOTE: This assumes that paths do not contain spaces. + lineNumber=$(caller "$frame" | cut --delimiter " " --field 1) + function=$(caller "$frame" | cut --delimiter " " --field 2) + file=$(caller "$frame" | cut --delimiter " " --field 3) + >&2 printf " %s:%d in function %s()\n" "$file" "$lineNumber" "$function" + + ((frame++)) + done +} + function fail() { printError "$@" return 1 } +function assertFail() +{ + printError "" + (( $# == 0 )) && printError "Assertion failed." + (( $# == 1 )) && printError "Assertion failed: $1" + printStackTrace + + # Intentionally using exit here because assertion failures are not supposed to be handled. + exit 2 +} + function msg_on_error() { local error_message From b85172b0557c3bb6624d39479d7a6cffac4f141f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 14 Oct 2021 18:43:04 +0200 Subject: [PATCH 2/4] common.sh: Print the source code from locations indicated in the stack trace --- scripts/common.sh | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/scripts/common.sh b/scripts/common.sh index 1ace4d5de..6d70a0840 100644 --- a/scripts/common.sh +++ b/scripts/common.sh @@ -19,6 +19,10 @@ # (c) 2016-2019 solidity contributors. # ------------------------------------------------------------------------------ +# Save the initial working directory so that printStackTrace() can access it even if the sourcing +# changes directory. The paths returned by `caller` are relative to it. +_initial_work_dir=$(pwd) + if [ "$CIRCLECI" ] then export TERM="${TERM:-xterm}" @@ -41,7 +45,7 @@ function printStackTrace local frame=1 while caller "$frame" > /dev/null do - local lineNumber file function + local lineNumber line file function # `caller` returns something that could already be printed as a stacktrace but we can make # it more readable by rearranging the components. @@ -49,7 +53,23 @@ function printStackTrace lineNumber=$(caller "$frame" | cut --delimiter " " --field 1) function=$(caller "$frame" | cut --delimiter " " --field 2) file=$(caller "$frame" | cut --delimiter " " --field 3) + + # Paths in the output from `caller` can be relative or absolute (depends on how the path + # with which the script was invoked) and if they're relative, they're not necessarily + # relative to the current working dir. This is a heuristic that will work if they're absolute, + # relative to current dir, or relative to the dir that was current when the script started. + # If neither works, it gives up. + line=$( + { + tail "--lines=+${lineNumber}" "$file" || + tail "--lines=+${lineNumber}" "${_initial_work_dir}/${file}" + } 2> /dev/null | + head --lines=1 | + sed -e 's/^[[:space:]]*//' + ) || line="" + >&2 printf " %s:%d in function %s()\n" "$file" "$lineNumber" "$function" + >&2 printf " %s\n" "$line" ((frame++)) done From 3b1b9a0bfb166e94d5690ee72c5f79f7d4d23dd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 14 Oct 2021 16:50:14 +0200 Subject: [PATCH 3/4] common.sh: Adjust formatting in msg_on_error() and add stack trace --- scripts/common.sh | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/scripts/common.sh b/scripts/common.sh index 6d70a0840..da5b2d2ef 100644 --- a/scripts/common.sh +++ b/scripts/common.sh @@ -138,24 +138,30 @@ function msg_on_error() rm "$stdout_file" "$stderr_file" return 0 else - printError "Command failed: $SOLC ${command[*]}" + printError "" + printError "Command failed: ${error_message}" + printError " command: $SOLC ${command[*]}" if [[ -s "$stdout_file" ]] then - printError "stdout:" + printError "--- stdout ---" + printError "-----------" >&2 cat "$stdout_file" + printError "--------------" else - printError "stdout: " + printError " stdout: " fi if [[ -s "$stderr_file" ]] then - printError "stderr:" + printError "--- stderr ---" >&2 cat "$stderr_file" + printError "--------------" else - printError "stderr: " + printError " stderr: " fi - printError "$error_message" rm "$stdout_file" "$stderr_file" + + printStackTrace return 1 fi } From 0280c8d00ec4756605dc83a8c8c0d9455f24f8cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 14 Oct 2021 18:39:59 +0200 Subject: [PATCH 4/4] Use fail and assertFail where appropriate in command-line tests --- scripts/common.sh | 7 ++++++- test/cmdlineTests.sh | 37 ++++++++++++++++++++----------------- 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/scripts/common.sh b/scripts/common.sh index da5b2d2ef..6d9b0a3ba 100644 --- a/scripts/common.sh +++ b/scripts/common.sh @@ -19,6 +19,9 @@ # (c) 2016-2019 solidity contributors. # ------------------------------------------------------------------------------ +# The fail() function defined below requires set -e to be enabled. +set -e + # Save the initial working directory so that printStackTrace() can access it even if the sourcing # changes directory. The paths returned by `caller` are relative to it. _initial_work_dir=$(pwd) @@ -78,6 +81,8 @@ function printStackTrace function fail() { printError "$@" + + # Using return rather than exit lets the invoking code handle the failure by suppressing the exit code. return 1 } @@ -120,7 +125,7 @@ function msg_on_error() shift ;; *) - fail "Invalid option for msg_on_error: $1" + assertFail "Invalid option for msg_on_error: $1" ;; esac done diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index 1304da8af..59c29ca45 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -92,7 +92,7 @@ echo "Using solc binary at ${SOLC}" INTERACTIVE=true if ! tty -s || [ "$CI" ] then - INTERACTIVE="" + INTERACTIVE=false fi # extend stack size in case we run via ASAN @@ -123,7 +123,7 @@ function update_expectation { function ask_expectation_update { - if [[ $INTERACTIVE != "" ]] + if [[ $INTERACTIVE == true ]] then local newExpectation="${1}" local expectationFile="${2}" @@ -142,12 +142,13 @@ function ask_expectation_update e*) "$editor" "$expectationFile"; break;; u*) update_expectation "$newExpectation" "$expectationFile"; break;; s*) return;; - q*) exit 1;; + q*) fail;; esac done fi else - exit 1 + [[ $INTERACTIVE == false ]] || assertFail + fail fi } @@ -252,7 +253,7 @@ EOF printError "Incorrect exit code. Expected $exit_code_expected but got $exitCode." [[ $exit_code_expectation_file != "" ]] && ask_expectation_update "$exitCode" "$exit_code_expectation_file" - [[ $exit_code_expectation_file == "" ]] && exit 1 + [[ $exit_code_expectation_file == "" ]] && fail fi if [[ "$(cat "$stdout_path")" != "${stdout_expected}" ]] @@ -266,7 +267,7 @@ EOF printError "When running $solc_command" [[ $stdout_expectation_file != "" ]] && ask_expectation_update "$(cat "$stdout_path")" "$stdout_expectation_file" - [[ $stdout_expectation_file == "" ]] && exit 1 + [[ $stdout_expectation_file == "" ]] && fail fi if [[ "$(cat "$stderr_path")" != "${stderr_expected}" ]] @@ -280,7 +281,7 @@ EOF printError "When running $solc_command" [[ $stderr_expectation_file != "" ]] && ask_expectation_update "$(cat "$stderr_path")" "$stderr_expectation_file" - [[ $stderr_expectation_file == "" ]] && exit 1 + [[ $stderr_expectation_file == "" ]] && fail fi rm "$stdout_path" "$stderr_path" @@ -300,10 +301,10 @@ function test_solc_assembly_output() if [ -z "$empty" ] then printError "Incorrect assembly output. Expected: " - echo -e "${expected}" + >&2 echo -e "${expected}" printError "with arguments ${solc_args[*]}, but got:" - echo "${output}" - exit 1 + >&2 echo "${output}" + fail fi } @@ -373,7 +374,7 @@ printTask "Running general commandline tests..." then printError "Ambiguous input. Found input files in multiple formats:" echo -e "${inputFiles}" - exit 1 + fail fi # Use printf to get rid of the trailing newline @@ -475,7 +476,8 @@ echo "Done." printTask "Testing library checksum..." echo '' | msg_on_error --no-stdout "$SOLC" - --link --libraries a=0x90f20564390eAe531E810af625A22f51385Cd222 -echo '' | "$SOLC" - --link --libraries a=0x80f20564390eAe531E810af625A22f51385Cd222 &>/dev/null && exit 1 +echo '' | "$SOLC" - --link --libraries a=0x80f20564390eAe531E810af625A22f51385Cd222 &>/dev/null && \ + fail "solc --link did not reject a library address with an invalid checksum." printTask "Testing long library names..." echo '' | msg_on_error --no-stdout "$SOLC" - --link --libraries aveeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeerylonglibraryname=0x90f20564390eAe531E810af625A22f51385Cd222 @@ -503,7 +505,8 @@ SOLTMPDIR=$(mktemp -d) # First time it works echo 'contract C {}' | msg_on_error --no-stderr "$SOLC" - --bin -o "$SOLTMPDIR/non-existing-stuff-to-create" # Second time it fails - echo 'contract C {}' | "$SOLC" - --bin -o "$SOLTMPDIR/non-existing-stuff-to-create" 2>/dev/null && exit 1 + echo 'contract C {}' | "$SOLC" - --bin -o "$SOLTMPDIR/non-existing-stuff-to-create" 2>/dev/null && \ + fail "solc did not refuse to overwrite $SOLTMPDIR/non-existing-stuff-to-create." # Unless we force echo 'contract C {}' | msg_on_error --no-stderr "$SOLC" - --overwrite --bin -o "$SOLTMPDIR/non-existing-stuff-to-create" ) @@ -517,8 +520,8 @@ printTask "Testing assemble, yul, strict-assembly and optimize..." # Test options above in conjunction with --optimize. # Using both, --assemble and --optimize should fail. - echo '{}' | "$SOLC" - --assemble --optimize &>/dev/null && exit 1 - echo '{}' | "$SOLC" - --yul --optimize &>/dev/null && exit 1 + echo '{}' | "$SOLC" - --assemble --optimize &>/dev/null && fail "solc --assemble --optimize did not fail as expected." + echo '{}' | "$SOLC" - --yul --optimize &>/dev/null && fail "solc --yul --optimize did not fail as expected." # Test yul and strict assembly output # Non-empty code results in non-empty binary representation with optimizations turned off, @@ -563,8 +566,8 @@ SOLTMPDIR=$(mktemp -d) cd "$SOLTMPDIR" if ! "$REPO_ROOT/scripts/ASTImportTest.sh" then - rm -rf "$SOLTMPDIR" - exit 1 + rm -r "$SOLTMPDIR" + fail fi ) rm -r "$SOLTMPDIR"