summaryrefslogtreecommitdiff
path: root/tests/misc/tee.sh
blob: 444cb688a1305831c1fecf7ef434bc9ccc4a51c2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#!/bin/sh
# test for basic tee functionality.

# Copyright (C) 2005-2023 Free Software Foundation, Inc.

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.

. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ tee

echo line >sample || framework_failure_

# POSIX says: "Processing of at least 13 file operands shall be supported."
for n in 0 1 2 12 13; do
  files=$(seq $n)
  rm -f $files
  tee $files <sample >out || fail=1
  for f in out $files; do
    compare sample $f || fail=1
  done
done

# Ensure tee treats '-' as the name of a file, as mandated by POSIX.
# Between v5.3.0 and v8.23, a '-' argument caused tee to send another
# copy of input to standard output.
tee - <sample >out 2>err || fail=1
compare sample ./- || fail=1
compare sample out || fail=1
compare /dev/null err || fail=1

# Ensure tee exits early if no more writable outputs
if test -w /dev/full && test -c /dev/full; then
  yes | returns_ 1 timeout 10 tee /dev/full 2>err >/dev/full || fail=1
  # Ensure an error for each of the 2 outputs
  # (and no redundant errors for stdout).
  test $(wc -l < err) = 2 || { cat err; fail=1; }


  # Ensure we continue with outputs that are OK
  seq 10000 > multi_read || framework_failure_

  returns_ 1 tee /dev/full out2 2>err >out1 <multi_read || fail=1
  cmp multi_read out1 || fail=1
  cmp multi_read out2 || fail=1
  # Ensure an error for failing output
  test $(wc -l < err) = 1 || { cat err; fail=1; }

  returns_ 1 tee out1 out2 2>err >/dev/full <multi_read || fail=1
  cmp multi_read out1 || fail=1
  cmp multi_read out2 || fail=1
  # Ensure an error for failing output
  test $(wc -l < err) = 1 || { cat err; fail=1; }
fi

case $host_triplet in
  *aix*) echo  'avoiding due to no way to detect closed outputs on AIX' ;;
  *)
# Test iopoll-powered early exit for closed pipes
tee_exited() { sleep $1; test -f tee.exited; }
# Currently this functionality is most useful with
# intermittent input from a terminal, but here we
# use an input pipe that doesn't write anything
# but will exit as soon as tee does, or it times out
retry_delay_ tee_exited .1 7 | # 12.7s (Must be > following timeout)
{ timeout 10 tee -p 2>err && touch tee.exited; } | :
test $(wc -l < err) = 0 || { cat err; fail=1; }
test -f tee.exited || fail=1 ;;
esac

# Test with unwriteable files
if ! uid_is_privileged_; then  # root does not get EPERM.
  touch file.ro || framework_failure_
  chmod a-w file.ro || framework_failure_
  returns_ 1 tee -p </dev/null file.ro || fail=1
fi

mkfifo_or_skip_ fifo

# Ensure tee handles nonblocking output correctly
# Terminate any background processes
cleanup_() { kill $pid 2>/dev/null && wait $pid; }
read_fifo_delayed() {
  { sleep .1; timeout 10 dd of=/dev/null status=none; } <fifo
}
read_fifo_delayed & pid=$!
dd count=20 bs=100K if=/dev/zero status=none |
{
  dd count=0 oflag=nonblock status=none
  tee || { cleanup_; touch tee.fail; }
} >fifo
test -f tee.fail && fail=1 || cleanup_

# Ensure tee honors --output-error modes
read_fifo() { timeout 10 dd count=1 if=fifo of=/dev/null status=none & }

# Determine platform sigpipe exit status
read_fifo
yes >fifo
pipe_status=$?

# Default operation is to continue on output errors but exit silently on SIGPIPE
read_fifo
yes | returns_ $pipe_status timeout 10 tee ./e/noent 2>err >fifo || fail=1
test $(wc -l < err) = 1 || { cat err; fail=1; }

# With -p, SIGPIPE is suppressed, exit 0 for EPIPE when all outputs finished
read_fifo
yes | timeout 10 tee -p 2>err >fifo || fail=1
test $(wc -l < err) = 0 || { cat err; fail=1; }

# With --output-error=warn, exit 1 for EPIPE when all outputs finished
read_fifo
yes | returns_ 1 timeout 10 tee --output-error=warn 2>err >fifo || fail=1
test $(wc -l < err) = 1 || { cat err; fail=1; }

# With --output-error=exit, exit 1 immediately for EPIPE
read_fifo
yes | returns_ 1 timeout 10 tee --output-error=exit /dev/null 2>err >fifo \
  || fail=1
test $(wc -l < err) = 1 || { cat err; fail=1; }

# With --output-error=exit, exit 1 immediately on output error
read_fifo
yes | returns_ 1 timeout 10 tee --output-error=exit ./e/noent 2>err >fifo \
  || fail=1
test $(wc -l < err) = 1 || { cat err; fail=1; }

# With --output-error=exit-nopipe, exit 0 for EPIPE
read_fifo
yes | timeout 10 tee --output-error=exit-nopipe 2>err >fifo || fail=1
test $(wc -l < err) = 0 || { cat err; fail=1; }

wait
Exit $fail