Print this page
3484 enhance and document tail follow support
Reviewed by: Joshua M. Clulow <jmc@joyent.com>

@@ -11,131 +11,357 @@
 # at http://www.illumos.org/license/CDDL.
 #
 
 #
 # Copyright 2010 Chris Love.  All rights reserved.
+# Copyright (c) 2013, Joyent, Inc. All rights reserved.
 #
 
+checktest()
+{
+        local actual=$1
+        local output=$2
+        local test=$3
 
+        if [[ "$actual" != "$output" ]]; then
+                echo "$CMD: test $test: FAIL"
+                echo -e "$CMD: test $test: expected output:\n$o"
+                echo -e "$CMD: test $test: actual output:\n$a"
+        else
+                echo "$CMD: test $test: pass"
+        fi
+}
+
 # 
 # Test cases for 'tail', some based on CoreUtils test cases (validated
-# with legacy Solaris 'tail' and/or xpg4 'tail')
+# with legacy Solaris 'tail' and/or xpg4 'tail').  Note that this is designed
+# to be able to run on BSD systems as well to check our behavior against
+# theirs (some behavior that is known to be idiosyncratic to illumos is
+# skipped on non-illumos systems).
 #
 PROG=/usr/bin/tail
+CMD=`basename $0`
+DIR=""
 
-case $1 in 
+while [[ $# -gt 0 ]]; do
+        case $1 in
     -x)
         PROG=/usr/xpg4/bin/tail
+                shift
         ;;
     -o)
         PROG=$2
+                shift 2
         ;;
-    -?)
-        echo "Usage: tailtests.sh [-x][-o <override tail executable>]"
+            -d)
+                DIR=$2
+                shift 2
+                ;;
+            *)
+                echo "Usage: tailtests.sh" \
+                    "[-x][-o <override tail executable>]" \
+                    "[-d <override output directory>]"
         exit 1
         ;;
-esac
+        esac
+done
 
-echo "Using $PROG"
+#
+# Shut bash up upon receiving a term so we can drop it on our children
+# without disrupting the output.
+#
+trap "exit 0" TERM
 
+echo "$CMD: program is $PROG"
+
+if [[ $DIR != "" ]]; then
+        echo "$CMD: directory is $DIR"
+fi
+
 o=`echo -e "bcd"`
 a=`echo -e "abcd" | $PROG +2c`
-[[ "$a" != "$o" ]] && echo "Fail test 1 - $a"
+checktest "$a" "$o" 1
 
 o=`echo -e ""`
 a=`echo "abcd" | $PROG +8c`
-[[ "$a" != "$o" ]] && echo "Fail test 2 - $a"
+checktest "$a" "$o" 2
 
 o=`echo -e "abcd"`
 a=`echo "abcd" | $PROG -9c`
-[[ "$a" != "$o" ]] && echo "Fail test 3 - $a"
+checktest "$a" "$o" 3
 
 o=`echo -e "x"`
 a=`echo -e "x" | $PROG -1l`
-[[ "$a" != "x" ]] && echo "Fail test 4 - $a"
+checktest "$a" "$o" 4
 
 o=`echo -e "\n"`
 a=`echo -e "x\ny\n" | $PROG -1l`
-[[ "$a" != "$o" ]] && echo "Fail test 5 - $a"
+checktest "$a" "$o" 5
 
 o=`echo -e "y\n"`
 a=`echo -e "x\ny\n" | $PROG -2l`
-[[ "$a" != "$o" ]] && echo "Fail test 6 - $a"
+checktest "$a" "$o" 6
 
 o=`echo -e "y"`
 a=`echo -e "x\ny" | $PROG -1l`
-[[ "$a" != "$o" ]] && echo "Fail test 7 - $a"
+checktest "$a" "$o" 7
 
 o=`echo -e "x\ny\n"`
 a=`echo -e "x\ny\n" | $PROG +1l`
-[[ "$a" != "$o" ]] && echo "Fail test 8 - $a"
+checktest "$a" "$o" 8
 
 o=`echo -e "y\n"`
 a=`echo -e "x\ny\n" | $PROG +2l`
-[[ "$a" != "$o" ]] && echo "Fail test 9 - $a"
+checktest "$a" "$o" 9
 
 o=`echo -e "x"`
 a=`echo -e "x" | $PROG -1`
-[[ "$a" != "$o" ]] && echo "Fail test 10 - $a"
+checktest "$a" "$o" 10
 
 o=`echo -e "\n"`
 a=`echo -e "x\ny\n" | $PROG -1`
