xref: /illumos-gate/usr/src/cmd/tail/tests/tailtests.sh (revision a02e855f)
1#!/bin/bash
2#
3#
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.
8#
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.
12#
13
14#
15# Copyright 2010 Chris Love.  All rights reserved.
16# Copyright 2017, Joyent, Inc.
17#
18
19BLOCK=""
20for i in {1..512}; do
21	BLOCK+="."
22done
23
24
25checktest()
26{
27	local actual=$1
28	local output=$2
29	local test=$3
30
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"
37	fi
38}
39
40checkfail()
41{
42	printf "foobar" | $PROG $* &> /dev/null
43
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" "$*"
48	fi
49}
50
51#
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).
57#
58PROG=/usr/bin/tail
59CMD=`basename $0`
60DIR=""
61
62while [[ $# -gt 0 ]]; do
63	case $1 in
64	    -x)
65		PROG=/usr/xpg4/bin/tail
66		shift
67		;;
68	    -o)
69		PROG=$2
70		shift 2
71		;;
72	    -d)
73		DIR=$2
74		shift 2
75		;;
76	    *)
77		echo "Usage: tailtests.sh" \
78		    "[-x][-o <override tail executable>]" \
79		    "[-d <override output directory>]"
80		exit 1
81		;;
82	esac
83done
84
85#
86# Shut bash up upon receiving a term so we can drop it on our children
87# without disrupting the output.
88#
89trap "exit 0" TERM
90
91echo "$CMD: program is $PROG"
92
93if [[ $DIR != "" ]]; then
94	echo "$CMD: directory is $DIR"
95fi
96
97o=`echo -e "bcd"`
98a=`echo -e "abcd" | $PROG +2c`
99checktest "$a" "$o" 1
100
101o=`echo -e ""`
102a=`echo "abcd" | $PROG +8c`
103checktest "$a" "$o" 2
104
105o=`echo -e "abcd"`
106a=`echo "abcd" | $PROG -9c`
107checktest "$a" "$o" 3
108
109o=`echo -e "x"`
110a=`echo -e "x" | $PROG -1l`
111checktest "$a" "$o" 4
112
113o=`echo -e "\n"`
114a=`echo -e "x\ny\n" | $PROG -1l`
115checktest "$a" "$o" 5
116
117o=`echo -e "y\n"`
118a=`echo -e "x\ny\n" | $PROG -2l`
119checktest "$a" "$o" 6
120
121o=`echo -e "y"`
122a=`echo -e "x\ny" | $PROG -1l`
123checktest "$a" "$o" 7
124
125o=`echo -e "x\ny\n"`
126a=`echo -e "x\ny\n" | $PROG +1l`
127checktest "$a" "$o" 8
128
129o=`echo -e "y\n"`
130a=`echo -e "x\ny\n" | $PROG +2l`
131checktest "$a" "$o" 9
132
133o=`echo -e "x"`
134a=`echo -e "x" | $PROG -1`
135checktest "$a" "$o" 10
136
137o=`echo -e "\n"`
138a=`echo -e "x\ny\n" | $PROG -1`
139checktest "$a" "$o" 11
140
141o=`echo -e "y\n"`
142a=`echo -e "x\ny\n" | $PROG -2`
143checktest "$a" "$o" 12
144
145o=`echo -e "y"`
146a=`echo -e "x\ny" | $PROG -1`
147checktest "$a" "$o" 13
148
149o=`echo -e "x\ny\n"`
150a=`echo -e "x\ny\n" | $PROG +1`
151checktest "$a" "$o" 14
152
153o=`echo -e "y\n"`
154a=`echo -e "x\ny\n" | $PROG +2`
155checktest "$a" "$o" 15
156
157o=`printf "yyz\n"`
158a=`printf "xyyyyyyyyyyz\n" | $PROG +10c`
159checktest "$a" "$o" 16
160
161o=`printf "y\ny\nz\n"`
162a=`printf "x\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\nz\n" | $PROG +10l`
163checktest "$a" "$o" 17
164
165o=`printf "y\ny\ny\ny\ny\ny\ny\ny\ny\nz\n"`
166a=`printf "x\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\nz\n" | $PROG -10l`
167checktest "$a" "$o" 18
168
169a=`printf "o\nn\nm\nl\nk\nj\ni\nh\ng\n"`
170o=`printf "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\nn\no\n" | $PROG +10lr`
171checktest "$a" "$o" 19
172
173a=`printf "o\nn\nm\nl\nk\nj\ni\nh\ng\nf\n"`
174o=`printf "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\nn\no\n" | $PROG -10lr`
175checktest "$a" "$o" 20
176
177a=`printf "o\nn\nm\nl\n"`
178o=`printf "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\nn\no\n" | $PROG +10cr`
179checktest "$a" "$o" 21
180
181a=`printf "o\nn\nm\nl\nk\n"`
182o=`printf "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\nn\no\n" | $PROG -10cr`
183checktest "$a" "$o" 22
184
185#
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.
190#
191if [[ `uname -s` == "SunOS" ]]; then
192	o=`printf "yyz\n"`
193	a=`printf "xyyyyyyyyyyz\n" | $PROG +c`
194	checktest "$a" "$o" 16a
195
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
199
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
203
204	a=`printf "o\nn\nm\nl\nk\nj\ni\nh\ng\n"`
205
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
208
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
211
212	a=`printf "o\nn\nm\nl\nk\nj\ni\nh\ng\nf\n"`
213
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
216
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
219
220	a=`printf "o\nn\nm\nl\n"`
221
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
224
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
227
228	a=`printf "o\nn\nm\nl\nk\n"`
229
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
232
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
235fi
236
237o=`echo -e "c\nb\na"`
238a=`echo -e "a\nb\nc" | $PROG -r`
239checktest "$a" "$o" 23
240
241#
242# Now we want to do a series of follow tests.
243#
244if [[ $DIR == "" ]]; then
245	export TMPDIR=/var/tmp
246	tdir=$(mktemp -d -t tailtest.XXXXXXXX || exit 1)
247else
248	tdir=$(mktemp -d $DIR/tailtest.XXXXXXXX || exit 1)
249fi
250
251follow=$tdir/follow
252moved=$tdir/follow.moved
253out=$tdir/out
254
255#
256# First, verify that following works in its most basic sense.
257#
258echo -e "a\nb\nc" > $follow
259$PROG -f $follow > $out 2> /dev/null &
260child=$!
261sleep 2
262echo -e "d\ne\nf" >> $follow
263sleep 1
264kill $child
265sleep 1
266
267o=`echo -e "a\nb\nc\nd\ne\nf\n"`
268a=`cat $out`
269checktest "$a" "$o" 24
270rm $follow
271
272#
273# Now verify that following correctly follows the file being moved.
274#
275echo -e "a\nb\nc" > $follow
276$PROG -f $follow > $out 2> /dev/null &
277child=$!
278sleep 2
279mv $follow $moved
280
281echo -e "d\ne\nf" >> $moved
282sleep 1
283kill $child
284sleep 1
285
286o=`echo -e "a\nb\nc\nd\ne\nf\n"`
287a=`cat $out`
288checktest "$a" "$o" 25
289rm $moved
290
291#
292# And now truncation with the new offset being less than the old offset.
293#
294echo -e "a\nb\nc" > $follow
295$PROG -f $follow > $out 2> /dev/null &
296child=$!
297sleep 2
298echo -e "d\ne\nf" >> $follow
299sleep 1
300echo -e "g\nh\ni" > $follow
301sleep 1
302kill $child
303sleep 1
304
305o=`echo -e "a\nb\nc\nd\ne\nf\ng\nh\ni\n"`
306a=`cat $out`
307checktest "$a" "$o" 26
308rm $follow
309
310#
311# And truncation with the new offset being greater than the old offset.
312#
313echo -e "a\nb\nc" > $follow
314sleep 1
315$PROG -f $follow > $out 2> /dev/null &
316child=$!
317sleep 2
318echo -e "d\ne\nf" >> $follow
319sleep 1
320echo -e "g\nh\ni\nj\nk\nl\nm\no\np\nq" > $follow
321sleep 1
322kill $child
323sleep 1
324
325o=`echo -e "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\no\np\nq"`
326a=`cat $out`
327checktest "$a" "$o" 27
328rm $follow
329
330#
331# Verify that we can follow the moved file and correctly see a truncation.
332#
333echo -e "a\nb\nc" > $follow
334$PROG -f $follow > $out 2> /dev/null &
335child=$!
336sleep 2
337mv $follow $moved
338
339echo -e "d\ne\nf" >> $moved
340sleep 1
341echo -e "g\nh\ni\nj\nk\nl\nm\no\np\nq" > $moved
342sleep 1
343kill $child
344sleep 1
345
346o=`echo -e "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\no\np\nq"`
347a=`cat $out`
348checktest "$a" "$o" 28
349rm $moved
350
351#
352# Verify that capital-F follow properly deals with truncation
353#
354echo -e "a\nb\nc" > $follow
355$PROG -F $follow > $out 2> /dev/null &
356child=$!
357sleep 2
358echo -e "d\ne\nf" >> $follow
359sleep 1
360echo -e "g\nh\ni" > $follow
361sleep 1
362kill $child
363sleep 1
364
365o=`echo -e "a\nb\nc\nd\ne\nf\ng\nh\ni\n"`
366a=`cat $out`
367checktest "$a" "$o" 29
368rm $follow
369
370#
371# Verify that capital-F follow _won't_ follow the moved file and will
372# correctly deal with recreation of the original file.
373#
374echo -e "a\nb\nc" > $follow
375$PROG -F $follow > $out 2> /dev/null &
376child=$!
377sleep 2
378mv $follow $moved
379
380echo -e "x\ny\nz" >> $moved
381
382#
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.
386#
387echo -e "d\ne\nf" > $follow
388sleep 5
389kill $child
390sleep 1
391
392o=`echo -e "a\nb\nc\nd\ne\nf\n"`
393a=`cat $out`
394checktest "$a" "$o" 30
395rm $moved
396
397#
398# Verify that following two files works.
399#
400echo -e "one" > $follow
401echo -e "two" > $moved
402$PROG -f $follow $moved > $out 2> /dev/null &
403child=$!
404sleep 2
405echo -e "three" >> $follow
406sleep 1
407echo -e "four" >> $moved
408sleep 1
409echo -e "five" >> $follow
410sleep 1
411kill $child
412sleep 1
413
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.
417o="one
418
419==> $follow <==
420two
421
422==> $moved <==
423
424==> $follow <==
425three
426
427==> $moved <==
428four
429
430==> $follow <==
431five"
432a=`cat $out`
433checktest "$a" "$o" 31
434rm $follow $moved
435
436if [[ `uname -s` == "SunOS" ]]; then
437	#
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.
441	#
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 
446
447		pid\$target::port_get:return
448		/++i == 5/
449		{
450			stop();
451			system("cat /dev/null > $follow");
452			system("prun %d", pid);
453		}
454
455		tick-1sec
456		{
457			system("echo %d >> $follow", j++);
458		}
459
460		tick-1sec
461		/j == 10/
462		{
463			exit(0);
464		}
465EOF
466
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
471
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 
476
477		pid\$target::port_get:return
478		/++i == 5/
479		{
480			stop();
481			system("mv $follow $moved");
482			system("cat /dev/null > $moved");
483			system("prun %d", pid);
484		}
485
486		tick-1sec
487		{
488			system("echo %d >> %s", j++,
489			    i < 5 ? "$follow" : "$moved");
490		}
491
492		tick-1sec
493		/j == 10/
494		{
495			exit(0);
496		}
497EOF
498
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
503
504	#
505	# Verify that -F will deal properly with the file being truncated
506	# not by truncation, but rather via an ftruncate() from logadm.
507	#
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
518
519	o=`echo -e "a\nb\nc\nd\ne\nf\ng\nh\ni\n"`
520	a=`cat $out`
521	checktest "$a" "$o" 31c
522fi
523
524#
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.
527#
528cat /dev/null > $follow
529( $PROG -f $follow > $out ) &
530tchild=$!
531( let i=0 ; while true; do echo $i > $follow ; sleep 0.1; let i=i+1 ; done ) &
532child=$!
533sleep 10
534kill $tchild
535kill $child
536
537a=`sort $out | uniq -c | sort -n | tail -1 | awk '{ print $1 }'`
538o=1
539
540checktest "$a" "$o" 32
541
542# Test different ways of specifying character offsets
543o=`printf "d\n"`
544
545a=`printf "hello\nworld\n" | $PROG -c2`
546checktest "$a" "$o" 33
547
548a=`printf "hello\nworld\n" | $PROG -c-2`
549checktest "$a" "$o" 34
550
551a=`printf "hello\nworld\n" | $PROG -c 2`
552checktest "$a" "$o" 35
553
554a=`printf "hello\nworld\n" | $PROG -c -2`
555checktest "$a" "$o" 36
556
557a=`printf "hello\nworld\n" | $PROG -2c`
558checktest "$a" "$o" 37
559
560o=`printf "llo\nworld\n"`
561
562a=`printf "hello\nworld\n" | $PROG -c +3`
563checktest "$a" "$o" 38
564
565a=`printf "hello\nworld\n" | $PROG -c+3`
566checktest "$a" "$o" 39
567
568a=`printf "hello\nworld\n" | $PROG +3c`
569checktest "$a" "$o" 40
570
571# Test various ways of specifying block offsets
572o=`printf "$BLOCK"`
573
574a=`printf "${BLOCK//./x}$BLOCK" | $PROG -b1`
575checktest "$a" "$o" 41
576
577a=`printf "${BLOCK//./x}$BLOCK" | $PROG -b 1`
578checktest "$a" "$o" 42
579
580a=`printf "${BLOCK//./x}$BLOCK" | $PROG -b -1`
581checktest "$a" "$o" 43
582
583a=`printf "${BLOCK//./x}$BLOCK" | $PROG -b +2`
584checktest "$a" "$o" 44
585
586# Test that illegal arguments aren't allowed
587
588checkfail +b2
589checkfail +c3
590checkfail -l3
591checkfail -cz
592checkfail -bz
593checkfail -nz
594checkfail -3n
595checkfail +3n
596checkfail +n3
597checkfail -lfoobar
598
599echo "$CMD: completed"
600
601exit $errs
602
603