xref: /freebsd/bin/ls/tests/ls_tests.sh (revision 96190b4fef3b4a0cc3ca0606b0c4e3e69a5e6717)
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#
28
29create_test_dir()
30{
31	[ -z "$ATF_TMPDIR" ] || return 0
32
33	export ATF_TMPDIR=$(pwd)
34
35	# XXX: need to nest this because of how kyua creates $TMPDIR; otherwise
36	# it will run into EPERM issues later
37	TEST_INPUTS_DIR="${ATF_TMPDIR}/test/inputs"
38
39	atf_check -e empty -s exit:0 mkdir -m 0777 -p $TEST_INPUTS_DIR
40	cd $TEST_INPUTS_DIR
41}
42
43create_test_inputs()
44{
45	create_test_dir
46
47	atf_check -e empty -s exit:0 mkdir -m 0755 -p a/b/1
48	atf_check -e empty -s exit:0 ln -s a/b c
49	atf_check -e empty -s exit:0 touch d
50	atf_check -e empty -s exit:0 ln d e
51	atf_check -e empty -s exit:0 touch .f
52	atf_check -e empty -s exit:0 mkdir .g
53	atf_check -e empty -s exit:0 mkfifo h
54	atf_check -e ignore -s exit:0 dd if=/dev/zero of=i count=1000 bs=1
55	atf_check -e empty -s exit:0 touch klmn
56	atf_check -e empty -s exit:0 touch opqr
57	atf_check -e empty -s exit:0 touch stuv
58	atf_check -e empty -s exit:0 install -m 0755 /dev/null wxyz
59	atf_check -e empty -s exit:0 touch 0b00000001
60	atf_check -e empty -s exit:0 touch 0b00000010
61	atf_check -e empty -s exit:0 touch 0b00000011
62	atf_check -e empty -s exit:0 touch 0b00000100
63	atf_check -e empty -s exit:0 touch 0b00000101
64	atf_check -e empty -s exit:0 touch 0b00000110
65	atf_check -e empty -s exit:0 touch 0b00000111
66	atf_check -e empty -s exit:0 touch 0b00001000
67	atf_check -e empty -s exit:0 touch 0b00001001
68	atf_check -e empty -s exit:0 touch 0b00001010
69	atf_check -e empty -s exit:0 touch 0b00001011
70	atf_check -e empty -s exit:0 touch 0b00001100
71	atf_check -e empty -s exit:0 touch 0b00001101
72	atf_check -e empty -s exit:0 touch 0b00001110
73	atf_check -e empty -s exit:0 touch 0b00001111
74}
75
76KB=1024
77MB=$(( 1024 * $KB ))
78GB=$(( 1024 * $MB ))
79TB=$(( 1024 * $GB ))
80PB=$(( 1024 * $TB ))
81
82create_test_inputs2()
83{
84	create_test_dir
85
86	if ! getconf MIN_HOLE_SIZE "$(pwd)"; then
87	    echo "getconf MIN_HOLE_SIZE $(pwd) failed; sparse files probably" \
88	         "not supported by file system"
89	    mount
90	    atf_skip "Test's work directory does not support sparse files;" \
91	             "try with a different TMPDIR?"
92	fi
93
94	for filesize in 1 512 $(( 2 * $KB )) $(( 10 * $KB )) $(( 512 * $KB )); \
95	do
96		atf_check -e ignore -o empty -s exit:0 \
97		    dd if=/dev/zero of=${filesize}.file bs=1 \
98		    count=1 oseek=${filesize} conv=sparse
99		files="${files} ${filesize}.file"
100	done
101
102	for filesize in $MB $GB $TB; do
103		atf_check -e ignore -o empty -s exit:0 \
104		    dd if=/dev/zero of=${filesize}.file bs=$MB \
105		    count=1 oseek=$(( $filesize / $MB )) conv=sparse
106		files="${files} ${filesize}.file"
107	done
108}
109
110atf_test_case A_flag
111A_flag_head()
112{
113	atf_set "descr" "Verify -A support with unprivileged users"
114}
115
116A_flag_body()
117{
118	create_test_dir
119
120	atf_check -e empty -o empty -s exit:0 ls -A
121
122	create_test_inputs
123
124	WITH_A=$PWD/../with_A.out
125	WITHOUT_A=$PWD/../without_A.out
126
127	atf_check -e empty -o save:$WITH_A -s exit:0 ls -A
128	atf_check -e empty -o save:$WITHOUT_A -s exit:0 ls
129
130	echo "-A usage"
131	cat $WITH_A
132	echo "No -A usage"
133	cat $WITHOUT_A
134
135	for dot_path in '\.f' '\.g'; do
136		atf_check -e empty -o not-empty -s exit:0 grep "${dot_path}" \
137		    $WITH_A
138		atf_check -e empty -o empty -s not-exit:0 grep "${dot_path}" \
139		    $WITHOUT_A
140	done
141}
142
143atf_test_case A_flag_implied_when_root
144A_flag_implied_when_root_head()
145{
146	atf_set "descr" "Verify that -A is implied for root"
147	atf_set "require.user" "root"
148}
149
150A_flag_implied_when_root_body()
151{
152	create_test_dir
153
154	atf_check -e empty -o empty -s exit:0 ls -A
155
156	create_test_inputs
157
158	WITH_EXPLICIT=$PWD/../with_explicit_A.out
159	WITH_IMPLIED=$PWD/../with_implied_A.out
160
161	atf_check -e empty -o save:$WITH_EXPLICIT -s exit:0 ls -A
162	atf_check -e empty -o save:$WITH_IMPLIED -s exit:0 ls
163
164	echo "Explicit -A usage"
165	cat $WITH_EXPLICIT
166	echo "Implicit -A usage"
167	cat $WITH_IMPLIED
168
169	atf_check_equal "$(cat $WITH_EXPLICIT)" "$(cat $WITH_IMPLIED)"
170}
171
172atf_test_case B_flag
173B_flag_head()
174{
175	atf_set "descr" "Verify that the output from ls -B prints out non-printable characters"
176}
177
178B_flag_body()
179{
180	atf_check -e empty -o empty -s exit:0 touch "$(printf "y\013z")"
181	atf_check -e empty -o match:'y\\013z' -s exit:0 ls -B
182}
183
184atf_test_case C_flag
185C_flag_head()
186{
187	atf_set "descr" "Verify that the output from ls -C is multi-column, sorted down"
188}
189
190print_index()
191{
192	local i=1
193	local wanted_index=$1; shift
194
195	while [ $i -le $wanted_index ]; do
196		if [ $i -eq $wanted_index ]; then
197			echo $1
198			return
199		fi
200		shift
201		: $(( i += 1 ))
202	done
203}
204
205C_flag_body()
206{
207	create_test_inputs
208
209	WITH_C=$PWD/../with_C.out
210
211	export COLUMNS=40
212	atf_check -e empty -o save:$WITH_C -s exit:0 ls -C
213
214	echo "With -C usage"
215	cat $WITH_C
216
217	paths=$(find -s . -mindepth 1 -maxdepth 1 \! -name '.*' -exec basename {} \; )
218	set -- $paths
219	num_paths=$#
220	num_columns=2
221
222	max_num_paths_per_column=$(( $(( $num_paths + 1 )) / $num_columns ))
223
224	local i=1
225	while [ $i -le $max_num_paths_per_column ]; do
226		column_1=$(print_index $i $paths)
227		column_2=$(print_index $(( $i + $max_num_paths_per_column )) $paths)
228		#echo "paths[$(( $i + $max_num_paths_per_column ))] = $column_2"
229		expected_expr="$column_1"
230		if [ -n "$column_2" ]; then
231			expected_expr="$expected_expr[[:space:]]+$column_2"
232		fi
233		atf_check -e ignore -o not-empty -s exit:0 \
234		    egrep "$expected_expr" $WITH_C
235		: $(( i += 1 ))
236	done
237}
238
239atf_test_case D_flag
240D_flag_head()
241{
242	atf_set "descr" "Verify that the output from ls -D modifies the time format used with ls -l"
243}
244
245D_flag_body()
246{
247	atf_check -e empty -o empty -s exit:0 touch a.file
248	atf_check -e empty -o match:"$(stat -f '%c[[:space:]]+%N' a.file)" \
249	    -s exit:0 ls -lD '%s'
250}
251
252atf_test_case F_flag
253F_flag_head()
254{
255	atf_set "descr" "Verify that the output from ls -F prints out appropriate symbols after files"
256}
257
258F_flag_body()
259{
260	create_test_inputs
261
262	atf_check -e empty -s exit:0 \
263	    sh -c "pid=${ATF_TMPDIR}/nc.pid; daemon -p \$pid nc -lU j; sleep 2; pkill -F \$pid"
264
265	atf_check -e empty -o match:'a/' -s exit:0 ls -F
266	atf_check -e empty -o match:'c@' -s exit:0 ls -F
267	atf_check -e empty -o match:'h\|' -s exit:0 ls -F
268	atf_check -e empty -o match:'j=' -s exit:0 ls -F
269	#atf_check -e empty -o match:'<whiteout-file>%' -s exit:0 ls -F
270	atf_check -e empty -o match:'stuv' -s exit:0 ls -F
271	atf_check -e empty -o match:'wxyz\*' -s exit:0 ls -F
272}
273
274atf_test_case H_flag
275H_flag_head()
276{
277	atf_set "descr" "Verify that ls -H follows symlinks"
278}
279
280H_flag_body()
281{
282	create_test_inputs
283
284	atf_check -e empty -o match:'1' -s exit:0 ls -H c
285}
286
287atf_test_case I_flag
288I_flag_head()
289{
290	atf_set "descr" "Verify that the output from ls -I is the same as ls for an unprivileged user"
291}
292
293I_flag_body()
294{
295	create_test_inputs
296
297	WITH_I=$PWD/../with_I.out
298	WITHOUT_I=$PWD/../without_I.out
299
300	atf_check -e empty -o save:$WITH_I -s exit:0 ls -I
301	atf_check -e empty -o save:$WITHOUT_I -s exit:0 ls
302
303	echo "Explicit -I usage"
304	cat $WITH_I
305	echo "No -I usage"
306	cat $WITHOUT_I
307
308	atf_check_equal "$(cat $WITH_I)" "$(cat $WITHOUT_I)"
309}
310
311atf_test_case I_flag_voids_implied_A_flag_when_root
312I_flag_voids_implied_A_flag_when_root_head()
313{
314	atf_set "descr" "Verify that -I voids out implied -A for root"
315	atf_set "require.user" "root"
316}
317
318I_flag_voids_implied_A_flag_when_root_body()
319{
320	create_test_inputs
321
322	atf_check -o not-match:'\.f' -s exit:0 ls -I
323	atf_check -o not-match:'\.g' -s exit:0 ls -I
324
325	atf_check -o match:'\.f' -s exit:0 ls -A -I
326	atf_check -o match:'\.g' -s exit:0 ls -A -I
327}
328
329atf_test_case L_flag
330L_flag_head()
331{
332	atf_set "descr" "Verify that -L prints out the symbolic link and conversely -P prints out the target for the symbolic link"
333}
334
335L_flag_body()
336{
337	atf_check -e empty -o empty -s exit:0 ln -s target1/target2 link1
338	atf_check -e empty -o match:link1 -s exit:0 ls -L
339	atf_check -e empty -o not-match:target1/target2 -s exit:0 ls -L
340}
341
342atf_test_case R_flag
343R_flag_head()
344{
345	atf_set "descr" "Verify that the output from ls -R prints out the directory contents recursively"
346}
347
348R_flag_body()
349{
350	create_test_inputs
351
352	WITH_R=$PWD/../with_R.out
353	WITH_R_expected_output=$PWD/../with_R_expected.out
354
355	atf_check -e empty -o save:$WITH_R -s exit:0 ls -R
356
357	set -- . $(find -s . \! -name '.*' -type d)
358	while [ $# -gt 0 ]; do
359		dir=$1; shift
360		[ "$dir" != "." ] && echo "$dir:"
361		(cd $dir && ls -1A | sed -e '/^\./d')
362		[ $# -ne 0 ] && echo
363	done > $WITH_R_expected_output
364
365	echo "-R usage"
366	cat $WITH_R
367	echo "-R expected output"
368	cat $WITH_R_expected_output
369
370	atf_check_equal "$(cat $WITH_R)" "$(cat $WITH_R_expected_output)"
371}
372
373atf_test_case S_flag
374S_flag_head()
375{
376	atf_set "descr" "Verify that -S sorts by file size, then by filename lexicographically"
377}
378
379S_flag_body()
380{
381	create_test_dir
382
383	file_list_dir=$PWD/../files
384
385	atf_check -e empty -o empty -s exit:0 mkdir -p $file_list_dir
386
387	create_test_inputs
388	create_test_inputs2
389
390	WITH_S=$PWD/../with_S.out
391	WITHOUT_S=$PWD/../without_S.out
392
393	atf_check -e empty -o save:$WITH_S ls -D '%s' -lS
394	atf_check -e empty -o save:$WITHOUT_S ls -D '%s' -l
395
396	WITH_S_parsed=$(awk '! /^total/ { print $7 }' $WITH_S)
397	set -- $(awk '! /^total/ { print $5, $7 }' $WITHOUT_S)
398	while [ $# -gt 0 ]; do
399		size=$1; shift
400		filename=$1; shift
401		echo $filename >> $file_list_dir/${size}
402	done
403	file_lists=$(find $file_list_dir -type f -exec basename {} \; | sort -nr)
404	WITHOUT_S_parsed=$(for file_list in $file_lists; do sort < $file_list_dir/$file_list; done)
405
406	echo "-lS usage (parsed)"
407	echo "$WITH_S_parsed"
408	echo "-l usage (parsed)"
409	echo "$WITHOUT_S_parsed"
410
411	atf_check_equal "$WITHOUT_S_parsed" "$WITH_S_parsed"
412}
413
414atf_test_case T_flag
415T_flag_head()
416{
417	atf_set "descr" "Verify -T support"
418}
419
420T_flag_body()
421{
422	create_test_dir
423
424	atf_check -e empty -o empty -s exit:0 touch a.file
425
426	mtime_in_secs=$(stat -f %m -t %s a.file)
427	mtime=$(date -j -f %s $mtime_in_secs +"[[:space:]]+%b[[:space:]]+%e[[:space:]]+%H:%M:%S[[:space:]]+%Y")
428
429	atf_check -e empty -o match:"$mtime"'[[:space:]]+a\.file' \
430	    -s exit:0 ls -lT a.file
431}
432
433atf_test_case a_flag
434a_flag_head()
435{
436	atf_set "descr" "Verify -a support"
437}
438
439a_flag_body()
440{
441	create_test_dir
442
443	# Make sure "." and ".." show up with -a
444	atf_check -e empty -o match:'\.[[:space:]]+\.\.'  -s exit:0 ls -ax
445
446	create_test_inputs
447
448	WITH_a=$PWD/../with_a.out
449	WITHOUT_a=$PWD/../without_a.out
450
451	atf_check -e empty -o save:$WITH_a -s exit:0 ls -a
452	atf_check -e empty -o save:$WITHOUT_a -s exit:0 ls
453
454	echo "-a usage"
455	cat $WITH_a
456	echo "No -a usage"
457	cat $WITHOUT_a
458
459	for dot_path in '\.f' '\.g'; do
460		atf_check -e empty -o not-empty -s exit:0 grep "${dot_path}" \
461		    $WITH_a
462		atf_check -e empty -o empty -s not-exit:0 grep "${dot_path}" \
463		    $WITHOUT_a
464	done
465}
466
467atf_test_case b_flag
468b_flag_head()
469{
470	atf_set "descr" "Verify that the output from ls -b prints out non-printable characters"
471}
472
473b_flag_body()
474{
475	atf_check -e empty -o empty -s exit:0 touch "$(printf "y\013z")"
476	atf_check -e empty -o match:'y\\vz' -s exit:0 ls -b
477}
478
479atf_test_case d_flag
480d_flag_head()
481{
482	atf_set "descr" "Verify that -d doesn't descend down directories"
483}
484
485d_flag_body()
486{
487	create_test_dir
488
489	output=$PWD/../output
490
491	atf_check -e empty -o empty -s exit:0 mkdir -p a/b
492
493	for path in . $PWD a; do
494		atf_check -e empty -o save:$output -s exit:0 ls -d $path
495		atf_check_equal "$(cat $output)" "$path"
496	done
497}
498
499atf_test_case f_flag
500f_flag_head()
501{
502	atf_set "descr" "Verify that -f prints out the contents of a directory unsorted"
503}
504
505f_flag_body()
506{
507	create_test_inputs
508
509	output=$PWD/../output
510
511	# XXX: I don't have enough understanding of how the algorithm works yet
512	# to determine more than the fact that all the entries printed out
513	# exist
514	paths=$(find -s . -mindepth 1 -maxdepth 1 \! -name '.*' -exec basename {} \; )
515
516	atf_check -e empty -o save:$output -s exit:0 ls -f
517
518	for path in $paths; do
519		atf_check -e ignore -o not-empty -s exit:0 \
520		    egrep "^$path$" $output
521	done
522}
523
524atf_test_case g_flag
525g_flag_head()
526{
527	atf_set "descr" "Verify that -g implies -l but omits the owner name field"
528}
529
530g_flag_body()
531{
532	atf_check -e empty -o empty -s exit:0 touch a.file
533
534	mtime_in_secs=$(stat -f "%m" -t "%s" a.file)
535	mtime=$(date -j -f "%s" $mtime_in_secs +"%b[[:space:]]+%e[[:space:]]+%H:%M")
536
537	expected_output=$(stat -f "%Sp[[:space:]]+%l[[:space:]]+%Sg[[:space:]]+%z[[:space:]]+$mtime[[:space:]]+a\\.file" a.file)
538
539	atf_check -e empty -o match:"$expected_output" -s exit:0 ls -g a.file
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.progs" "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	mtime_in_secs=$(stat -f "%m" -t "%s" a.file)
634	mtime=$(date -j -f "%s" $mtime_in_secs +"%b[[:space:]]+%e[[:space:]]+%H:%M")
635
636	expected_output=$(stat -f "%Sp[[:space:]]+%l[[:space:]]+%Su[[:space:]]+%Sg[[:space:]]+%z[[:space:]]+$mtime[[: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 -n 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}
701
702o_flag_body()
703{
704	local size=12345
705
706	create_test_dir
707
708	atf_check -e ignore -o empty -s exit:0 dd if=/dev/zero of=a.file \
709	    bs=$size count=1
710	atf_check -e ignore -o empty -s exit:0 dd if=/dev/zero of=b.file \
711	    bs=$size count=1
712	atf_check -e empty -o empty -s exit:0 chflags uarch a.file
713	atf_check -e empty -o empty -s exit:0 chflags 0 b.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	create_test_dir
755
756	test_file="$(printf "y\01z")"
757
758	atf_check -e empty -o empty -s exit:0 touch "$test_file"
759
760	atf_check -e empty -o match:'y\?z' -s exit:0 ls -q "$test_file"
761	atf_check -e empty -o match:"$test_file" -s exit:0 ls -w "$test_file"
762}
763
764atf_test_case r_flag
765r_flag_head()
766{
767	atf_set "descr" "Verify that the output from ls -r sorts the same way as reverse sorting with sort(1)"
768}
769
770r_flag_body()
771{
772	create_test_inputs
773
774	WITH_r=$PWD/../with_r.out
775	WITH_sort=$PWD/../with_sort.out
776
777	atf_check -e empty -o save:$WITH_r -s exit:0 ls -1r
778	atf_check -e empty -o save:$WITH_sort -s exit:0 sh -c 'ls -1 | sort -r'
779
780	echo "Sorted with -r"
781	cat $WITH_r
782	echo "Reverse sorted with sort(1)"
783	cat $WITH_sort
784
785	atf_check_equal "$(cat $WITH_r)" "$(cat $WITH_sort)"
786}
787
788atf_test_case s_flag
789s_flag_head()
790{
791	atf_set "descr" "Verify that the output from ls -s matches the output from stat(1)"
792}
793
794s_flag_body()
795{
796	create_test_inputs2
797	for file in $files; do
798		atf_check -e empty \
799		    -o match:"$(stat -f "%b" $file)[[:space:]]+$file" ls -s $file
800	done
801}
802
803atf_test_case scomma_flag
804scomma_flag_head()
805{
806	atf_set "descr" "Verify that -s, prints out the size with ',' delimiters"
807}
808
809scomma_flag_body()
810{
811	export LC_ALL=en_US.UTF-8
812	atf_check -e ignore dd if=/dev/urandom of=file bs=65536 count=64
813	blocks=$(stat -f "%b" file)
814	cblocks=$(printf "%'d" $blocks)
815	atf_check -e empty -o match:"$cblocks[[:space:]]+file" ls -s, file
816}
817
818atf_test_case t_flag
819t_flag_head()
820{
821	atf_set "descr" "Verify that the output from ls -t sorts by modification time"
822}
823
824t_flag_body()
825{
826	create_test_dir
827
828	atf_check -e empty -o empty -s exit:0 touch a.file
829	atf_check -e empty -o empty -s exit:0 touch b.file
830
831	atf_check -e empty -o match:'a\.file' -s exit:0 sh -c 'ls -lt | tail -n 1'
832	atf_check -e empty -o match:'b\.file.*a\.file' -s exit:0 ls -Ct
833
834	atf_check -e empty -o empty -s exit:0 rm a.file
835	atf_check -e empty -o empty -s exit:0 sh -c 'echo "i am a" > a.file'
836
837	atf_check -e empty -o match:'b\.file' -s exit:0 sh -c 'ls -lt | tail -n 1'
838	atf_check -e empty -o match:'a\.file.*b\.file' -s exit:0 ls -Ct
839}
840
841atf_test_case u_flag
842u_flag_head()
843{
844	atf_set "descr" "Verify that the output from ls -u sorts by last access"
845}
846
847u_flag_body()
848{
849	create_test_dir
850
851	atf_check -e empty -o empty -s exit:0 touch a.file
852	atf_check -e empty -o empty -s exit:0 touch b.file
853
854	atf_check -e empty -o match:'b\.file' -s exit:0 sh -c 'ls -lu | tail -n 1'
855	atf_check -e empty -o match:'a\.file.*b\.file' -s exit:0 ls -Cu
856
857	atf_check -e empty -o empty -s exit:0 sh -c 'echo "i am a" > a.file'
858	atf_check -e empty -o match:'i am a' -s exit:0 cat a.file
859
860	atf_check -e empty -o match:'b\.file' -s exit:0 sh -c 'ls -lu | tail -n 1'
861	atf_check -e empty -o match:'a\.file.*b\.file' -s exit:0 ls -Cu
862}
863
864atf_test_case v_flag
865v_flag_head()
866{
867	atf_set "descr" "Verify that the output from ls -v sorts based on strverscmp(3)"
868}
869
870v_flag_body()
871{
872	create_test_dir
873
874	atf_check -e empty -o empty -s exit:0 touch 000 00 01 010 09 0 1 9 10
875	atf_check -e empty -o match:"000.00.01.010.09.0.1.9.10" -s exit:0 sh -c 'ls -Cv'
876}
877
878atf_test_case x_flag
879x_flag_head()
880{
881	atf_set "descr" "Verify that the output from ls -x is multi-column, sorted across"
882}
883
884x_flag_body()
885{
886	create_test_inputs
887
888	WITH_x=$PWD/../with_x.out
889
890	atf_check -e empty -o save:$WITH_x -s exit:0 ls -x
891
892	echo "With -x usage"
893	cat $WITH_x
894
895	atf_check -e ignore -o not-empty -s exit:0 \
896	    egrep "a[[:space:]]+c[[:space:]]+d[[:space:]]+e[[:space:]]+h" $WITH_x
897	atf_check -e ignore -o not-empty -s exit:0 \
898	    egrep "i[[:space:]]+klmn[[:space:]]+opqr[[:space:]]+stuv[[:space:]]+wxyz" $WITH_x
899}
900
901atf_test_case y_flag
902y_flag_head()
903{
904	atf_set "descr" "Verify that the output from ls -y sorts the same way as sort(1)"
905}
906
907y_flag_body()
908{
909	create_test_inputs
910
911	WITH_sort=$PWD/../with_sort.out
912	WITH_y=$PWD/../with_y.out
913
914	atf_check -e empty -o save:$WITH_sort -s exit:0 sh -c 'ls -1 | sort'
915	atf_check -e empty -o save:$WITH_y -s exit:0 ls -1y
916
917	echo "Sorted with sort(1)"
918	cat $WITH_sort
919	echo "Sorted with -y"
920	cat $WITH_y
921
922	atf_check_equal "$(cat $WITH_sort)" "$(cat $WITH_y)"
923}
924
925atf_test_case 1_flag
9261_flag_head()
927{
928	atf_set "descr" "Verify that -1 prints out one item per line"
929}
930
9311_flag_body()
932{
933	create_test_inputs
934
935	WITH_1=$PWD/../with_1.out
936	WITHOUT_1=$PWD/../without_1.out
937
938	atf_check -e empty -o save:$WITH_1 -s exit:0 ls -1
939	atf_check -e empty -o save:$WITHOUT_1 -s exit:0 \
940		sh -c 'for i in $(ls); do echo $i; done'
941
942	echo "Explicit -1 usage"
943	cat $WITH_1
944	echo "No -1 usage"
945	cat $WITHOUT_1
946
947	atf_check_equal "$(cat $WITH_1)" "$(cat $WITHOUT_1)"
948}
949
950atf_init_test_cases()
951{
952	export BLOCKSIZE=512
953
954	atf_add_test_case A_flag
955	atf_add_test_case A_flag_implied_when_root
956	atf_add_test_case B_flag
957	atf_add_test_case C_flag
958	atf_add_test_case D_flag
959	atf_add_test_case F_flag
960	#atf_add_test_case G_flag
961	atf_add_test_case H_flag
962	atf_add_test_case I_flag
963	atf_add_test_case I_flag_voids_implied_A_flag_when_root
964	atf_add_test_case L_flag
965	#atf_add_test_case P_flag
966	atf_add_test_case R_flag
967	atf_add_test_case S_flag
968	atf_add_test_case T_flag
969	#atf_add_test_case U_flag
970	#atf_add_test_case W_flag
971	#atf_add_test_case Z_flag
972	atf_add_test_case a_flag
973	atf_add_test_case b_flag
974	#atf_add_test_case c_flag
975	atf_add_test_case d_flag
976	atf_add_test_case f_flag
977	atf_add_test_case g_flag
978	atf_add_test_case h_flag
979	atf_add_test_case i_flag
980	atf_add_test_case k_flag
981	atf_add_test_case l_flag
982	atf_add_test_case lcomma_flag
983	atf_add_test_case m_flag
984	atf_add_test_case n_flag
985	atf_add_test_case o_flag
986	atf_add_test_case p_flag
987	atf_add_test_case q_flag_and_w_flag
988	atf_add_test_case r_flag
989	atf_add_test_case s_flag
990	atf_add_test_case scomma_flag
991	atf_add_test_case t_flag
992	atf_add_test_case u_flag
993	atf_add_test_case v_flag
994	atf_add_test_case x_flag
995	atf_add_test_case y_flag
996	atf_add_test_case 1_flag
997}
998