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

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