8671 tail(1) ignores arguments after -c/b/l
[unleashed.git] / usr / src / cmd / tail / tests / tailtests.sh
blob69ca0e3ad132c3b2dc183802a285af183e08fa8f
1 #!/bin/bash
4 # This file and its contents are supplied under the terms of the
5 # Common Development and Distribution License ("CDDL"), version 1.0.
6 # You may only use this file in accordance with the terms of version
7 # 1.0 of the CDDL.
9 # A full copy of the text of the CDDL should have accompanied this
10 # source. A copy is of the CDDL is also available via the Internet
11 # at http://www.illumos.org/license/CDDL.
15 # Copyright 2010 Chris Love. All rights reserved.
16 # Copyright 2017, Joyent, Inc.
19 BLOCK=""
20 for i in {1..512}; do
21 BLOCK+="."
22 done
25 checktest()
27 local actual=$1
28 local output=$2
29 local test=$3
31 if [[ "$actual" != "$output" ]]; then
32 echo "$CMD: test $test: FAIL"
33 echo -e "$CMD: test $test: expected output:\n$output"
34 echo -e "$CMD: test $test: actual output:\n$actual"
35 else
36 echo "$CMD: test $test: pass"
40 checkfail()
42 printf "foobar" | $PROG $* &> /dev/null
44 if [[ $? -eq 0 ]]; then
45 printf '%s: test "test %s": was supposed to fail\n' "$CMD" "$*"
46 else
47 printf '%s: test "%s": pass\n' "$CMD" "$*"
52 # Test cases for 'tail', some based on CoreUtils test cases (validated
53 # with legacy Solaris 'tail' and/or xpg4 'tail'). Note that this is designed
54 # to be able to run on BSD systems as well to check our behavior against
55 # theirs (some behavior that is known to be idiosyncratic to illumos is
56 # skipped on non-illumos systems).
58 PROG=/usr/bin/tail
59 CMD=`basename $0`
60 DIR=""
62 while [[ $# -gt 0 ]]; do
63 case $1 in
64 -x)
65 PROG=/usr/xpg4/bin/tail
66 shift
68 -o)
69 PROG=$2
70 shift 2
72 -d)
73 DIR=$2
74 shift 2
77 echo "Usage: tailtests.sh" \
78 "[-x][-o <override tail executable>]" \
79 "[-d <override output directory>]"
80 exit 1
82 esac
83 done
86 # Shut bash up upon receiving a term so we can drop it on our children
87 # without disrupting the output.
89 trap "exit 0" TERM
91 echo "$CMD: program is $PROG"
93 if [[ $DIR != "" ]]; then
94 echo "$CMD: directory is $DIR"
97 o=`echo -e "bcd"`
98 a=`echo -e "abcd" | $PROG +2c`
99 checktest "$a" "$o" 1
101 o=`echo -e ""`
102 a=`echo "abcd" | $PROG +8c`
103 checktest "$a" "$o" 2
105 o=`echo -e "abcd"`
106 a=`echo "abcd" | $PROG -9c`
107 checktest "$a" "$o" 3
109 o=`echo -e "x"`
110 a=`echo -e "x" | $PROG -1l`
111 checktest "$a" "$o" 4
113 o=`echo -e "\n"`
114 a=`echo -e "x\ny\n" | $PROG -1l`
115 checktest "$a" "$o" 5
117 o=`echo -e "y\n"`
118 a=`echo -e "x\ny\n" | $PROG -2l`
119 checktest "$a" "$o" 6
121 o=`echo -e "y"`
122 a=`echo -e "x\ny" | $PROG -1l`
123 checktest "$a" "$o" 7
125 o=`echo -e "x\ny\n"`
126 a=`echo -e "x\ny\n" | $PROG +1l`
127 checktest "$a" "$o" 8
129 o=`echo -e "y\n"`
130 a=`echo -e "x\ny\n" | $PROG +2l`
131 checktest "$a" "$o" 9
133 o=`echo -e "x"`
134 a=`echo -e "x" | $PROG -1`
135 checktest "$a" "$o" 10
137 o=`echo -e "\n"`
138 a=`echo -e "x\ny\n" | $PROG -1`
139 checktest "$a" "$o" 11
141 o=`echo -e "y\n"`
142 a=`echo -e "x\ny\n" | $PROG -2`
143 checktest "$a" "$o" 12
145 o=`echo -e "y"`
146 a=`echo -e "x\ny" | $PROG -1`
147 checktest "$a" "$o" 13
149 o=`echo -e "x\ny\n"`
150 a=`echo -e "x\ny\n" | $PROG +1`
151 checktest "$a" "$o" 14
153 o=`echo -e "y\n"`
154 a=`echo -e "x\ny\n" | $PROG +2`
155 checktest "$a" "$o" 15
157 o=`printf "yyz\n"`
158 a=`printf "xyyyyyyyyyyz\n" | $PROG +10c`
159 checktest "$a" "$o" 16
161 o=`printf "y\ny\nz\n"`
162 a=`printf "x\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\nz\n" | $PROG +10l`
163 checktest "$a" "$o" 17
165 o=`printf "y\ny\ny\ny\ny\ny\ny\ny\ny\nz\n"`
166 a=`printf "x\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\nz\n" | $PROG -10l`
167 checktest "$a" "$o" 18
169 a=`printf "o\nn\nm\nl\nk\nj\ni\nh\ng\n"`
170 o=`printf "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\nn\no\n" | $PROG +10lr`
171 checktest "$a" "$o" 19
173 a=`printf "o\nn\nm\nl\nk\nj\ni\nh\ng\nf\n"`
174 o=`printf "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\nn\no\n" | $PROG -10lr`
175 checktest "$a" "$o" 20
177 a=`printf "o\nn\nm\nl\n"`
178 o=`printf "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\nn\no\n" | $PROG +10cr`
179 checktest "$a" "$o" 21
181 a=`printf "o\nn\nm\nl\nk\n"`
182 o=`printf "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\nn\no\n" | $PROG -10cr`
183 checktest "$a" "$o" 22
186 # For reasons that are presumably as accidental as they are ancient, legacy
187 # (and closed) Solaris tail(1) allows +c, +l and -l to be aliases for +10c,
188 # +10l and -10l, respectively. If we are on SunOS, verify that this silly
189 # behavior is functional.
191 if [[ `uname -s` == "SunOS" ]]; then
192 o=`printf "yyz\n"`
193 a=`printf "xyyyyyyyyyyz\n" | $PROG +c`
194 checktest "$a" "$o" 16a
196 o=`printf "y\ny\nz\n"`
197 a=`printf "x\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\nz\n" | $PROG +l`
198 checktest "$a" "$o" 17a
200 o=`printf "y\ny\ny\ny\ny\ny\ny\ny\ny\nz\n"`
201 a=`printf "x\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\nz\n" | $PROG -l`
202 checktest "$a" "$o" 18a
204 a=`printf "o\nn\nm\nl\nk\nj\ni\nh\ng\n"`
206 o=`printf "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\nn\no\n" | $PROG +lr`
207 checktest "$a" "$o" 19a
209 o=`printf "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\nn\no\n" | $PROG +l -r`
210 checktest "$a" "$o" 19a
212 a=`printf "o\nn\nm\nl\nk\nj\ni\nh\ng\nf\n"`
214 o=`printf "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\nn\no\n" | $PROG -lr`
215 checktest "$a" "$o" 20a
217 o=`printf "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\nn\no\n" | $PROG -l -r`
218 checktest "$a" "$o" 20b
220 a=`printf "o\nn\nm\nl\n"`
222 o=`printf "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\nn\no\n" | $PROG +cr`
223 checktest "$a" "$o" 21a
225 o=`printf "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\nn\no\n" | $PROG +c -r`
226 checktest "$a" "$o" 21a
228 a=`printf "o\nn\nm\nl\nk\n"`
230 o=`printf "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\nn\no\n" | $PROG -cr`
231 checktest "$a" "$o" 22a
233 o=`printf "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\nn\no\n" | $PROG -c -r`
234 checktest "$a" "$o" 22b
237 o=`echo -e "c\nb\na"`
238 a=`echo -e "a\nb\nc" | $PROG -r`
239 checktest "$a" "$o" 23
242 # Now we want to do a series of follow tests.
244 if [[ $DIR == "" ]]; then
245 export TMPDIR=/var/tmp
246 tdir=$(mktemp -d -t tailtest.XXXXXXXX || exit 1)
247 else
248 tdir=$(mktemp -d $DIR/tailtest.XXXXXXXX || exit 1)
251 follow=$tdir/follow
252 moved=$tdir/follow.moved
253 out=$tdir/out
256 # First, verify that following works in its most basic sense.
258 echo -e "a\nb\nc" > $follow
259 $PROG -f $follow > $out 2> /dev/null &
260 child=$!
261 sleep 2
262 echo -e "d\ne\nf" >> $follow
263 sleep 1
264 kill $child
265 sleep 1
267 o=`echo -e "a\nb\nc\nd\ne\nf\n"`
268 a=`cat $out`
269 checktest "$a" "$o" 24
270 rm $follow
273 # Now verify that following correctly follows the file being moved.
275 echo -e "a\nb\nc" > $follow
276 $PROG -f $follow > $out 2> /dev/null &
277 child=$!
278 sleep 2
279 mv $follow $moved
281 echo -e "d\ne\nf" >> $moved
282 sleep 1
283 kill $child
284 sleep 1
286 o=`echo -e "a\nb\nc\nd\ne\nf\n"`
287 a=`cat $out`
288 checktest "$a" "$o" 25
289 rm $moved
292 # And now truncation with the new offset being less than the old offset.
294 echo -e "a\nb\nc" > $follow
295 $PROG -f $follow > $out 2> /dev/null &
296 child=$!
297 sleep 2
298 echo -e "d\ne\nf" >> $follow
299 sleep 1
300 echo -e "g\nh\ni" > $follow
301 sleep 1
302 kill $child
303 sleep 1
305 o=`echo -e "a\nb\nc\nd\ne\nf\ng\nh\ni\n"`
306 a=`cat $out`
307 checktest "$a" "$o" 26
308 rm $follow
311 # And truncation with the new offset being greater than the old offset.
313 echo -e "a\nb\nc" > $follow
314 sleep 1
315 $PROG -f $follow > $out 2> /dev/null &
316 child=$!
317 sleep 2
318 echo -e "d\ne\nf" >> $follow
319 sleep 1
320 echo -e "g\nh\ni\nj\nk\nl\nm\no\np\nq" > $follow
321 sleep 1
322 kill $child
323 sleep 1
325 o=`echo -e "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\no\np\nq"`
326 a=`cat $out`
327 checktest "$a" "$o" 27
328 rm $follow
331 # Verify that we can follow the moved file and correctly see a truncation.
333 echo -e "a\nb\nc" > $follow
334 $PROG -f $follow > $out 2> /dev/null &
335 child=$!
336 sleep 2
337 mv $follow $moved
339 echo -e "d\ne\nf" >> $moved
340 sleep 1
341 echo -e "g\nh\ni\nj\nk\nl\nm\no\np\nq" > $moved
342 sleep 1
343 kill $child
344 sleep 1
346 o=`echo -e "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\no\np\nq"`
347 a=`cat $out`
348 checktest "$a" "$o" 28
349 rm $moved
352 # Verify that capital-F follow properly deals with truncation
354 echo -e "a\nb\nc" > $follow
355 $PROG -F $follow > $out 2> /dev/null &
356 child=$!
357 sleep 2
358 echo -e "d\ne\nf" >> $follow
359 sleep 1
360 echo -e "g\nh\ni" > $follow
361 sleep 1
362 kill $child
363 sleep 1
365 o=`echo -e "a\nb\nc\nd\ne\nf\ng\nh\ni\n"`
366 a=`cat $out`
367 checktest "$a" "$o" 29
368 rm $follow
371 # Verify that capital-F follow _won't_ follow the moved file and will
372 # correctly deal with recreation of the original file.
374 echo -e "a\nb\nc" > $follow
375 $PROG -F $follow > $out 2> /dev/null &
376 child=$!
377 sleep 2
378 mv $follow $moved
380 echo -e "x\ny\nz" >> $moved
383 # At this point, tail is polling on stat'ing the missing file; we need to
384 # be sure to sleep long enough after recreating it to know that it will pick
385 # it up.
387 echo -e "d\ne\nf" > $follow
388 sleep 5
389 kill $child
390 sleep 1
392 o=`echo -e "a\nb\nc\nd\ne\nf\n"`
393 a=`cat $out`
394 checktest "$a" "$o" 30
395 rm $moved
398 # Verify that following two files works.
400 echo -e "one" > $follow
401 echo -e "two" > $moved
402 $PROG -f $follow $moved > $out 2> /dev/null &
403 child=$!
404 sleep 2
405 echo -e "three" >> $follow
406 sleep 1
407 echo -e "four" >> $moved
408 sleep 1
409 echo -e "five" >> $follow
410 sleep 1
411 kill $child
412 sleep 1
414 # There is a bug where the content comes before the header lines,
415 # where rlines/mapprint happens before the header. A pain to fix.
416 # In this test, just make sure we see both files change.
417 o="one
419 ==> $follow <==
422 ==> $moved <==
424 ==> $follow <==
425 three
427 ==> $moved <==
428 four
430 ==> $follow <==
431 five"
432 a=`cat $out`
433 checktest "$a" "$o" 31
434 rm $follow $moved
436 if [[ `uname -s` == "SunOS" ]]; then
438 # Use DTrace to truncate the file between the return from port_get()
439 # and the reassociation of the file object with the port, exposing
440 # any race conditions whereby FILE_TRUNC events are lost.
442 cat /dev/null > $follow
443 dtrace -c "$PROG -f $follow" -s /dev/stdin > $out <<EOF
444 #pragma D option destructive
445 #pragma D option quiet
447 pid\$target::port_get:return
448 /++i == 5/
450 stop();
451 system("cat /dev/null > $follow");
452 system("prun %d", pid);
455 tick-1sec
457 system("echo %d >> $follow", j++);
460 tick-1sec
461 /j == 10/
463 exit(0);
467 o=`echo -e "0\n1\n2\n3\n5\n6\n7\n8\n9\n"`
468 a=`cat $out`
469 checktest "$a" "$o" 31a
470 rm $follow
472 cat /dev/null > $follow
473 dtrace -c "$PROG -f $follow" -s /dev/stdin > $out <<EOF
474 #pragma D option destructive
475 #pragma D option quiet
477 pid\$target::port_get:return
478 /++i == 5/
480 stop();
481 system("mv $follow $moved");
482 system("cat /dev/null > $moved");
483 system("prun %d", pid);
486 tick-1sec
488 system("echo %d >> %s", j++,
489 i < 5 ? "$follow" : "$moved");
492 tick-1sec
493 /j == 10/
495 exit(0);
499 o=`echo -e "0\n1\n2\n3\n5\n6\n7\n8\n9\n"`
500 a=`cat $out`
501 checktest "$a" "$o" 31b
502 rm $moved
505 # Verify that -F will deal properly with the file being truncated
506 # not by truncation, but rather via an ftruncate() from logadm.
508 cat /dev/null > $follow
509 ( $PROG -F $follow > $out ) &
510 child=$!
511 echo -e "a\nb\nc\nd\ne\nf" >> $follow
512 logadm -c $follow
513 sleep 2
514 echo -e "g\nh\ni" >> $follow
515 sleep 2
516 kill $child
517 sleep 1
519 o=`echo -e "a\nb\nc\nd\ne\nf\ng\nh\ni\n"`
520 a=`cat $out`
521 checktest "$a" "$o" 31c
525 # We're now going to test that while we may miss output due to truncations
526 # occurring faster than tail can read, we don't ever repeat output.
528 cat /dev/null > $follow
529 ( $PROG -f $follow > $out ) &
530 tchild=$!
531 ( let i=0 ; while true; do echo $i > $follow ; sleep 0.1; let i=i+1 ; done ) &
532 child=$!
533 sleep 10
534 kill $tchild
535 kill $child
537 a=`sort $out | uniq -c | sort -n | tail -1 | awk '{ print $1 }'`
540 checktest "$a" "$o" 32
542 # Test different ways of specifying character offsets
543 o=`printf "d\n"`
545 a=`printf "hello\nworld\n" | $PROG -c2`
546 checktest "$a" "$o" 33
548 a=`printf "hello\nworld\n" | $PROG -c-2`
549 checktest "$a" "$o" 34
551 a=`printf "hello\nworld\n" | $PROG -c 2`
552 checktest "$a" "$o" 35
554 a=`printf "hello\nworld\n" | $PROG -c -2`
555 checktest "$a" "$o" 36
557 a=`printf "hello\nworld\n" | $PROG -2c`
558 checktest "$a" "$o" 37
560 o=`printf "llo\nworld\n"`
562 a=`printf "hello\nworld\n" | $PROG -c +3`
563 checktest "$a" "$o" 38
565 a=`printf "hello\nworld\n" | $PROG -c+3`
566 checktest "$a" "$o" 39
568 a=`printf "hello\nworld\n" | $PROG +3c`
569 checktest "$a" "$o" 40
571 # Test various ways of specifying block offsets
572 o=`printf "$BLOCK"`
574 a=`printf "${BLOCK//./x}$BLOCK" | $PROG -b1`
575 checktest "$a" "$o" 41
577 a=`printf "${BLOCK//./x}$BLOCK" | $PROG -b 1`
578 checktest "$a" "$o" 42
580 a=`printf "${BLOCK//./x}$BLOCK" | $PROG -b -1`
581 checktest "$a" "$o" 43
583 a=`printf "${BLOCK//./x}$BLOCK" | $PROG -b +2`
584 checktest "$a" "$o" 44
586 # Test that illegal arguments aren't allowed
588 checkfail +b2
589 checkfail +c3
590 checkfail -l3
591 checkfail -cz
592 checkfail -bz
593 checkfail -nz
594 checkfail -3n
595 checkfail +3n
596 checkfail +n3
597 checkfail -lfoobar
599 echo "$CMD: completed"
601 exit $errs