From e239a5e30206d5979d986e873d4c7d340dbb3ccb Mon Sep 17 00:00:00 2001 From: Ole Tange Date: Mon, 14 Sep 2020 08:33:42 +0200 Subject: [PATCH] find-first-fail: Bug if multiple options fixed. --- Makefile | 18 +++++------ find-first-fail/find-first-fail | 54 +++++++++++++++++++++++---------- find-first-fail/testsuite | 38 +++++++++++++++++++++++ 3 files changed, 85 insertions(+), 25 deletions(-) create mode 100644 find-first-fail/testsuite diff --git a/Makefile b/Makefile index 8d672a4..a05c187 100644 --- a/Makefile +++ b/Makefile @@ -1,14 +1,14 @@ -CMD = binsearch blink 2grep 2search burncpu drac duplicate-packets em \ - encdir field forever fxkill G gitnext gitundo goodpasswd \ - histogram mtrr mirrorpdf neno off parsort pdfman pidcmd \ - pidtree plotpipe puniq ramusage rand rclean rina rn rrm \ - seekmaniac shython sound-reload splitvideo stdout swapout T \ - teetime timestamp tracefile transpose upsidedown vid \ +CMD = blink 2grep 2search burncpu drac duplicate-packets em encdir \ + field find-first-fail forever fxkill G gitnext gitundo \ + goodpasswd histogram mtrr mirrorpdf neno off parsort pdfman \ + pidcmd pidtree plotpipe puniq ramusage rand rclean rina rn \ + rrm seekmaniac shython sound-reload splitvideo stdout swapout \ + T teetime timestamp tracefile transpose upsidedown vid \ w4it-for-port-open whitehash wifi-reload wssh ytv yyyymmdd -all: binsearch/binsearch.1 blink/blink.1 2search/2grep.1 \ - 2search/2search.1 burncpu/burncpu.1 drac/drac.1 \ - encdir/encdir.1 field/field.1 G/G.1 gitnext/gitnext.1 \ +all: blink/blink.1 2search/2grep.1 2search/2search.1 \ + burncpu/burncpu.1 drac/drac.1 encdir/encdir.1 field/field.1 \ + find-first-fail/find-first-fail.1 G/G.1 gitnext/gitnext.1 \ gitundo/gitundo.1 goodpasswd/goodpasswd.1 \ histogram/histogram.1 mirrorpdf/mirrorpdf.1 neno/neno.1 \ off/off.1 parsort/parsort.1 pdfman/pdfman.1 pidcmd/pidcmd.1 \ diff --git a/find-first-fail/find-first-fail b/find-first-fail/find-first-fail index 6c1d21c..5bbf4b7 100755 --- a/find-first-fail/find-first-fail +++ b/find-first-fail/find-first-fail @@ -18,12 +18,13 @@ B [-2] [-q] [-s I] I B runs I with a single number. It returns highest value that I succeeds for. -It finds the value by first testing the value 1. As long as the value -succeeds, the value is doubled. When the value fails, B -does a binary search between this value and the previous value. +It finds the value by first testing the I value (which defaults +to 1). As long as the value succeeds, the value is doubled. When the +value fails, B does a binary search between this +value and the previous value. -If the value 1 fails, B instead searches for the highest -value that I fails for. +If the I value fails, B instead searches for +the highest value that I fails for. =head1 OPTIONS @@ -43,6 +44,10 @@ Quiet. Ignore output from I. Start searching from the value I. Normally searching will start from the value 1. +=item B<-v> + +Verbose. Show the commands being run. + =back @@ -89,6 +94,20 @@ Complex commands can also be run: find-first-fail -v perl -e 'exit(shift > 129)' +=head2 Find the second limit of a program + +Assume you have a program that is OK in the range 123..12345. How do +you find the limits? + + myprog() { perl -e '$a=shift;if($a <= 123) { exit 0; } + else { exit ($a <= 12345) }' "$@"; } + export -f myprog + # Finds 123 + find-first-fail myprog + # Finds 12345 + find-first-fail -s 200 myprog + + =head1 AUTHOR Copyright (C) 2020 Ole Tange, @@ -121,15 +140,16 @@ B(1) find-first-fail() { _find-first-fail() { - low=$1 - high=$2 + local low=$1 + local high=$2 # echo $low-$high if [ $low -gt $(($high - 2)) ]; then + echo $low return fi shift shift - middle=$(( ( $low + $high ) / 2 )) + local middle=$(( ( $low + $high ) / 2 )) if _run $low $middle "$@" ; then low=$middle else @@ -165,24 +185,25 @@ find-first-fail() { eval "$not" _inner_run "$quiet" } - quiet="" - opt2=false - verbose=false - start=1 + local opt2=false + local quiet="" + local start=1 + local verbose=false # Parse and remove options - while getopts "2vqs:" options; do + while getopts "2qs:v" options; do case "${options}" in (2) opt2=true;; (q) quiet=">/dev/null 2>/dev/null";; + (s) start="$OPTARG";; (v) verbose=true;; - (s) start="$2";; (-) break;; esac done shift $(( OPTIND - 1)) # If function(1) = false: run 'not function()' instead + local not if _run "$start" "$start" "$@" ; then not='' else @@ -192,7 +213,8 @@ find-first-fail() { # exponential search for the first value that is false # low = previous value (function($low) == true) # high = low * 2 (function($high) == false) - high=$start + local high=$start + local low while _run $start $high "$@" ; do low=$high high=$(( $high*2 )) @@ -207,7 +229,7 @@ find-first-fail() { # Search low..high # echo "low: $low high: $high not: $not" _find-first-fail $low $high "$@" 2>/dev/null - echo $low + unset low high start } if [ -z "$*" ] ; then diff --git a/find-first-fail/testsuite b/find-first-fail/testsuite new file mode 100644 index 0000000..58042c5 --- /dev/null +++ b/find-first-fail/testsuite @@ -0,0 +1,38 @@ +#!/bin/bash + +test_unexported_function() { + myprog() { perl -e 'exit (shift > 12345678)' "$@"; } + # myprog is a function, so source find-first-fail first + . `which find-first-fail` + find-first-fail myprog +} + +test_exported_function() { + myprog() { perl -e 'exit (shift > 12345678)' "$@"; } + # myprog is an exported function + export -f myprog + find-first-fail myprog +} + +test_startvalue() { + # exit value changes at 100 and 12345678 + myprog() { perl -e '$a=shift;if($a <= 123) { exit 0; } + else { exit ($a <= 12345678) }' "$@"; } + export -f myprog + # Finds 123 + find-first-fail myprog + # Finds 12345678 + find-first-fail -s 200 myprog +} + +test_s_v_12() { + # Multiple options + myprog() { perl -e 'exit (shift > 12)' "$@"; } + export -f myprog + find-first-fail -v -s 10 myprog + find-first-fail -v -q -s 10 myprog +} + +export -f $(compgen -A function | grep test_) +compgen -A function | grep test_ | LC_ALL=C sort | + parallel --timeout 1000% --tag -k --joblog /tmp/jl-`basename $0` '{} 2>&1'