1#
2# Copyright 2014, Google Inc. All rights reserved.
3#
4# Redistribution and use in source and binary forms, with or without
5# modification, are permitted provided that the following conditions are
6# met:
7#
8# * Redistributions of source code must retain the above copyright
9#   notice, this list of conditions and the following disclaimer.
10# * Redistributions in binary form must reproduce the above copyright
11#   notice, this list of conditions and the following disclaimer in the
12#   documentation and/or other materials provided with the distribution.
13# * Neither the name of Google Inc. nor the names of its
14#   contributors may be used to endorse or promote products derived from
15#   this software without specific written permission.
16#
17# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28#
29# $FreeBSD$
30#
31
32# Helper function that is always used to create and fill stderr.txt for these
33# tests.
34_custom_create_file()
35{
36	# The first argument is a command.
37	# The second is just a string.
38	case "${1}" in
39		creat) > stderr.txt ;;
40		print) [ "${2}" ] && \
41		    printf "%s\n" "${2}" >> stderr.txt ;;
42	esac
43}
44
45# Helper function that create the file stderr.txt that contains the string
46# passed in as the first argument.
47create_stderr_file()
48{
49	_custom_create_file creat
50	_custom_create_file print "${1}"
51}
52
53# Helper function that create the file stderr.txt that contains the expected
54# truncate utility usage message.
55create_stderr_usage_file()
56{
57	_custom_create_file creat
58	_custom_create_file print "${1}"
59	_custom_create_file print \
60	    "usage: truncate [-c] -s [+|-|%|/]size[K|k|M|m|G|g|T|t] file ..."
61	_custom_create_file print "       truncate [-c] -r rfile file ..."
62	_custom_create_file print "       truncate [-c] -d [-o offset[K|k|M|m|G|g|T|t]] -l length[K|k|M|m|G|g|T|t] file ..."
63}
64
65atf_test_case illegal_option
66illegal_option_head()
67{
68	atf_set "descr" "Verifies that truncate exits >0 when passed an" \
69	    "invalid command line option"
70}
71illegal_option_body()
72{
73	create_stderr_usage_file 'truncate: illegal option -- 7'
74
75	# We expect the error message, with no new files.
76	atf_check -s not-exit:0 -e file:stderr.txt truncate -7 -s0 output.txt
77	[ ! -e output.txt ] || atf_fail "output.txt should not exist"
78}
79
80atf_test_case illegal_size
81illegal_size_head()
82{
83	atf_set "descr" "Verifies that truncate exits >0 when passed an" \
84	    "invalid power of two convention"
85}
86illegal_size_body()
87{
88	create_stderr_file "truncate: invalid size argument \`+1L'"
89
90	# We expect the error message, with no new files.
91	atf_check -s not-exit:0 -e file:stderr.txt truncate -s+1L output.txt
92	[ ! -e output.txt ] || atf_fail "output.txt should not exist"
93}
94
95atf_test_case too_large_size
96too_large_size_head()
97{
98	atf_set "descr" "Verifies that truncate exits >0 when passed an" \
99	    "a size that is INT64_MAX < size <= UINT64_MAX"
100}
101too_large_size_body()
102{
103	create_stderr_file "truncate: invalid size argument \`8388608t'"
104
105	# We expect the error message, with no new files.
106	atf_check -s not-exit:0 -e file:stderr.txt \
107	    truncate -s8388608t output.txt
108	[ ! -e output.txt ] || atf_fail "output.txt should not exist"
109}
110
111atf_test_case opt_c
112opt_c_head()
113{
114	atf_set "descr" "Verifies that -c prevents creation of new files"
115}
116opt_c_body()
117{
118	# No new files and truncate returns 0 as if this is a success.
119	atf_check truncate -c -s 0 doesnotexist.txt
120	[ ! -e output.txt ] || atf_fail "doesnotexist.txt should not exist"
121	> reference
122	atf_check truncate -c -r reference doesnotexist.txt
123	[ ! -e output.txt ] || atf_fail "doesnotexist.txt should not exist"
124
125	create_stderr_file
126
127	# The existing file will be altered by truncate.
128	> exists.txt
129	atf_check -e file:stderr.txt truncate -c -s1 exists.txt
130	[ -s exists.txt ] || atf_fail "exists.txt be larger than zero bytes"
131}
132
133atf_test_case opt_rs
134opt_rs_head()
135{
136	atf_set "descr" "Verifies that truncate command line flags" \
137	    "-s and -r cannot be specifed together"
138}
139opt_rs_body()
140{
141	create_stderr_usage_file
142
143	# Force an error due to the use of both -s and -r.
144	> afile
145	atf_check -s not-exit:0 -e file:stderr.txt truncate -s0 -r afile afile
146}
147
148atf_test_case no_files
149no_files_head()
150{
151	atf_set "descr" "Verifies that truncate needs a list of files on" \
152	    "the command line"
153}
154no_files_body()
155{
156	create_stderr_usage_file
157
158	# A list of files must be present on the command line.
159	atf_check -s not-exit:0 -e file:stderr.txt truncate -s1
160}
161
162atf_test_case bad_refer
163bad_refer_head()
164{
165	atf_set "descr" "Verifies that truncate detects a non-existent" \
166	    "reference file"
167}
168bad_refer_body()
169{
170	create_stderr_file "truncate: afile: No such file or directory"
171
172	# The reference file must exist before you try to use it.
173	atf_check -s not-exit:0 -e file:stderr.txt truncate -r afile afile
174	[ ! -e afile ] || atf_fail "afile should not exist"
175}
176
177atf_test_case bad_truncate
178bad_truncate_head()
179{
180	atf_set "descr" "Verifies that truncate reports an error during" \
181	    "truncation"
182	atf_set "require.user" "unprivileged"
183}
184bad_truncate_body()
185{
186	create_stderr_file "truncate: exists.txt: Permission denied"
187
188	# Trying to get the ftruncate() call to return -1.
189	> exists.txt
190	atf_check chmod 444 exists.txt
191
192	atf_check -s not-exit:0 -e file:stderr.txt truncate -s1 exists.txt
193}
194
195atf_test_case new_absolute_grow
196new_absolute_grow_head()
197{
198	atf_set "descr" "Verifies truncate can make and grow a new 1m file"
199}
200new_absolute_grow_body()
201{
202	create_stderr_file
203
204	# Create a new file and grow it to 1024 bytes.
205	atf_check -s exit:0 -e file:stderr.txt truncate -s1k output.txt
206	atf_check -s exit:1 cmp -s output.txt /dev/zero
207	eval $(stat -s output.txt)
208	[ ${st_size} -eq 1024 ] || atf_fail "expected file size of 1k"
209
210	create_stderr_file
211
212	# Grow the existing file to 1M.  We are using absolute sizes.
213	atf_check -s exit:0 -e file:stderr.txt truncate -c -s1M output.txt
214	atf_check -s exit:1 cmp -s output.txt /dev/zero
215	eval $(stat -s output.txt)
216	[ ${st_size} -eq 1048576 ] || atf_fail "expected file size of 1m"
217}
218
219atf_test_case new_absolute_shrink
220new_absolute_shrink_head()
221{
222	atf_set "descr" "Verifies that truncate can make and" \
223	    "shrink a new 1m file"
224}
225new_absolute_shrink_body()
226{
227	create_stderr_file
228
229	# Create a new file and grow it to 1048576 bytes.
230	atf_check -s exit:0 -e file:stderr.txt truncate -s1M output.txt
231	atf_check -s exit:1 cmp -s output.txt /dev/zero
232	eval $(stat -s output.txt)
233	[ ${st_size} -eq 1048576 ] || atf_fail "expected file size of 1m"
234
235	create_stderr_file
236
237	# Shrink the existing file to 1k.  We are using absolute sizes.
238	atf_check -s exit:0 -e file:stderr.txt truncate -s1k output.txt
239	atf_check -s exit:1 cmp -s output.txt /dev/zero
240	eval $(stat -s output.txt)
241	[ ${st_size} -eq 1024 ] || atf_fail "expected file size of 1k"
242}
243
244atf_test_case new_relative_grow
245new_relative_grow_head()
246{
247	atf_set "descr" "Verifies truncate can make and grow a new 1m file" \
248	    "using relative sizes"
249}
250new_relative_grow_body()
251{
252	create_stderr_file
253
254	# Create a new file and grow it to 1024 bytes.
255	atf_check -s exit:0 -e file:stderr.txt truncate -s+1k output.txt
256	atf_check -s exit:1 cmp -s output.txt /dev/zero
257	eval $(stat -s output.txt)
258	[ ${st_size} -eq 1024 ] || atf_fail "expected file size of 1k"
259
260	create_stderr_file
261
262	# Grow the existing file to 1M.  We are using relative sizes.
263	atf_check -s exit:0 -e file:stderr.txt truncate -s+1047552 output.txt
264	atf_check -s exit:1 cmp -s output.txt /dev/zero
265	eval $(stat -s output.txt)
266	[ ${st_size} -eq 1048576 ] || atf_fail "expected file size of 1m"
267}
268
269atf_test_case new_relative_shrink
270new_relative_shrink_head()
271{
272	atf_set "descr" "Verifies truncate can make and shrink a new 1m file" \
273	    "using relative sizes"
274}
275new_relative_shrink_body()
276{
277	create_stderr_file
278
279	# Create a new file and grow it to 1049600 bytes.
280	atf_check -s exit:0 -e file:stderr.txt truncate -s+1049600 output.txt
281	atf_check -s exit:1 cmp -s output.txt /dev/zero
282	eval $(stat -s output.txt)
283	[ ${st_size} -eq 1049600 ] || atf_fail "expected file size of 1m"
284
285	create_stderr_file
286
287	# Shrink the existing file to 1k.  We are using relative sizes.
288	atf_check -s exit:0 -e file:stderr.txt truncate -s-1M output.txt
289	atf_check -s exit:1 cmp -s output.txt /dev/zero
290	eval $(stat -s output.txt)
291	[ ${st_size} -eq 1024 ] || atf_fail "expected file size of 1k"
292}
293
294atf_test_case cannot_open
295cannot_open_head()
296{
297	atf_set "descr" "Verifies truncate handles open failures correctly" \
298	    "in a list of files"
299	atf_set "require.user" "unprivileged"
300}
301cannot_open_body()
302{
303	# Create three files -- the middle file cannot allow writes.
304	> before
305	> 0000
306	> after
307	atf_check chmod 0000 0000
308
309	create_stderr_file "truncate: 0000: Permission denied"
310
311	# Create a new file and grow it to 1024 bytes.
312	atf_check -s not-exit:0 -e file:stderr.txt \
313	truncate -c -s1k before 0000 after
314	eval $(stat -s before)
315	[ ${st_size} -eq 1024 ] || atf_fail "expected file size of 1k"
316	eval $(stat -s after)
317	[ ${st_size} -eq 1024 ] || atf_fail "expected file size of 1k"
318	eval $(stat -s 0000)
319	[ ${st_size} -eq 0 ] || atf_fail "expected file size of zero"
320}
321
322atf_test_case reference
323reference_head()
324{
325	atf_set "descr" "Verifies that truncate can use a reference file"
326}
327reference_body()
328{
329	# Create a 4 byte reference file.
330	printf "123\n" > reference
331	eval $(stat -s reference)
332	[ ${st_size} -eq 4 ] || atf_fail "reference file should be 4 bytes"
333
334	create_stderr_file
335
336	# Create a new file and grow it to 4 bytes.
337	atf_check -e file:stderr.txt truncate -r reference afile
338	eval $(stat -s afile)
339	[ ${st_size} -eq 4 ] || atf_fail "new file should also be 4 bytes"
340}
341
342atf_test_case new_zero
343new_zero_head()
344{
345	atf_set "descr" "Verifies truncate can make and grow zero byte file"
346}
347new_zero_body()
348{
349	create_stderr_file
350
351	# Create a new file and grow it to zero bytes.
352	atf_check -s exit:0 -e file:stderr.txt truncate -s0 output.txt
353	eval $(stat -s output.txt)
354	[ ${st_size} -eq 0 ] || atf_fail "expected file size of zero"
355
356	# Pretend to grow the file.
357	atf_check -s exit:0 -e file:stderr.txt truncate -s+0 output.txt
358	eval $(stat -s output.txt)
359	[ ${st_size} -eq 0 ] || atf_fail "expected file size of zero"
360}
361
362atf_test_case negative
363negative_head()
364{
365	atf_set "descr" "Verifies truncate treats negative sizes as zero"
366}
367negative_body()
368{
369	# Create a 5 byte file.
370	printf "abcd\n" > afile
371	eval $(stat -s afile)
372	[ ${st_size} -eq 5 ] || atf_fail "afile file should be 5 bytes"
373
374	create_stderr_file
375
376	# Create a new file and do a 100 byte negative relative shrink.
377	atf_check -e file:stderr.txt truncate -s-100 afile
378	eval $(stat -s afile)
379	[ ${st_size} -eq 0 ] || atf_fail "new file should now be zero bytes"
380}
381
382atf_test_case roundup
383roundup_head()
384{
385	atf_set "descr" "Verifies truncate round up"
386}
387roundup_body()
388{
389	# Create a 5 byte file.
390	printf "abcd\n" > afile
391	eval $(stat -s afile)
392	[ ${st_size} -eq 5 ] || atf_fail "afile file should be 5 bytes"
393
394	create_stderr_file
395
396	# Create a new file and do a 100 byte roundup.
397	atf_check -e file:stderr.txt truncate -s%100 afile
398	eval $(stat -s afile)
399	[ ${st_size} -eq 100 ] || atf_fail "new file should now be 100 bytes"
400}
401
402atf_test_case rounddown
403rounddown_head()
404{
405	atf_set "descr" "Verifies truncate round down"
406}
407rounddown_body()
408{
409	# Create a 5 byte file.
410	printf "abcd\n" > afile
411	eval $(stat -s afile)
412	[ ${st_size} -eq 5 ] || atf_fail "afile file should be 5 bytes"
413
414	create_stderr_file
415
416	# Create a new file and do a 2 byte roundup.
417	atf_check -e file:stderr.txt truncate -s/2 afile
418	eval $(stat -s afile)
419	[ ${st_size} -eq 4 ] || atf_fail "new file should now be 4 bytes"
420}
421
422atf_test_case rounddown_zero
423rounddown_zero_head()
424{
425	atf_set "descr" "Verifies truncate round down to zero"
426}
427rounddown_zero_body()
428{
429	# Create a 5 byte file.
430	printf "abcd\n" > afile
431	eval $(stat -s afile)
432	[ ${st_size} -eq 5 ] || atf_fail "afile file should be 5 bytes"
433
434	create_stderr_file
435
436	# Create a new file and do a 10 byte roundup.
437	atf_check -e file:stderr.txt truncate -s/10 afile
438	eval $(stat -s afile)
439	[ ${st_size} -eq 0 ] || atf_fail "new file should now be 0 bytes"
440}
441
442atf_init_test_cases()
443{
444	atf_add_test_case illegal_option
445	atf_add_test_case illegal_size
446	atf_add_test_case too_large_size
447	atf_add_test_case opt_c
448	atf_add_test_case opt_rs
449	atf_add_test_case no_files
450	atf_add_test_case bad_refer
451	atf_add_test_case bad_truncate
452	atf_add_test_case cannot_open
453	atf_add_test_case new_absolute_grow
454	atf_add_test_case new_absolute_shrink
455	atf_add_test_case new_relative_grow
456	atf_add_test_case new_relative_shrink
457	atf_add_test_case reference
458	atf_add_test_case new_zero
459	atf_add_test_case negative
460	atf_add_test_case roundup
461	atf_add_test_case rounddown
462	atf_add_test_case rounddown_zero
463}
464