xref: /freebsd/bin/ls/tests/ls_tests.sh (revision 4436b51dff5736e74da464946049ea6899a88938)
1#
2# Copyright 2015 EMC Corp.
3# All rights reserved.
4#
5# Redistribution and use in source and binary forms, with or without
6# modification, are permitted provided that the following conditions are
7# met:
8#
9# * Redistributions of source code must retain the above copyright
10#   notice, this list of conditions and the following disclaimer.
11# * Redistributions in binary form must reproduce the above copyright
12#   notice, this list of conditions and the following disclaimer in the
13#   documentation and/or other materials provided with the distribution.
14#
15# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26#
27# $FreeBSD$
28#
29
30create_test_dir()
31{
32	[ -z "$ATF_TMPDIR" ] || return 0
33
34	export ATF_TMPDIR=$(pwd)
35
36	# XXX: need to nest this because of how kyua creates $TMPDIR; otherwise
37	# it will run into EPERM issues later
38	TEST_INPUTS_DIR="${ATF_TMPDIR}/test/inputs"
39
40	atf_check -e empty -s exit:0 mkdir -m 0777 -p $TEST_INPUTS_DIR
41	cd $TEST_INPUTS_DIR
42}
43
44create_test_inputs()
45{
46	create_test_dir
47
48	atf_check -e empty -s exit:0 mkdir -m 0755 -p a/b/1
49	atf_check -e empty -s exit:0 ln -s a/b c
50	atf_check -e empty -s exit:0 touch d
51	atf_check -e empty -s exit:0 ln d e
52	atf_check -e empty -s exit:0 touch .f
53	atf_check -e empty -s exit:0 mkdir .g
54	atf_check -e empty -s exit:0 mkfifo h
55	atf_check -e ignore -s exit:0 dd if=/dev/zero of=i count=1000 bs=1
56	atf_check -e empty -s exit:0 touch klmn
57	atf_check -e empty -s exit:0 touch opqr
58	atf_check -e empty -s exit:0 touch stuv
59	atf_check -e empty -s exit:0 install -m 0755 /dev/null wxyz
60	atf_check -e empty -s exit:0 touch 0b00000001
61	atf_check -e empty -s exit:0 touch 0b00000010
62	atf_check -e empty -s exit:0 touch 0b00000011
63	atf_check -e empty -s exit:0 touch 0b00000100
64	atf_check -e empty -s exit:0 touch 0b00000101
65	atf_check -e empty -s exit:0 touch 0b00000110
66	atf_check -e empty -s exit:0 touch 0b00000111
67	atf_check -e empty -s exit:0 touch 0b00001000
68	atf_check -e empty -s exit:0 touch 0b00001001
69	atf_check -e empty -s exit:0 touch 0b00001010
70	atf_check -e empty -s exit:0 touch 0b00001011
71	atf_check -e empty -s exit:0 touch 0b00001100
72	atf_check -e empty -s exit:0 touch 0b00001101
73	atf_check -e empty -s exit:0 touch 0b00001110
74	atf_check -e empty -s exit:0 touch 0b00001111
75
76	atf_check -e empty -s exit:0 sync
77}
78
79KB=1024
80MB=$(( 1024 * $KB ))
81GB=$(( 1024 * $MB ))
82TB=$(( 1024 * $GB ))
83PB=$(( 1024 * $TB ))
84
85create_test_inputs2()
86{
87	create_test_dir
88
89	for filesize in 1 512 $(( 2 * $KB )) $(( 10 * $KB )) $(( 512 * $KB )); \
90	do
91		atf_check -e ignore -o empty -s exit:0 \
92		    dd if=/dev/zero of=${filesize}.file bs=1 \
93		    count=1 oseek=${filesize} conv=sparse
94		files="${files} ${filesize}.file"
95	done
96
97	for filesize in $MB $GB $TB; do
98		atf_check -e ignore -o empty -s exit:0 \
99		    dd if=/dev/zero of=${filesize}.file bs=$MB \
100		    count=1 oseek=$(( $filesize / $MB )) conv=sparse
101		files="${files} ${filesize}.file"
102	done
103
104	atf_check -e empty -s exit:0 sync
105}
106
107atf_test_case A_flag
108A_flag_head()
109{
110	atf_set "descr" "Verify -A support with unprivileged users"
111}
112
113A_flag_body()
114{
115	create_test_dir
116
117	atf_check -e empty -o empty -s exit:0 ls -A
118
119	create_test_inputs
120
121	WITH_A=$PWD/../with_A.out
122	WITHOUT_A=$PWD/../without_A.out
123
124	atf_check -e empty -o save:$WITH_A -s exit:0 ls -A
125	atf_check -e empty -o save:$WITHOUT_A -s exit:0 ls
126
127	echo "-A usage"
128	cat $WITH_A
129	echo "No -A usage"
130	cat $WITHOUT_A
131
132	for dot_path in '\.f' '\.g'; do
133		atf_check -e empty -o not-empty -s exit:0 grep "${dot_path}" \
134		    $WITH_A
135		atf_check -e empty -o empty -s not-exit:0 grep "${dot_path}" \
136		    $WITHOUT_A
137	done
138}
139
140atf_test_case A_flag_implied_when_root
141A_flag_implied_when_root_head()
142{
143	atf_set "descr" "Verify that -A is implied for root"
144	atf_set "require.user" "root"
145}
146
147A_flag_implied_when_root_body()
148{
149	create_test_dir
150
151	atf_check -e empty -o empty -s exit:0 ls -A
152
153	create_test_inputs
154
155	WITH_EXPLICIT=$PWD/../with_explicit_A.out
156	WITH_IMPLIED=$PWD/../with_implied_A.out
157
158	atf_check -e empty -o save:$WITH_EXPLICIT -s exit:0 ls -A
159	atf_check -e empty -o save:$WITH_IMPLIED -s exit:0 ls
160
161	echo "Explicit -A usage"
162	cat $WITH_EXPLICIT
163	echo "Implicit -A usage"
164	cat $WITH_IMPLIED
165
166	atf_check_equal "$(cat $WITH_EXPLICIT)" "$(cat $WITH_IMPLIED)"
167}
168
169atf_test_case B_flag
170B_flag_head()
171{
172	atf_set "descr" "Verify that the output from ls -B prints out non-printable characters"
173}
174
175B_flag_body()
176{
177	atf_skip "kyua report-jenkins doesn't properly escape non-printable chars: https://github.com/jmmv/kyua/issues/136"
178
179	atf_check -e empty -o empty -s exit:0 touch "$(printf "y\013z")"
180	atf_check -e empty -o match:'y\\013z' -s exit:0 ls -B
181}
182
183atf_test_case C_flag
184C_flag_head()
185{
186	atf_set "descr" "Verify that the output from ls -C is multi-column, sorted down"
187}
188
189print_index()
190{
191	local i=1
192	local wanted_index=$1; shift
193
194	while [ $i -le $wanted_index ]; do
195		if [ $i -eq $wanted_index ]; then
196			echo $1
197			return
198		fi
199		shift
200		: $(( i += 1 ))
201	done
202}
203
204C_flag_body()
205{
206	create_test_inputs
207
208	WITH_C=$PWD/../with_C.out
209
210	export COLUMNS=40
211	atf_check -e empty -o save:$WITH_C -s exit:0 ls -C
212
213	echo "With -C usage"
214	cat $WITH_C
215
216	paths=$(find -s . -mindepth 1 -maxdepth 1 \! -name '.*' -exec basename {} \; )
217	set -- $paths
218	num_paths=$#
219	num_columns=2
220
221	max_num_paths_per_column=$(( $(( $num_paths + 1 )) / $num_columns ))
222
223	local i=1
224	while [ $i -le $max_num_paths_per_column ]; do
225		column_1=$(print_index $i $paths)
226		column_2=$(print_index $(( $i + $max_num_paths_per_column )) $paths)
227		#echo "paths[$(( $i + $max_num_paths_per_column ))] = $column_2"
228		expected_expr="$column_1"
229		if [ -n "$column_2" ]; then
230			expected_expr="$expected_expr[[:space:]]+$column_2"
231		fi
232		atf_check -e ignore -o not-empty -s exit:0 \
233		    egrep "$expected_expr" $WITH_C
234		: $(( i += 1 ))
235	done
236}
237
238atf_test_case D_flag
239D_flag_head()
240{
241	atf_set "descr" "Verify that the output from ls -D modifies the time format used with ls -l"
242}
243
244D_flag_body()
245{
246	atf_check -e empty -o empty -s exit:0 touch a.file
247	atf_check -e empty -o match:"$(stat -f '%c[[:space:]]+%N' a.file)" \
248	    -s exit:0 ls -lD '%s'
249}
250
251atf_test_case F_flag
252F_flag_head()
253{
254	atf_set "descr" "Verify that the output from ls -F prints out appropriate symbols after files"
255}
256
257F_flag_body()
258{
259	create_test_inputs
260
261	atf_check -e empty -s exit:0 \
262	    sh -c "pid=${ATF_TMPDIR}/nc.pid; daemon -p \$pid nc -lU j; sleep 2; pkill -F \$pid"
263
264	atf_check -e empty -o match:'a/' -s exit:0 ls -F
265	atf_check -e empty -o match:'c@' -s exit:0 ls -F
266	atf_check -e empty -o match:'h\|' -s exit:0 ls -F
267	atf_check -e empty -o match:'j=' -s exit:0 ls -F
268	#atf_check -e empty -o match:'<whiteout-file>%' -s exit:0 ls -F
269	atf_check -e empty -o match:'stuv' -s exit:0 ls -F
270	atf_check -e empty -o match:'wxyz\*' -s exit:0 ls -F
271}
272
273atf_test_case H_flag
274H_flag_head()
275{
276	atf_set "descr" "Verify that ls -H follows symlinks"
277}
278
279H_flag_body()
280{
281	create_test_inputs
282
283	atf_check -e empty -o match:'1' -s exit:0 ls -H c
284}
285
286atf_test_case I_flag
287I_flag_head()
288{
289	atf_set "descr" "Verify that the output from ls -I is the same as ls for an unprivileged user"
290}
291
292I_flag_body()
293{
294	create_test_inputs
295
296	WITH_I=$PWD/../with_I.out
297	WITHOUT_I=$PWD/../without_I.out
298
299	atf_check -e empty -o save:$WITH_I -s exit:0 ls -I
300	atf_check -e empty -o save:$WITHOUT_I -s exit:0 ls
301
302	echo "Explicit -I usage"
303	cat $WITH_I
304	echo "No -I usage"
305	cat $WITHOUT_I
306
307	atf_check_equal "$(cat $WITH_I)" "$(cat $WITHOUT_I)"
308}
309
310atf_test_case I_flag_voids_implied_A_flag_when_root
311I_flag_voids_implied_A_flag_when_root_head()
312{
313	atf_set "descr" "Verify that -I voids out implied -A for root"
314	atf_set "require.user" "root"
315}
316
317I_flag_voids_implied_A_flag_when_root_body()
318{
319	create_test_inputs
320
321	atf_check -o not-match:'\.f' -s exit:0 ls -I
322	atf_check -o not-match:'\.g' -s exit:0 ls -I
323
324	atf_check -o match:'\.f' -s exit:0 ls -A -I
325	atf_check -o match:'\.g' -s exit:0 ls -A -I
326}
327
328atf_test_case L_flag
329L_flag_head()
330{
331	atf_set "descr" "Verify that -L prints out the symbolic link and conversely -P prints out the target for the symbolic link"
332}
333
334L_flag_body()
335{
336	atf_check -e empty -o empty -s exit:0 ln -s target1/target2 link1
337	atf_check -e empty -o match:link1 -s exit:0 ls -L
338	atf_check -e empty -o not-match:target1/target2 -s exit:0 ls -L
339}
340
341atf_test_case R_flag
342R_flag_head()
343{
344	atf_set "descr" "Verify that the output from ls -R prints out the directory contents recursively"
345}
346
347R_flag_body()
348{
349	create_test_inputs
350
351	WITH_R=$PWD/../with_R.out
352	WITH_R_expected_output=$PWD/../with_R_expected.out
353
354	atf_check -e empty -o save:$WITH_R -s exit:0 ls -R
355
356	set -- . $(find -s . \! -name '.*' -type d)
357	while [ $# -gt 0 ]; do
358		dir=$1; shift
359		[ "$dir" != "." ] && echo "$dir:"
360		(cd $dir && ls -1A | sed -e '/^\./d')
361		[ $# -ne 0 ] && echo
362	done > $WITH_R_expected_output
363
364	echo "-R usage"
365	cat $WITH_R
366	echo "-R expected output"
367	cat $WITH_R_expected_output
368
369	atf_check_equal "$(cat $WITH_R)" "$(cat $WITH_R_expected_output)"
370}
371
372atf_test_case S_flag
373S_flag_head()
374{
375	atf_set "descr" "Verify that -S sorts by file size, then by filename lexicographically"
376}
377
378S_flag_body()
379{
380	create_test_dir
381
382	file_list_dir=$PWD/../files
383
384	atf_check -e empty -o empty -s exit:0 mkdir -p $file_list_dir
385
386	create_test_inputs
387	create_test_inputs2
388
389	WITH_S=$PWD/../with_S.out
390	WITHOUT_S=$PWD/../without_S.out
391
392	atf_check -e empty -o save:$WITH_S ls -D '%s' -lS
393	atf_check -e empty -o save:$WITHOUT_S ls -D '%s' -l
394
395	WITH_S_parsed=$(awk '! /^total/ { print $7 }' $WITH_S)
396	set -- $(awk '! /^total/ { print $5, $7 }' $WITHOUT_S)
397	while [ $# -gt 0 ]; do
398		size=$1; shift
399		filename=$1; shift
400		echo $filename >> $file_list_dir/${size}
401	done
402	file_lists=$(find $file_list_dir -type f -exec basename {} \; | sort -nr)
403	WITHOUT_S_parsed=$(for file_list in $file_lists; do sort < $file_list_dir/$file_list; done)
404
405	echo "-lS usage (parsed)"
406	echo "$WITH_S_parsed"
407	echo "-l usage (parsed)"
408	echo "$WITHOUT_S_parsed"
409
410	atf_check_equal "$WITHOUT_S_parsed" "$WITH_S_parsed"
411}
412
413atf_test_case T_flag
414T_flag_head()
415{
416	atf_set "descr" "Verify -T support"
417}
418
419T_flag_body()
420{
421	create_test_dir
422
423	atf_check -e empty -o empty -s exit:0 touch a.file
424
425	birthtime_in_secs=$(stat -f %B -t %s a.file)
426	birthtime=$(date -j -f %s $birthtime_in_secs +"[[:space:]]+%b[[:space:]]+%e[[:space:]]+%H:%M:%S[[:space:]]+%Y")
427
428	atf_check -e empty -o match:"$birthtime"'[[:space:]]+a\.file' \
429	    -s exit:0 ls -lT a.file
430}
431
432atf_test_case a_flag
433a_flag_head()
434{
435	atf_set "descr" "Verify -a support"
436}
437
438a_flag_body()
439{
440	create_test_dir
441
442	# Make sure "." and ".." show up with -a
443	atf_check -e empty -o match:'\.[[:space:]]+\.\.'  -s exit:0 ls -ax
444
445	create_test_inputs
446
447	WITH_a=$PWD/../with_a.out
448	WITHOUT_a=$PWD/../without_a.out
449
450	atf_check -e empty -o save:$WITH_a -s exit:0 ls -a
451	atf_check -e empty -o save:$WITHOUT_a -s exit:0 ls
452
453	echo "-a usage"
454	cat $WITH_a
455	echo "No -a usage"
456	cat $WITHOUT_a
457
458	for dot_path in '\.f' '\.g'; do
459		atf_check -e empty -o not-empty -s exit:0 grep "${dot_path}" \
460		    $WITH_a
461		atf_check -e empty -o empty -s not-exit:0 grep "${dot_path}" \
462		    $WITHOUT_a
463	done
464}
465
466atf_test_case b_flag
467b_flag_head()
468{
469	atf_set "descr" "Verify that the output from ls -b prints out non-printable characters"
470}
471
472b_flag_body()
473{
474	atf_skip "kyua report-jenkins doesn't properly escape non-printable chars: https://github.com/jmmv/kyua/issues/136"
475
476	atf_check -e empty -o empty -s exit:0 touch "$(printf "y\013z")"
477	atf_check -e empty -o match:'y\\vz' -s exit:0 ls -b
478}
479
480atf_test_case d_flag
481d_flag_head()
482{
483	atf_set "descr" "Verify that -d doesn't descend down directories"
484}
485
486d_flag_body()
487{
488	create_test_dir
489
490	output=$PWD/../output
491
492	atf_check -e empty -o empty -s exit:0 mkdir -p a/b
493
494	for path in . $PWD a; do
495		atf_check -e empty -o save:$output -s exit:0 ls -d $path
496		atf_check_equal "$(cat $output)" "$path"
497	done
498}
499
500atf_test_case f_flag
501f_flag_head()
502{
503	atf_set "descr" "Verify that -f prints out the contents of a directory unsorted"
504}
505
506f_flag_body()
507{
508	create_test_inputs
509
510	output=$PWD/../output
511
512	# XXX: I don't have enough understanding of how the algorithm works yet
513	# to determine more than the fact that all the entries printed out
514	# exist
515	paths=$(find -s . -mindepth 1 -maxdepth 1 \! -name '.*' -exec basename {} \; )
516
517	atf_check -e empty -o save:$output -s exit:0 ls -f
518
519	for path in $paths; do
520		atf_check -e ignore -o not-empty -s exit:0 \
521		    egrep "^$path$" $output
522	done
523}
524
525atf_test_case g_flag
526g_flag_head()
527{
528	atf_set "descr" "Verify that -g does nothing (compatibility flag)"
529}
530
531g_flag_body()
532{
533	create_test_inputs2
534	for file in $files; do
535		atf_check -e empty -o match:"$(ls -a $file)" -s exit:0 \
536		    ls -ag $file
537		atf_check -e empty -o match:"$(ls -la $file)" -s exit:0 \
538		    ls -alg $file
539	done
540}
541
542atf_test_case h_flag
543h_flag_head()
544{
545	atf_set "descr" "Verify that -h prints out the humanized units for file sizes with ls -l"
546	atf_set "require.files" "/usr/bin/bc"
547}
548
549h_flag_body()
550{
551	# XXX: this test doesn't currently show how 999 bytes will be 999B,
552	# but 1000 bytes will be 1.0K, due to how humanize_number(3) works.
553	create_test_inputs2
554	for file in $files; do
555		file_size=$(stat -f '%z' "$file") || \
556		    atf_fail "stat'ing $file failed"
557		scale=2
558		if [ $file_size -lt $KB ]; then
559			divisor=1
560			scale=0
561			suffix=B
562		elif [ $file_size -lt $MB ]; then
563			divisor=$KB
564			suffix=K
565		elif [ $file_size -lt $GB ]; then
566			divisor=$MB
567			suffix=M
568		elif [ $file_size -lt $TB ]; then
569			divisor=$GB
570			suffix=G
571		elif [ $file_size -lt $PB ]; then
572			divisor=$TB
573			suffix=T
574		else
575			divisor=$PB
576			suffix=P
577		fi
578
579		bc_expr="$(printf "scale=%s\n%s/%s\nquit" $scale $file_size $divisor)"
580		size_humanized=$(bc -e "$bc_expr" | tr '.' '\.' | sed -e 's,\.00,,')
581
582		atf_check -e empty -o match:"$size_humanized.+$file" \
583		    -s exit:0 ls -hl $file
584	done
585}
586
587atf_test_case i_flag
588i_flag_head()
589{
590	atf_set "descr" "Verify that -i prints out the inode for files"
591}
592
593i_flag_body()
594{
595	create_test_inputs
596
597	paths=$(find -L . -mindepth 1)
598	[ -n "$paths" ] || atf_skip 'Could not find any paths to iterate over (!)'
599
600	for path in $paths; do
601		atf_check -e empty \
602		    -o match:"$(stat -f '[[:space:]]*%i[[:space:]]+%N' $path)" \
603		    -s exit:0 ls -d1i $path
604	done
605}
606
607atf_test_case k_flag
608k_flag_head()
609{
610	atf_set "descr" "Verify that -k prints out the size with a block size of 1kB"
611}
612
613k_flag_body()
614{
615	create_test_inputs2
616	for file in $files; do
617		atf_check -e empty \
618		    -o match:"[[:space:]]+$(stat -f "%z" $file)[[:space:]]+.+[[:space:]]+$file" ls -lk $file
619	done
620}
621
622atf_test_case l_flag
623l_flag_head()
624{
625	atf_set "descr" "Verify that -l prints out the output in long format"
626}
627
628l_flag_body()
629{
630
631	atf_check -e empty -o empty -s exit:0 touch a.file
632
633	birthtime_in_secs=$(stat -f "%B" -t "%s" a.file)
634	birthtime=$(date -j -f "%s" $birthtime_in_secs +"%b[[:space:]]+%e[[:space:]]+%H:%M")
635
636	expected_output=$(stat -f "%Sp[[:space:]]+%l[[:space:]]+%Su[[:space:]]+%Sg[[:space:]]+%z[[:space:]]+$birthtime[[:space:]]+a\\.file" a.file)
637
638	atf_check -e empty -o match:"$expected_output" -s exit:0 ls -l a.file
639}
640
641atf_test_case lcomma_flag
642lcomma_flag_head()
643{
644	atf_set "descr" "Verify that -l, prints out the size with ',' delimiters"
645}
646
647lcomma_flag_body()
648{
649	create_test_inputs
650
651	atf_check \
652	    -o match:'\-rw\-r\-\-r\-\-[[:space:]]+.+[[:space:]]+1,000[[:space:]]+.+i' \
653	    env LC_ALL=en_US.ISO8859-1 ls -l, i
654}
655
656atf_test_case m_flag
657m_flag_head()
658{
659	atf_set "descr" "Verify that the output from ls -m is comma-separated"
660}
661
662m_flag_body()
663{
664	create_test_dir
665
666	output=$PWD/../output
667
668	atf_check -e empty -o empty -s exit:0 touch ,, "a,b " c d e
669
670	atf_check -e empty -o save:$output -s exit:0 ls -m
671
672	atf_check_equal "$(cat $output)" ",,, a,b , c, d, e"
673}
674
675atf_test_case n_flag
676n_flag_head()
677{
678	atf_set "descr" "Verify that the output from ls -n prints out numeric GIDs/UIDs instead of symbolic GIDs/UIDs"
679	atf_set "require.user" "root"
680}
681
682n_flag_body()
683{
684	daemon_gid=$(id -g daemon) || atf_skip "could not resolve gid for daemon (!)"
685	nobody_uid=$(id -u nobody) || atf_skip "could not resolve uid for nobody (!)"
686
687	atf_check -e empty -o empty -s exit:0 touch a.file
688	atf_check -e empty -o empty -s exit:0 chown $nobody_uid:$daemon_gid a.file
689
690	atf_check -e empty \
691	    -o match:'\-rw\-r\-\-r\-\-[[:space:]]+1[[:space:]]+'"$nobody_uid[[:space:]]+$daemon_gid"'[[:space:]]+.+a\.file' \
692	    ls -ln a.file
693
694}
695
696atf_test_case o_flag
697o_flag_head()
698{
699	atf_set "descr" "Verify that the output from ls -o prints out the chflag values or '-' if none are set"
700	atf_set "require.user" "root"
701}
702
703o_flag_body()
704{
705	local size=12345
706
707	create_test_dir
708
709	atf_check -e ignore -o empty -s exit:0 dd if=/dev/zero of=a.file \
710	    bs=$size count=1
711	atf_check -e ignore -o empty -s exit:0 dd if=/dev/zero of=b.file \
712	    bs=$size count=1
713	atf_check -e empty -o empty -s exit:0 chflags uarch a.file
714
715	atf_check -e empty -o match:"[[:space:]]+uarch[[:space:]]$size+.+a\\.file" \
716	    -s exit:0 ls -lo a.file
717	atf_check -e empty -o match:"[[:space:]]+\\-[[:space:]]$size+.+b\\.file" \
718	    -s exit:0 ls -lo b.file
719}
720
721atf_test_case p_flag
722p_flag_head()
723{
724	atf_set "descr" "Verify that the output from ls -p prints out '/' after directories"
725}
726
727p_flag_body()
728{
729	create_test_inputs
730
731	paths=$(find -L .)
732	[ -n "$paths" ] || atf_skip 'Could not find any paths to iterate over (!)'
733
734	for path in $paths; do
735		suffix=
736		# If path is not a symlink and is a directory, then the suffix
737		# must be "/".
738		if [ ! -L "${path}" -a -d "$path" ]; then
739			suffix=/
740		fi
741		atf_check -e empty -o match:"$path${suffix}" -s exit:0 \
742		    ls -dp $path
743	done
744}
745
746atf_test_case q_flag_and_w_flag
747q_flag_and_w_flag_head()
748{
749	atf_set "descr" "Verify that the output from ls -q prints out '?' for ESC and ls -w prints out the escape character"
750}
751
752q_flag_and_w_flag_body()
753{
754	atf_skip "kyua report-jenkins doesn't properly escape non-printable chars: https://github.com/jmmv/kyua/issues/136"
755
756	create_test_dir
757
758	test_file="$(printf "y\01z")"
759
760	atf_check -e empty -o empty -s exit:0 touch "$test_file"
761
762	atf_check -e empty -o match:'y\?z' -s exit:0 ls -q "$test_file"
763	atf_check -e empty -o match:"$test_file" -s exit:0 ls -w "$test_file"
764}
765
766atf_test_case r_flag
767r_flag_head()
768{
769	atf_set "descr" "Verify that the output from ls -r sorts the same way as reverse sorting with sort(1)"
770}
771
772r_flag_body()
773{
774	create_test_inputs
775
776	WITH_r=$PWD/../with_r.out
777	WITH_sort=$PWD/../with_sort.out
778
779	atf_check -e empty -o save:$WITH_r -s exit:0 ls -1r
780	atf_check -e empty -o save:$WITH_sort -s exit:0 sh -c 'ls -1 | sort -r'
781
782	echo "Sorted with -r"
783	cat $WITH_r
784	echo "Reverse sorted with sort(1)"
785	cat $WITH_sort
786
787	atf_check_equal "$(cat $WITH_r)" "$(cat $WITH_sort)"
788}
789
790atf_test_case s_flag
791s_flag_head()
792{
793	atf_set "descr" "Verify that the output from ls -s matches the output from stat(1)"
794}
795
796s_flag_body()
797{
798	create_test_inputs2
799	for file in $files; do
800		atf_check -e empty \
801		    -o match:"$(stat -f "%b" $file)[[:space:]]+$file" ls -s $file
802	done
803}
804
805atf_test_case t_flag
806t_flag_head()
807{
808	atf_set "descr" "Verify that the output from ls -t sorts by modification time"
809}
810
811t_flag_body()
812{
813	create_test_dir
814
815	atf_check -e empty -o empty -s exit:0 touch a.file
816	atf_check -e empty -o empty -s exit:0 touch b.file
817
818	atf_check -e empty -s exit:0 sync
819
820	atf_check -e empty -o match:'a\.file' -s exit:0 sh -c 'ls -lt | tail -n 1'
821	atf_check -e empty -o match:'b\.file.*a\.file' -s exit:0 ls -Ct
822
823	atf_check -e empty -o empty -s exit:0 rm a.file
824	atf_check -e empty -o empty -s exit:0 sh -c 'echo "i am a" > a.file'
825
826	atf_check -e empty -s exit:0 sync
827
828	atf_check -e empty -o match:'b\.file' -s exit:0 sh -c 'ls -lt | tail -n 1'
829	atf_check -e empty -o match:'a\.file.*b\.file' -s exit:0 ls -Ct
830}
831
832atf_test_case u_flag
833u_flag_head()
834{
835	atf_set "descr" "Verify that the output from ls -u sorts by last access"
836}
837
838u_flag_body()
839{
840	create_test_dir
841
842	atf_check -e empty -o empty -s exit:0 touch a.file
843	atf_check -e empty -o empty -s exit:0 touch b.file
844	atf_check -e empty -s exit:0 sync
845
846	atf_check -e empty -o match:'b\.file' -s exit:0 sh -c 'ls -lu | tail -n 1'
847	atf_check -e empty -o match:'a\.file.*b\.file' -s exit:0 ls -Cu
848
849	atf_check -e empty -o empty -s exit:0 sh -c 'echo "i am a" > a.file'
850	atf_check -e empty -o match:'i am a' -s exit:0 cat a.file
851	atf_check -e empty -s exit:0 sync
852
853	atf_check -e empty -o match:'b\.file' -s exit:0 sh -c 'ls -lu | tail -n 1'
854	atf_check -e empty -o match:'a\.file.*b\.file' -s exit:0 ls -Cu
855}
856
857atf_test_case x_flag
858x_flag_head()
859{
860	atf_set "descr" "Verify that the output from ls -x is multi-column, sorted across"
861}
862
863x_flag_body()
864{
865	create_test_inputs
866
867	WITH_x=$PWD/../with_x.out
868
869	atf_check -e empty -o save:$WITH_x -s exit:0 ls -x
870
871	echo "With -x usage"
872	cat $WITH_x
873
874	atf_check -e ignore -o not-empty -s exit:0 \
875	    egrep "a[[:space:]]+c[[:space:]]+d[[:space:]]+e[[:space:]]+h" $WITH_x
876	atf_check -e ignore -o not-empty -s exit:0 \
877	    egrep "i[[:space:]]+klmn[[:space:]]+opqr[[:space:]]+stuv[[:space:]]+wxyz" $WITH_x
878}
879
880atf_test_case y_flag
881y_flag_head()
882{
883	atf_set "descr" "Verify that the output from ls -y sorts the same way as sort(1)"
884}
885
886y_flag_body()
887{
888	create_test_inputs
889
890	WITH_sort=$PWD/../with_sort.out
891	WITH_y=$PWD/../with_y.out
892
893	atf_check -e empty -o save:$WITH_sort -s exit:0 sh -c 'ls -1 | sort'
894	atf_check -e empty -o save:$WITH_y -s exit:0 ls -1y
895
896	echo "Sorted with sort(1)"
897	cat $WITH_sort
898	echo "Sorted with -y"
899	cat $WITH_y
900
901	atf_check_equal "$(cat $WITH_sort)" "$(cat $WITH_y)"
902}
903
904atf_test_case 1_flag
9051_flag_head()
906{
907	atf_set "descr" "Verify that -1 prints out one item per line"
908}
909
9101_flag_body()
911{
912	create_test_inputs
913
914	WITH_1=$PWD/../with_1.out
915	WITHOUT_1=$PWD/../without_1.out
916
917	atf_check -e empty -o save:$WITH_1 -s exit:0 ls -1
918	atf_check -e empty -o save:$WITHOUT_1 -s exit:0 \
919		sh -c 'for i in $(ls); do echo $i; done'
920
921	echo "Explicit -1 usage"
922	cat $WITH_1
923	echo "No -1 usage"
924	cat $WITHOUT_1
925
926	atf_check_equal "$(cat $WITH_1)" "$(cat $WITHOUT_1)"
927}
928
929atf_init_test_cases()
930{
931	export BLOCKSIZE=512
932
933	atf_add_test_case A_flag
934	atf_add_test_case A_flag_implied_when_root
935	atf_add_test_case B_flag
936	atf_add_test_case C_flag
937	atf_add_test_case D_flag
938	atf_add_test_case F_flag
939	#atf_add_test_case G_flag
940	atf_add_test_case H_flag
941	atf_add_test_case I_flag
942	atf_add_test_case I_flag_voids_implied_A_flag_when_root
943	atf_add_test_case L_flag
944	#atf_add_test_case P_flag
945	atf_add_test_case R_flag
946	atf_add_test_case S_flag
947	atf_add_test_case T_flag
948	#atf_add_test_case U_flag
949	#atf_add_test_case W_flag
950	#atf_add_test_case Z_flag
951	atf_add_test_case a_flag
952	atf_add_test_case b_flag
953	#atf_add_test_case c_flag
954	atf_add_test_case d_flag
955	atf_add_test_case f_flag
956	atf_add_test_case g_flag
957	atf_add_test_case h_flag
958	atf_add_test_case i_flag
959	atf_add_test_case k_flag
960	atf_add_test_case l_flag
961	atf_add_test_case lcomma_flag
962	atf_add_test_case m_flag
963	atf_add_test_case n_flag
964	atf_add_test_case o_flag
965	atf_add_test_case p_flag
966	atf_add_test_case q_flag_and_w_flag
967	atf_add_test_case r_flag
968	atf_add_test_case s_flag
969	atf_add_test_case t_flag
970	atf_add_test_case u_flag
971	atf_add_test_case x_flag
972	atf_add_test_case y_flag
973	atf_add_test_case 1_flag
974}
975