xref: /src/bin/ls/tests/ls_tests.sh (revision 7bf81e39d83087dc7f984077b5eed5a48df794d4)
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 childerr
480childerr_head()
481{
482	atf_set "descr" "Verify that fts_children() in pre-order errors are checked"
483	atf_set "require.user" "unprivileged"
484}
485
486childerr_body()
487{
488	atf_check mkdir -p root/dir root/edir
489	atf_check touch root/c
490
491	# Check that listing an empty directory hasn't regressed into being
492	# called an error.
493	atf_check -o match:"total 0" -e empty ls -l root/dir
494
495	atf_check chmod 0 root/dir
496
497	# If we did not abort after fts_children() properly, then stdout would
498	# have an output of the total files enumerated (0).  Thus, assert that
499	# it's empty and that we see the correct error on stderr.
500	atf_check -s not-exit:0 -e match:"Permission denied" ls -l root/dir
501
502	# Now ensure that we didn't just stop there, we printed out a directory
503	# that would've been enumerated later.
504	atf_check -s not-exit:0 -o match:"^root/edir" \
505	    -e match:"Permission denied" ls -lR root
506}
507
508atf_test_case d_flag
509d_flag_head()
510{
511	atf_set "descr" "Verify that -d doesn't descend down directories"
512}
513
514d_flag_body()
515{
516	create_test_dir
517
518	output=$PWD/../output
519
520	atf_check -e empty -o empty -s exit:0 mkdir -p a/b
521
522	for path in . $PWD a; do
523		atf_check -e empty -o save:$output -s exit:0 ls -d $path
524		atf_check_equal "$(cat $output)" "$path"
525	done
526}
527
528atf_test_case f_flag
529f_flag_head()
530{
531	atf_set "descr" "Verify that -f prints out the contents of a directory unsorted"
532}
533
534f_flag_body()
535{
536	create_test_inputs
537
538	output=$PWD/../output
539
540	# XXX: I don't have enough understanding of how the algorithm works yet
541	# to determine more than the fact that all the entries printed out
542	# exist
543	paths=$(find -s . -mindepth 1 -maxdepth 1 \! -name '.*' -exec basename {} \; )
544
545	atf_check -e empty -o save:$output -s exit:0 ls -f
546
547	for path in $paths; do
548		atf_check -e ignore -o not-empty -s exit:0 \
549		    egrep "^$path$" $output
550	done
551}
552
553atf_test_case g_flag
554g_flag_head()
555{
556	atf_set "descr" "Verify that -g implies -l but omits the owner name field"
557}
558
559g_flag_body()
560{
561	atf_check -e empty -o empty -s exit:0 touch a.file
562
563	mtime_in_secs=$(stat -f "%m" -t "%s" a.file)
564	mtime=$(date -j -f "%s" $mtime_in_secs +"%b[[:space:]]+%e[[:space:]]+%H:%M")
565
566	expected_output=$(stat -f "%Sp[[:space:]]+%l[[:space:]]+%Sg[[:space:]]+%z[[:space:]]+$mtime[[:space:]]+a\\.file" a.file)
567
568	atf_check -e empty -o match:"$expected_output" -s exit:0 ls -g a.file
569}
570
571atf_test_case h_flag
572h_flag_head()
573{
574	atf_set "descr" "Verify that -h prints out the humanized units for file sizes with ls -l"
575	atf_set "require.progs" "bc"
576}
577
578h_flag_body()
579{
580	# XXX: this test doesn't currently show how 999 bytes will be 999B,
581	# but 1000 bytes will be 1.0K, due to how humanize_number(3) works.
582	create_test_inputs2
583	for file in $files; do
584		file_size=$(stat -f '%z' "$file") || \
585		    atf_fail "stat'ing $file failed"
586		scale=2
587		if [ $file_size -lt $KB ]; then
588			divisor=1
589			scale=0
590			suffix=B
591		elif [ $file_size -lt $MB ]; then
592			divisor=$KB
593			suffix=K
594		elif [ $file_size -lt $GB ]; then
595			divisor=$MB
596			suffix=M
597		elif [ $file_size -lt $TB ]; then
598			divisor=$GB
599			suffix=G
600		elif [ $file_size -lt $PB ]; then
601			divisor=$TB
602			suffix=T
603		else
604			divisor=$PB
605			suffix=P
606		fi
607
608		bc_expr="$(printf "scale=%s\n%s/%s\nquit" $scale $file_size $divisor)"
609		size_humanized=$(bc -e "$bc_expr" | tr '.' '\.' | sed -e 's,\.00,,')
610
611		atf_check -e empty -o match:"$size_humanized.+$file" \
612		    -s exit:0 ls -hl $file
613	done
614}
615
616atf_test_case i_flag
617i_flag_head()
618{
619	atf_set "descr" "Verify that -i prints out the inode for files"
620}
621
622i_flag_body()
623{
624	create_test_inputs
625
626	paths=$(find -L . -mindepth 1)
627	[ -n "$paths" ] || atf_skip 'Could not find any paths to iterate over (!)'
628
629	for path in $paths; do
630		atf_check -e empty \
631		    -o match:"$(stat -f '[[:space:]]*%i[[:space:]]+%N' $path)" \
632		    -s exit:0 ls -d1i $path
633	done
634}
635
636atf_test_case k_flag
637k_flag_head()
638{
639	atf_set "descr" "Verify that -k prints out the size with a block size of 1kB"
640}
641
642k_flag_body()
643{
644	create_test_inputs2
645	for file in $files; do
646		atf_check -e empty \
647		    -o match:"[[:space:]]+$(stat -f "%z" $file)[[:space:]]+.+[[:space:]]+$file" ls -lk $file
648	done
649}
650
651atf_test_case l_flag
652l_flag_head()
653{
654	atf_set "descr" "Verify that -l prints out the output in long format"
655}
656
657l_flag_body()
658{
659
660	atf_check -e empty -o empty -s exit:0 touch a.file
661
662	mtime_in_secs=$(stat -f "%m" -t "%s" a.file)
663	mtime=$(date -j -f "%s" $mtime_in_secs +"%b[[:space:]]+%e[[:space:]]+%H:%M")
664
665	expected_output=$(stat -f "%Sp[[:space:]]+%l[[:space:]]+%Su[[:space:]]+%Sg[[:space:]]+%z[[:space:]]+$mtime[[:space:]]+a\\.file" a.file)
666
667	atf_check -e empty -o match:"$expected_output" -s exit:0 ls -l a.file
668}
669
670atf_test_case lcomma_flag
671lcomma_flag_head()
672{
673	atf_set "descr" "Verify that -l, prints out the size with ',' delimiters"
674}
675
676lcomma_flag_body()
677{
678	create_test_inputs
679
680	atf_check \
681	    -o match:'\-rw\-r\-\-r\-\-[[:space:]]+.+[[:space:]]+1,000[[:space:]]+.+i' \
682	    env LC_ALL=en_US.ISO8859-1 ls -l, i
683}
684
685atf_test_case m_flag
686m_flag_head()
687{
688	atf_set "descr" "Verify that the output from ls -m is comma-separated"
689}
690
691m_flag_body()
692{
693	create_test_dir
694
695	output=$PWD/../output
696
697	atf_check -e empty -o empty -s exit:0 touch ,, "a,b " c d e
698
699	atf_check -e empty -o save:$output -s exit:0 ls -m
700
701	atf_check_equal "$(cat $output)" ",,, a,b , c, d, e"
702}
703
704atf_test_case n_flag
705n_flag_head()
706{
707	atf_set "descr" "Verify that the output from ls -n prints out numeric GIDs/UIDs instead of symbolic GIDs/UIDs"
708	atf_set "require.user" "root"
709}
710
711n_flag_body()
712{
713	daemon_gid=$(id -g daemon) || atf_skip "could not resolve gid for daemon (!)"
714	nobody_uid=$(id -u nobody) || atf_skip "could not resolve uid for nobody (!)"
715
716	atf_check -e empty -o empty -s exit:0 touch a.file
717	atf_check -e empty -o empty -s exit:0 chown $nobody_uid:$daemon_gid a.file
718
719	atf_check -e empty \
720	    -o match:'\-rw\-r\-\-r\-\-[[:space:]]+1[[:space:]]+'"$nobody_uid[[:space:]]+$daemon_gid"'[[:space:]]+.+a\.file' \
721	    ls -n a.file
722
723}
724
725atf_test_case o_flag
726o_flag_head()
727{
728	atf_set "descr" "Verify that the output from ls -o prints out the chflag values or '-' if none are set"
729}
730
731o_flag_body()
732{
733	local size=12345
734
735	create_test_dir
736
737	atf_check -e ignore -o empty -s exit:0 dd if=/dev/zero of=a.file \
738	    bs=$size count=1
739	atf_check -e ignore -o empty -s exit:0 dd if=/dev/zero of=b.file \
740	    bs=$size count=1
741	atf_check -e empty -o empty -s exit:0 chflags uarch a.file
742	atf_check -e empty -o empty -s exit:0 chflags 0 b.file
743
744	atf_check -e empty -o match:"[[:space:]]+uarch[[:space:]]$size+.+a\\.file" \
745	    -s exit:0 ls -lo a.file
746	atf_check -e empty -o match:"[[:space:]]+\\-[[:space:]]$size+.+b\\.file" \
747	    -s exit:0 ls -lo b.file
748}
749
750atf_test_case p_flag
751p_flag_head()
752{
753	atf_set "descr" "Verify that the output from ls -p prints out '/' after directories"
754}
755
756p_flag_body()
757{
758	create_test_inputs
759
760	paths=$(find -L .)
761	[ -n "$paths" ] || atf_skip 'Could not find any paths to iterate over (!)'
762
763	for path in $paths; do
764		suffix=
765		# If path is not a symlink and is a directory, then the suffix
766		# must be "/".
767		if [ ! -L "${path}" -a -d "$path" ]; then
768			suffix=/
769		fi
770		atf_check -e empty -o match:"$path${suffix}" -s exit:0 \
771		    ls -dp $path
772	done
773}
774
775atf_test_case q_flag_and_w_flag
776q_flag_and_w_flag_head()
777{
778	atf_set "descr" "Verify that the output from ls -q prints out '?' for ESC and ls -w prints out the escape character"
779}
780
781q_flag_and_w_flag_body()
782{
783	create_test_dir
784
785	test_file="$(printf "y\01z")"
786
787	atf_check -e empty -o empty -s exit:0 touch "$test_file"
788
789	atf_check -e empty -o match:'y\?z' -s exit:0 ls -q "$test_file"
790	atf_check -e empty -o match:"$test_file" -s exit:0 ls -w "$test_file"
791}
792
793atf_test_case r_flag
794r_flag_head()
795{
796	atf_set "descr" "Verify that the output from ls -r sorts the same way as reverse sorting with sort(1)"
797}
798
799r_flag_body()
800{
801	create_test_inputs
802
803	WITH_r=$PWD/../with_r.out
804	WITH_sort=$PWD/../with_sort.out
805
806	atf_check -e empty -o save:$WITH_r -s exit:0 ls -1r
807	atf_check -e empty -o save:$WITH_sort -s exit:0 sh -c 'ls -1 | sort -r'
808
809	echo "Sorted with -r"
810	cat $WITH_r
811	echo "Reverse sorted with sort(1)"
812	cat $WITH_sort
813
814	atf_check_equal "$(cat $WITH_r)" "$(cat $WITH_sort)"
815}
816
817atf_test_case s_flag
818s_flag_head()
819{
820	atf_set "descr" "Verify that the output from ls -s matches the output from stat(1)"
821}
822
823s_flag_body()
824{
825	create_test_inputs2
826	for file in $files; do
827		atf_check -e empty \
828		    -o match:"$(stat -f "%b" $file)[[:space:]]+$file" ls -s $file
829	done
830}
831
832atf_test_case scomma_flag
833scomma_flag_head()
834{
835	atf_set "descr" "Verify that -s, prints out the size with ',' delimiters"
836}
837
838scomma_flag_body()
839{
840	export LC_ALL=en_US.UTF-8
841	atf_check -e ignore dd if=/dev/urandom of=file bs=65536 count=64
842	blocks=$(stat -f "%b" file)
843	cblocks=$(printf "%'d" $blocks)
844	atf_check -e empty -o match:"$cblocks[[:space:]]+file" ls -s, file
845}
846
847atf_test_case t_flag
848t_flag_head()
849{
850	atf_set "descr" "Verify that the output from ls -t sorts by modification time"
851}
852
853t_flag_body()
854{
855	create_test_dir
856
857	atf_check -e empty -o empty -s exit:0 touch a.file
858	atf_check -e empty -o empty -s exit:0 touch b.file
859
860	atf_check -e empty -o match:'a\.file' -s exit:0 sh -c 'ls -lt | tail -n 1'
861	atf_check -e empty -o match:'b\.file.*a\.file' -s exit:0 ls -Ct
862
863	atf_check -e empty -o empty -s exit:0 rm a.file
864	atf_check -e empty -o empty -s exit:0 sh -c 'echo "i am a" > a.file'
865
866	atf_check -e empty -o match:'b\.file' -s exit:0 sh -c 'ls -lt | tail -n 1'
867	atf_check -e empty -o match:'a\.file.*b\.file' -s exit:0 ls -Ct
868}
869
870atf_test_case u_flag
871u_flag_head()
872{
873	atf_set "descr" "Verify that the output from ls -u sorts by last access"
874}
875
876u_flag_body()
877{
878	create_test_dir
879
880	atf_check -e empty -o empty -s exit:0 touch a.file
881	atf_check -e empty -o empty -s exit:0 touch b.file
882
883	atf_check -e empty -o match:'b\.file' -s exit:0 sh -c 'ls -lu | tail -n 1'
884	atf_check -e empty -o match:'a\.file.*b\.file' -s exit:0 ls -Cu
885
886	atf_check -e empty -o empty -s exit:0 sh -c 'echo "i am a" > a.file'
887	atf_check -e empty -o match:'i am a' -s exit:0 cat a.file
888
889	atf_check -e empty -o match:'b\.file' -s exit:0 sh -c 'ls -lu | tail -n 1'
890	atf_check -e empty -o match:'a\.file.*b\.file' -s exit:0 ls -Cu
891}
892
893atf_test_case v_flag
894v_flag_head()
895{
896	atf_set "descr" "Verify that the output from ls -v sorts based on strverscmp(3)"
897}
898
899v_flag_body()
900{
901	create_test_dir
902
903	atf_check -e empty -o empty -s exit:0 touch 000 00 01 010 09 0 1 9 10
904	atf_check -e empty -o match:"000.00.01.010.09.0.1.9.10" -s exit:0 sh -c 'ls -Cv'
905}
906
907atf_test_case x_flag
908x_flag_head()
909{
910	atf_set "descr" "Verify that the output from ls -x is multi-column, sorted across"
911}
912
913x_flag_body()
914{
915	create_test_inputs
916
917	WITH_x=$PWD/../with_x.out
918
919	atf_check -e empty -o save:$WITH_x -s exit:0 ls -x
920
921	echo "With -x usage"
922	cat $WITH_x
923
924	atf_check -e ignore -o not-empty -s exit:0 \
925	    egrep "a[[:space:]]+c[[:space:]]+d[[:space:]]+e[[:space:]]+h" $WITH_x
926	atf_check -e ignore -o not-empty -s exit:0 \
927	    egrep "i[[:space:]]+klmn[[:space:]]+opqr[[:space:]]+stuv[[:space:]]+wxyz" $WITH_x
928}
929
930atf_test_case y_flag
931y_flag_head()
932{
933	atf_set "descr" "Verify that the output from ls -y sorts the same way as sort(1)"
934}
935
936y_flag_body()
937{
938	create_test_inputs
939
940	WITH_sort=$PWD/../with_sort.out
941	WITH_y=$PWD/../with_y.out
942
943	atf_check -e empty -o save:$WITH_sort -s exit:0 sh -c 'ls -1 | sort'
944	atf_check -e empty -o save:$WITH_y -s exit:0 ls -1y
945
946	echo "Sorted with sort(1)"
947	cat $WITH_sort
948	echo "Sorted with -y"
949	cat $WITH_y
950
951	atf_check_equal "$(cat $WITH_sort)" "$(cat $WITH_y)"
952}
953
954atf_test_case 1_flag
9551_flag_head()
956{
957	atf_set "descr" "Verify that -1 prints out one item per line"
958}
959
9601_flag_body()
961{
962	create_test_inputs
963
964	WITH_1=$PWD/../with_1.out
965	WITHOUT_1=$PWD/../without_1.out
966
967	atf_check -e empty -o save:$WITH_1 -s exit:0 ls -1
968	atf_check -e empty -o save:$WITHOUT_1 -s exit:0 \
969		sh -c 'for i in $(ls); do echo $i; done'
970
971	echo "Explicit -1 usage"
972	cat $WITH_1
973	echo "No -1 usage"
974	cat $WITHOUT_1
975
976	atf_check_equal "$(cat $WITH_1)" "$(cat $WITHOUT_1)"
977}
978
979atf_init_test_cases()
980{
981	export BLOCKSIZE=512
982
983	atf_add_test_case A_flag
984	atf_add_test_case A_flag_implied_when_root
985	atf_add_test_case B_flag
986	atf_add_test_case C_flag
987	atf_add_test_case D_flag
988	atf_add_test_case F_flag
989	#atf_add_test_case G_flag
990	atf_add_test_case H_flag
991	atf_add_test_case I_flag
992	atf_add_test_case I_flag_voids_implied_A_flag_when_root
993	atf_add_test_case L_flag
994	#atf_add_test_case P_flag
995	atf_add_test_case R_flag
996	atf_add_test_case S_flag
997	atf_add_test_case T_flag
998	#atf_add_test_case U_flag
999	#atf_add_test_case W_flag
1000	#atf_add_test_case Z_flag
1001	atf_add_test_case a_flag
1002	atf_add_test_case b_flag
1003	atf_add_test_case childerr
1004	#atf_add_test_case c_flag
1005	atf_add_test_case d_flag
1006	atf_add_test_case f_flag
1007	atf_add_test_case g_flag
1008	atf_add_test_case h_flag
1009	atf_add_test_case i_flag
1010	atf_add_test_case k_flag
1011	atf_add_test_case l_flag
1012	atf_add_test_case lcomma_flag
1013	atf_add_test_case m_flag
1014	atf_add_test_case n_flag
1015	atf_add_test_case o_flag
1016	atf_add_test_case p_flag
1017	atf_add_test_case q_flag_and_w_flag
1018	atf_add_test_case r_flag
1019	atf_add_test_case s_flag
1020	atf_add_test_case scomma_flag
1021	atf_add_test_case t_flag
1022	atf_add_test_case u_flag
1023	atf_add_test_case v_flag
1024	atf_add_test_case x_flag
1025	atf_add_test_case y_flag
1026	atf_add_test_case 1_flag
1027}
1028