-[[ "$a" != "$o" ]] && echo "Fail test 11 - $a"
+checktest "$a" "$o" 11
 
 o=`echo -e "y\n"`
 a=`echo -e "x\ny\n" | $PROG -2`
-[[ "$a" != "$o" ]] && echo "Fail test 12 - $a"
+checktest "$a" "$o" 12
 
 o=`echo -e "y"`
 a=`echo -e "x\ny" | $PROG -1`
-[[ "$a" != "$o" ]] && echo "Fail test 13 - $a"
+checktest "$a" "$o" 13
 
 o=`echo -e "x\ny\n"`
 a=`echo -e "x\ny\n" | $PROG +1`
-[[ "$a" != "$o" ]] && echo "Fail test 14 - $a"
+checktest "$a" "$o" 14
 
 o=`echo -e "y\n"`
 a=`echo -e "x\ny\n" | $PROG +2`
-[[ "$a" != "$o" ]] && echo "Fail test 15 - $a"
+checktest "$a" "$o" 15
 
-# For compatibility with Legacy Solaris tail this should also work as '+c'
 o=`echo -e "yyz"`
 a=`echo -e "xyyyyyyyyyyz" | $PROG +10c`
-[[ "$a" != "$o" ]] && echo "Fail test 16 - $a"
+checktest "$a" "$o" 16
 
-o=`echo -e "yyz"`
-a=`echo -e "xyyyyyyyyyyz" | $PROG +c`
-[[ "$a" != "$o" ]] && echo "Fail test 16a - $a"
-
-
-# For compatibility with Legacy Solaris tail this should also work as '+l'
 o=`echo -e "y\ny\nz"`
 a=`echo -e "x\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\nz" | $PROG +10l`
-[[ "$a" != "$o" ]] && echo "Fail test 17 - $a"
+checktest "$a" "$o" 17
 
-o=`echo -e "y\ny\nz"`
-a=`echo -e "x\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\nz" | $PROG +l`
-[[ "$a" != "$o" ]] && echo "Fail test 17a - $a"
-
-
-# For compatibility with Legacy Solaris tail this should also work as '-l'
 o=`echo -e "y\ny\ny\ny\ny\ny\ny\ny\ny\nz"`
 a=`echo -e "x\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\nz" | $PROG -10l`
-[[ "$a" != "$o" ]] && echo "Fail test 18 - $a"
+checktest "$a" "$o" 18
 
-o=`echo -e "y\ny\ny\ny\ny\ny\ny\ny\ny\nz"`
-a=`echo -e "x\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\nz" | $PROG -l`
-[[ "$a" != "$o" ]] && echo "Fail test 18a - $a"
+#
+# For reasons that are presumably as accidental as they are ancient, legacy
+# (and closed) Solaris tail(1) allows +c, +l and -l to be aliases for +10c,
+# +10l and -10l, respectively.  If we are on SunOS, verify that this silly
+# behavior is functional.
+#
+if [[ `uname -s` == "SunOS" ]]; then
+        o=`echo -e "yyz"`
+        a=`echo -e "xyyyyyyyyyyz" | $PROG +c`
+        checktest "$a" "$o" 16a
 
+        o=`echo -e "y\ny\nz"`
+        a=`echo -e "x\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\nz" | $PROG +l`
+        checktest "$a" "$o" 17a
+
+        o=`echo -e "y\ny\ny\ny\ny\ny\ny\ny\ny\nz"`
+        a=`echo -e "x\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\nz" | $PROG -l`
+        checktest "$a" "$o" 18a
+fi
+
 o=`echo -e "c\nb\na"`
 a=`echo -e "a\nb\nc" | $PROG -r`
-[[ "$a" != "$o" ]] && echo "Fail test 19 - $a"
+checktest "$a" "$o" 19
 
+#
+# Now we want to do a series of follow tests.
+#
+if [[ $DIR == "" ]]; then
+        tdir=$(mktemp -d -t tailtest.XXXXXXXX || exit 1)
+else
+        tdir=$(mktemp -d $DIR/tailtest.XXXXXXXX || exit 1)
+fi
 
-echo "Completed"
+follow=$tdir/follow
+moved=$tdir/follow.moved
+out=$tdir/out
 
-exit 0
+#
+# First, verify that following works in its most basic sense.
+#
+echo -e "a\nb\nc" > $follow
+$PROG -f $follow > $out 2> /dev/null &
+child=$!
+sleep 2
+echo -e "d\ne\nf" >> $follow
+sleep 1
+kill $child
+sleep 1
 
