From 9fa4ed673debd53b231d5e834d508e5ffb169030 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Wed, 26 Aug 2020 22:35:46 +0200 Subject: [PATCH] Avoid using grep for test case names if possible If `$FILTER` (`-f`) and `$EXCLUDE` (`-e`) are simple selections that can be expressed as shell patterns, use a case statement instead of calling grep to determine whether a test case should be executed. Using a case statement significantly reduces the time it takes to determine that a test case is excluded (but the improvement is small compared to running the test). This noticeably speeds up running a single test or a small number of tests. Before: ``` tests/ssl-opt.sh -f Default 1.75s user 0.54s system 79% cpu 2.885 total ``` After: ``` tests/ssl-opt.sh -f Default 0.37s user 0.14s system 29% cpu 1.715 total ``` There is no perceptible difference when running a large number of tests. Signed-off-by: Gilles Peskine --- tests/ssl-opt.sh | 47 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh index a2ffcb4a4..8adbdc3a8 100755 --- a/tests/ssl-opt.sh +++ b/tests/ssl-opt.sh @@ -114,8 +114,8 @@ print_usage() { echo "Usage: $0 [options]" printf " -h|--help\tPrint this help.\n" printf " -m|--memcheck\tCheck memory leaks and errors.\n" - printf " -f|--filter\tOnly matching tests are executed (BRE)\n" - printf " -e|--exclude\tMatching tests are excluded (BRE)\n" + printf " -f|--filter\tOnly matching tests are executed (substring or BRE)\n" + printf " -e|--exclude\tMatching tests are excluded (substring or BRE)\n" printf " -n|--number\tExecute only numbered test (comma-separated, e.g. '245,256')\n" printf " -s|--show-numbers\tShow test numbers in front of test names\n" printf " -p|--preserve-logs\tPreserve logs of successful tests as well\n" @@ -665,8 +665,7 @@ run_test() { NAME="$1" shift 1 - if echo "$NAME" | grep "$FILTER" | grep -v "$EXCLUDE" >/dev/null; then : - else + if is_excluded "$NAME"; then SKIP_NEXT="NO" # There was no request to run the test, so don't record its outcome. return @@ -1032,6 +1031,46 @@ cleanup() { get_options "$@" +# Optimize filters: if $FILTER and $EXCLUDE can be expressed as shell +# patterns rather than regular expressions, use a case statement instead +# of calling grep. To keep the optimizer simple, it is incomplete and only +# detects simple cases: plain substring, everything, nothing. +# +# As an exception, the character '.' is treated as an ordinary character +# if it is the only special character in the string. This is because it's +# rare to need "any one character", but needing a literal '.' is common +# (e.g. '-f "DTLS 1.2"'). +need_grep= +case "$FILTER" in + '^$') simple_filter=;; + '.*') simple_filter='*';; + *[][\$^+*?{|}]*) # Regexp special characters (other than .), we need grep + need_grep=1;; + *) # No regexp or shell-pattern special character + simple_filter="*$FILTER*";; +esac +case "$EXCLUDE" in + '^$') simple_exclude=;; + '.*') simple_exclude='*';; + *[][\$^+*?{|}]*) # Regexp special characters (other than .), we need grep + need_grep=1;; + *) # No regexp or shell-pattern special character + simple_exclude="*$EXCLUDE*";; +esac +if [ -n "$need_grep" ]; then + is_excluded () { + ! echo "$1" | grep "$FILTER" | grep -q -v "$EXCLUDE" + } +else + is_excluded () { + case "$1" in + $simple_exclude) true;; + $simple_filter) false;; + *) true;; + esac + } +fi + # sanity checks, avoid an avalanche of errors P_SRV_BIN="${P_SRV%%[ ]*}" P_CLI_BIN="${P_CLI%%[ ]*}"