OILS / spec / builtin-cd.test.sh View on Github | oils.pub

523 lines, 243 significant
1## compare_shells: dash bash mksh zsh ash
2## oils_failures_allowed: 3
3## oils_cpp_failures_allowed: 3
4
5#### cd and $PWD
6cd /
7echo $PWD
8## stdout: /
9
10#### cd BAD/..
11
12# Odd divergence in shells: dash and mksh normalize the path and don't check
13# this error.
14# TODO: I would like OSH to behave like bash and zsh, but separating chdir_arg
15# and pwd_arg breaks case 17.
16
17cd nonexistent_ZZ/..
18echo status=$?
19## STDOUT:
20status=1
21## END
22## BUG dash/ash/mksh STDOUT:
23status=0
24## END
25
26#### cd with 2 or more args - with strict_arg_parse
27
28shopt -s strict_arg_parse
29
30mkdir -p foo
31cd foo
32echo status=$?
33cd ..
34echo status=$?
35
36
37cd foo bar
38st=$?
39if test $st -ne 0; then
40 echo 'failed with multiple args'
41fi
42
43## STDOUT:
44status=0
45status=0
46failed with multiple args
47## END
48
49## N-I dash/ash STDOUT:
50status=0
51status=0
52## END
53
54#### cd with 2 or more args is allowed (strict_arg_parse disabled)
55
56mkdir -p foo
57cd foo bar
58
59## status: 0
60## OK bash/zsh status: 1
61## OK mksh status: 2
62
63#### cd - without OLDPWD
64
65cd - > /dev/null # silence dash output
66echo status=$?
67#pwd
68
69## STDOUT:
70status=1
71## END
72
73## OK mksh STDOUT:
74status=2
75## END
76
77## BUG dash/ash/zsh STDOUT:
78status=0
79## END
80
81#### $OLDPWD
82cd /
83cd $TMP
84echo "old: $OLDPWD"
85env | grep OLDPWD # It's EXPORTED too!
86cd -
87## STDOUT:
88old: /
89OLDPWD=/
90/
91## END
92## BUG mksh STDOUT:
93old: /
94/
95## END
96## BUG zsh STDOUT:
97old: /
98OLDPWD=/
99## END
100
101#### pwd
102cd /
103pwd
104## STDOUT:
105/
106## END
107
108#### pwd after cd ..
109dir=$TMP/dir-one/dir-two
110mkdir -p $dir
111cd $dir
112echo $(basename $(pwd))
113cd ..
114echo $(basename $(pwd))
115## STDOUT:
116dir-two
117dir-one
118## END
119
120#### pwd with symlink and -P
121tmp=$TMP/builtins-pwd-1
122mkdir -p $tmp/target
123ln -s -f $tmp/target $tmp/symlink
124
125cd $tmp/symlink
126
127echo pwd:
128basename $(pwd)
129
130echo pwd -P:
131basename $(pwd -P)
132
133## STDOUT:
134pwd:
135symlink
136pwd -P:
137target
138## END
139
140#### setting $PWD doesn't affect the value of 'pwd' builtin
141dir=/tmp/oil-spec-test/pwd
142mkdir -p $dir
143cd $dir
144
145PWD=foo
146echo before $PWD
147pwd
148echo after $PWD
149## STDOUT:
150before foo
151/tmp/oil-spec-test/pwd
152after foo
153## END
154
155#### unset PWD; then pwd
156dir=/tmp/oil-spec-test/pwd
157mkdir -p $dir
158cd $dir
159
160unset PWD
161echo PWD=$PWD
162pwd
163echo PWD=$PWD
164## STDOUT:
165PWD=
166/tmp/oil-spec-test/pwd
167PWD=
168## END
169
170#### 'unset PWD; pwd' before any cd (tickles a rare corner case)
171dir=/tmp/oil-spec-test/pwd-2
172mkdir -p $dir
173cd $dir
174
175# ensure clean shell process state
176$SH -c 'unset PWD; pwd'
177
178## STDOUT:
179/tmp/oil-spec-test/pwd-2
180## END
181
182#### lie about PWD; pwd before any cd
183dir=/tmp/oil-spec-test/pwd-3
184mkdir -p $dir
185cd $dir
186
187# ensure clean shell process state
188$SH -c 'PWD=foo; pwd'
189
190## STDOUT:
191/tmp/oil-spec-test/pwd-3
192## END
193
194#### remove pwd dir
195dir=/tmp/oil-spec-test/pwd
196mkdir -p $dir
197cd $dir
198pwd
199rmdir $dir
200echo status=$?
201pwd
202echo status=$?
203## STDOUT:
204/tmp/oil-spec-test/pwd
205status=0
206/tmp/oil-spec-test/pwd
207status=0
208## END
209## OK mksh STDOUT:
210/tmp/oil-spec-test/pwd
211status=0
212status=1
213## END
214
215#### pwd in symlinked dir on shell initialization
216tmp=$TMP/builtins-pwd-2
217mkdir -p $tmp
218mkdir -p $tmp/target
219ln -s -f $tmp/target $tmp/symlink
220
221cd $tmp/symlink
222$SH -c 'basename $(pwd)'
223unset PWD
224$SH -c 'basename $(pwd)'
225
226## STDOUT:
227symlink
228target
229## END
230## OK mksh STDOUT:
231target
232target
233## END
234## stderr-json: ""
235
236#### Test the current directory after 'cd ..' involving symlinks
237dir=$TMP/symlinktest
238mkdir -p $dir
239cd $dir
240mkdir -p a/b/c
241mkdir -p a/b/d
242ln -s -f a/b/c c > /dev/null
243cd c
244cd ..
245# Expecting a c/ (since we are in symlinktest) but osh gives c d (thinks we are
246# in b/)
247ls
248## STDOUT:
249a
250c
251## END
252
253#### cd with no arguments
254HOME=$TMP/home
255mkdir -p $HOME
256cd
257test $(pwd) = "$HOME" && echo OK
258## stdout: OK
259
260#### cd to nonexistent dir
261cd /nonexistent/dir
262echo status=$?
263## stdout: status=1
264## OK dash/ash/mksh stdout: status=2
265
266#### cd away from dir that was deleted
267dir=$TMP/cd-nonexistent
268mkdir -p $dir
269cd $dir
270rmdir $dir
271cd $TMP
272echo $(basename $OLDPWD)
273echo status=$?
274## STDOUT:
275cd-nonexistent
276status=0
277## END
278
279#### cd permits double bare dash
280cd -- /
281echo $PWD
282## stdout: /
283
284#### cd to symlink with -L and -P
285targ=$TMP/cd-symtarget
286lnk=$TMP/cd-symlink
287mkdir -p $targ
288ln -s $targ $lnk
289
290# -L behavior is the default
291cd $lnk
292test $PWD = "$TMP/cd-symlink" && echo OK
293
294cd -L $lnk
295test $PWD = "$TMP/cd-symlink" && echo OK
296
297cd -P $lnk
298test $PWD = "$TMP/cd-symtarget" && echo OK || echo $PWD
299## STDOUT:
300OK
301OK
302OK
303## END
304
305#### cd to relative path with -L and -P
306die() { echo "$@"; exit 1; }
307
308targ=$TMP/cd-symtarget/subdir
309lnk=$TMP/cd-symlink
310mkdir -p $targ
311ln -s $TMP/cd-symtarget $lnk
312
313# -L behavior is the default
314cd $lnk/subdir
315test $PWD = "$TMP/cd-symlink/subdir" || die "failed"
316cd ..
317test $PWD = "$TMP/cd-symlink" && echo OK
318
319cd $lnk/subdir
320test $PWD = "$TMP/cd-symlink/subdir" || die "failed"
321cd -L ..
322test $PWD = "$TMP/cd-symlink" && echo OK
323
324cd $lnk/subdir
325test $PWD = "$TMP/cd-symlink/subdir" || die "failed"
326cd -P ..
327test $PWD = "$TMP/cd-symtarget" && echo OK || echo $PWD
328## STDOUT:
329OK
330OK
331OK
332## END
333
334#### unset PWD; cd /tmp is allowed (regression)
335
336unset PWD; cd /tmp
337pwd
338
339## STDOUT:
340/tmp
341## END
342
343#### CDPATH is respected
344
345mkdir -p /tmp/spam/foo /tmp/eggs/foo
346
347CDPATH='/tmp/spam:/tmp/eggs'
348
349cd foo
350echo status=$?
351pwd
352
353## STDOUT:
354/tmp/spam/foo
355status=0
356/tmp/spam/foo
357## END
358
359# doesn't print the dir
360## BUG zsh STDOUT:
361status=0
362/tmp/spam/foo
363## END
364
365
366#### Change directory in non-shell parent process (make or Python)
367
368# inspired by Perl package bug
369
370old_dir=$(pwd)
371
372mkdir -p cpan/Encode/Byte
373
374# Simulate make changing the dir
375wrapped_chdir() {
376 #set -- $SH -c 'echo BEFORE; pwd; echo CD; cd Byte; echo AFTER; pwd'
377
378 set -- $SH -c 'cd Byte; pwd'
379 # strace comes out the same - one getcwd() and one chdir()
380 #set -- strace -e 'getcwd,chdir' "$@"
381
382 python2 -c '
383from __future__ import print_function
384import os, sys, subprocess
385
386argv = sys.argv[1:]
387print("Python PWD = %r" % os.getenv("PWD"), file=sys.stderr)
388print("Python argv = %r" % argv, file=sys.stderr)
389
390os.chdir("cpan/Encode")
391subprocess.check_call(argv)
392' "$@"
393}
394
395#wrapped_chdir
396new_dir=$(wrapped_chdir)
397
398#echo $old_dir
399
400# Make the test insensitive to absolute paths
401echo "${new_dir##$old_dir}"
402
403## STDOUT:
404/cpan/Encode/Byte
405## END
406
407#### What happens when inherited $PWD and current dir disagree?
408
409DIR=/tmp/osh-spec-cd
410mkdir -p $DIR
411cd $DIR
412
413old_dir=$(pwd)
414
415mkdir -p cpan/Encode/Byte
416
417# Simulate make changing the dir
418wrapped_chdir() {
419 #set -- $SH -c 'echo BEFORE; pwd; echo CD; cd Byte; echo AFTER; pwd'
420
421 # disagreement before we gert here
422 set -- $SH -c '
423echo "PWD = $PWD"; pwd
424cd Byte; echo cd=$?
425echo "PWD = $PWD"; pwd
426'
427
428 # strace comes out the same - one getcwd() and one chdir()
429 #set -- strace -e 'getcwd,chdir' "$@"
430
431 python2 -c '
432from __future__ import print_function
433import os, sys, subprocess
434
435argv = sys.argv[1:]
436print("Python argv = %r" % argv, file=sys.stderr)
437
438os.chdir("cpan/Encode")
439print("Python PWD = %r" % os.getenv("PWD"), file=sys.stdout)
440sys.stdout.flush()
441
442subprocess.check_call(argv)
443' "$@"
444}
445
446#unset PWD
447wrapped_chdir
448
449## STDOUT:
450Python PWD = '/tmp/osh-spec-cd'
451PWD = /tmp/osh-spec-cd/cpan/Encode
452/tmp/osh-spec-cd/cpan/Encode
453cd=0
454PWD = /tmp/osh-spec-cd/cpan/Encode/Byte
455/tmp/osh-spec-cd/cpan/Encode/Byte
456## END
457
458## BUG mksh STDOUT:
459Python PWD = None
460PWD = /tmp/osh-spec-cd/cpan/Encode
461/tmp/osh-spec-cd/cpan/Encode
462cd=0
463PWD = /tmp/osh-spec-cd/cpan/Encode/Byte
464/tmp/osh-spec-cd/cpan/Encode/Byte
465## END
466
467#### Survey of getcwd() syscall
468
469# This is not that important -- see core/sh_init.py
470# Instead of verifying that stat('.') == stat(PWD), which is two sycalls,
471# OSH just calls getcwd() unconditionally.
472
473# so C++ leak sanitizer doesn't print to stderr
474export ASAN_OPTIONS='detect_leaks=0'
475
476strace -e getcwd -- $SH -c 'echo hi; pwd; echo $PWD' 1> /dev/null 2> err.txt
477
478wc -l err.txt
479#cat err.txt
480
481## STDOUT:
4821 err.txt
483## END
484## BUG mksh STDOUT:
4852 err.txt
486## END
487
488#### chdir is a synonym for cd - busybox ash
489
490chdir /tmp
491
492if test $? -ne 0; then
493 echo fail
494 exit
495fi
496
497pwd
498
499# It's the same with no args, but mksh fails because of $HOME
500#chdir
501#echo status=$?
502
503## STDOUT:
504/tmp
505## END
506
507## N-I bash STDOUT:
508fail
509## END
510
511#### arguments to pwd
512pwd /
513## status: 0
514## OK zsh/mksh status: 1
515
516#### pwd errors out on args with strict_arg_parse
517shopt -s strict_arg_parse || true
518pwd / >/dev/null || echo 'too many args!'
519## N-I bash/dash/ash STDOUT:
520## END
521## STDOUT:
522too many args!
523## END