-# Template for additional test cases
-#o=`echo -e ""`
-#a=`echo -e "" | $PROG `
-#[[ "$a" != "$o" ]] && echo "Fail test  - $a"
+o=`echo -e "a\nb\nc\nd\ne\nf\n"`
+a=`cat $out`
+checktest "$a" "$o" 20
+rm $follow
+
+#
+# Now verify that following correctly follows the file being moved.
+#
+echo -e "a\nb\nc" > $follow
+$PROG -f $follow > $out 2> /dev/null &
+child=$!
+sleep 2
+mv $follow $moved
+
+echo -e "d\ne\nf" >> $moved
+sleep 1
+kill $child
+sleep 1
+
+o=`echo -e "a\nb\nc\nd\ne\nf\n"`
+a=`cat $out`
+checktest "$a" "$o" 21
+rm $moved
+
+#
+# And now truncation with the new offset being less than the old offset.
+#
+echo -e "a\nb\nc" > $follow
+$PROG -f $follow > $out 2> /dev/null &
+child=$!
+sleep 2
+echo -e "d\ne\nf" >> $follow
+sleep 1
+echo -e "g\nh\ni" > $follow
+sleep 1
+kill $child
+sleep 1
+
+o=`echo -e "a\nb\nc\nd\ne\nf\ng\nh\ni\n"`
+a=`cat $out`
+checktest "$a" "$o" 22
+rm $follow
+
+#
+# And truncation with the new offset being greater than the old offset.
+#
+echo -e "a\nb\nc" > $follow
+sleep 1
+$PROG -f $follow > $out 2> /dev/null &
+child=$!
+sleep 2
+echo -e "d\ne\nf" >> $follow
+sleep 1
+echo -e "g\nh\ni\nj\nk\nl\nm\no\np\nq" > $follow
+sleep 1
+kill $child
+sleep 1
+
+o=`echo -e "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\no\np\nq"`
+a=`cat $out`
+checktest "$a" "$o" 23
+rm $follow
+
+#
+# Verify that we can follow the moved file and correctly see a truncation.
+#
+echo -e "a\nb\nc" > $follow
+$PROG -f $follow > $out 2> /dev/null &
+child=$!
+sleep 2
+mv $follow $moved
+
+echo -e "d\ne\nf" >> $moved
+sleep 1
+echo -e "g\nh\ni\nj\nk\nl\nm\no\np\nq" > $moved
+sleep 1
+kill $child
+sleep 1
+
+o=`echo -e "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\no\np\nq"`
+a=`cat $out`
+checktest "$a" "$o" 24
+rm $moved
+
+#
+# Verify that capital-F follow properly deals with truncation
+#
+echo -e "a\nb\nc" > $follow
+$PROG -F $follow > $out 2> /dev/null &
+child=$!
+sleep 2
+echo -e "d\ne\nf" >> $follow
+sleep 1
+echo -e "g\nh\ni" > $follow
+sleep 1
+kill $child
+sleep 1
+
+o=`echo -e "a\nb\nc\nd\ne\nf\ng\nh\ni\n"`
+a=`cat $out`
+checktest "$a" "$o" 25
+rm $follow
+
+#
+# Verify that capital-F follow _won't_ follow the moved file and will
+# correctly deal with recreation of the original file.
+#
+echo -e "a\nb\nc" > $follow
+$PROG -F $follow > $out 2> /dev/null &
+child=$!
+sleep 2
+mv $follow $moved
+
+echo -e "x\ny\nz" >> $moved
+echo -e "d\ne\nf" > $follow
+sleep 1
+kill $child
+sleep 1
+
+o=`echo -e "a\nb\nc\nd\ne\nf\n"`
+a=`cat $out`
+checktest "$a" "$o" 26
+rm $moved
+
+#
+# Verify that following two files works.
+#
+echo -e "one" > $follow
+echo -e "two" > $moved
+$PROG -f $follow $moved > $out 2> /dev/null &
+child=$!
+sleep 2
+echo -e "three" >> $follow
+sleep 1
+echo -e "four" >> $moved
+sleep 1
+echo -e "five" >> $follow
+sleep 1
+kill $child
+sleep 1
+
+# There is a bug where the content comes before the header lines,
+# where rlines/mapprint happens before the header.  A pain to fix.
+# In this test, just make sure we see both files change.
+o="one
+
+==> $follow <==
+two
+
+==> $moved <==
+
+==> $follow <==
+three
+
+==> $moved <==
+four
+
+==> $follow <==
+five"
+a=`cat $out`
+checktest "$a" "$o" 27
+rm $follow $moved
+
+echo "$CMD: completed"
+
+exit $errs
+