Files
sausage/userland/brush/source/brush-shell/tests/cases/compat/prompt.yaml
T
2026-11-11 22:04:16 +00:00

373 lines
10 KiB
YAML

name: "Prompt"
cases:
- name: "Simple prompts"
stdin: |
prompt='$ '
echo "Prompt: '${prompt@P}'"
prompt='a\r\n> '
echo "Prompt: '${prompt@P}'"
prompt='\\'
echo "Prompt: '${prompt@P}'"
prompt='\?'
echo "Prompt: '${prompt@P}'"
prompt='\81'
echo "Prompt: '${prompt@P}'"
- name: "Working dir based prompts"
stdin: |
cd /usr
prompt='\w '
echo "Prompt: '${prompt@P}'"
prompt='\W '
echo "Prompt: '${prompt@P}'"
arr=("\w" "\w")
echo "Prompt: '${arr@P}'"
echo "Prompt: '${arr[@]@P}'"
echo "Prompt: '${arr[*]@P}'"
- name: "Non-printing chars"
stdin: |
prompt='\[\]Prompt>\[\]'
echo "Prompt: '${prompt@P}'"
- name: "Hostname in prompts"
stdin: |
prompt='\h '
echo "Prompt: '${prompt@P}'"
prompt='\H '
echo "Prompt: '${prompt@P}'"
- name: "Shell name"
stdin: |
prompt='\s'
[[ "${prompt@P}" == "$(basename $0)" ]]
- name: "Shell version info"
stdin: |
prompt='\v'
[[ "${prompt@P}" =~ ^\d+\.\d+$ ]] && echo "Version is well-formatted"
prompt='\V'
[[ "${prompt@P}" == ^\d+\.\d+\.\d+$ ]] && echo "Release is correct"
- name: "Simple date"
stdin: |
prompt='\d'
[[ "${prompt@P}" == $(date +'%a %b %d') ]] && echo '\d date matches'
- name: "Date format with plain string"
stdin: |
prompt='\D{something}'
echo "Date format with plain string: '${prompt@P}'"
- name: "Date format with year"
stdin: |
prompt='\d{%Y}'
echo "Date format with year: '${prompt@P}'"
- name: "Time format: A"
stdin: |
prompt='\A'
expanded=${prompt@P}
roundtripped=$(date --date="${expanded}" +'%H:%M')
[[ ${expanded} == ${roundtripped} ]] && echo "Time matches"
- name: "Time format: @"
stdin: |
prompt='\@'
expanded=${prompt@P}
roundtripped=$(date --date="${expanded}" +'%I:%M %p')
[[ ${expanded} == ${roundtripped} ]] && echo "Time matches"
- name: "Time format: T"
stdin: |
prompt='\T'
expanded=${prompt@P}
roundtripped=$(date --date="${expanded}" +'%I:%M:%S')
[[ ${expanded} == ${roundtripped} ]] && echo "Time matches"
- name: "Time format: t"
stdin: |
prompt='\t'
expanded=${prompt@P}
roundtripped=$(date --date="${expanded}" +'%H:%M:%S')
[[ ${expanded} == ${roundtripped} ]] && echo "Time matches"
- name: "Current history number"
known_failure: true
stdin: |
prompt='\!'
expanded=${prompt@P}
roundtripped=$(history | tail -n 1 | cut -d ' ' -f 1)
[[ ${expanded} == ${roundtripped} ]] && echo "History number matches"
- name: "Current command number"
known_failure: true # bash advances the number before a command runs, brush after
stdin: |
prompt='\#'
expanded=${prompt@P}
echo "${expanded}"
- name: "Current command number at each interactive prompt"
args: ["-i"]
ignore_stderr: true
env:
n: '\#'
PROMPT_COMMAND: 'echo "prompt: ${n@P}"'
stdin: |
true
true; true; true
if true; then
true
fi
cd .
env true
- name: "Current command number skips blank and comment-only lines"
known_failure: true # brush counts every line its interactive loop runs
args: ["-i"]
ignore_stderr: true
env:
n: '\#'
PROMPT_COMMAND: 'echo "prompt: ${n@P}"'
stdin: |
true
# comment only
true
- name: "Current command number in a -c string"
args:
- "-c"
- |
n='\#'
echo "${n@P}"
true
echo "${n@P}"
- name: "Escaped dollar-sign"
stdin: |
prompt='\$'
echo "Prompt: '${prompt@P}'"
- name: "Var in prompt"
stdin: |
var=value
prompt="\$var"
shopt -s promptvars
echo "Prompt (dollar-sign not escaped, with promptvars): ${prompt@P}"
shopt -u promptvars
echo "Prompt (dollar-sign not escaped, without promptvars): ${prompt@P}"
prompt="\\\$var"
shopt -s promptvars
echo "Prompt (dollar-sign escaped, with promptvars): ${prompt@P}"
shopt -u promptvars
echo "Prompt (dollar-sign escaped, without promptvars): ${prompt@P}"
- name: "Tilde in prompt"
stdin: |
HOME=$(pwd)
prompt='~'
shopt -s promptvars
echo "Prompt (with promptvars): ${prompt@P}"
shopt -u promptvars
echo "Prompt (without promptvars): ${prompt@P}"
- name: "Command substitution in prompt"
stdin: |
prompt='$(echo Hello)'
echo "Without spaces: ${prompt@P}"
prompt=' $(echo Hello) '
echo "With spaces: ${prompt@P}"
- name: "Backquoted command substitution in prompt"
stdin: |
prompt='`echo Hello`'
echo "Without spaces: ${prompt@P}"
prompt=' `echo Hello` '
echo "With spaces: ${prompt@P}"
- name: "Parameter expansion with non-printing chars"
stdin: |
var=value
prompt="\${var:+\[text\]}"
echo "Conditional (set): '${prompt@P}'"
var=
prompt="\${var:+\[text\]}"
echo "Conditional (unset): '${prompt@P}'"
- name: "Command substitution in prompt and exit code"
args: ["-i"]
ignore_stderr: true
stdin: |
PS1='$(echo Hello)> '
false
echo $?
- name: "Brackets and var"
stdin: |
prompt='\[$var\]\u > '
echo "Prompt: ${prompt@P}"
- name: "Brackets and var (interactive mode)"
args: ["-i"]
ignore_stderr: true
stdin: |
prompt='\[$var\]\u > '
echo "Prompt: ${prompt@P}"
# The following cases exercise double-quote escape semantics in the second
# pass of prompt expansion: a backslash only escapes (and is consumed by)
# one of the same characters it would escape inside a double-quoted string;
# for any other character, the backslash is preserved literally.
#
# We pipe the expansion result through hexdump -C to get a deterministic
# byte-level view, avoiding any divergence in `printf '%q'` formatting.
- name: "Backslash-CR survives prompt expansion (issue #1119)"
stdin: |
# OSC 133;C followed by ST (\e\\) and CR. The ST must end with a literal
# backslash byte (0x5c); without it, the OSC is malformed and terminals
# may eat the first character of subsequent output.
prompt='\e]133;C\e\\\r'
printf '%s' "${prompt@P}" | hexdump -C
- name: "Backslash before non-special char preserves backslash"
stdin: |
# Under double-quote escape rules, a backslash before a non-special
# character is preserved literally. \\X (raw) = '\' + 'X' should expand
# to literal '\X'.
prompt='\\X'
printf '%s' "${prompt@P}" | hexdump -C
prompt='\\Z'
printf '%s' "${prompt@P}" | hexdump -C
prompt='\\n'
printf '%s' "${prompt@P}" | hexdump -C
prompt=$'\\\\\t'
printf '%s' "${prompt@P}" | hexdump -C
- name: "Backslash before dollar escapes parameter expansion"
stdin: |
# `\\$VAR` (PS0 raw chars: \\, $, V, A, R) → step 1 produces \$VAR, then
# step 2's `\$` consumes the backslash and leaves a literal $, so $VAR
# remains literal (not expanded).
var=hello
prompt='\\$var'
printf '%s' "${prompt@P}" | hexdump -C
- name: "Double backslash before dollar yields backslash + expansion"
stdin: |
# `\\\\$VAR` → step 1 produces \\$VAR, then step 2's `\\` reduces to a
# literal `\` and `$VAR` is expanded normally.
var=hello
prompt='\\\\$var'
printf '%s' "${prompt@P}" | hexdump -C
- name: "Backslash before escaped dollar"
stdin: |
# `\\\$` → step 1 produces \$ (Backslash + DollarOrPound). Step 2 keeps
# the literal backslash and the literal $, so the result is `\$`.
prompt='\\\$'
printf '%s' "${prompt@P}" | hexdump -C
- name: "Many backslashes before non-special"
stdin: |
# Each `\\` in step 1 produces a literal `\`. Step 2 reduces consecutive
# backslashes pairwise.
prompt='\\\\\\\\X'
printf '%s' "${prompt@P}" | hexdump -C
prompt=$'\\\\\\\\\r'
printf '%s' "${prompt@P}" | hexdump -C
- name: "Backslash before newline is line continuation"
stdin: |
# `\\\n` produces (Backslash, Newline) → \<LF> in formatted prompt.
# In double-quote escape rules, \<newline> is line continuation and
# consumes both characters.
prompt='\\\n'
printf '%s' "${prompt@P}" | hexdump -C
# Same expected behavior when the LF byte is supplied directly.
prompt=$'\\\\\n'
printf '%s' "${prompt@P}" | hexdump -C
- name: "Trailing literal backslash survives"
stdin: |
# `\\` at end of prompt should produce a single literal backslash.
prompt='\\'
printf '%s' "${prompt@P}" | hexdump -C
# `\\X\\` should produce `\X\`.
prompt='\\X\\'
printf '%s' "${prompt@P}" | hexdump -C
- name: "Backslash followed by working-dir prompt piece"
stdin: |
# Make sure prompt pieces whose formatted output happens to contain `\`
# (e.g., AsciiCharacter for octal `\134`) survive intact.
prompt='\134'
printf '%s' "${prompt@P}" | hexdump -C
# `\\\w` should yield `\<cwd>`. Use a fixed cwd so brush vs. oracle
# output is deterministic.
cd /
prompt='\\\w'
printf '%s' "${prompt@P}" | hexdump -C
- name: "Bare \\$ leaves no protective backslash (issue #1167)"
stdin: |
# `\$` alone must format to a bare `$` or `#` — pass 1's protective `\`
# must not leak when the resulting char (e.g. `#` for root) isn't
# double-quote-escapable.
prompt='\$'
printf '%s' "${prompt@P}" | hexdump -C
# Same in a context where promptvars would otherwise re-expand a bare $:
var=SHOULD_NOT_APPEAR
prompt='\$var'
shopt -s promptvars
printf '%s' "${prompt@P}" | hexdump -C
shopt -u promptvars
- name: "Backslash adjacent to unrecognized escape sequence"
stdin: |
# `\\\X` (Backslash + EscapedSequence(\X)) must reduce to a single `\X`.
# An over-eager protective `\` on the EscapedSequence would leak (X
# isn't dq-escapable) and emit `\\X`.
prompt='\\\X'
printf '%s' "${prompt@P}" | hexdump -C
prompt='\\\?'
printf '%s' "${prompt@P}" | hexdump -C
# X here is dq-escapable, so pass 2 consumes the EscapedSequence's own
# `\` and emits just the literal char.
prompt='\"'
printf '%s' "${prompt@P}" | hexdump -C
prompt='\`'
printf '%s' "${prompt@P}" | hexdump -C