xref: /illumos-gate/usr/src/cmd/tail/tests/tailtests.sh (revision 3ba944265c4ae1fcf23ef758537c2e4f4feec16e)
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 (c) 2013, Joyent, Inc. All rights reserved.
17#
18
19checktest()
20{
21	local actual=$1
22	local output=$2
23	local test=$3
24
25	if [[ "$actual" != "$output" ]]; then
26		echo "$CMD: test $test: FAIL"
27		echo -e "$CMD: test $test: expected output:\n$output"
28		echo -e "$CMD: test $test: actual output:\n$actual"
29	else
30		echo "$CMD: test $test: pass"
31	fi
32}
33
34#
35# Test cases for 'tail', some based on CoreUtils test cases (validated
36# with legacy Solaris 'tail' and/or xpg4 'tail').  Note that this is designed
37# to be able to run on BSD systems as well to check our behavior against
38# theirs (some behavior that is known to be idiosyncratic to illumos is
39# skipped on non-illumos systems).
40#
41PROG=/usr/bin/tail
42CMD=`basename $0`
43DIR=""
44
45while [[ $# -gt 0 ]]; do
46	case $1 in
47	    -x)
48		PROG=/usr/xpg4/bin/tail
49		shift
50		;;
51	    -o)
52		PROG=$2
53		shift 2
54		;;
55	    -d)
56		DIR=$2
57		shift 2
58		;;
59	    *)
60		echo "Usage: tailtests.sh" \
61		    "[-x][-o <override tail executable>]" \
62		    "[-d <override output directory>]"
63		exit 1
64		;;
65	esac
66done
67
68#
69# Shut bash up upon receiving a term so we can drop it on our children
70# without disrupting the output.
71#
72trap "exit 0" TERM
73
74echo "$CMD: program is $PROG"
75
76if [[ $DIR != "" ]]; then
77	echo "$CMD: directory is $DIR"
78fi
79
80o=`echo -e "bcd"`
81a=`echo -e "abcd" | $PROG +2c`
82checktest "$a" "$o" 1
83
84o=`echo -e ""`
85a=`echo "abcd" | $PROG +8c`
86checktest "$a" "$o" 2
87
88o=`echo -e "abcd"`
89a=`echo "abcd" | $PROG -9c`
90checktest "$a" "$o" 3
91
92o=`echo -e "x"`
93a=`echo -e "x" | $PROG -1l`
94checktest "$a" "$o" 4
95
96o=`echo -e "\n"`
97a=`echo -e "x\ny\n" | $PROG -1l`
98checktest "$a" "$o" 5
99
100o=`echo -e "y\n"`
101a=`echo -e "x\ny\n" | $PROG -2l`
102checktest "$a" "$o" 6
103
104o=`echo -e "y"`
105a=`echo -e "x\ny" | $PROG -1l`
106checktest "$a" "$o" 7
107
108o=`echo -e "x\ny\n"`
109a=`echo -e "x\ny\n" | $PROG +1l`
110checktest "$a" "$o" 8
111
112o=`echo -e "y\n"`
113a=`echo -e "x\ny\n" | $PROG +2l`
114checktest "$a" "$o" 9
115
116o=`echo -e "x"`
117a=`echo -e "x" | $PROG -1`
118checktest "$a" "$o" 10
119
120o=`echo -e "\n"`
121a=`echo -e "x\ny\n" | $PROG -1`
122checktest "$a" "$o" 11
123
124o=`echo -e "y\n"`
125a=`echo -e "x\ny\n" | $PROG -2`
126checktest "$a" "$o" 12
127
128o=`echo -e "y"`
129a=`echo -e "x\ny" | $PROG -1`
130checktest "$a" "$o" 13
131
132o=`echo -e "x\ny\n"`
133a=`echo -e "x\ny\n" | $PROG +1`
134checktest "$a" "$o" 14
135
136o=`echo -e "y\n"`
137a=`echo -e "x\ny\n" | $PROG +2`
138checktest "$a" "$o" 15
139
140o=`echo -e "yyz"`
141a=`echo -e "xyyyyyyyyyyz" | $PROG +10c`
142checktest "$a" "$o" 16
143
144o=`echo -e "y\ny\nz"`
145a=`echo -e "x\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\nz" | $PROG +10l`
146checktest "$a" "$o" 17
147
148o=`echo -e "y\ny\ny\ny\ny\ny\ny\ny\ny\nz"`
149a=`echo -e "x\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\nz" | $PROG -10l`
150checktest "$a" "$o" 18
151
152#
153# For reasons that are presumably as accidental as they are ancient, legacy
154# (and closed) Solaris tail(1) allows +c, +l and -l to be aliases for +10c,
155# +10l and -10l, respectively.  If we are on SunOS, verify that this silly
156# behavior is functional.
157#
158if [[ `uname -s` == "SunOS" ]]; then
159	o=`echo -e "yyz"`
160	a=`echo -e "xyyyyyyyyyyz" | $PROG +c`
161	checktest "$a" "$o" 16a
162
163	o=`echo -e "y\ny\nz"`
164	a=`echo -e "x\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\nz" | $PROG +l`
165	checktest "$a" "$o" 17a
166
167	o=`echo -e "y\ny\ny\ny\ny\ny\ny\ny\ny\nz"`
168	a=`echo -e "x\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\nz" | $PROG -l`
169	checktest "$a" "$o" 18a
170fi
171
172o=`echo -e "c\nb\na"`
173a=`echo -e "a\nb\nc" | $PROG -r`
174checktest "$a" "$o" 19
175
176#
177# Now we want to do a series of follow tests.
178#
179if [[ $DIR == "" ]]; then
180	export TMPDIR=/var/tmp
181	tdir=$(mktemp -d -t tailtest.XXXXXXXX || exit 1)
182else
183	tdir=$(mktemp -d $DIR/tailtest.XXXXXXXX || exit 1)
184fi
185
186follow=$tdir/follow
187moved=$tdir/follow.moved
188out=$tdir/out
189
190#
191# First, verify that following works in its most basic sense.
192#
193echo -e "a\nb\nc" > $follow
194$PROG -f $follow > $out 2> /dev/null &
195child=$!
196sleep 2
197echo -e "d\ne\nf" >> $follow
198sleep 1
199kill $child
200sleep 1
201
202o=`echo -e "a\nb\nc\nd\ne\nf\n"`
203a=`cat $out`
204checktest "$a" "$o" 20
205rm $follow
206
207#
208# Now verify that following correctly follows the file being moved.
209#
210echo -e "a\nb\nc" > $follow
211$PROG -f $follow > $out 2> /dev/null &
212child=$!
213sleep 2
214mv $follow $moved
215
216echo -e "d\ne\nf" >> $moved
217sleep 1
218kill $child
219sleep 1
220
221o=`echo -e "a\nb\nc\nd\ne\nf\n"`
222a=`cat $out`
223checktest "$a" "$o" 21
224rm $moved
225
226#
227# And now truncation with the new offset being less than the old offset.
228#
229echo -e "a\nb\nc" > $follow
230$PROG -f $follow > $out 2> /dev/null &
231child=$!
232sleep 2
233echo -e "d\ne\nf" >> $follow
234sleep 1
235echo -e "g\nh\ni" > $follow
236sleep 1
237kill $child
238sleep 1
239
240o=`echo -e "a\nb\nc\nd\ne\nf\ng\nh\ni\n"`
241a=`cat $out`
242checktest "$a" "$o" 22
243rm $follow
244
245#
246# And truncation with the new offset being greater than the old offset.
247#
248echo -e "a\nb\nc" > $follow
249sleep 1
250$PROG -f $follow > $out 2> /dev/null &
251child=$!
252sleep 2
253echo -e "d\ne\nf" >> $follow
254sleep 1
255echo -e "g\nh\ni\nj\nk\nl\nm\no\np\nq" > $follow
256sleep 1
257kill $child
258sleep 1
259
260o=`echo -e "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\no\np\nq"`
261a=`cat $out`
262checktest "$a" "$o" 23
263rm $follow
264
265#
266# Verify that we can follow the moved file and correctly see a truncation.
267#
268echo -e "a\nb\nc" > $follow
269$PROG -f $follow > $out 2> /dev/null &
270child=$!
271sleep 2
272mv $follow $moved
273
274echo -e "d\ne\nf" >> $moved
275sleep 1
276echo -e "g\nh\ni\nj\nk\nl\nm\no\np\nq" > $moved
277sleep 1
278kill $child
279sleep 1
280
281o=`echo -e "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\no\np\nq"`
282a=`cat $out`
283checktest "$a" "$o" 24
284rm $moved
285
286#
287# Verify that capital-F follow properly deals with truncation
288#
289echo -e "a\nb\nc" > $follow
290$PROG -F $follow > $out 2> /dev/null &
291child=$!
292sleep 2
293echo -e "d\ne\nf" >> $follow
294sleep 1
295echo -e "g\nh\ni" > $follow
296sleep 1
297kill $child
298sleep 1
299
300o=`echo -e "a\nb\nc\nd\ne\nf\ng\nh\ni\n"`
301a=`cat $out`
302checktest "$a" "$o" 25
303rm $follow
304
305#
306# Verify that capital-F follow _won't_ follow the moved file and will
307# correctly deal with recreation of the original file.
308#
309echo -e "a\nb\nc" > $follow
310$PROG -F $follow > $out 2> /dev/null &
311child=$!
312sleep 2
313mv $follow $moved
314
315echo -e "x\ny\nz" >> $moved
316
317#
318# At this point, tail is polling on stat'ing the missing file; we need to
319# be sure to sleep long enough after recreating it to know that it will pick
320# it up.
321#
322echo -e "d\ne\nf" > $follow
323sleep 5
324kill $child
325sleep 1
326
327o=`echo -e "a\nb\nc\nd\ne\nf\n"`
328a=`cat $out`
329checktest "$a" "$o" 26
330rm $moved
331
332#
333# Verify that following two files works.
334#
335echo -e "one" > $follow
336echo -e "two" > $moved
337$PROG -f $follow $moved > $out 2> /dev/null &
338child=$!
339sleep 2
340echo -e "three" >> $follow
341sleep 1
342echo -e "four" >> $moved
343sleep 1
344echo -e "five" >> $follow
345sleep 1
346kill $child
347sleep 1
348
349# There is a bug where the content comes before the header lines,
350# where rlines/mapprint happens before the header.  A pain to fix.
351# In this test, just make sure we see both files change.
352o="one
353
354==> $follow <==
355two
356
357==> $moved <==
358
359==> $follow <==
360three
361
362==> $moved <==
363four
364
365==> $follow <==
366five"
367a=`cat $out`
368checktest "$a" "$o" 27
369rm $follow $moved
370
371if [[ `uname -s` == "SunOS" ]]; then
372	#
373	# Use DTrace to truncate the file between the return from port_get()
374	# and the reassociation of the file object with the port, exposing
375	# any race conditions whereby FILE_TRUNC events are lost.
376	#
377	cat /dev/null > $follow
378	dtrace -c "$PROG -f $follow" -s /dev/stdin > $out <<EOF
379		#pragma D option destructive
380		#pragma D option quiet 
381
382		pid\$target::port_get:return
383		/++i == 5/
384		{
385			stop();
386			system("cat /dev/null > $follow");
387			system("prun %d", pid);
388		}
389
390		tick-1sec
391		{
392			system("echo %d >> $follow", j++);
393		}
394
395		tick-1sec
396		/j == 10/
397		{
398			exit(0);
399		}
400EOF
401
402	o=`echo -e "0\n1\n2\n3\n5\n6\n7\n8\n9\n"`
403	a=`cat $out`
404	checktest "$a" "$o" 27a
405	rm $follow
406
407	cat /dev/null > $follow
408	dtrace -c "$PROG -f $follow" -s /dev/stdin > $out <<EOF
409		#pragma D option destructive
410		#pragma D option quiet 
411
412		pid\$target::port_get:return
413		/++i == 5/
414		{
415			stop();
416			system("mv $follow $moved");
417			system("cat /dev/null > $moved");
418			system("prun %d", pid);
419		}
420
421		tick-1sec
422		{
423			system("echo %d >> %s", j++,
424			    i < 5 ? "$follow" : "$moved");
425		}
426
427		tick-1sec
428		/j == 10/
429		{
430			exit(0);
431		}
432EOF
433
434	o=`echo -e "0\n1\n2\n3\n5\n6\n7\n8\n9\n"`
435	a=`cat $out`
436	checktest "$a" "$o" 27b
437	rm $moved
438
439	#
440	# Verify that -F will deal properly with the file being truncated
441	# not by truncation, but rather via an ftruncate() from logadm.
442	#
443	cat /dev/null > $follow
444	( $PROG -F $follow > $out ) &
445	child=$!
446	echo -e "a\nb\nc\nd\ne\nf" >> $follow
447	logadm -c $follow
448	sleep 2
449	echo -e "g\nh\ni" >> $follow
450	sleep 2
451	kill $child
452	sleep 1
453
454	o=`echo -e "a\nb\nc\nd\ne\nf\ng\nh\ni\n"`
455	a=`cat $out`
456	checktest "$a" "$o" 27c
457fi
458
459#
460# We're now going to test that while we may miss output due to truncations
461# occurring faster than tail can read, we don't ever repeat output.
462#
463cat /dev/null > $follow
464( $PROG -f $follow > $out ) &
465tchild=$!
466( let i=0 ; while true; do echo $i > $follow ; sleep 0.1; let i=i+1 ; done ) &
467child=$!
468sleep 10
469kill $tchild
470kill $child
471
472a=`sort $out | uniq -c | sort -n | tail -1 | awk '{ print $1 }'`
473o=1
474
475checktest "$a" "$o" 28
476
477echo "$CMD: completed"
478
479exit $errs
480
481