threads / patch / 36705

patchgit-prompt.sh: don't assume the shell expands the value of PS1

Subject: [PATCH] git-prompt.sh: don't assume the shell expands the value of PS1

## tl;dr

35 messages between May 19, 2014 and Jun 11, 2014. Diffs are folded; open one to read it.

replies: 34people: 5as markdown or json

Richard Hansen· May 19, 2014, 22:55 UTC · lore

Not all shells subject the prompt string to parameter expansion. Test whether the shell will expand the value of PS1, and use the result to control whether raw ref names are included directly in PS1.

This fixes a regression introduced in commit 8976500 ("git-prompt.sh: don't put unsanitized branch names in $PS1"): zsh does not expand PS1 by default, but that commit assumed it did. The bug resulted in prompts containing the literal string '${__git_ps1_branch_name}' instead of the actual branch name.

Reported-by: Caleb Thompson <caleb@calebthompson.io>
Signed-off-by: Richard Hansen <rhansen@bbn.com>
---

To prevent a regression like this from happening again, I plan on adding new zsh test cases and expanding the bash test cases (to test the behavior with 'shopt -u promptvars'). I'd like the zsh tests to cover the same stuff as the bash tests. These are the steps I am considering:

  1. delete the last test case in t9903 ("prompt - zsh color pc mode")
  2. add two new functions to t/lib-bash.sh:
         ps1_expansion_enable () { shopt -s promptvars; }
         ps1_expansion_disable () { shopt -u promptvars; }
  3. loop over the relevant test cases twice:  once after calling
     ps1_expansion_enable and once after calling ps1_expansion_disable
     (with appropriate adjustments to the expected output)
  4. move the test cases in t9903 to a separate library file and
     source it from t9903-bash-prompt.sh
  5. create two new files:
       * t/lib-zsh.sh (same as t/lib-bash.sh but tweaked for zsh)
       * t/t9904-zsh-prompt.sh (same as t/t9903-bash-prompt.sh but
         tweaked for zsh)
Does this approach sound reasonable?
 contrib/completion/git-prompt.sh | 56 ++++++++++++++++++++++++++++------------
 t/t9903-bash-prompt.sh           |  6 ++---
 2 files changed, 42 insertions(+), 20 deletions(-)
Show changes to 2 files +42 −20

contrib/completion/git-prompt.sh, t/t9903-bash-prompt.sh

diff --git a/contrib/completion/git-prompt.sh b/contrib/completion/git-prompt.sh
index 853425d..9d684b1 100644
--- a/contrib/completion/git-prompt.sh
+++ b/contrib/completion/git-prompt.sh
@@ -209,9 +209,7 @@ __git_ps1_show_upstream ()
 		if [[ -n "$count" && -n "$name" ]]; then
 			__git_ps1_upstream_name=$(git rev-parse \
 				--abbrev-ref "$upstream" 2>/dev/null)
-			if [ $pcmode = yes ]; then
-				# see the comments around the
-				# __git_ps1_branch_name variable below
+			if [ $pcmode = yes ] && [ $ps1_expanded = yes ]; then
 				p="$p \${__git_ps1_upstream_name}"
 			else
 				p="$p ${__git_ps1_upstream_name}"
@@ -308,6 +306,43 @@ __git_ps1 ()
 		;;
 	esac
 
+	# ps1_expanded:  This variable is set to 'yes' if the shell
+	# subjects the value of PS1 to parameter expansion:
+	#
+	#   * bash does unless the promptvars option is disabled
+	#   * zsh does not unless the PROMPT_SUBST option is set
+	#   * POSIX shells always do
+	#
+	# If the shell would expand the contents of PS1 when drawing
+	# the prompt, a raw ref name must not be included in PS1.
+	# This protects the user from arbitrary code execution via
+	# specially crafted ref names.  For example, a ref named
+	# 'refs/heads/$(IFS=_;cmd=sudo_rm_-rf_/;$cmd)' might cause the
+	# shell to execute 'sudo rm -rf /' when the prompt is drawn.
+	#
+	# Instead, the ref name should be placed in a separate global
+	# variable (in the __git_ps1_* namespace to avoid colliding
+	# with the user's environment) and that variable should be
+	# referenced from PS1.  For example:
+	#
+	#     __git_ps1_foo=$(do_something_to_get_ref_name)
+	#     PS1="...stuff...\${__git_ps1_foo}...stuff..."
+	#
+	# If the shell does not expand the contents of PS1, the raw
+	# ref name must be included in PS1.
+	#
+	# The value of this variable is only relevant when in pcmode.
+	#
+	# Assume that the shell follows the POSIX specification and
+	# expands PS1 unless determined otherwise.  (This is more
+	# likely to be correct if the user has a non-bash, non-zsh
+	# shell and safer than the alternative if the assumption is
+	# incorrect.)
+	#
+	local ps1_expanded=yes
+	[ -z "$ZSH_VERSION" ] || [[ -o PROMPT_SUBST ]] || ps1_expanded=no
+	[ -z "$BASH_VERSION" ] || shopt -q promptvars || ps1_expanded=no
+
 	local repo_info rev_parse_exit_code
 	repo_info="$(git rev-parse --git-dir --is-inside-git-dir \
 		--is-bare-repository --is-inside-work-tree \
@@ -457,21 +492,8 @@ __git_ps1 ()
 	fi
 
 	b=${b##refs/heads/}
-	if [ $pcmode = yes ]; then
-		# In pcmode (and only pcmode) the contents of
-		# $gitstring are subject to expansion by the shell.
-		# Avoid putting the raw ref name in the prompt to
-		# protect the user from arbitrary code execution via
-		# specially crafted ref names (e.g., a ref named
-		# '$(IFS=_;cmd=sudo_rm_-rf_/;$cmd)' would execute
-		# 'sudo rm -rf /' when the prompt is drawn).  Instead,
-		# put the ref name in a new global variable (in the
-		# __git_ps1_* namespace to avoid colliding with the
-		# user's environment) and reference that variable from
-		# PS1.
+	if [ $pcmode = yes ] && [ $ps1_expanded = yes ]; then
 		__git_ps1_branch_name=$b
-		# note that the $ is escaped -- the variable will be
-		# expanded later (when it's time to draw the prompt)
 		b="\${__git_ps1_branch_name}"
 	fi
 
diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh
index 6efd0d9..9150984 100755
--- a/t/t9903-bash-prompt.sh
+++ b/t/t9903-bash-prompt.sh
@@ -578,12 +578,12 @@ test_expect_success 'prompt - bash color pc mode - untracked files status indica
 '
 
 test_expect_success 'prompt - zsh color pc mode' '
-	printf "BEFORE: (%%F{green}\${__git_ps1_branch_name}%%f):AFTER\\nmaster" >expected &&
+	printf "BEFORE: (%%F{green}master%%f):AFTER" >expected &&
 	(
 		ZSH_VERSION=5.0.0 &&
 		GIT_PS1_SHOWCOLORHINTS=y &&
-		__git_ps1 "BEFORE:" ":AFTER" >"$actual"
-		printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+		__git_ps1 "BEFORE:" ":AFTER" &&
+		printf "%s" "$PS1" >"$actual"
 	) &&
 	test_cmp expected "$actual"
 '
-- 
1.9.3
Junio C Hamano· May 20, 2014, 18:38 UTC · re: Richard Hansen · lore

Re: [PATCH] git-prompt.sh: don't assume the shell expands the value of PS1

Richard Hansen <rhansen@bbn.com> writes:
Show 13 quoted lines
> Not all shells subject the prompt string to parameter expansion.  Test
> whether the shell will expand the value of PS1, and use the result to
> control whether raw ref names are included directly in PS1.
>
> This fixes a regression introduced in commit 8976500 ("git-prompt.sh:
> don't put unsanitized branch names in $PS1"):  zsh does not expand PS1
> by default, but that commit assumed it did.  The bug resulted in
> prompts containing the literal string '${__git_ps1_branch_name}'
> instead of the actual branch name.
>
> Reported-by: Caleb Thompson <caleb@calebthompson.io>
> Signed-off-by: Richard Hansen <rhansen@bbn.com>
> ---
Thanks, applied.
Show 21 quoted lines
> To prevent a regression like this from happening again, I plan on
> adding new zsh test cases and expanding the bash test cases (to test
> the behavior with 'shopt -u promptvars').  I'd like the zsh tests to
> cover the same stuff as the bash tests.  These are the steps I am
> considering:
>
>   1. delete the last test case in t9903 ("prompt - zsh color pc mode")
>   2. add two new functions to t/lib-bash.sh:
>          ps1_expansion_enable () { shopt -s promptvars; }
>          ps1_expansion_disable () { shopt -u promptvars; }
>   3. loop over the relevant test cases twice:  once after calling
>      ps1_expansion_enable and once after calling ps1_expansion_disable
>      (with appropriate adjustments to the expected output)
>   4. move the test cases in t9903 to a separate library file and
>      source it from t9903-bash-prompt.sh
>   5. create two new files:
>        * t/lib-zsh.sh (same as t/lib-bash.sh but tweaked for zsh)
>        * t/t9904-zsh-prompt.sh (same as t/t9903-bash-prompt.sh but
>          tweaked for zsh)
>
> Does this approach sound reasonable?

Sounds like a plan, especially if step 4 does a reasonable job of factoring out as much common stuff as possible.

Richard Hansen· May 27, 2014, 07:40 UTC · re: Junio C Hamano · lore

[PATCH 00/10] Zsh prompt tests

This series adds test cases for running __git_ps1 (see contrib/completion/git-prompt.sh) from Zsh.

This series also adds more Bash test cases to test how __git_ps1 reacts to disabling Bash's PS1 parameter expansion. (This is related to adding Zsh test cases: Zsh doesn't perform parameter expansion on PS1 by default but many users turn it on, so the Zsh test script must test __git_ps1 in both states. Bash expands PS1 by default and users rarely turn it off, but testing both states in Bash improves the symmetry with the Zsh test cases.)

This is the approach I took:
  1. delete the last test case in t9903 ("prompt - zsh color pc mode")
  2. add two new functions to t/lib-bash.sh:
         ps1_expansion_enable () { shopt -s promptvars; }
         ps1_expansion_disable () { shopt -u promptvars; }
  3. loop over the relevant test cases twice:  once after calling
     ps1_expansion_enable and once after calling ps1_expansion_disable
     (with appropriate adjustments to the expected output)
  4. move the test cases in t9903 to a separate library file and
     source it from t9903-bash-prompt.sh
  5. create two new files:
       * t/lib-zsh.sh (same as t/lib-bash.sh but tweaked for zsh)
       * t/t9904-zsh-prompt.sh (same as t/t9903-bash-prompt.sh but
         tweaked for zsh)

There are a lot of indendation changes, so I recommend examining the changes via diff -w.

Richard Hansen (10):
  t9903: remove Zsh test from the suite of Bash prompt tests
  t9903: put the Bash pc mode prompt test cases in a function
  t9903: move test name prefix to a separate variable
  t9903: run pc mode tests again with PS1 expansion disabled
  t9903: include "Bash" in test names via new $shellname var
  t9903: move PS1 color code variable definitions to lib-bash.sh
  t9903: move prompt tests to a new lib-prompt-tests.sh file
  lib-prompt-tests.sh: put all tests inside a function
  lib-prompt-tests.sh: add variable for string that encodes percent in
    PS1
  t9904: new __git_ps1 tests for Zsh
 t/lib-bash.sh          |  12 +
 t/lib-prompt-tests.sh  | 633 +++++++++++++++++++++++++++++++++++++++++++++++++
 t/lib-zsh.sh           |  30 +++
 t/t9903-bash-prompt.sh | 582 +--------------------------------------------
 t/t9904-zsh-prompt.sh  |  10 +
 5 files changed, 687 insertions(+), 580 deletions(-)
 create mode 100644 t/lib-prompt-tests.sh
 create mode 100644 t/lib-zsh.sh
 create mode 100755 t/t9904-zsh-prompt.sh
-- 
1.9.3
Richard Hansen· May 27, 2014, 07:40 UTC · re: Richard Hansen · lore

[PATCH 01/10] t9903: remove Zsh test from the suite of Bash prompt tests

This test is about to become redundant: All of the Bash prompt tests will be moved into a separate library file that will also be used by a new Zsh-specific test script.

Signed-off-by: Richard Hansen <rhansen@bbn.com>
---
 t/t9903-bash-prompt.sh | 11 -----------
 1 file changed, 11 deletions(-)
Show changes to t/t9903-bash-prompt.sh +0 −11
diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh
index 9150984..335383d 100755
--- a/t/t9903-bash-prompt.sh
+++ b/t/t9903-bash-prompt.sh
@@ -577,15 +577,4 @@ test_expect_success 'prompt - bash color pc mode - untracked files status indica
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - zsh color pc mode' '
-	printf "BEFORE: (%%F{green}master%%f):AFTER" >expected &&
-	(
-		ZSH_VERSION=5.0.0 &&
-		GIT_PS1_SHOWCOLORHINTS=y &&
-		__git_ps1 "BEFORE:" ":AFTER" &&
-		printf "%s" "$PS1" >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
 test_done
-- 
1.9.3
Richard Hansen· May 27, 2014, 07:40 UTC · re: Richard Hansen · lore

[PATCH 02/10] t9903: put the Bash pc mode prompt test cases in a function

This is a step toward invoking the same pc mode test cases twice: once with PS1 parameter expansion enabled and once with it disabled.

Signed-off-by: Richard Hansen <rhansen@bbn.com>
---
 t/t9903-bash-prompt.sh | 236 +++++++++++++++++++++++++------------------------
 1 file changed, 120 insertions(+), 116 deletions(-)
Show changes to t/t9903-bash-prompt.sh +120 −116
diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh
index 335383d..c691869 100755
--- a/t/t9903-bash-prompt.sh
+++ b/t/t9903-bash-prompt.sh
@@ -451,130 +451,134 @@ test_expect_success 'prompt - format string starting with dash' '
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - pc mode' '
-	printf "BEFORE: (\${__git_ps1_branch_name}):AFTER\\nmaster" >expected &&
-	printf "" >expected_output &&
-	(
-		__git_ps1 "BEFORE:" ":AFTER" >"$actual" &&
-		test_cmp expected_output "$actual" &&
-		printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+run_pcmode_tests () {
+	test_expect_success 'prompt - pc mode' '
+		printf "BEFORE: (\${__git_ps1_branch_name}):AFTER\\nmaster" >expected &&
+		printf "" >expected_output &&
+		(
+			__git_ps1 "BEFORE:" ":AFTER" >"$actual" &&
+			test_cmp expected_output "$actual" &&
+			printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success 'prompt - bash color pc mode - branch name' '
-	printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear}):AFTER\\nmaster" >expected &&
-	(
-		GIT_PS1_SHOWCOLORHINTS=y &&
-		__git_ps1 "BEFORE:" ":AFTER" >"$actual"
-		printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success 'prompt - bash color pc mode - branch name' '
+		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear}):AFTER\\nmaster" >expected &&
+		(
+			GIT_PS1_SHOWCOLORHINTS=y &&
+			__git_ps1 "BEFORE:" ":AFTER" >"$actual"
+			printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success 'prompt - bash color pc mode - detached head' '
-	printf "BEFORE: (${c_red}\${__git_ps1_branch_name}${c_clear}):AFTER\\n(%s...)" $(git log -1 --format="%h" b1^) >expected &&
-	git checkout b1^ &&
-	test_when_finished "git checkout master" &&
-	(
-		GIT_PS1_SHOWCOLORHINTS=y &&
-		__git_ps1 "BEFORE:" ":AFTER" &&
-		printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success 'prompt - bash color pc mode - detached head' '
+		printf "BEFORE: (${c_red}\${__git_ps1_branch_name}${c_clear}):AFTER\\n(%s...)" $(git log -1 --format="%h" b1^) >expected &&
+		git checkout b1^ &&
+		test_when_finished "git checkout master" &&
+		(
+			GIT_PS1_SHOWCOLORHINTS=y &&
+			__git_ps1 "BEFORE:" ":AFTER" &&
+			printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success 'prompt - bash color pc mode - dirty status indicator - dirty worktree' '
-	printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_red}*${c_clear}):AFTER\\nmaster" >expected &&
-	echo "dirty" >file &&
-	test_when_finished "git reset --hard" &&
-	(
-		GIT_PS1_SHOWDIRTYSTATE=y &&
-		GIT_PS1_SHOWCOLORHINTS=y &&
-		__git_ps1 "BEFORE:" ":AFTER" &&
-		printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success 'prompt - bash color pc mode - dirty status indicator - dirty worktree' '
+		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_red}*${c_clear}):AFTER\\nmaster" >expected &&
+		echo "dirty" >file &&
+		test_when_finished "git reset --hard" &&
+		(
+			GIT_PS1_SHOWDIRTYSTATE=y &&
+			GIT_PS1_SHOWCOLORHINTS=y &&
+			__git_ps1 "BEFORE:" ":AFTER" &&
+			printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success 'prompt - bash color pc mode - dirty status indicator - dirty index' '
-	printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_green}+${c_clear}):AFTER\\nmaster" >expected &&
-	echo "dirty" >file &&
-	test_when_finished "git reset --hard" &&
-	git add -u &&
-	(
-		GIT_PS1_SHOWDIRTYSTATE=y &&
-		GIT_PS1_SHOWCOLORHINTS=y &&
-		__git_ps1 "BEFORE:" ":AFTER" &&
-		printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success 'prompt - bash color pc mode - dirty status indicator - dirty index' '
+		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_green}+${c_clear}):AFTER\\nmaster" >expected &&
+		echo "dirty" >file &&
+		test_when_finished "git reset --hard" &&
+		git add -u &&
+		(
+			GIT_PS1_SHOWDIRTYSTATE=y &&
+			GIT_PS1_SHOWCOLORHINTS=y &&
+			__git_ps1 "BEFORE:" ":AFTER" &&
+			printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success 'prompt - bash color pc mode - dirty status indicator - dirty index and worktree' '
-	printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_red}*${c_green}+${c_clear}):AFTER\\nmaster" >expected &&
-	echo "dirty index" >file &&
-	test_when_finished "git reset --hard" &&
-	git add -u &&
-	echo "dirty worktree" >file &&
-	(
-		GIT_PS1_SHOWCOLORHINTS=y &&
-		GIT_PS1_SHOWDIRTYSTATE=y &&
-		__git_ps1 "BEFORE:" ":AFTER" &&
-		printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success 'prompt - bash color pc mode - dirty status indicator - dirty index and worktree' '
+		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_red}*${c_green}+${c_clear}):AFTER\\nmaster" >expected &&
+		echo "dirty index" >file &&
+		test_when_finished "git reset --hard" &&
+		git add -u &&
+		echo "dirty worktree" >file &&
+		(
+			GIT_PS1_SHOWCOLORHINTS=y &&
+			GIT_PS1_SHOWDIRTYSTATE=y &&
+			__git_ps1 "BEFORE:" ":AFTER" &&
+			printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success 'prompt - bash color pc mode - dirty status indicator - before root commit' '
-	printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_green}#${c_clear}):AFTER\\nmaster" >expected &&
-	(
-		GIT_PS1_SHOWDIRTYSTATE=y &&
-		GIT_PS1_SHOWCOLORHINTS=y &&
-		cd otherrepo &&
-		__git_ps1 "BEFORE:" ":AFTER" &&
-		printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success 'prompt - bash color pc mode - dirty status indicator - before root commit' '
+		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_green}#${c_clear}):AFTER\\nmaster" >expected &&
+		(
+			GIT_PS1_SHOWDIRTYSTATE=y &&
+			GIT_PS1_SHOWCOLORHINTS=y &&
+			cd otherrepo &&
+			__git_ps1 "BEFORE:" ":AFTER" &&
+			printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success 'prompt - bash color pc mode - inside .git directory' '
-	printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear}):AFTER\\nGIT_DIR!" >expected &&
-	echo "dirty" >file &&
-	test_when_finished "git reset --hard" &&
-	(
-		GIT_PS1_SHOWDIRTYSTATE=y &&
-		GIT_PS1_SHOWCOLORHINTS=y &&
-		cd .git &&
-		__git_ps1 "BEFORE:" ":AFTER" &&
-		printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success 'prompt - bash color pc mode - inside .git directory' '
+		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear}):AFTER\\nGIT_DIR!" >expected &&
+		echo "dirty" >file &&
+		test_when_finished "git reset --hard" &&
+		(
+			GIT_PS1_SHOWDIRTYSTATE=y &&
+			GIT_PS1_SHOWCOLORHINTS=y &&
+			cd .git &&
+			__git_ps1 "BEFORE:" ":AFTER" &&
+			printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success 'prompt - bash color pc mode - stash status indicator' '
-	printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_lblue}\$${c_clear}):AFTER\\nmaster" >expected &&
-	echo 2 >file &&
-	git stash &&
-	test_when_finished "git stash drop" &&
-	(
-		GIT_PS1_SHOWSTASHSTATE=y &&
-		GIT_PS1_SHOWCOLORHINTS=y &&
-		__git_ps1 "BEFORE:" ":AFTER" &&
-		printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success 'prompt - bash color pc mode - stash status indicator' '
+		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_lblue}\$${c_clear}):AFTER\\nmaster" >expected &&
+		echo 2 >file &&
+		git stash &&
+		test_when_finished "git stash drop" &&
+		(
+			GIT_PS1_SHOWSTASHSTATE=y &&
+			GIT_PS1_SHOWCOLORHINTS=y &&
+			__git_ps1 "BEFORE:" ":AFTER" &&
+			printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success 'prompt - bash color pc mode - untracked files status indicator' '
-	printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_red}%%${c_clear}):AFTER\\nmaster" >expected &&
-	(
-		GIT_PS1_SHOWUNTRACKEDFILES=y &&
-		GIT_PS1_SHOWCOLORHINTS=y &&
-		__git_ps1 "BEFORE:" ":AFTER" &&
-		printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success 'prompt - bash color pc mode - untracked files status indicator' '
+		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_red}%%${c_clear}):AFTER\\nmaster" >expected &&
+		(
+			GIT_PS1_SHOWUNTRACKEDFILES=y &&
+			GIT_PS1_SHOWCOLORHINTS=y &&
+			__git_ps1 "BEFORE:" ":AFTER" &&
+			printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
+}
+
+run_pcmode_tests
 
 test_done
-- 
1.9.3
Richard Hansen· May 27, 2014, 07:40 UTC · re: Richard Hansen · lore

[PATCH 03/10] t9903: move test name prefix to a separate variable

This is a step toward reusing the same test cases after disabling PS1 parameter expansion.

Signed-off-by: Richard Hansen <rhansen@bbn.com>
---
 t/t9903-bash-prompt.sh | 20 +++++++++++---------
 1 file changed, 11 insertions(+), 9 deletions(-)
Show changes to t/t9903-bash-prompt.sh +11 −9
diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh
index c691869..d29dd2b 100755
--- a/t/t9903-bash-prompt.sh
+++ b/t/t9903-bash-prompt.sh
@@ -463,7 +463,9 @@ run_pcmode_tests () {
 		test_cmp expected "$actual"
 	'
 
-	test_expect_success 'prompt - bash color pc mode - branch name' '
+	pfx="prompt - bash color pc mode"
+
+	test_expect_success "$pfx - branch name" '
 		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear}):AFTER\\nmaster" >expected &&
 		(
 			GIT_PS1_SHOWCOLORHINTS=y &&
@@ -473,7 +475,7 @@ run_pcmode_tests () {
 		test_cmp expected "$actual"
 	'
 
-	test_expect_success 'prompt - bash color pc mode - detached head' '
+	test_expect_success "$pfx - detached head" '
 		printf "BEFORE: (${c_red}\${__git_ps1_branch_name}${c_clear}):AFTER\\n(%s...)" $(git log -1 --format="%h" b1^) >expected &&
 		git checkout b1^ &&
 		test_when_finished "git checkout master" &&
@@ -485,7 +487,7 @@ run_pcmode_tests () {
 		test_cmp expected "$actual"
 	'
 
-	test_expect_success 'prompt - bash color pc mode - dirty status indicator - dirty worktree' '
+	test_expect_success "$pfx - dirty status indicator - dirty worktree" '
 		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_red}*${c_clear}):AFTER\\nmaster" >expected &&
 		echo "dirty" >file &&
 		test_when_finished "git reset --hard" &&
@@ -498,7 +500,7 @@ run_pcmode_tests () {
 		test_cmp expected "$actual"
 	'
 
-	test_expect_success 'prompt - bash color pc mode - dirty status indicator - dirty index' '
+	test_expect_success "$pfx - dirty status indicator - dirty index" '
 		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_green}+${c_clear}):AFTER\\nmaster" >expected &&
 		echo "dirty" >file &&
 		test_when_finished "git reset --hard" &&
@@ -512,7 +514,7 @@ run_pcmode_tests () {
 		test_cmp expected "$actual"
 	'
 
-	test_expect_success 'prompt - bash color pc mode - dirty status indicator - dirty index and worktree' '
+	test_expect_success "$pfx - dirty status indicator - dirty index and worktree" '
 		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_red}*${c_green}+${c_clear}):AFTER\\nmaster" >expected &&
 		echo "dirty index" >file &&
 		test_when_finished "git reset --hard" &&
@@ -527,7 +529,7 @@ run_pcmode_tests () {
 		test_cmp expected "$actual"
 	'
 
-	test_expect_success 'prompt - bash color pc mode - dirty status indicator - before root commit' '
+	test_expect_success "$pfx - dirty status indicator - before root commit" '
 		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_green}#${c_clear}):AFTER\\nmaster" >expected &&
 		(
 			GIT_PS1_SHOWDIRTYSTATE=y &&
@@ -539,7 +541,7 @@ run_pcmode_tests () {
 		test_cmp expected "$actual"
 	'
 
-	test_expect_success 'prompt - bash color pc mode - inside .git directory' '
+	test_expect_success "$pfx - inside .git directory" '
 		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear}):AFTER\\nGIT_DIR!" >expected &&
 		echo "dirty" >file &&
 		test_when_finished "git reset --hard" &&
@@ -553,7 +555,7 @@ run_pcmode_tests () {
 		test_cmp expected "$actual"
 	'
 
-	test_expect_success 'prompt - bash color pc mode - stash status indicator' '
+	test_expect_success "$pfx - stash status indicator" '
 		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_lblue}\$${c_clear}):AFTER\\nmaster" >expected &&
 		echo 2 >file &&
 		git stash &&
@@ -567,7 +569,7 @@ run_pcmode_tests () {
 		test_cmp expected "$actual"
 	'
 
-	test_expect_success 'prompt - bash color pc mode - untracked files status indicator' '
+	test_expect_success "$pfx - untracked files status indicator" '
 		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_red}%%${c_clear}):AFTER\\nmaster" >expected &&
 		(
 			GIT_PS1_SHOWUNTRACKEDFILES=y &&
-- 
1.9.3
Richard Hansen· May 27, 2014, 07:40 UTC · re: Richard Hansen · lore

[PATCH 04/10] t9903: run pc mode tests again with PS1 expansion disabled

Bash has a shell option that makes it possible to disable parameter expansion in PS1. Test __git_ps1's ability to detect and react to disabled PS1 expansion by running the "pc mode" tests twice: once with PS1 parameter expansion enabled and once with it disabled.

Signed-off-by: Richard Hansen <rhansen@bbn.com>
---
 t/lib-bash.sh          |  3 ++
 t/t9903-bash-prompt.sh | 75 ++++++++++++++++++++++++++++++++++----------------
 2 files changed, 55 insertions(+), 23 deletions(-)
Show changes to 2 files +55 −23

t/lib-bash.sh, t/t9903-bash-prompt.sh

diff --git a/t/lib-bash.sh b/t/lib-bash.sh
index 2be955f..37a48fd 100644
--- a/t/lib-bash.sh
+++ b/t/lib-bash.sh
@@ -14,4 +14,7 @@ else
 	exit 0
 fi
 
+ps1_expansion_enable () { shopt -s promptvars; }
+ps1_expansion_disable () { shopt -u promptvars; }
+
 . ./test-lib.sh
diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh
index d29dd2b..fbd77e6 100755
--- a/t/t9903-bash-prompt.sh
+++ b/t/t9903-bash-prompt.sh
@@ -451,57 +451,81 @@ test_expect_success 'prompt - format string starting with dash' '
 	test_cmp expected "$actual"
 '
 
-run_pcmode_tests () {
-	test_expect_success 'prompt - pc mode' '
-		printf "BEFORE: (\${__git_ps1_branch_name}):AFTER\\nmaster" >expected &&
+pcmode_expected () {
+	case $ps1expansion in
+	on) printf "$1" '${__git_ps1_branch_name}' "$2";;
+	off) printf "$1" "$2" "";;
+	esac >expected
+}
+
+pcmode_actual () {
+	case $ps1expansion in
+	on) printf %s\\n%s "$PS1" "${__git_ps1_branch_name}";;
+	off) printf %s\\n "$PS1";;
+	esac >"$actual"
+}
+
+_run_pcmode_tests () {
+	ps1expansion=$1; shift
+
+	case $ps1expansion in
+	# if the shell doesn't allow ps1 expansion to be enabled,
+	# quietly skip the tests (same goes for disabling)
+	on) ps1_expansion_enable || return 0;;
+	off) ps1_expansion_disable || return 0;;
+	*) error "invalid argument to _run_pcmode_tests: $ps1expansion";;
+	esac
+
+	test_expect_success "prompt - pc mode (PS1 expansion $ps1expansion)" '
+		pcmode_expected "BEFORE: (%s):AFTER\\n%s" master &&
 		printf "" >expected_output &&
 		(
 			__git_ps1 "BEFORE:" ":AFTER" >"$actual" &&
 			test_cmp expected_output "$actual" &&
-			printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+			pcmode_actual
 		) &&
 		test_cmp expected "$actual"
 	'
 
-	pfx="prompt - bash color pc mode"
+	pfx="prompt - bash color pc mode (PS1 expansion $ps1expansion)"
 
 	test_expect_success "$pfx - branch name" '
-		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear}):AFTER\\nmaster" >expected &&
+		pcmode_expected "BEFORE: (${c_green}%s${c_clear}):AFTER\\n%s" master &&
 		(
 			GIT_PS1_SHOWCOLORHINTS=y &&
 			__git_ps1 "BEFORE:" ":AFTER" >"$actual"
-			printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+			pcmode_actual
 		) &&
 		test_cmp expected "$actual"
 	'
 
 	test_expect_success "$pfx - detached head" '
-		printf "BEFORE: (${c_red}\${__git_ps1_branch_name}${c_clear}):AFTER\\n(%s...)" $(git log -1 --format="%h" b1^) >expected &&
+		pcmode_expected "BEFORE: (${c_red}%s${c_clear}):AFTER\\n%s" "($(git log -1 --format="%h" b1^)...)" &&
 		git checkout b1^ &&
 		test_when_finished "git checkout master" &&
 		(
 			GIT_PS1_SHOWCOLORHINTS=y &&
 			__git_ps1 "BEFORE:" ":AFTER" &&
-			printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+			pcmode_actual
 		) &&
 		test_cmp expected "$actual"
 	'
 
 	test_expect_success "$pfx - dirty status indicator - dirty worktree" '
-		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_red}*${c_clear}):AFTER\\nmaster" >expected &&
+		pcmode_expected "BEFORE: (${c_green}%s${c_clear} ${c_red}*${c_clear}):AFTER\\n%s" master &&
 		echo "dirty" >file &&
 		test_when_finished "git reset --hard" &&
 		(
 			GIT_PS1_SHOWDIRTYSTATE=y &&
 			GIT_PS1_SHOWCOLORHINTS=y &&
 			__git_ps1 "BEFORE:" ":AFTER" &&
-			printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+			pcmode_actual
 		) &&
 		test_cmp expected "$actual"
 	'
 
 	test_expect_success "$pfx - dirty status indicator - dirty index" '
-		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_green}+${c_clear}):AFTER\\nmaster" >expected &&
+		pcmode_expected "BEFORE: (${c_green}%s${c_clear} ${c_green}+${c_clear}):AFTER\\n%s" master &&
 		echo "dirty" >file &&
 		test_when_finished "git reset --hard" &&
 		git add -u &&
@@ -509,13 +533,13 @@ run_pcmode_tests () {
 			GIT_PS1_SHOWDIRTYSTATE=y &&
 			GIT_PS1_SHOWCOLORHINTS=y &&
 			__git_ps1 "BEFORE:" ":AFTER" &&
-			printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+			pcmode_actual
 		) &&
 		test_cmp expected "$actual"
 	'
 
 	test_expect_success "$pfx - dirty status indicator - dirty index and worktree" '
-		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_red}*${c_green}+${c_clear}):AFTER\\nmaster" >expected &&
+		pcmode_expected "BEFORE: (${c_green}%s${c_clear} ${c_red}*${c_green}+${c_clear}):AFTER\\n%s" master &&
 		echo "dirty index" >file &&
 		test_when_finished "git reset --hard" &&
 		git add -u &&
@@ -524,25 +548,25 @@ run_pcmode_tests () {
 			GIT_PS1_SHOWCOLORHINTS=y &&
 			GIT_PS1_SHOWDIRTYSTATE=y &&
 			__git_ps1 "BEFORE:" ":AFTER" &&
-			printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+			pcmode_actual
 		) &&
 		test_cmp expected "$actual"
 	'
 
 	test_expect_success "$pfx - dirty status indicator - before root commit" '
-		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_green}#${c_clear}):AFTER\\nmaster" >expected &&
+		pcmode_expected "BEFORE: (${c_green}%s${c_clear} ${c_green}#${c_clear}):AFTER\\n%s" master &&
 		(
 			GIT_PS1_SHOWDIRTYSTATE=y &&
 			GIT_PS1_SHOWCOLORHINTS=y &&
 			cd otherrepo &&
 			__git_ps1 "BEFORE:" ":AFTER" &&
-			printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+			pcmode_actual
 		) &&
 		test_cmp expected "$actual"
 	'
 
 	test_expect_success "$pfx - inside .git directory" '
-		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear}):AFTER\\nGIT_DIR!" >expected &&
+		pcmode_expected "BEFORE: (${c_green}%s${c_clear}):AFTER\\n%s" "GIT_DIR!" &&
 		echo "dirty" >file &&
 		test_when_finished "git reset --hard" &&
 		(
@@ -550,13 +574,13 @@ run_pcmode_tests () {
 			GIT_PS1_SHOWCOLORHINTS=y &&
 			cd .git &&
 			__git_ps1 "BEFORE:" ":AFTER" &&
-			printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+			pcmode_actual
 		) &&
 		test_cmp expected "$actual"
 	'
 
 	test_expect_success "$pfx - stash status indicator" '
-		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_lblue}\$${c_clear}):AFTER\\nmaster" >expected &&
+		pcmode_expected "BEFORE: (${c_green}%s${c_clear} ${c_lblue}\$${c_clear}):AFTER\\n%s" master &&
 		echo 2 >file &&
 		git stash &&
 		test_when_finished "git stash drop" &&
@@ -564,23 +588,28 @@ run_pcmode_tests () {
 			GIT_PS1_SHOWSTASHSTATE=y &&
 			GIT_PS1_SHOWCOLORHINTS=y &&
 			__git_ps1 "BEFORE:" ":AFTER" &&
-			printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+			pcmode_actual
 		) &&
 		test_cmp expected "$actual"
 	'
 
 	test_expect_success "$pfx - untracked files status indicator" '
-		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_red}%%${c_clear}):AFTER\\nmaster" >expected &&
+		pcmode_expected "BEFORE: (${c_green}%s${c_clear} ${c_red}%%${c_clear}):AFTER\\n%s" master &&
 		(
 			GIT_PS1_SHOWUNTRACKEDFILES=y &&
 			GIT_PS1_SHOWCOLORHINTS=y &&
 			__git_ps1 "BEFORE:" ":AFTER" &&
-			printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+			pcmode_actual
 		) &&
 		test_cmp expected "$actual"
 	'
 }
 
+run_pcmode_tests () {
+	_run_pcmode_tests on
+	_run_pcmode_tests off
+}
+
 run_pcmode_tests
 
 test_done
-- 
1.9.3
Richard Hansen· May 27, 2014, 07:40 UTC · re: Richard Hansen · lore

[PATCH 05/10] t9903: include "Bash" in test names via new $shellname var

Define a new 'shellname' variable in lib-bash.sh and use it in the prompt test names. This is a step toward moving the shell prompt tests to a separate library file so that they can be reused to test prompting in Zsh.

Signed-off-by: Richard Hansen <rhansen@bbn.com>
---
 t/lib-bash.sh          |  2 ++
 t/t9903-bash-prompt.sh | 86 ++++++++++++++++++++++++++------------------------
 2 files changed, 46 insertions(+), 42 deletions(-)
Show changes to 2 files +46 −42

t/lib-bash.sh, t/t9903-bash-prompt.sh

diff --git a/t/lib-bash.sh b/t/lib-bash.sh
index 37a48fd..a0f4e16 100644
--- a/t/lib-bash.sh
+++ b/t/lib-bash.sh
@@ -14,6 +14,8 @@ else
 	exit 0
 fi
 
+shellname=Bash
+
 ps1_expansion_enable () { shopt -s promptvars; }
 ps1_expansion_disable () { shopt -u promptvars; }
 
diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh
index fbd77e6..05ff246 100755
--- a/t/t9903-bash-prompt.sh
+++ b/t/t9903-bash-prompt.sh
@@ -15,7 +15,7 @@ c_green='\\[\\e[32m\\]'
 c_lblue='\\[\\e[1;34m\\]'
 c_clear='\\[\\e[0m\\]'
 
-test_expect_success 'setup for prompt tests' '
+test_expect_success "setup for $shellname prompt tests" '
 	git init otherrepo &&
 	echo 1 >file &&
 	git add file &&
@@ -38,13 +38,15 @@ test_expect_success 'setup for prompt tests' '
 	git checkout master
 '
 
-test_expect_success 'prompt - branch name' '
+pfx="$shellname prompt"
+
+test_expect_success "$pfx - branch name" '
 	printf " (master)" >expected &&
 	__git_ps1 >"$actual" &&
 	test_cmp expected "$actual"
 '
 
-test_expect_success SYMLINKS 'prompt - branch name - symlink symref' '
+test_expect_success SYMLINKS "$pfx - branch name - symlink symref" '
 	printf " (master)" >expected &&
 	test_when_finished "git checkout master" &&
 	test_config core.preferSymlinkRefs true &&
@@ -53,7 +55,7 @@ test_expect_success SYMLINKS 'prompt - branch name - symlink symref' '
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - unborn branch' '
+test_expect_success "$pfx - unborn branch" '
 	printf " (unborn)" >expected &&
 	git checkout --orphan unborn &&
 	test_when_finished "git checkout master" &&
@@ -72,7 +74,7 @@ else
 	say 'Your filesystem does not allow newlines in filenames.'
 fi
 
-test_expect_success FUNNYNAMES 'prompt - with newline in path' '
+test_expect_success FUNNYNAMES "$pfx - with newline in path" '
 	printf " (master)" >expected &&
 	git init "$repo_with_newline" &&
 	test_when_finished "rm -rf \"$repo_with_newline\"" &&
@@ -84,7 +86,7 @@ test_expect_success FUNNYNAMES 'prompt - with newline in path' '
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - detached head' '
+test_expect_success "$pfx - detached head" '
 	printf " ((%s...))" $(git log -1 --format="%h" --abbrev=13 b1^) >expected &&
 	test_config core.abbrev 13 &&
 	git checkout b1^ &&
@@ -93,7 +95,7 @@ test_expect_success 'prompt - detached head' '
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - describe detached head - contains' '
+test_expect_success "$pfx - describe detached head - contains" '
 	printf " ((t2~1))" >expected &&
 	git checkout b1^ &&
 	test_when_finished "git checkout master" &&
@@ -104,7 +106,7 @@ test_expect_success 'prompt - describe detached head - contains' '
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - describe detached head - branch' '
+test_expect_success "$pfx - describe detached head - branch" '
 	printf " ((b1~1))" >expected &&
 	git checkout b1^ &&
 	test_when_finished "git checkout master" &&
@@ -115,7 +117,7 @@ test_expect_success 'prompt - describe detached head - branch' '
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - describe detached head - describe' '
+test_expect_success "$pfx - describe detached head - describe" '
 	printf " ((t1-1-g%s))" $(git log -1 --format="%h" b1^) >expected &&
 	git checkout b1^ &&
 	test_when_finished "git checkout master" &&
@@ -126,7 +128,7 @@ test_expect_success 'prompt - describe detached head - describe' '
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - describe detached head - default' '
+test_expect_success "$pfx - describe detached head - default" '
 	printf " ((t2))" >expected &&
 	git checkout --detach b1 &&
 	test_when_finished "git checkout master" &&
@@ -134,7 +136,7 @@ test_expect_success 'prompt - describe detached head - default' '
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - inside .git directory' '
+test_expect_success "$pfx - inside .git directory" '
 	printf " (GIT_DIR!)" >expected &&
 	(
 		cd .git &&
@@ -143,7 +145,7 @@ test_expect_success 'prompt - inside .git directory' '
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - deep inside .git directory' '
+test_expect_success "$pfx - deep inside .git directory" '
 	printf " (GIT_DIR!)" >expected &&
 	(
 		cd .git/refs/heads &&
@@ -152,7 +154,7 @@ test_expect_success 'prompt - deep inside .git directory' '
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - inside bare repository' '
+test_expect_success "$pfx - inside bare repository" '
 	printf " (BARE:master)" >expected &&
 	git init --bare bare.git &&
 	test_when_finished "rm -rf bare.git" &&
@@ -163,7 +165,7 @@ test_expect_success 'prompt - inside bare repository' '
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - interactive rebase' '
+test_expect_success "$pfx - interactive rebase" '
 	printf " (b1|REBASE-i 2/3)" >expected
 	write_script fake_editor.sh <<-\EOF &&
 		echo "exec echo" >"$1"
@@ -180,7 +182,7 @@ test_expect_success 'prompt - interactive rebase' '
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - rebase merge' '
+test_expect_success "$pfx - rebase merge" '
 	printf " (b2|REBASE-m 1/3)" >expected &&
 	git checkout b2 &&
 	test_when_finished "git checkout master" &&
@@ -190,7 +192,7 @@ test_expect_success 'prompt - rebase merge' '
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - rebase' '
+test_expect_success "$pfx - rebase" '
 	printf " (b2|REBASE 1/3)" >expected &&
 	git checkout b2 &&
 	test_when_finished "git checkout master" &&
@@ -200,7 +202,7 @@ test_expect_success 'prompt - rebase' '
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - merge' '
+test_expect_success "$pfx - merge" '
 	printf " (b1|MERGING)" >expected &&
 	git checkout b1 &&
 	test_when_finished "git checkout master" &&
@@ -210,7 +212,7 @@ test_expect_success 'prompt - merge' '
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - cherry-pick' '
+test_expect_success "$pfx - cherry-pick" '
 	printf " (master|CHERRY-PICKING)" >expected &&
 	test_must_fail git cherry-pick b1 &&
 	test_when_finished "git reset --hard" &&
@@ -218,7 +220,7 @@ test_expect_success 'prompt - cherry-pick' '
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - bisect' '
+test_expect_success "$pfx - bisect" '
 	printf " (master|BISECTING)" >expected &&
 	git bisect start &&
 	test_when_finished "git bisect reset" &&
@@ -226,7 +228,7 @@ test_expect_success 'prompt - bisect' '
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - dirty status indicator - clean' '
+test_expect_success "$pfx - dirty status indicator - clean" '
 	printf " (master)" >expected &&
 	(
 		GIT_PS1_SHOWDIRTYSTATE=y &&
@@ -235,7 +237,7 @@ test_expect_success 'prompt - dirty status indicator - clean' '
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - dirty status indicator - dirty worktree' '
+test_expect_success "$pfx - dirty status indicator - dirty worktree" '
 	printf " (master *)" >expected &&
 	echo "dirty" >file &&
 	test_when_finished "git reset --hard" &&
@@ -246,7 +248,7 @@ test_expect_success 'prompt - dirty status indicator - dirty worktree' '
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - dirty status indicator - dirty index' '
+test_expect_success "$pfx - dirty status indicator - dirty index" '
 	printf " (master +)" >expected &&
 	echo "dirty" >file &&
 	test_when_finished "git reset --hard" &&
@@ -258,7 +260,7 @@ test_expect_success 'prompt - dirty status indicator - dirty index' '
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - dirty status indicator - dirty index and worktree' '
+test_expect_success "$pfx - dirty status indicator - dirty index and worktree" '
 	printf " (master *+)" >expected &&
 	echo "dirty index" >file &&
 	test_when_finished "git reset --hard" &&
@@ -271,7 +273,7 @@ test_expect_success 'prompt - dirty status indicator - dirty index and worktree'
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - dirty status indicator - before root commit' '
+test_expect_success "$pfx - dirty status indicator - before root commit" '
 	printf " (master #)" >expected &&
 	(
 		GIT_PS1_SHOWDIRTYSTATE=y &&
@@ -281,7 +283,7 @@ test_expect_success 'prompt - dirty status indicator - before root commit' '
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - dirty status indicator - shell variable unset with config disabled' '
+test_expect_success "$pfx - dirty status indicator - shell variable unset with config disabled" '
 	printf " (master)" >expected &&
 	echo "dirty" >file &&
 	test_when_finished "git reset --hard" &&
@@ -293,7 +295,7 @@ test_expect_success 'prompt - dirty status indicator - shell variable unset with
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - dirty status indicator - shell variable unset with config enabled' '
+test_expect_success "$pfx - dirty status indicator - shell variable unset with config enabled" '
 	printf " (master)" >expected &&
 	echo "dirty" >file &&
 	test_when_finished "git reset --hard" &&
@@ -305,7 +307,7 @@ test_expect_success 'prompt - dirty status indicator - shell variable unset with
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - dirty status indicator - shell variable set with config disabled' '
+test_expect_success "$pfx - dirty status indicator - shell variable set with config disabled" '
 	printf " (master)" >expected &&
 	echo "dirty" >file &&
 	test_when_finished "git reset --hard" &&
@@ -317,7 +319,7 @@ test_expect_success 'prompt - dirty status indicator - shell variable set with c
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - dirty status indicator - shell variable set with config enabled' '
+test_expect_success "$pfx - dirty status indicator - shell variable set with config enabled" '
 	printf " (master *)" >expected &&
 	echo "dirty" >file &&
 	test_when_finished "git reset --hard" &&
@@ -329,7 +331,7 @@ test_expect_success 'prompt - dirty status indicator - shell variable set with c
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - dirty status indicator - not shown inside .git directory' '
+test_expect_success "$pfx - dirty status indicator - not shown inside .git directory" '
 	printf " (GIT_DIR!)" >expected &&
 	echo "dirty" >file &&
 	test_when_finished "git reset --hard" &&
@@ -341,7 +343,7 @@ test_expect_success 'prompt - dirty status indicator - not shown inside .git dir
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - stash status indicator - no stash' '
+test_expect_success "$pfx - stash status indicator - no stash" '
 	printf " (master)" >expected &&
 	(
 		GIT_PS1_SHOWSTASHSTATE=y &&
@@ -350,7 +352,7 @@ test_expect_success 'prompt - stash status indicator - no stash' '
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - stash status indicator - stash' '
+test_expect_success "$pfx - stash status indicator - stash" '
 	printf " (master $)" >expected &&
 	echo 2 >file &&
 	git stash &&
@@ -363,7 +365,7 @@ test_expect_success 'prompt - stash status indicator - stash' '
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - stash status indicator - not shown inside .git directory' '
+test_expect_success "$pfx - stash status indicator - not shown inside .git directory" '
 	printf " (GIT_DIR!)" >expected &&
 	echo 2 >file &&
 	git stash &&
@@ -376,7 +378,7 @@ test_expect_success 'prompt - stash status indicator - not shown inside .git dir
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - untracked files status indicator - no untracked files' '
+test_expect_success "$pfx - untracked files status indicator - no untracked files" '
 	printf " (master)" >expected &&
 	(
 		GIT_PS1_SHOWUNTRACKEDFILES=y &&
@@ -386,7 +388,7 @@ test_expect_success 'prompt - untracked files status indicator - no untracked fi
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - untracked files status indicator - untracked files' '
+test_expect_success "$pfx - untracked files status indicator - untracked files" '
 	printf " (master %%)" >expected &&
 	(
 		GIT_PS1_SHOWUNTRACKEDFILES=y &&
@@ -395,7 +397,7 @@ test_expect_success 'prompt - untracked files status indicator - untracked files
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - untracked files status indicator - shell variable unset with config disabled' '
+test_expect_success "$pfx - untracked files status indicator - shell variable unset with config disabled" '
 	printf " (master)" >expected &&
 	test_config bash.showUntrackedFiles false &&
 	(
@@ -405,7 +407,7 @@ test_expect_success 'prompt - untracked files status indicator - shell variable
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - untracked files status indicator - shell variable unset with config enabled' '
+test_expect_success "$pfx - untracked files status indicator - shell variable unset with config enabled" '
 	printf " (master)" >expected &&
 	test_config bash.showUntrackedFiles true &&
 	(
@@ -415,7 +417,7 @@ test_expect_success 'prompt - untracked files status indicator - shell variable
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - untracked files status indicator - shell variable set with config disabled' '
+test_expect_success "$pfx - untracked files status indicator - shell variable set with config disabled" '
 	printf " (master)" >expected &&
 	test_config bash.showUntrackedFiles false &&
 	(
@@ -425,7 +427,7 @@ test_expect_success 'prompt - untracked files status indicator - shell variable
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - untracked files status indicator - shell variable set with config enabled' '
+test_expect_success "$pfx - untracked files status indicator - shell variable set with config enabled" '
 	printf " (master %%)" >expected &&
 	test_config bash.showUntrackedFiles true &&
 	(
@@ -435,7 +437,7 @@ test_expect_success 'prompt - untracked files status indicator - shell variable
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - untracked files status indicator - not shown inside .git directory' '
+test_expect_success "$pfx - untracked files status indicator - not shown inside .git directory" '
 	printf " (GIT_DIR!)" >expected &&
 	(
 		GIT_PS1_SHOWUNTRACKEDFILES=y &&
@@ -445,7 +447,7 @@ test_expect_success 'prompt - untracked files status indicator - not shown insid
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - format string starting with dash' '
+test_expect_success "$pfx - format string starting with dash" '
 	printf -- "-master" >expected &&
 	__git_ps1 "-%s" >"$actual" &&
 	test_cmp expected "$actual"
@@ -476,7 +478,7 @@ _run_pcmode_tests () {
 	*) error "invalid argument to _run_pcmode_tests: $ps1expansion";;
 	esac
 
-	test_expect_success "prompt - pc mode (PS1 expansion $ps1expansion)" '
+	test_expect_success "$shellname prompt - pc mode (PS1 expansion $ps1expansion)" '
 		pcmode_expected "BEFORE: (%s):AFTER\\n%s" master &&
 		printf "" >expected_output &&
 		(
@@ -487,7 +489,7 @@ _run_pcmode_tests () {
 		test_cmp expected "$actual"
 	'
 
-	pfx="prompt - bash color pc mode (PS1 expansion $ps1expansion)"
+	pfx="$shellname prompt - color pc mode (PS1 expansion $ps1expansion)"
 
 	test_expect_success "$pfx - branch name" '
 		pcmode_expected "BEFORE: (${c_green}%s${c_clear}):AFTER\\n%s" master &&
-- 
1.9.3
Richard Hansen· May 27, 2014, 07:40 UTC · re: Richard Hansen · lore

[PATCH 06/10] t9903: move PS1 color code variable definitions to lib-bash.sh

Define a new 'set_ps1_format_vars' function in lib-bash.sh that sets the c_red, c_green, c_lblue, and c_clear variables. Call this function from run_pcmode_tests(). This is a step toward moving the shell prompt tests to a separate library file so that they can be reused to test prompting in Zsh.

Signed-off-by: Richard Hansen <rhansen@bbn.com>
---
 t/lib-bash.sh          | 6 ++++++
 t/t9903-bash-prompt.sh | 5 +----
 2 files changed, 7 insertions(+), 4 deletions(-)
Show changes to 2 files +7 −4

t/lib-bash.sh, t/t9903-bash-prompt.sh

diff --git a/t/lib-bash.sh b/t/lib-bash.sh
index a0f4e16..9d428bd 100644
--- a/t/lib-bash.sh
+++ b/t/lib-bash.sh
@@ -18,5 +18,11 @@ shellname=Bash
 
 ps1_expansion_enable () { shopt -s promptvars; }
 ps1_expansion_disable () { shopt -u promptvars; }
+set_ps1_format_vars () {
+	c_red='\\[\\e[31m\\]'
+	c_green='\\[\\e[32m\\]'
+	c_lblue='\\[\\e[1;34m\\]'
+	c_clear='\\[\\e[0m\\]'
+}
 
 . ./test-lib.sh
diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh
index 05ff246..ef10e34 100755
--- a/t/t9903-bash-prompt.sh
+++ b/t/t9903-bash-prompt.sh
@@ -10,10 +10,7 @@ test_description='test git-specific bash prompt functions'
 . "$GIT_BUILD_DIR/contrib/completion/git-prompt.sh"
 
 actual="$TRASH_DIRECTORY/actual"
-c_red='\\[\\e[31m\\]'
-c_green='\\[\\e[32m\\]'
-c_lblue='\\[\\e[1;34m\\]'
-c_clear='\\[\\e[0m\\]'
+set_ps1_format_vars
 
 test_expect_success "setup for $shellname prompt tests" '
 	git init otherrepo &&
-- 
1.9.3
Richard Hansen· May 27, 2014, 07:40 UTC · re: Richard Hansen · lore

[PATCH 07/10] t9903: move prompt tests to a new lib-prompt-tests.sh file

This is a step toward creating a new test script that runs the same prompt tests as t9903 but with Zsh instead of Bash.

Signed-off-by: Richard Hansen <rhansen@bbn.com>
---
 t/lib-prompt-tests.sh  | 632 +++++++++++++++++++++++++++++++++++++++++++++++++
 t/t9903-bash-prompt.sh | 605 +---------------------------------------------
 2 files changed, 633 insertions(+), 604 deletions(-)
 create mode 100644 t/lib-prompt-tests.sh
Show changes to 2 files +633 −604

t/lib-prompt-tests.sh, t/t9903-bash-prompt.sh

diff --git a/t/lib-prompt-tests.sh b/t/lib-prompt-tests.sh
new file mode 100644
index 0000000..32c3c08
--- /dev/null
+++ b/t/lib-prompt-tests.sh
@@ -0,0 +1,632 @@
+# Copyright (c) 2012 SZEDER Gábor
+
+# To use this library:
+#   1. set the variable shellname to the name of the shell (e.g.,
+#      "Bash")
+#   2. define functions named ps1_expansion_enable and
+#      ps1_expansion_disable that, upon return, guarantee that the
+#      shell will and will not (respectively) perform parameter
+#      expansion on PS1, if supported by the shell.  If it is not
+#      possible to configure the shell to disable (enable) PS1
+#      expansion, ps1_expansion_enable should simply return 0
+#      (non-zero) and ps1_expansion_disable should simply return
+#      non-zero (0)
+#   3. define a function named set_ps1_format_vars that sets the
+#      variables c_red, c_green, c_lblue, and c_clear to the strings
+#      that __git_ps1 uses to add color to the prompt.  The values of
+#      these variables are used in the first argument to the printf
+#      command, so they must be escaped appropriately.
+#   4. source this library
+
+# sanity checks
+[ -n "$shellname" ] || error "shellname must be set to the name of the shell"
+for i in ps1_expansion_enable ps1_expansion_disable set_ps1_format_vars
+do
+	command -v "$i" >/dev/null 2>&1 || error "function $i not defined"
+done
+(ps1_expansion_enable || ps1_expansion_disable) \
+	|| error "either ps1_expansion_enable or ps1_expansion_disable must return true"
+
+. "$GIT_BUILD_DIR/contrib/completion/git-prompt.sh"
+
+actual="$TRASH_DIRECTORY/actual"
+set_ps1_format_vars
+
+test_expect_success "setup for $shellname prompt tests" '
+	git init otherrepo &&
+	echo 1 >file &&
+	git add file &&
+	test_tick &&
+	git commit -m initial &&
+	git tag -a -m msg1 t1 &&
+	git checkout -b b1 &&
+	echo 2 >file &&
+	git commit -m "second b1" file &&
+	echo 3 >file &&
+	git commit -m "third b1" file &&
+	git tag -a -m msg2 t2 &&
+	git checkout -b b2 master &&
+	echo 0 >file &&
+	git commit -m "second b2" file &&
+	echo 00 >file &&
+	git commit -m "another b2" file &&
+	echo 000 >file &&
+	git commit -m "yet another b2" file &&
+	git checkout master
+'
+
+pfx="$shellname prompt"
+
+test_expect_success "$pfx - branch name" '
+	printf " (master)" >expected &&
+	__git_ps1 >"$actual" &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success SYMLINKS "$pfx - branch name - symlink symref" '
+	printf " (master)" >expected &&
+	test_when_finished "git checkout master" &&
+	test_config core.preferSymlinkRefs true &&
+	git checkout master &&
+	__git_ps1 >"$actual" &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - unborn branch" '
+	printf " (unborn)" >expected &&
+	git checkout --orphan unborn &&
+	test_when_finished "git checkout master" &&
+	__git_ps1 >"$actual" &&
+	test_cmp expected "$actual"
+'
+
+repo_with_newline='repo
+with
+newline'
+
+if mkdir "$repo_with_newline" 2>/dev/null
+then
+	test_set_prereq FUNNYNAMES
+else
+	say 'Your filesystem does not allow newlines in filenames.'
+fi
+
+test_expect_success FUNNYNAMES "$pfx - with newline in path" '
+	printf " (master)" >expected &&
+	git init "$repo_with_newline" &&
+	test_when_finished "rm -rf \"$repo_with_newline\"" &&
+	mkdir "$repo_with_newline"/subdir &&
+	(
+		cd "$repo_with_newline/subdir" &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - detached head" '
+	printf " ((%s...))" $(git log -1 --format="%h" --abbrev=13 b1^) >expected &&
+	test_config core.abbrev 13 &&
+	git checkout b1^ &&
+	test_when_finished "git checkout master" &&
+	__git_ps1 >"$actual" &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - describe detached head - contains" '
+	printf " ((t2~1))" >expected &&
+	git checkout b1^ &&
+	test_when_finished "git checkout master" &&
+	(
+		GIT_PS1_DESCRIBE_STYLE=contains &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - describe detached head - branch" '
+	printf " ((b1~1))" >expected &&
+	git checkout b1^ &&
+	test_when_finished "git checkout master" &&
+	(
+		GIT_PS1_DESCRIBE_STYLE=branch &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - describe detached head - describe" '
+	printf " ((t1-1-g%s))" $(git log -1 --format="%h" b1^) >expected &&
+	git checkout b1^ &&
+	test_when_finished "git checkout master" &&
+	(
+		GIT_PS1_DESCRIBE_STYLE=describe &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - describe detached head - default" '
+	printf " ((t2))" >expected &&
+	git checkout --detach b1 &&
+	test_when_finished "git checkout master" &&
+	__git_ps1 >"$actual" &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - inside .git directory" '
+	printf " (GIT_DIR!)" >expected &&
+	(
+		cd .git &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - deep inside .git directory" '
+	printf " (GIT_DIR!)" >expected &&
+	(
+		cd .git/refs/heads &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - inside bare repository" '
+	printf " (BARE:master)" >expected &&
+	git init --bare bare.git &&
+	test_when_finished "rm -rf bare.git" &&
+	(
+		cd bare.git &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - interactive rebase" '
+	printf " (b1|REBASE-i 2/3)" >expected
+	write_script fake_editor.sh <<-\EOF &&
+		echo "exec echo" >"$1"
+		echo "edit $(git log -1 --format="%h")" >>"$1"
+		echo "exec echo" >>"$1"
+	EOF
+	test_when_finished "rm -f fake_editor.sh" &&
+	test_set_editor "$TRASH_DIRECTORY/fake_editor.sh" &&
+	git checkout b1 &&
+	test_when_finished "git checkout master" &&
+	git rebase -i HEAD^ &&
+	test_when_finished "git rebase --abort"
+	__git_ps1 >"$actual" &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - rebase merge" '
+	printf " (b2|REBASE-m 1/3)" >expected &&
+	git checkout b2 &&
+	test_when_finished "git checkout master" &&
+	test_must_fail git rebase --merge b1 b2 &&
+	test_when_finished "git rebase --abort" &&
+	__git_ps1 >"$actual" &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - rebase" '
+	printf " (b2|REBASE 1/3)" >expected &&
+	git checkout b2 &&
+	test_when_finished "git checkout master" &&
+	test_must_fail git rebase b1 b2 &&
+	test_when_finished "git rebase --abort" &&
+	__git_ps1 >"$actual" &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - merge" '
+	printf " (b1|MERGING)" >expected &&
+	git checkout b1 &&
+	test_when_finished "git checkout master" &&
+	test_must_fail git merge b2 &&
+	test_when_finished "git reset --hard" &&
+	__git_ps1 >"$actual" &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - cherry-pick" '
+	printf " (master|CHERRY-PICKING)" >expected &&
+	test_must_fail git cherry-pick b1 &&
+	test_when_finished "git reset --hard" &&
+	__git_ps1 >"$actual" &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - bisect" '
+	printf " (master|BISECTING)" >expected &&
+	git bisect start &&
+	test_when_finished "git bisect reset" &&
+	__git_ps1 >"$actual" &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - dirty status indicator - clean" '
+	printf " (master)" >expected &&
+	(
+		GIT_PS1_SHOWDIRTYSTATE=y &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - dirty status indicator - dirty worktree" '
+	printf " (master *)" >expected &&
+	echo "dirty" >file &&
+	test_when_finished "git reset --hard" &&
+	(
+		GIT_PS1_SHOWDIRTYSTATE=y &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - dirty status indicator - dirty index" '
+	printf " (master +)" >expected &&
+	echo "dirty" >file &&
+	test_when_finished "git reset --hard" &&
+	git add -u &&
+	(
+		GIT_PS1_SHOWDIRTYSTATE=y &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - dirty status indicator - dirty index and worktree" '
+	printf " (master *+)" >expected &&
+	echo "dirty index" >file &&
+	test_when_finished "git reset --hard" &&
+	git add -u &&
+	echo "dirty worktree" >file &&
+	(
+		GIT_PS1_SHOWDIRTYSTATE=y &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - dirty status indicator - before root commit" '
+	printf " (master #)" >expected &&
+	(
+		GIT_PS1_SHOWDIRTYSTATE=y &&
+		cd otherrepo &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - dirty status indicator - shell variable unset with config disabled" '
+	printf " (master)" >expected &&
+	echo "dirty" >file &&
+	test_when_finished "git reset --hard" &&
+	test_config bash.showDirtyState false &&
+	(
+		sane_unset GIT_PS1_SHOWDIRTYSTATE &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - dirty status indicator - shell variable unset with config enabled" '
+	printf " (master)" >expected &&
+	echo "dirty" >file &&
+	test_when_finished "git reset --hard" &&
+	test_config bash.showDirtyState true &&
+	(
+		sane_unset GIT_PS1_SHOWDIRTYSTATE &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - dirty status indicator - shell variable set with config disabled" '
+	printf " (master)" >expected &&
+	echo "dirty" >file &&
+	test_when_finished "git reset --hard" &&
+	test_config bash.showDirtyState false &&
+	(
+		GIT_PS1_SHOWDIRTYSTATE=y &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - dirty status indicator - shell variable set with config enabled" '
+	printf " (master *)" >expected &&
+	echo "dirty" >file &&
+	test_when_finished "git reset --hard" &&
+	test_config bash.showDirtyState true &&
+	(
+		GIT_PS1_SHOWDIRTYSTATE=y &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - dirty status indicator - not shown inside .git directory" '
+	printf " (GIT_DIR!)" >expected &&
+	echo "dirty" >file &&
+	test_when_finished "git reset --hard" &&
+	(
+		GIT_PS1_SHOWDIRTYSTATE=y &&
+		cd .git &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - stash status indicator - no stash" '
+	printf " (master)" >expected &&
+	(
+		GIT_PS1_SHOWSTASHSTATE=y &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - stash status indicator - stash" '
+	printf " (master $)" >expected &&
+	echo 2 >file &&
+	git stash &&
+	test_when_finished "git stash drop" &&
+	git pack-refs --all &&
+	(
+		GIT_PS1_SHOWSTASHSTATE=y &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - stash status indicator - not shown inside .git directory" '
+	printf " (GIT_DIR!)" >expected &&
+	echo 2 >file &&
+	git stash &&
+	test_when_finished "git stash drop" &&
+	(
+		GIT_PS1_SHOWSTASHSTATE=y &&
+		cd .git &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - untracked files status indicator - no untracked files" '
+	printf " (master)" >expected &&
+	(
+		GIT_PS1_SHOWUNTRACKEDFILES=y &&
+		cd otherrepo &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - untracked files status indicator - untracked files" '
+	printf " (master %%)" >expected &&
+	(
+		GIT_PS1_SHOWUNTRACKEDFILES=y &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - untracked files status indicator - shell variable unset with config disabled" '
+	printf " (master)" >expected &&
+	test_config bash.showUntrackedFiles false &&
+	(
+		sane_unset GIT_PS1_SHOWUNTRACKEDFILES &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - untracked files status indicator - shell variable unset with config enabled" '
+	printf " (master)" >expected &&
+	test_config bash.showUntrackedFiles true &&
+	(
+		sane_unset GIT_PS1_SHOWUNTRACKEDFILES &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - untracked files status indicator - shell variable set with config disabled" '
+	printf " (master)" >expected &&
+	test_config bash.showUntrackedFiles false &&
+	(
+		GIT_PS1_SHOWUNTRACKEDFILES=y &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - untracked files status indicator - shell variable set with config enabled" '
+	printf " (master %%)" >expected &&
+	test_config bash.showUntrackedFiles true &&
+	(
+		GIT_PS1_SHOWUNTRACKEDFILES=y &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - untracked files status indicator - not shown inside .git directory" '
+	printf " (GIT_DIR!)" >expected &&
+	(
+		GIT_PS1_SHOWUNTRACKEDFILES=y &&
+		cd .git &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - format string starting with dash" '
+	printf -- "-master" >expected &&
+	__git_ps1 "-%s" >"$actual" &&
+	test_cmp expected "$actual"
+'
+
+pcmode_expected () {
+	case $ps1expansion in
+	on) printf "$1" '${__git_ps1_branch_name}' "$2";;
+	off) printf "$1" "$2" "";;
+	esac >expected
+}
+
+pcmode_actual () {
+	case $ps1expansion in
+	on) printf %s\\n%s "$PS1" "${__git_ps1_branch_name}";;
+	off) printf %s\\n "$PS1";;
+	esac >"$actual"
+}
+
+_run_pcmode_tests () {
+	ps1expansion=$1; shift
+
+	case $ps1expansion in
+	# if the shell doesn't allow ps1 expansion to be enabled,
+	# quietly skip the tests (same goes for disabling)
+	on) ps1_expansion_enable || return 0;;
+	off) ps1_expansion_disable || return 0;;
+	*) error "invalid argument to _run_pcmode_tests: $ps1expansion";;
+	esac
+
+	test_expect_success "$shellname prompt - pc mode (PS1 expansion $ps1expansion)" '
+		pcmode_expected "BEFORE: (%s):AFTER\\n%s" master &&
+		printf "" >expected_output &&
+		(
+			__git_ps1 "BEFORE:" ":AFTER" >"$actual" &&
+			test_cmp expected_output "$actual" &&
+			pcmode_actual
+		) &&
+		test_cmp expected "$actual"
+	'
+
+	pfx="$shellname prompt - color pc mode (PS1 expansion $ps1expansion)"
+
+	test_expect_success "$pfx - branch name" '
+		pcmode_expected "BEFORE: (${c_green}%s${c_clear}):AFTER\\n%s" master &&
+		(
+			GIT_PS1_SHOWCOLORHINTS=y &&
+			__git_ps1 "BEFORE:" ":AFTER" >"$actual"
+			pcmode_actual
+		) &&
+		test_cmp expected "$actual"
+	'
+
+	test_expect_success "$pfx - detached head" '
+		pcmode_expected "BEFORE: (${c_red}%s${c_clear}):AFTER\\n%s" "($(git log -1 --format="%h" b1^)...)" &&
+		git checkout b1^ &&
+		test_when_finished "git checkout master" &&
+		(
+			GIT_PS1_SHOWCOLORHINTS=y &&
+			__git_ps1 "BEFORE:" ":AFTER" &&
+			pcmode_actual
+		) &&
+		test_cmp expected "$actual"
+	'
+
+	test_expect_success "$pfx - dirty status indicator - dirty worktree" '
+		pcmode_expected "BEFORE: (${c_green}%s${c_clear} ${c_red}*${c_clear}):AFTER\\n%s" master &&
+		echo "dirty" >file &&
+		test_when_finished "git reset --hard" &&
+		(
+			GIT_PS1_SHOWDIRTYSTATE=y &&
+			GIT_PS1_SHOWCOLORHINTS=y &&
+			__git_ps1 "BEFORE:" ":AFTER" &&
+			pcmode_actual
+		) &&
+		test_cmp expected "$actual"
+	'
+
+	test_expect_success "$pfx - dirty status indicator - dirty index" '
+		pcmode_expected "BEFORE: (${c_green}%s${c_clear} ${c_green}+${c_clear}):AFTER\\n%s" master &&
+		echo "dirty" >file &&
+		test_when_finished "git reset --hard" &&
+		git add -u &&
+		(
+			GIT_PS1_SHOWDIRTYSTATE=y &&
+			GIT_PS1_SHOWCOLORHINTS=y &&
+			__git_ps1 "BEFORE:" ":AFTER" &&
+			pcmode_actual
+		) &&
+		test_cmp expected "$actual"
+	'
+
+	test_expect_success "$pfx - dirty status indicator - dirty index and worktree" '
+		pcmode_expected "BEFORE: (${c_green}%s${c_clear} ${c_red}*${c_green}+${c_clear}):AFTER\\n%s" master &&
+		echo "dirty index" >file &&
+		test_when_finished "git reset --hard" &&
+		git add -u &&
+		echo "dirty worktree" >file &&
+		(
+			GIT_PS1_SHOWCOLORHINTS=y &&
+			GIT_PS1_SHOWDIRTYSTATE=y &&
+			__git_ps1 "BEFORE:" ":AFTER" &&
+			pcmode_actual
+		) &&
+		test_cmp expected "$actual"
+	'
+
+	test_expect_success "$pfx - dirty status indicator - before root commit" '
+		pcmode_expected "BEFORE: (${c_green}%s${c_clear} ${c_green}#${c_clear}):AFTER\\n%s" master &&
+		(
+			GIT_PS1_SHOWDIRTYSTATE=y &&
+			GIT_PS1_SHOWCOLORHINTS=y &&
+			cd otherrepo &&
+			__git_ps1 "BEFORE:" ":AFTER" &&
+			pcmode_actual
+		) &&
+		test_cmp expected "$actual"
+	'
+
+	test_expect_success "$pfx - inside .git directory" '
+		pcmode_expected "BEFORE: (${c_green}%s${c_clear}):AFTER\\n%s" "GIT_DIR!" &&
+		echo "dirty" >file &&
+		test_when_finished "git reset --hard" &&
+		(
+			GIT_PS1_SHOWDIRTYSTATE=y &&
+			GIT_PS1_SHOWCOLORHINTS=y &&
+			cd .git &&
+			__git_ps1 "BEFORE:" ":AFTER" &&
+			pcmode_actual
+		) &&
+		test_cmp expected "$actual"
+	'
+
+	test_expect_success "$pfx - stash status indicator" '
+		pcmode_expected "BEFORE: (${c_green}%s${c_clear} ${c_lblue}\$${c_clear}):AFTER\\n%s" master &&
+		echo 2 >file &&
+		git stash &&
+		test_when_finished "git stash drop" &&
+		(
+			GIT_PS1_SHOWSTASHSTATE=y &&
+			GIT_PS1_SHOWCOLORHINTS=y &&
+			__git_ps1 "BEFORE:" ":AFTER" &&
+			pcmode_actual
+		) &&
+		test_cmp expected "$actual"
+	'
+
+	test_expect_success "$pfx - untracked files status indicator" '
+		pcmode_expected "BEFORE: (${c_green}%s${c_clear} ${c_red}%%${c_clear}):AFTER\\n%s" master &&
+		(
+			GIT_PS1_SHOWUNTRACKEDFILES=y &&
+			GIT_PS1_SHOWCOLORHINTS=y &&
+			__git_ps1 "BEFORE:" ":AFTER" &&
+			pcmode_actual
+		) &&
+		test_cmp expected "$actual"
+	'
+}
+
+run_pcmode_tests () {
+	_run_pcmode_tests on
+	_run_pcmode_tests off
+}
+
+run_pcmode_tests
diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh
index ef10e34..b698fe9 100755
--- a/t/t9903-bash-prompt.sh
+++ b/t/t9903-bash-prompt.sh
@@ -6,609 +6,6 @@
 test_description='test git-specific bash prompt functions'
 
 . ./lib-bash.sh
-
-. "$GIT_BUILD_DIR/contrib/completion/git-prompt.sh"
-
-actual="$TRASH_DIRECTORY/actual"
-set_ps1_format_vars
-
-test_expect_success "setup for $shellname prompt tests" '
-	git init otherrepo &&
-	echo 1 >file &&
-	git add file &&
-	test_tick &&
-	git commit -m initial &&
-	git tag -a -m msg1 t1 &&
-	git checkout -b b1 &&
-	echo 2 >file &&
-	git commit -m "second b1" file &&
-	echo 3 >file &&
-	git commit -m "third b1" file &&
-	git tag -a -m msg2 t2 &&
-	git checkout -b b2 master &&
-	echo 0 >file &&
-	git commit -m "second b2" file &&
-	echo 00 >file &&
-	git commit -m "another b2" file &&
-	echo 000 >file &&
-	git commit -m "yet another b2" file &&
-	git checkout master
-'
-
-pfx="$shellname prompt"
-
-test_expect_success "$pfx - branch name" '
-	printf " (master)" >expected &&
-	__git_ps1 >"$actual" &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success SYMLINKS "$pfx - branch name - symlink symref" '
-	printf " (master)" >expected &&
-	test_when_finished "git checkout master" &&
-	test_config core.preferSymlinkRefs true &&
-	git checkout master &&
-	__git_ps1 >"$actual" &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - unborn branch" '
-	printf " (unborn)" >expected &&
-	git checkout --orphan unborn &&
-	test_when_finished "git checkout master" &&
-	__git_ps1 >"$actual" &&
-	test_cmp expected "$actual"
-'
-
-repo_with_newline='repo
-with
-newline'
-
-if mkdir "$repo_with_newline" 2>/dev/null
-then
-	test_set_prereq FUNNYNAMES
-else
-	say 'Your filesystem does not allow newlines in filenames.'
-fi
-
-test_expect_success FUNNYNAMES "$pfx - with newline in path" '
-	printf " (master)" >expected &&
-	git init "$repo_with_newline" &&
-	test_when_finished "rm -rf \"$repo_with_newline\"" &&
-	mkdir "$repo_with_newline"/subdir &&
-	(
-		cd "$repo_with_newline/subdir" &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - detached head" '
-	printf " ((%s...))" $(git log -1 --format="%h" --abbrev=13 b1^) >expected &&
-	test_config core.abbrev 13 &&
-	git checkout b1^ &&
-	test_when_finished "git checkout master" &&
-	__git_ps1 >"$actual" &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - describe detached head - contains" '
-	printf " ((t2~1))" >expected &&
-	git checkout b1^ &&
-	test_when_finished "git checkout master" &&
-	(
-		GIT_PS1_DESCRIBE_STYLE=contains &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - describe detached head - branch" '
-	printf " ((b1~1))" >expected &&
-	git checkout b1^ &&
-	test_when_finished "git checkout master" &&
-	(
-		GIT_PS1_DESCRIBE_STYLE=branch &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - describe detached head - describe" '
-	printf " ((t1-1-g%s))" $(git log -1 --format="%h" b1^) >expected &&
-	git checkout b1^ &&
-	test_when_finished "git checkout master" &&
-	(
-		GIT_PS1_DESCRIBE_STYLE=describe &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - describe detached head - default" '
-	printf " ((t2))" >expected &&
-	git checkout --detach b1 &&
-	test_when_finished "git checkout master" &&
-	__git_ps1 >"$actual" &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - inside .git directory" '
-	printf " (GIT_DIR!)" >expected &&
-	(
-		cd .git &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - deep inside .git directory" '
-	printf " (GIT_DIR!)" >expected &&
-	(
-		cd .git/refs/heads &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - inside bare repository" '
-	printf " (BARE:master)" >expected &&
-	git init --bare bare.git &&
-	test_when_finished "rm -rf bare.git" &&
-	(
-		cd bare.git &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - interactive rebase" '
-	printf " (b1|REBASE-i 2/3)" >expected
-	write_script fake_editor.sh <<-\EOF &&
-		echo "exec echo" >"$1"
-		echo "edit $(git log -1 --format="%h")" >>"$1"
-		echo "exec echo" >>"$1"
-	EOF
-	test_when_finished "rm -f fake_editor.sh" &&
-	test_set_editor "$TRASH_DIRECTORY/fake_editor.sh" &&
-	git checkout b1 &&
-	test_when_finished "git checkout master" &&
-	git rebase -i HEAD^ &&
-	test_when_finished "git rebase --abort"
-	__git_ps1 >"$actual" &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - rebase merge" '
-	printf " (b2|REBASE-m 1/3)" >expected &&
-	git checkout b2 &&
-	test_when_finished "git checkout master" &&
-	test_must_fail git rebase --merge b1 b2 &&
-	test_when_finished "git rebase --abort" &&
-	__git_ps1 >"$actual" &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - rebase" '
-	printf " (b2|REBASE 1/3)" >expected &&
-	git checkout b2 &&
-	test_when_finished "git checkout master" &&
-	test_must_fail git rebase b1 b2 &&
-	test_when_finished "git rebase --abort" &&
-	__git_ps1 >"$actual" &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - merge" '
-	printf " (b1|MERGING)" >expected &&
-	git checkout b1 &&
-	test_when_finished "git checkout master" &&
-	test_must_fail git merge b2 &&
-	test_when_finished "git reset --hard" &&
-	__git_ps1 >"$actual" &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - cherry-pick" '
-	printf " (master|CHERRY-PICKING)" >expected &&
-	test_must_fail git cherry-pick b1 &&
-	test_when_finished "git reset --hard" &&
-	__git_ps1 >"$actual" &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - bisect" '
-	printf " (master|BISECTING)" >expected &&
-	git bisect start &&
-	test_when_finished "git bisect reset" &&
-	__git_ps1 >"$actual" &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - dirty status indicator - clean" '
-	printf " (master)" >expected &&
-	(
-		GIT_PS1_SHOWDIRTYSTATE=y &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - dirty status indicator - dirty worktree" '
-	printf " (master *)" >expected &&
-	echo "dirty" >file &&
-	test_when_finished "git reset --hard" &&
-	(
-		GIT_PS1_SHOWDIRTYSTATE=y &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - dirty status indicator - dirty index" '
-	printf " (master +)" >expected &&
-	echo "dirty" >file &&
-	test_when_finished "git reset --hard" &&
-	git add -u &&
-	(
-		GIT_PS1_SHOWDIRTYSTATE=y &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - dirty status indicator - dirty index and worktree" '
-	printf " (master *+)" >expected &&
-	echo "dirty index" >file &&
-	test_when_finished "git reset --hard" &&
-	git add -u &&
-	echo "dirty worktree" >file &&
-	(
-		GIT_PS1_SHOWDIRTYSTATE=y &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - dirty status indicator - before root commit" '
-	printf " (master #)" >expected &&
-	(
-		GIT_PS1_SHOWDIRTYSTATE=y &&
-		cd otherrepo &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - dirty status indicator - shell variable unset with config disabled" '
-	printf " (master)" >expected &&
-	echo "dirty" >file &&
-	test_when_finished "git reset --hard" &&
-	test_config bash.showDirtyState false &&
-	(
-		sane_unset GIT_PS1_SHOWDIRTYSTATE &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - dirty status indicator - shell variable unset with config enabled" '
-	printf " (master)" >expected &&
-	echo "dirty" >file &&
-	test_when_finished "git reset --hard" &&
-	test_config bash.showDirtyState true &&
-	(
-		sane_unset GIT_PS1_SHOWDIRTYSTATE &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - dirty status indicator - shell variable set with config disabled" '
-	printf " (master)" >expected &&
-	echo "dirty" >file &&
-	test_when_finished "git reset --hard" &&
-	test_config bash.showDirtyState false &&
-	(
-		GIT_PS1_SHOWDIRTYSTATE=y &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - dirty status indicator - shell variable set with config enabled" '
-	printf " (master *)" >expected &&
-	echo "dirty" >file &&
-	test_when_finished "git reset --hard" &&
-	test_config bash.showDirtyState true &&
-	(
-		GIT_PS1_SHOWDIRTYSTATE=y &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - dirty status indicator - not shown inside .git directory" '
-	printf " (GIT_DIR!)" >expected &&
-	echo "dirty" >file &&
-	test_when_finished "git reset --hard" &&
-	(
-		GIT_PS1_SHOWDIRTYSTATE=y &&
-		cd .git &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - stash status indicator - no stash" '
-	printf " (master)" >expected &&
-	(
-		GIT_PS1_SHOWSTASHSTATE=y &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - stash status indicator - stash" '
-	printf " (master $)" >expected &&
-	echo 2 >file &&
-	git stash &&
-	test_when_finished "git stash drop" &&
-	git pack-refs --all &&
-	(
-		GIT_PS1_SHOWSTASHSTATE=y &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - stash status indicator - not shown inside .git directory" '
-	printf " (GIT_DIR!)" >expected &&
-	echo 2 >file &&
-	git stash &&
-	test_when_finished "git stash drop" &&
-	(
-		GIT_PS1_SHOWSTASHSTATE=y &&
-		cd .git &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - untracked files status indicator - no untracked files" '
-	printf " (master)" >expected &&
-	(
-		GIT_PS1_SHOWUNTRACKEDFILES=y &&
-		cd otherrepo &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - untracked files status indicator - untracked files" '
-	printf " (master %%)" >expected &&
-	(
-		GIT_PS1_SHOWUNTRACKEDFILES=y &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - untracked files status indicator - shell variable unset with config disabled" '
-	printf " (master)" >expected &&
-	test_config bash.showUntrackedFiles false &&
-	(
-		sane_unset GIT_PS1_SHOWUNTRACKEDFILES &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - untracked files status indicator - shell variable unset with config enabled" '
-	printf " (master)" >expected &&
-	test_config bash.showUntrackedFiles true &&
-	(
-		sane_unset GIT_PS1_SHOWUNTRACKEDFILES &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - untracked files status indicator - shell variable set with config disabled" '
-	printf " (master)" >expected &&
-	test_config bash.showUntrackedFiles false &&
-	(
-		GIT_PS1_SHOWUNTRACKEDFILES=y &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - untracked files status indicator - shell variable set with config enabled" '
-	printf " (master %%)" >expected &&
-	test_config bash.showUntrackedFiles true &&
-	(
-		GIT_PS1_SHOWUNTRACKEDFILES=y &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - untracked files status indicator - not shown inside .git directory" '
-	printf " (GIT_DIR!)" >expected &&
-	(
-		GIT_PS1_SHOWUNTRACKEDFILES=y &&
-		cd .git &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - format string starting with dash" '
-	printf -- "-master" >expected &&
-	__git_ps1 "-%s" >"$actual" &&
-	test_cmp expected "$actual"
-'
-
-pcmode_expected () {
-	case $ps1expansion in
-	on) printf "$1" '${__git_ps1_branch_name}' "$2";;
-	off) printf "$1" "$2" "";;
-	esac >expected
-}
-
-pcmode_actual () {
-	case $ps1expansion in
-	on) printf %s\\n%s "$PS1" "${__git_ps1_branch_name}";;
-	off) printf %s\\n "$PS1";;
-	esac >"$actual"
-}
-
-_run_pcmode_tests () {
-	ps1expansion=$1; shift
-
-	case $ps1expansion in
-	# if the shell doesn't allow ps1 expansion to be enabled,
-	# quietly skip the tests (same goes for disabling)
-	on) ps1_expansion_enable || return 0;;
-	off) ps1_expansion_disable || return 0;;
-	*) error "invalid argument to _run_pcmode_tests: $ps1expansion";;
-	esac
-
-	test_expect_success "$shellname prompt - pc mode (PS1 expansion $ps1expansion)" '
-		pcmode_expected "BEFORE: (%s):AFTER\\n%s" master &&
-		printf "" >expected_output &&
-		(
-			__git_ps1 "BEFORE:" ":AFTER" >"$actual" &&
-			test_cmp expected_output "$actual" &&
-			pcmode_actual
-		) &&
-		test_cmp expected "$actual"
-	'
-
-	pfx="$shellname prompt - color pc mode (PS1 expansion $ps1expansion)"
-
-	test_expect_success "$pfx - branch name" '
-		pcmode_expected "BEFORE: (${c_green}%s${c_clear}):AFTER\\n%s" master &&
-		(
-			GIT_PS1_SHOWCOLORHINTS=y &&
-			__git_ps1 "BEFORE:" ":AFTER" >"$actual"
-			pcmode_actual
-		) &&
-		test_cmp expected "$actual"
-	'
-
-	test_expect_success "$pfx - detached head" '
-		pcmode_expected "BEFORE: (${c_red}%s${c_clear}):AFTER\\n%s" "($(git log -1 --format="%h" b1^)...)" &&
-		git checkout b1^ &&
-		test_when_finished "git checkout master" &&
-		(
-			GIT_PS1_SHOWCOLORHINTS=y &&
-			__git_ps1 "BEFORE:" ":AFTER" &&
-			pcmode_actual
-		) &&
-		test_cmp expected "$actual"
-	'
-
-	test_expect_success "$pfx - dirty status indicator - dirty worktree" '
-		pcmode_expected "BEFORE: (${c_green}%s${c_clear} ${c_red}*${c_clear}):AFTER\\n%s" master &&
-		echo "dirty" >file &&
-		test_when_finished "git reset --hard" &&
-		(
-			GIT_PS1_SHOWDIRTYSTATE=y &&
-			GIT_PS1_SHOWCOLORHINTS=y &&
-			__git_ps1 "BEFORE:" ":AFTER" &&
-			pcmode_actual
-		) &&
-		test_cmp expected "$actual"
-	'
-
-	test_expect_success "$pfx - dirty status indicator - dirty index" '
-		pcmode_expected "BEFORE: (${c_green}%s${c_clear} ${c_green}+${c_clear}):AFTER\\n%s" master &&
-		echo "dirty" >file &&
-		test_when_finished "git reset --hard" &&
-		git add -u &&
-		(
-			GIT_PS1_SHOWDIRTYSTATE=y &&
-			GIT_PS1_SHOWCOLORHINTS=y &&
-			__git_ps1 "BEFORE:" ":AFTER" &&
-			pcmode_actual
-		) &&
-		test_cmp expected "$actual"
-	'
-
-	test_expect_success "$pfx - dirty status indicator - dirty index and worktree" '
-		pcmode_expected "BEFORE: (${c_green}%s${c_clear} ${c_red}*${c_green}+${c_clear}):AFTER\\n%s" master &&
-		echo "dirty index" >file &&
-		test_when_finished "git reset --hard" &&
-		git add -u &&
-		echo "dirty worktree" >file &&
-		(
-			GIT_PS1_SHOWCOLORHINTS=y &&
-			GIT_PS1_SHOWDIRTYSTATE=y &&
-			__git_ps1 "BEFORE:" ":AFTER" &&
-			pcmode_actual
-		) &&
-		test_cmp expected "$actual"
-	'
-
-	test_expect_success "$pfx - dirty status indicator - before root commit" '
-		pcmode_expected "BEFORE: (${c_green}%s${c_clear} ${c_green}#${c_clear}):AFTER\\n%s" master &&
-		(
-			GIT_PS1_SHOWDIRTYSTATE=y &&
-			GIT_PS1_SHOWCOLORHINTS=y &&
-			cd otherrepo &&
-			__git_ps1 "BEFORE:" ":AFTER" &&
-			pcmode_actual
-		) &&
-		test_cmp expected "$actual"
-	'
-
-	test_expect_success "$pfx - inside .git directory" '
-		pcmode_expected "BEFORE: (${c_green}%s${c_clear}):AFTER\\n%s" "GIT_DIR!" &&
-		echo "dirty" >file &&
-		test_when_finished "git reset --hard" &&
-		(
-			GIT_PS1_SHOWDIRTYSTATE=y &&
-			GIT_PS1_SHOWCOLORHINTS=y &&
-			cd .git &&
-			__git_ps1 "BEFORE:" ":AFTER" &&
-			pcmode_actual
-		) &&
-		test_cmp expected "$actual"
-	'
-
-	test_expect_success "$pfx - stash status indicator" '
-		pcmode_expected "BEFORE: (${c_green}%s${c_clear} ${c_lblue}\$${c_clear}):AFTER\\n%s" master &&
-		echo 2 >file &&
-		git stash &&
-		test_when_finished "git stash drop" &&
-		(
-			GIT_PS1_SHOWSTASHSTATE=y &&
-			GIT_PS1_SHOWCOLORHINTS=y &&
-			__git_ps1 "BEFORE:" ":AFTER" &&
-			pcmode_actual
-		) &&
-		test_cmp expected "$actual"
-	'
-
-	test_expect_success "$pfx - untracked files status indicator" '
-		pcmode_expected "BEFORE: (${c_green}%s${c_clear} ${c_red}%%${c_clear}):AFTER\\n%s" master &&
-		(
-			GIT_PS1_SHOWUNTRACKEDFILES=y &&
-			GIT_PS1_SHOWCOLORHINTS=y &&
-			__git_ps1 "BEFORE:" ":AFTER" &&
-			pcmode_actual
-		) &&
-		test_cmp expected "$actual"
-	'
-}
-
-run_pcmode_tests () {
-	_run_pcmode_tests on
-	_run_pcmode_tests off
-}
-
-run_pcmode_tests
+. "$TEST_DIRECTORY"/lib-prompt-tests.sh
 
 test_done
-- 
1.9.3
Richard Hansen· May 27, 2014, 07:40 UTC · re: Richard Hansen · lore

[PATCH 08/10] lib-prompt-tests.sh: put all tests inside a function

Modify lib-prompt-tests.sh so that it does nothing when sourced except define a function for running the prompt tests (plus some "private" helper functions).

Signed-off-by: Richard Hansen <rhansen@bbn.com>
---
 t/lib-prompt-tests.sh  | 802 ++++++++++++++++++++++++-------------------------
 t/t9903-bash-prompt.sh |   2 +
 2 files changed, 403 insertions(+), 401 deletions(-)
Show changes to 2 files +403 −401

t/lib-prompt-tests.sh, t/t9903-bash-prompt.sh

diff --git a/t/lib-prompt-tests.sh b/t/lib-prompt-tests.sh
index 32c3c08..baa6762 100644
--- a/t/lib-prompt-tests.sh
+++ b/t/lib-prompt-tests.sh
@@ -17,6 +17,7 @@
 #      these variables are used in the first argument to the printf
 #      command, so they must be escaped appropriately.
 #   4. source this library
+#   5. invoke the run_prompt_tests function
 
 # sanity checks
 [ -n "$shellname" ] || error "shellname must be set to the name of the shell"
@@ -27,448 +28,445 @@ done
 (ps1_expansion_enable || ps1_expansion_disable) \
 	|| error "either ps1_expansion_enable or ps1_expansion_disable must return true"
 
-. "$GIT_BUILD_DIR/contrib/completion/git-prompt.sh"
+_run_non_pcmode_tests () {
+	test_expect_success "setup for $shellname prompt tests" '
+		git init otherrepo &&
+		echo 1 >file &&
+		git add file &&
+		test_tick &&
+		git commit -m initial &&
+		git tag -a -m msg1 t1 &&
+		git checkout -b b1 &&
+		echo 2 >file &&
+		git commit -m "second b1" file &&
+		echo 3 >file &&
+		git commit -m "third b1" file &&
+		git tag -a -m msg2 t2 &&
+		git checkout -b b2 master &&
+		echo 0 >file &&
+		git commit -m "second b2" file &&
+		echo 00 >file &&
+		git commit -m "another b2" file &&
+		echo 000 >file &&
+		git commit -m "yet another b2" file &&
+		git checkout master
+	'
 
-actual="$TRASH_DIRECTORY/actual"
-set_ps1_format_vars
+	pfx="$shellname prompt"
 
-test_expect_success "setup for $shellname prompt tests" '
-	git init otherrepo &&
-	echo 1 >file &&
-	git add file &&
-	test_tick &&
-	git commit -m initial &&
-	git tag -a -m msg1 t1 &&
-	git checkout -b b1 &&
-	echo 2 >file &&
-	git commit -m "second b1" file &&
-	echo 3 >file &&
-	git commit -m "third b1" file &&
-	git tag -a -m msg2 t2 &&
-	git checkout -b b2 master &&
-	echo 0 >file &&
-	git commit -m "second b2" file &&
-	echo 00 >file &&
-	git commit -m "another b2" file &&
-	echo 000 >file &&
-	git commit -m "yet another b2" file &&
-	git checkout master
-'
+	test_expect_success "$pfx - branch name" '
+		printf " (master)" >expected &&
+		__git_ps1 >"$actual" &&
+		test_cmp expected "$actual"
+	'
 
-pfx="$shellname prompt"
+	test_expect_success SYMLINKS "$pfx - branch name - symlink symref" '
+		printf " (master)" >expected &&
+		test_when_finished "git checkout master" &&
+		test_config core.preferSymlinkRefs true &&
+		git checkout master &&
+		__git_ps1 >"$actual" &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - branch name" '
-	printf " (master)" >expected &&
-	__git_ps1 >"$actual" &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - unborn branch" '
+		printf " (unborn)" >expected &&
+		git checkout --orphan unborn &&
+		test_when_finished "git checkout master" &&
+		__git_ps1 >"$actual" &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success SYMLINKS "$pfx - branch name - symlink symref" '
-	printf " (master)" >expected &&
-	test_when_finished "git checkout master" &&
-	test_config core.preferSymlinkRefs true &&
-	git checkout master &&
-	__git_ps1 >"$actual" &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - unborn branch" '
-	printf " (unborn)" >expected &&
-	git checkout --orphan unborn &&
-	test_when_finished "git checkout master" &&
-	__git_ps1 >"$actual" &&
-	test_cmp expected "$actual"
-'
-
-repo_with_newline='repo
+	repo_with_newline='repo
 with
 newline'
 
-if mkdir "$repo_with_newline" 2>/dev/null
-then
-	test_set_prereq FUNNYNAMES
-else
-	say 'Your filesystem does not allow newlines in filenames.'
-fi
+	if mkdir "$repo_with_newline" 2>/dev/null
+	then
+		test_set_prereq FUNNYNAMES
+	else
+		say 'Your filesystem does not allow newlines in filenames.'
+	fi
 
-test_expect_success FUNNYNAMES "$pfx - with newline in path" '
-	printf " (master)" >expected &&
-	git init "$repo_with_newline" &&
-	test_when_finished "rm -rf \"$repo_with_newline\"" &&
-	mkdir "$repo_with_newline"/subdir &&
-	(
-		cd "$repo_with_newline/subdir" &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success FUNNYNAMES "$pfx - with newline in path" '
+		printf " (master)" >expected &&
+		git init "$repo_with_newline" &&
+		test_when_finished "rm -rf \"$repo_with_newline\"" &&
+		mkdir "$repo_with_newline"/subdir &&
+		(
+			cd "$repo_with_newline/subdir" &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - detached head" '
-	printf " ((%s...))" $(git log -1 --format="%h" --abbrev=13 b1^) >expected &&
-	test_config core.abbrev 13 &&
-	git checkout b1^ &&
-	test_when_finished "git checkout master" &&
-	__git_ps1 >"$actual" &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - detached head" '
+		printf " ((%s...))" $(git log -1 --format="%h" --abbrev=13 b1^) >expected &&
+		test_config core.abbrev 13 &&
+		git checkout b1^ &&
+		test_when_finished "git checkout master" &&
+		__git_ps1 >"$actual" &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - describe detached head - contains" '
-	printf " ((t2~1))" >expected &&
-	git checkout b1^ &&
-	test_when_finished "git checkout master" &&
-	(
-		GIT_PS1_DESCRIBE_STYLE=contains &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - describe detached head - contains" '
+		printf " ((t2~1))" >expected &&
+		git checkout b1^ &&
+		test_when_finished "git checkout master" &&
+		(
+			GIT_PS1_DESCRIBE_STYLE=contains &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - describe detached head - branch" '
-	printf " ((b1~1))" >expected &&
-	git checkout b1^ &&
-	test_when_finished "git checkout master" &&
-	(
-		GIT_PS1_DESCRIBE_STYLE=branch &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - describe detached head - branch" '
+		printf " ((b1~1))" >expected &&
+		git checkout b1^ &&
+		test_when_finished "git checkout master" &&
+		(
+			GIT_PS1_DESCRIBE_STYLE=branch &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - describe detached head - describe" '
-	printf " ((t1-1-g%s))" $(git log -1 --format="%h" b1^) >expected &&
-	git checkout b1^ &&
-	test_when_finished "git checkout master" &&
-	(
-		GIT_PS1_DESCRIBE_STYLE=describe &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - describe detached head - describe" '
+		printf " ((t1-1-g%s))" $(git log -1 --format="%h" b1^) >expected &&
+		git checkout b1^ &&
+		test_when_finished "git checkout master" &&
+		(
+			GIT_PS1_DESCRIBE_STYLE=describe &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - describe detached head - default" '
-	printf " ((t2))" >expected &&
-	git checkout --detach b1 &&
-	test_when_finished "git checkout master" &&
-	__git_ps1 >"$actual" &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - describe detached head - default" '
+		printf " ((t2))" >expected &&
+		git checkout --detach b1 &&
+		test_when_finished "git checkout master" &&
+		__git_ps1 >"$actual" &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - inside .git directory" '
-	printf " (GIT_DIR!)" >expected &&
-	(
-		cd .git &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - inside .git directory" '
+		printf " (GIT_DIR!)" >expected &&
+		(
+			cd .git &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - deep inside .git directory" '
-	printf " (GIT_DIR!)" >expected &&
-	(
-		cd .git/refs/heads &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - deep inside .git directory" '
+		printf " (GIT_DIR!)" >expected &&
+		(
+			cd .git/refs/heads &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - inside bare repository" '
-	printf " (BARE:master)" >expected &&
-	git init --bare bare.git &&
-	test_when_finished "rm -rf bare.git" &&
-	(
-		cd bare.git &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - inside bare repository" '
+		printf " (BARE:master)" >expected &&
+		git init --bare bare.git &&
+		test_when_finished "rm -rf bare.git" &&
+		(
+			cd bare.git &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - interactive rebase" '
-	printf " (b1|REBASE-i 2/3)" >expected
-	write_script fake_editor.sh <<-\EOF &&
-		echo "exec echo" >"$1"
-		echo "edit $(git log -1 --format="%h")" >>"$1"
-		echo "exec echo" >>"$1"
-	EOF
-	test_when_finished "rm -f fake_editor.sh" &&
-	test_set_editor "$TRASH_DIRECTORY/fake_editor.sh" &&
-	git checkout b1 &&
-	test_when_finished "git checkout master" &&
-	git rebase -i HEAD^ &&
-	test_when_finished "git rebase --abort"
-	__git_ps1 >"$actual" &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - interactive rebase" '
+		printf " (b1|REBASE-i 2/3)" >expected
+		write_script fake_editor.sh <<-\EOF &&
+			echo "exec echo" >"$1"
+			echo "edit $(git log -1 --format="%h")" >>"$1"
+			echo "exec echo" >>"$1"
+		EOF
+		test_when_finished "rm -f fake_editor.sh" &&
+		test_set_editor "$TRASH_DIRECTORY/fake_editor.sh" &&
+		git checkout b1 &&
+		test_when_finished "git checkout master" &&
+		git rebase -i HEAD^ &&
+		test_when_finished "git rebase --abort"
+		__git_ps1 >"$actual" &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - rebase merge" '
-	printf " (b2|REBASE-m 1/3)" >expected &&
-	git checkout b2 &&
-	test_when_finished "git checkout master" &&
-	test_must_fail git rebase --merge b1 b2 &&
-	test_when_finished "git rebase --abort" &&
-	__git_ps1 >"$actual" &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - rebase merge" '
+		printf " (b2|REBASE-m 1/3)" >expected &&
+		git checkout b2 &&
+		test_when_finished "git checkout master" &&
+		test_must_fail git rebase --merge b1 b2 &&
+		test_when_finished "git rebase --abort" &&
+		__git_ps1 >"$actual" &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - rebase" '
-	printf " (b2|REBASE 1/3)" >expected &&
-	git checkout b2 &&
-	test_when_finished "git checkout master" &&
-	test_must_fail git rebase b1 b2 &&
-	test_when_finished "git rebase --abort" &&
-	__git_ps1 >"$actual" &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - rebase" '
+		printf " (b2|REBASE 1/3)" >expected &&
+		git checkout b2 &&
+		test_when_finished "git checkout master" &&
+		test_must_fail git rebase b1 b2 &&
+		test_when_finished "git rebase --abort" &&
+		__git_ps1 >"$actual" &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - merge" '
-	printf " (b1|MERGING)" >expected &&
-	git checkout b1 &&
-	test_when_finished "git checkout master" &&
-	test_must_fail git merge b2 &&
-	test_when_finished "git reset --hard" &&
-	__git_ps1 >"$actual" &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - merge" '
+		printf " (b1|MERGING)" >expected &&
+		git checkout b1 &&
+		test_when_finished "git checkout master" &&
+		test_must_fail git merge b2 &&
+		test_when_finished "git reset --hard" &&
+		__git_ps1 >"$actual" &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - cherry-pick" '
-	printf " (master|CHERRY-PICKING)" >expected &&
-	test_must_fail git cherry-pick b1 &&
-	test_when_finished "git reset --hard" &&
-	__git_ps1 >"$actual" &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - cherry-pick" '
+		printf " (master|CHERRY-PICKING)" >expected &&
+		test_must_fail git cherry-pick b1 &&
+		test_when_finished "git reset --hard" &&
+		__git_ps1 >"$actual" &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - bisect" '
-	printf " (master|BISECTING)" >expected &&
-	git bisect start &&
-	test_when_finished "git bisect reset" &&
-	__git_ps1 >"$actual" &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - bisect" '
+		printf " (master|BISECTING)" >expected &&
+		git bisect start &&
+		test_when_finished "git bisect reset" &&
+		__git_ps1 >"$actual" &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - dirty status indicator - clean" '
-	printf " (master)" >expected &&
-	(
-		GIT_PS1_SHOWDIRTYSTATE=y &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - dirty status indicator - clean" '
+		printf " (master)" >expected &&
+		(
+			GIT_PS1_SHOWDIRTYSTATE=y &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - dirty status indicator - dirty worktree" '
-	printf " (master *)" >expected &&
-	echo "dirty" >file &&
-	test_when_finished "git reset --hard" &&
-	(
-		GIT_PS1_SHOWDIRTYSTATE=y &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - dirty status indicator - dirty worktree" '
+		printf " (master *)" >expected &&
+		echo "dirty" >file &&
+		test_when_finished "git reset --hard" &&
+		(
+			GIT_PS1_SHOWDIRTYSTATE=y &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - dirty status indicator - dirty index" '
-	printf " (master +)" >expected &&
-	echo "dirty" >file &&
-	test_when_finished "git reset --hard" &&
-	git add -u &&
-	(
-		GIT_PS1_SHOWDIRTYSTATE=y &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - dirty status indicator - dirty index" '
+		printf " (master +)" >expected &&
+		echo "dirty" >file &&
+		test_when_finished "git reset --hard" &&
+		git add -u &&
+		(
+			GIT_PS1_SHOWDIRTYSTATE=y &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - dirty status indicator - dirty index and worktree" '
-	printf " (master *+)" >expected &&
-	echo "dirty index" >file &&
-	test_when_finished "git reset --hard" &&
-	git add -u &&
-	echo "dirty worktree" >file &&
-	(
-		GIT_PS1_SHOWDIRTYSTATE=y &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - dirty status indicator - dirty index and worktree" '
+		printf " (master *+)" >expected &&
+		echo "dirty index" >file &&
+		test_when_finished "git reset --hard" &&
+		git add -u &&
+		echo "dirty worktree" >file &&
+		(
+			GIT_PS1_SHOWDIRTYSTATE=y &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - dirty status indicator - before root commit" '
-	printf " (master #)" >expected &&
-	(
-		GIT_PS1_SHOWDIRTYSTATE=y &&
-		cd otherrepo &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - dirty status indicator - before root commit" '
+		printf " (master #)" >expected &&
+		(
+			GIT_PS1_SHOWDIRTYSTATE=y &&
+			cd otherrepo &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - dirty status indicator - shell variable unset with config disabled" '
-	printf " (master)" >expected &&
-	echo "dirty" >file &&
-	test_when_finished "git reset --hard" &&
-	test_config bash.showDirtyState false &&
-	(
-		sane_unset GIT_PS1_SHOWDIRTYSTATE &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - dirty status indicator - shell variable unset with config disabled" '
+		printf " (master)" >expected &&
+		echo "dirty" >file &&
+		test_when_finished "git reset --hard" &&
+		test_config bash.showDirtyState false &&
+		(
+			sane_unset GIT_PS1_SHOWDIRTYSTATE &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - dirty status indicator - shell variable unset with config enabled" '
-	printf " (master)" >expected &&
-	echo "dirty" >file &&
-	test_when_finished "git reset --hard" &&
-	test_config bash.showDirtyState true &&
-	(
-		sane_unset GIT_PS1_SHOWDIRTYSTATE &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - dirty status indicator - shell variable unset with config enabled" '
+		printf " (master)" >expected &&
+		echo "dirty" >file &&
+		test_when_finished "git reset --hard" &&
+		test_config bash.showDirtyState true &&
+		(
+			sane_unset GIT_PS1_SHOWDIRTYSTATE &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - dirty status indicator - shell variable set with config disabled" '
-	printf " (master)" >expected &&
-	echo "dirty" >file &&
-	test_when_finished "git reset --hard" &&
-	test_config bash.showDirtyState false &&
-	(
-		GIT_PS1_SHOWDIRTYSTATE=y &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - dirty status indicator - shell variable set with config disabled" '
+		printf " (master)" >expected &&
+		echo "dirty" >file &&
+		test_when_finished "git reset --hard" &&
+		test_config bash.showDirtyState false &&
+		(
+			GIT_PS1_SHOWDIRTYSTATE=y &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - dirty status indicator - shell variable set with config enabled" '
-	printf " (master *)" >expected &&
-	echo "dirty" >file &&
-	test_when_finished "git reset --hard" &&
-	test_config bash.showDirtyState true &&
-	(
-		GIT_PS1_SHOWDIRTYSTATE=y &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - dirty status indicator - shell variable set with config enabled" '
+		printf " (master *)" >expected &&
+		echo "dirty" >file &&
+		test_when_finished "git reset --hard" &&
+		test_config bash.showDirtyState true &&
+		(
+			GIT_PS1_SHOWDIRTYSTATE=y &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - dirty status indicator - not shown inside .git directory" '
-	printf " (GIT_DIR!)" >expected &&
-	echo "dirty" >file &&
-	test_when_finished "git reset --hard" &&
-	(
-		GIT_PS1_SHOWDIRTYSTATE=y &&
-		cd .git &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - dirty status indicator - not shown inside .git directory" '
+		printf " (GIT_DIR!)" >expected &&
+		echo "dirty" >file &&
+		test_when_finished "git reset --hard" &&
+		(
+			GIT_PS1_SHOWDIRTYSTATE=y &&
+			cd .git &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - stash status indicator - no stash" '
-	printf " (master)" >expected &&
-	(
-		GIT_PS1_SHOWSTASHSTATE=y &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - stash status indicator - no stash" '
+		printf " (master)" >expected &&
+		(
+			GIT_PS1_SHOWSTASHSTATE=y &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - stash status indicator - stash" '
-	printf " (master $)" >expected &&
-	echo 2 >file &&
-	git stash &&
-	test_when_finished "git stash drop" &&
-	git pack-refs --all &&
-	(
-		GIT_PS1_SHOWSTASHSTATE=y &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - stash status indicator - stash" '
+		printf " (master $)" >expected &&
+		echo 2 >file &&
+		git stash &&
+		test_when_finished "git stash drop" &&
+		git pack-refs --all &&
+		(
+			GIT_PS1_SHOWSTASHSTATE=y &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - stash status indicator - not shown inside .git directory" '
-	printf " (GIT_DIR!)" >expected &&
-	echo 2 >file &&
-	git stash &&
-	test_when_finished "git stash drop" &&
-	(
-		GIT_PS1_SHOWSTASHSTATE=y &&
-		cd .git &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - stash status indicator - not shown inside .git directory" '
+		printf " (GIT_DIR!)" >expected &&
+		echo 2 >file &&
+		git stash &&
+		test_when_finished "git stash drop" &&
+		(
+			GIT_PS1_SHOWSTASHSTATE=y &&
+			cd .git &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - untracked files status indicator - no untracked files" '
-	printf " (master)" >expected &&
-	(
-		GIT_PS1_SHOWUNTRACKEDFILES=y &&
-		cd otherrepo &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - untracked files status indicator - no untracked files" '
+		printf " (master)" >expected &&
+		(
+			GIT_PS1_SHOWUNTRACKEDFILES=y &&
+			cd otherrepo &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - untracked files status indicator - untracked files" '
-	printf " (master %%)" >expected &&
-	(
-		GIT_PS1_SHOWUNTRACKEDFILES=y &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - untracked files status indicator - untracked files" '
+		printf " (master %%)" >expected &&
+		(
+			GIT_PS1_SHOWUNTRACKEDFILES=y &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - untracked files status indicator - shell variable unset with config disabled" '
-	printf " (master)" >expected &&
-	test_config bash.showUntrackedFiles false &&
-	(
-		sane_unset GIT_PS1_SHOWUNTRACKEDFILES &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - untracked files status indicator - shell variable unset with config disabled" '
+		printf " (master)" >expected &&
+		test_config bash.showUntrackedFiles false &&
+		(
+			sane_unset GIT_PS1_SHOWUNTRACKEDFILES &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - untracked files status indicator - shell variable unset with config enabled" '
-	printf " (master)" >expected &&
-	test_config bash.showUntrackedFiles true &&
-	(
-		sane_unset GIT_PS1_SHOWUNTRACKEDFILES &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - untracked files status indicator - shell variable unset with config enabled" '
+		printf " (master)" >expected &&
+		test_config bash.showUntrackedFiles true &&
+		(
+			sane_unset GIT_PS1_SHOWUNTRACKEDFILES &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - untracked files status indicator - shell variable set with config disabled" '
-	printf " (master)" >expected &&
-	test_config bash.showUntrackedFiles false &&
-	(
-		GIT_PS1_SHOWUNTRACKEDFILES=y &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - untracked files status indicator - shell variable set with config disabled" '
+		printf " (master)" >expected &&
+		test_config bash.showUntrackedFiles false &&
+		(
+			GIT_PS1_SHOWUNTRACKEDFILES=y &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - untracked files status indicator - shell variable set with config enabled" '
-	printf " (master %%)" >expected &&
-	test_config bash.showUntrackedFiles true &&
-	(
-		GIT_PS1_SHOWUNTRACKEDFILES=y &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - untracked files status indicator - shell variable set with config enabled" '
+		printf " (master %%)" >expected &&
+		test_config bash.showUntrackedFiles true &&
+		(
+			GIT_PS1_SHOWUNTRACKEDFILES=y &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - untracked files status indicator - not shown inside .git directory" '
-	printf " (GIT_DIR!)" >expected &&
-	(
-		GIT_PS1_SHOWUNTRACKEDFILES=y &&
-		cd .git &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - untracked files status indicator - not shown inside .git directory" '
+		printf " (GIT_DIR!)" >expected &&
+		(
+			GIT_PS1_SHOWUNTRACKEDFILES=y &&
+			cd .git &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - format string starting with dash" '
-	printf -- "-master" >expected &&
-	__git_ps1 "-%s" >"$actual" &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - format string starting with dash" '
+		printf -- "-master" >expected &&
+		__git_ps1 "-%s" >"$actual" &&
+		test_cmp expected "$actual"
+	'
+}
 
 pcmode_expected () {
 	case $ps1expansion in
@@ -624,9 +622,11 @@ _run_pcmode_tests () {
 	'
 }
 
-run_pcmode_tests () {
+run_prompt_tests () {
+	. "$GIT_BUILD_DIR/contrib/completion/git-prompt.sh"
+	actual="$TRASH_DIRECTORY/actual"
+	set_ps1_format_vars
+	_run_non_pcmode_tests
 	_run_pcmode_tests on
 	_run_pcmode_tests off
 }
-
-run_pcmode_tests
diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh
index b698fe9..858f0cd 100755
--- a/t/t9903-bash-prompt.sh
+++ b/t/t9903-bash-prompt.sh
@@ -8,4 +8,6 @@ test_description='test git-specific bash prompt functions'
 . ./lib-bash.sh
 . "$TEST_DIRECTORY"/lib-prompt-tests.sh
 
+run_prompt_tests
+
 test_done
-- 
1.9.3
Richard Hansen· May 27, 2014, 07:40 UTC · re: Richard Hansen · lore

[PATCH 09/10] lib-prompt-tests.sh: add variable for string that encodes percent in PS1

To add a literal percent character to a Zsh prompt, the string "%%" is used in PS1. Bash and POSIX shells simply use "%". To accommodate this difference, use ${percent} where a percent character is expected and define the percent variable in the set_ps1_format_vars function.

Signed-off-by: Richard Hansen <rhansen@bbn.com>
---
 t/lib-bash.sh         |  1 +
 t/lib-prompt-tests.sh | 15 ++++++++-------
 2 files changed, 9 insertions(+), 7 deletions(-)
Show changes to 2 files +9 −7

t/lib-bash.sh, t/lib-prompt-tests.sh

diff --git a/t/lib-bash.sh b/t/lib-bash.sh
index 9d428bd..8a030ac 100644
--- a/t/lib-bash.sh
+++ b/t/lib-bash.sh
@@ -19,6 +19,7 @@ shellname=Bash
 ps1_expansion_enable () { shopt -s promptvars; }
 ps1_expansion_disable () { shopt -u promptvars; }
 set_ps1_format_vars () {
+	percent='%%'
 	c_red='\\[\\e[31m\\]'
 	c_green='\\[\\e[32m\\]'
 	c_lblue='\\[\\e[1;34m\\]'
diff --git a/t/lib-prompt-tests.sh b/t/lib-prompt-tests.sh
index baa6762..244e765 100644
--- a/t/lib-prompt-tests.sh
+++ b/t/lib-prompt-tests.sh
@@ -12,10 +12,11 @@
 #      (non-zero) and ps1_expansion_disable should simply return
 #      non-zero (0)
 #   3. define a function named set_ps1_format_vars that sets the
-#      variables c_red, c_green, c_lblue, and c_clear to the strings
-#      that __git_ps1 uses to add color to the prompt.  The values of
-#      these variables are used in the first argument to the printf
-#      command, so they must be escaped appropriately.
+#      variables percent, c_red, c_green, c_lblue, and c_clear to the
+#      strings that __git_ps1 uses to add percent characters and color
+#      to the prompt.  The values of these variables are used in the
+#      first argument to the printf command, so they must be escaped
+#      appropriately.
 #   4. source this library
 #   5. invoke the run_prompt_tests function
 
@@ -403,7 +404,7 @@ newline'
 	'
 
 	test_expect_success "$pfx - untracked files status indicator - untracked files" '
-		printf " (master %%)" >expected &&
+		printf " (master ${percent})" >expected &&
 		(
 			GIT_PS1_SHOWUNTRACKEDFILES=y &&
 			__git_ps1 >"$actual"
@@ -442,7 +443,7 @@ newline'
 	'
 
 	test_expect_success "$pfx - untracked files status indicator - shell variable set with config enabled" '
-		printf " (master %%)" >expected &&
+		printf " (master ${percent})" >expected &&
 		test_config bash.showUntrackedFiles true &&
 		(
 			GIT_PS1_SHOWUNTRACKEDFILES=y &&
@@ -611,7 +612,7 @@ _run_pcmode_tests () {
 	'
 
 	test_expect_success "$pfx - untracked files status indicator" '
-		pcmode_expected "BEFORE: (${c_green}%s${c_clear} ${c_red}%%${c_clear}):AFTER\\n%s" master &&
+		pcmode_expected "BEFORE: (${c_green}%s${c_clear} ${c_red}${percent}${c_clear}):AFTER\\n%s" master &&
 		(
 			GIT_PS1_SHOWUNTRACKEDFILES=y &&
 			GIT_PS1_SHOWCOLORHINTS=y &&
-- 
1.9.3
Richard Hansen· May 27, 2014, 07:41 UTC · re: Richard Hansen · lore

[PATCH 10/10] t9904: new __git_ps1 tests for Zsh

These are the same tests as in t9903, but run in zsh instead of bash.
Signed-off-by: Richard Hansen <rhansen@bbn.com>
---
 t/lib-zsh.sh          | 30 ++++++++++++++++++++++++++++++
 t/t9904-zsh-prompt.sh | 10 ++++++++++
 2 files changed, 40 insertions(+)
 create mode 100644 t/lib-zsh.sh
 create mode 100755 t/t9904-zsh-prompt.sh
Show changes to 2 files +40 −0

t/lib-zsh.sh, t/t9904-zsh-prompt.sh

diff --git a/t/lib-zsh.sh b/t/lib-zsh.sh
new file mode 100644
index 0000000..fa6fcd9
--- /dev/null
+++ b/t/lib-zsh.sh
@@ -0,0 +1,30 @@
+# Shell library sourced instead of ./test-lib.sh by tests that need to
+# run under Zsh; primarily intended for tests of the git-prompt.sh
+# script.
+
+if test -n "$ZSH_VERSION" && test -z "$POSIXLY_CORRECT"; then
+	true
+elif command -v zsh >/dev/null 2>&1; then
+	unset POSIXLY_CORRECT
+	exec zsh "$0" "$@"
+else
+	echo '1..0 #SKIP skipping Zsh-specific tests; zsh not available'
+	exit 0
+fi
+
+# ensure that we are in full-on Zsh mode
+emulate -R zsh || exit 1
+
+shellname=Zsh
+
+ps1_expansion_enable () { setopt PROMPT_SUBST; }
+ps1_expansion_disable () { unsetopt PROMPT_SUBST; }
+set_ps1_format_vars () {
+	percent='%%%%'
+	c_red='%%F{red}'
+	c_green='%%F{green}'
+	c_lblue='%%F{blue}'
+	c_clear='%%f'
+}
+
+emulate sh -c '. ./test-lib.sh'
diff --git a/t/t9904-zsh-prompt.sh b/t/t9904-zsh-prompt.sh
new file mode 100755
index 0000000..a38a3fd
--- /dev/null
+++ b/t/t9904-zsh-prompt.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+test_description='test git-specific Zsh prompt functions'
+
+. ./lib-zsh.sh
+. "$TEST_DIRECTORY"/lib-prompt-tests.sh
+
+run_prompt_tests
+
+test_done
-- 
1.9.3
Thomas Rast· May 29, 2014, 19:02 UTC · re: Richard Hansen · lore

Re: [PATCH 10/10] t9904: new __git_ps1 tests for Zsh

Richard Hansen <rhansen@bbn.com> writes:
Show 9 quoted lines
> These are the same tests as in t9903, but run in zsh instead of bash.
>
> Signed-off-by: Richard Hansen <rhansen@bbn.com>
> ---
>  t/lib-zsh.sh          | 30 ++++++++++++++++++++++++++++++
>  t/t9904-zsh-prompt.sh | 10 ++++++++++
>  2 files changed, 40 insertions(+)
>  create mode 100644 t/lib-zsh.sh
>  create mode 100755 t/t9904-zsh-prompt.sh
This doesn't appear to work in valgrind mode:

$ ./t9904-zsh-prompt.sh --valgrind error: Test script did not set test_description.

t9903 however works. I'm not sure how much of a difference it makes, but: I use bash as my shell and as /bin/sh, but I do have zsh installed.

Can you look into it?
-- 
Thomas Rast
tr@thomasrast.ch
Richard Hansen· May 29, 2014, 22:30 UTC · re: Thomas Rast · lore

[PATCH 11/10] fixup! t9904: new __git_ps1 tests for Zsh

Signed-off-by: Richard Hansen <rhansen@bbn.com>
---
On 2014-05-29 15:02, Thomas Rast wrote:
Show 21 quoted lines
> Richard Hansen <rhansen@bbn.com> writes:
>
>> These are the same tests as in t9903, but run in zsh instead of bash.
>>
>> Signed-off-by: Richard Hansen <rhansen@bbn.com>
>> ---
>>  t/lib-zsh.sh          | 30 ++++++++++++++++++++++++++++++
>>  t/t9904-zsh-prompt.sh | 10 ++++++++++
>>  2 files changed, 40 insertions(+)
>>  create mode 100644 t/lib-zsh.sh
>>  create mode 100755 t/t9904-zsh-prompt.sh
>
> This doesn't appear to work in valgrind mode:
>
> $ ./t9904-zsh-prompt.sh --valgrind
> error: Test script did not set test_description.
>
> t9903 however works.  I'm not sure how much of a difference it makes,
> but: I use bash as my shell and as /bin/sh, but I do have zsh installed.
>
> Can you look into it?

*sigh* By default, Zsh munges $0 whenever a function is called or a file is sourced, with no (immediately obvious) way to get the original value of $0. This fixup causes that feature to be temporarily turned off so that test-lib.sh does the right thing when it execs $0.

Thank you for finding this bug!
-Richard
 t/lib-zsh.sh | 15 ++++++++++++---
 1 file changed, 12 insertions(+), 3 deletions(-)
Show changes to t/lib-zsh.sh +12 −3
diff --git a/t/lib-zsh.sh b/t/lib-zsh.sh
index fa6fcd9..ab4bef2 100644
--- a/t/lib-zsh.sh
+++ b/t/lib-zsh.sh
@@ -2,17 +2,23 @@
 # run under Zsh; primarily intended for tests of the git-prompt.sh
 # script.
 
-if test -n "$ZSH_VERSION" && test -z "$POSIXLY_CORRECT"; then
+if test -n "$ZSH_VERSION" && test -z "$POSIXLY_CORRECT" && [[ ! -o FUNCTION_ARGZERO ]]; then
 	true
 elif command -v zsh >/dev/null 2>&1; then
 	unset POSIXLY_CORRECT
-	exec zsh "$0" "$@"
+	# Run Zsh with the FUNCTION_ARGZERO option disabled so that
+	# test-lib.sh sees the test script pathname when it examines
+	# $0 instead of "./lib-zsh.sh".  (This works around a Zsh bug;
+	# 'emulate sh -c' should temporarily restore $0 to the POSIX
+	# specification for $0, but it doesn't.)
+	exec zsh +o FUNCTION_ARGZERO "$0" "$@"
 else
 	echo '1..0 #SKIP skipping Zsh-specific tests; zsh not available'
 	exit 0
 fi
 
-# ensure that we are in full-on Zsh mode
+# ensure that we are in full-on Zsh mode.  note: this re-enables the
+# FUNCTION_ARGZERO option
 emulate -R zsh || exit 1
 
 shellname=Zsh
@@ -27,4 +33,7 @@ set_ps1_format_vars () {
 	c_clear='%%f'
 }
 
+# note: although the FUNCTION_ARGZERO option is currently enabled, sh
+# emulation mode temporarily turns it off ($0 is left alone when
+# sourcing test-lib.sh)
 emulate sh -c '. ./test-lib.sh'
-- 
2.0.0
Richard Hansen· Jun 4, 2014, 21:01 UTC · re: Richard Hansen · lore

[PATCH v2 00/11] Zsh prompt tests

Changes from v1:
  * fix a bug that caused the Zsh test cases to run in Zsh's sh
    emulation mode, not Zsh "native" mode
Description:

This series adds test cases for running __git_ps1 (see contrib/completion/git-prompt.sh) from Zsh.

This series also adds more Bash test cases to test how __git_ps1 reacts to disabling Bash's PS1 parameter expansion. (This is related to adding Zsh test cases: Zsh doesn't perform parameter expansion on PS1 by default but many users turn it on, so the Zsh test script must test __git_ps1 in both states. Bash expands PS1 by default and users rarely turn it off, but testing both states in Bash improves the symmetry with the Zsh test cases.)

This is the approach I took:
  1. delete the last test case in t9903 ("prompt - zsh color pc mode")
  2. add two new functions to t/lib-bash.sh:
         ps1_expansion_enable () { shopt -s promptvars; }
         ps1_expansion_disable () { shopt -u promptvars; }
  3. loop over the relevant test cases twice:  once after calling
     ps1_expansion_enable and once after calling ps1_expansion_disable
     (with appropriate adjustments to the expected output)
  4. move the test cases in t9903 to a separate library file and
     source it from t9903-bash-prompt.sh
  5. create two new files:
       * t/lib-zsh.sh (same as t/lib-bash.sh but tweaked for zsh)
       * t/t9904-zsh-prompt.sh (same as t/t9903-bash-prompt.sh but
         tweaked for zsh)

There are a lot of indendation changes, so I recommend examining the changes via diff -w.

Richard Hansen (11):
  t9903: remove Zsh test from the suite of Bash prompt tests
  t9903: put the Bash pc mode prompt test cases in a function
  t9903: move test name prefix to a separate variable
  t9903: run pc mode tests again with PS1 expansion disabled
  t9903: include "Bash" in test names via new $shellname var
  t9903: move PS1 color code variable definitions to lib-bash.sh
  t9903: move prompt tests to a new lib-prompt-tests.sh file
  lib-prompt-tests.sh: put all tests inside a function
  lib-prompt-tests.sh: add variable for string that encodes percent in
    PS1
  test-lib: make it possible to override how test code is eval'd
  t9904: new __git_ps1 tests for Zsh
 t/lib-bash.sh          |  12 +
 t/lib-prompt-tests.sh  | 654 +++++++++++++++++++++++++++++++++++++++++++++++++
 t/lib-zsh.sh           |  52 ++++
 t/t9903-bash-prompt.sh | 582 +------------------------------------------
 t/t9904-zsh-prompt.sh  |  10 +
 t/test-lib.sh          |   7 +-
 6 files changed, 736 insertions(+), 581 deletions(-)
 create mode 100644 t/lib-prompt-tests.sh
 create mode 100644 t/lib-zsh.sh
 create mode 100755 t/t9904-zsh-prompt.sh
-- 
2.0.0
Richard Hansen· Jun 4, 2014, 21:01 UTC · re: Richard Hansen · lore

[PATCH v2 01/11] t9903: remove Zsh test from the suite of Bash prompt tests

This test is about to become redundant: All of the Bash prompt tests will be moved into a separate library file that will also be used by a new Zsh-specific test script.

Signed-off-by: Richard Hansen <rhansen@bbn.com>
---
 t/t9903-bash-prompt.sh | 11 -----------
 1 file changed, 11 deletions(-)
Show changes to t/t9903-bash-prompt.sh +0 −11
diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh
index 9150984..335383d 100755
--- a/t/t9903-bash-prompt.sh
+++ b/t/t9903-bash-prompt.sh
@@ -577,15 +577,4 @@ test_expect_success 'prompt - bash color pc mode - untracked files status indica
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - zsh color pc mode' '
-	printf "BEFORE: (%%F{green}master%%f):AFTER" >expected &&
-	(
-		ZSH_VERSION=5.0.0 &&
-		GIT_PS1_SHOWCOLORHINTS=y &&
-		__git_ps1 "BEFORE:" ":AFTER" &&
-		printf "%s" "$PS1" >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
 test_done
-- 
2.0.0
Richard Hansen· Jun 4, 2014, 21:01 UTC · re: Richard Hansen · lore

[PATCH v2 02/11] t9903: put the Bash pc mode prompt test cases in a function

This is a step toward invoking the same pc mode test cases twice: once with PS1 parameter expansion enabled and once with it disabled.

Signed-off-by: Richard Hansen <rhansen@bbn.com>
---
 t/t9903-bash-prompt.sh | 236 +++++++++++++++++++++++++------------------------
 1 file changed, 120 insertions(+), 116 deletions(-)
Show changes to t/t9903-bash-prompt.sh +120 −116
diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh
index 335383d..c691869 100755
--- a/t/t9903-bash-prompt.sh
+++ b/t/t9903-bash-prompt.sh
@@ -451,130 +451,134 @@ test_expect_success 'prompt - format string starting with dash' '
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - pc mode' '
-	printf "BEFORE: (\${__git_ps1_branch_name}):AFTER\\nmaster" >expected &&
-	printf "" >expected_output &&
-	(
-		__git_ps1 "BEFORE:" ":AFTER" >"$actual" &&
-		test_cmp expected_output "$actual" &&
-		printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+run_pcmode_tests () {
+	test_expect_success 'prompt - pc mode' '
+		printf "BEFORE: (\${__git_ps1_branch_name}):AFTER\\nmaster" >expected &&
+		printf "" >expected_output &&
+		(
+			__git_ps1 "BEFORE:" ":AFTER" >"$actual" &&
+			test_cmp expected_output "$actual" &&
+			printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success 'prompt - bash color pc mode - branch name' '
-	printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear}):AFTER\\nmaster" >expected &&
-	(
-		GIT_PS1_SHOWCOLORHINTS=y &&
-		__git_ps1 "BEFORE:" ":AFTER" >"$actual"
-		printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success 'prompt - bash color pc mode - branch name' '
+		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear}):AFTER\\nmaster" >expected &&
+		(
+			GIT_PS1_SHOWCOLORHINTS=y &&
+			__git_ps1 "BEFORE:" ":AFTER" >"$actual"
+			printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success 'prompt - bash color pc mode - detached head' '
-	printf "BEFORE: (${c_red}\${__git_ps1_branch_name}${c_clear}):AFTER\\n(%s...)" $(git log -1 --format="%h" b1^) >expected &&
-	git checkout b1^ &&
-	test_when_finished "git checkout master" &&
-	(
-		GIT_PS1_SHOWCOLORHINTS=y &&
-		__git_ps1 "BEFORE:" ":AFTER" &&
-		printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success 'prompt - bash color pc mode - detached head' '
+		printf "BEFORE: (${c_red}\${__git_ps1_branch_name}${c_clear}):AFTER\\n(%s...)" $(git log -1 --format="%h" b1^) >expected &&
+		git checkout b1^ &&
+		test_when_finished "git checkout master" &&
+		(
+			GIT_PS1_SHOWCOLORHINTS=y &&
+			__git_ps1 "BEFORE:" ":AFTER" &&
+			printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success 'prompt - bash color pc mode - dirty status indicator - dirty worktree' '
-	printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_red}*${c_clear}):AFTER\\nmaster" >expected &&
-	echo "dirty" >file &&
-	test_when_finished "git reset --hard" &&
-	(
-		GIT_PS1_SHOWDIRTYSTATE=y &&
-		GIT_PS1_SHOWCOLORHINTS=y &&
-		__git_ps1 "BEFORE:" ":AFTER" &&
-		printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success 'prompt - bash color pc mode - dirty status indicator - dirty worktree' '
+		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_red}*${c_clear}):AFTER\\nmaster" >expected &&
+		echo "dirty" >file &&
+		test_when_finished "git reset --hard" &&
+		(
+			GIT_PS1_SHOWDIRTYSTATE=y &&
+			GIT_PS1_SHOWCOLORHINTS=y &&
+			__git_ps1 "BEFORE:" ":AFTER" &&
+			printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success 'prompt - bash color pc mode - dirty status indicator - dirty index' '
-	printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_green}+${c_clear}):AFTER\\nmaster" >expected &&
-	echo "dirty" >file &&
-	test_when_finished "git reset --hard" &&
-	git add -u &&
-	(
-		GIT_PS1_SHOWDIRTYSTATE=y &&
-		GIT_PS1_SHOWCOLORHINTS=y &&
-		__git_ps1 "BEFORE:" ":AFTER" &&
-		printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success 'prompt - bash color pc mode - dirty status indicator - dirty index' '
+		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_green}+${c_clear}):AFTER\\nmaster" >expected &&
+		echo "dirty" >file &&
+		test_when_finished "git reset --hard" &&
+		git add -u &&
+		(
+			GIT_PS1_SHOWDIRTYSTATE=y &&
+			GIT_PS1_SHOWCOLORHINTS=y &&
+			__git_ps1 "BEFORE:" ":AFTER" &&
+			printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success 'prompt - bash color pc mode - dirty status indicator - dirty index and worktree' '
-	printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_red}*${c_green}+${c_clear}):AFTER\\nmaster" >expected &&
-	echo "dirty index" >file &&
-	test_when_finished "git reset --hard" &&
-	git add -u &&
-	echo "dirty worktree" >file &&
-	(
-		GIT_PS1_SHOWCOLORHINTS=y &&
-		GIT_PS1_SHOWDIRTYSTATE=y &&
-		__git_ps1 "BEFORE:" ":AFTER" &&
-		printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success 'prompt - bash color pc mode - dirty status indicator - dirty index and worktree' '
+		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_red}*${c_green}+${c_clear}):AFTER\\nmaster" >expected &&
+		echo "dirty index" >file &&
+		test_when_finished "git reset --hard" &&
+		git add -u &&
+		echo "dirty worktree" >file &&
+		(
+			GIT_PS1_SHOWCOLORHINTS=y &&
+			GIT_PS1_SHOWDIRTYSTATE=y &&
+			__git_ps1 "BEFORE:" ":AFTER" &&
+			printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success 'prompt - bash color pc mode - dirty status indicator - before root commit' '
-	printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_green}#${c_clear}):AFTER\\nmaster" >expected &&
-	(
-		GIT_PS1_SHOWDIRTYSTATE=y &&
-		GIT_PS1_SHOWCOLORHINTS=y &&
-		cd otherrepo &&
-		__git_ps1 "BEFORE:" ":AFTER" &&
-		printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success 'prompt - bash color pc mode - dirty status indicator - before root commit' '
+		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_green}#${c_clear}):AFTER\\nmaster" >expected &&
+		(
+			GIT_PS1_SHOWDIRTYSTATE=y &&
+			GIT_PS1_SHOWCOLORHINTS=y &&
+			cd otherrepo &&
+			__git_ps1 "BEFORE:" ":AFTER" &&
+			printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success 'prompt - bash color pc mode - inside .git directory' '
-	printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear}):AFTER\\nGIT_DIR!" >expected &&
-	echo "dirty" >file &&
-	test_when_finished "git reset --hard" &&
-	(
-		GIT_PS1_SHOWDIRTYSTATE=y &&
-		GIT_PS1_SHOWCOLORHINTS=y &&
-		cd .git &&
-		__git_ps1 "BEFORE:" ":AFTER" &&
-		printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success 'prompt - bash color pc mode - inside .git directory' '
+		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear}):AFTER\\nGIT_DIR!" >expected &&
+		echo "dirty" >file &&
+		test_when_finished "git reset --hard" &&
+		(
+			GIT_PS1_SHOWDIRTYSTATE=y &&
+			GIT_PS1_SHOWCOLORHINTS=y &&
+			cd .git &&
+			__git_ps1 "BEFORE:" ":AFTER" &&
+			printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success 'prompt - bash color pc mode - stash status indicator' '
-	printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_lblue}\$${c_clear}):AFTER\\nmaster" >expected &&
-	echo 2 >file &&
-	git stash &&
-	test_when_finished "git stash drop" &&
-	(
-		GIT_PS1_SHOWSTASHSTATE=y &&
-		GIT_PS1_SHOWCOLORHINTS=y &&
-		__git_ps1 "BEFORE:" ":AFTER" &&
-		printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success 'prompt - bash color pc mode - stash status indicator' '
+		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_lblue}\$${c_clear}):AFTER\\nmaster" >expected &&
+		echo 2 >file &&
+		git stash &&
+		test_when_finished "git stash drop" &&
+		(
+			GIT_PS1_SHOWSTASHSTATE=y &&
+			GIT_PS1_SHOWCOLORHINTS=y &&
+			__git_ps1 "BEFORE:" ":AFTER" &&
+			printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success 'prompt - bash color pc mode - untracked files status indicator' '
-	printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_red}%%${c_clear}):AFTER\\nmaster" >expected &&
-	(
-		GIT_PS1_SHOWUNTRACKEDFILES=y &&
-		GIT_PS1_SHOWCOLORHINTS=y &&
-		__git_ps1 "BEFORE:" ":AFTER" &&
-		printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success 'prompt - bash color pc mode - untracked files status indicator' '
+		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_red}%%${c_clear}):AFTER\\nmaster" >expected &&
+		(
+			GIT_PS1_SHOWUNTRACKEDFILES=y &&
+			GIT_PS1_SHOWCOLORHINTS=y &&
+			__git_ps1 "BEFORE:" ":AFTER" &&
+			printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
+}
+
+run_pcmode_tests
 
 test_done
-- 
2.0.0
Richard Hansen· Jun 4, 2014, 21:01 UTC · re: Richard Hansen · lore

[PATCH v2 03/11] t9903: move test name prefix to a separate variable

This is a step toward reusing the same test cases after disabling PS1 parameter expansion.

Signed-off-by: Richard Hansen <rhansen@bbn.com>
---
 t/t9903-bash-prompt.sh | 20 +++++++++++---------
 1 file changed, 11 insertions(+), 9 deletions(-)
Show changes to t/t9903-bash-prompt.sh +11 −9
diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh
index c691869..d29dd2b 100755
--- a/t/t9903-bash-prompt.sh
+++ b/t/t9903-bash-prompt.sh
@@ -463,7 +463,9 @@ run_pcmode_tests () {
 		test_cmp expected "$actual"
 	'
 
-	test_expect_success 'prompt - bash color pc mode - branch name' '
+	pfx="prompt - bash color pc mode"
+
+	test_expect_success "$pfx - branch name" '
 		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear}):AFTER\\nmaster" >expected &&
 		(
 			GIT_PS1_SHOWCOLORHINTS=y &&
@@ -473,7 +475,7 @@ run_pcmode_tests () {
 		test_cmp expected "$actual"
 	'
 
-	test_expect_success 'prompt - bash color pc mode - detached head' '
+	test_expect_success "$pfx - detached head" '
 		printf "BEFORE: (${c_red}\${__git_ps1_branch_name}${c_clear}):AFTER\\n(%s...)" $(git log -1 --format="%h" b1^) >expected &&
 		git checkout b1^ &&
 		test_when_finished "git checkout master" &&
@@ -485,7 +487,7 @@ run_pcmode_tests () {
 		test_cmp expected "$actual"
 	'
 
-	test_expect_success 'prompt - bash color pc mode - dirty status indicator - dirty worktree' '
+	test_expect_success "$pfx - dirty status indicator - dirty worktree" '
 		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_red}*${c_clear}):AFTER\\nmaster" >expected &&
 		echo "dirty" >file &&
 		test_when_finished "git reset --hard" &&
@@ -498,7 +500,7 @@ run_pcmode_tests () {
 		test_cmp expected "$actual"
 	'
 
-	test_expect_success 'prompt - bash color pc mode - dirty status indicator - dirty index' '
+	test_expect_success "$pfx - dirty status indicator - dirty index" '
 		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_green}+${c_clear}):AFTER\\nmaster" >expected &&
 		echo "dirty" >file &&
 		test_when_finished "git reset --hard" &&
@@ -512,7 +514,7 @@ run_pcmode_tests () {
 		test_cmp expected "$actual"
 	'
 
-	test_expect_success 'prompt - bash color pc mode - dirty status indicator - dirty index and worktree' '
+	test_expect_success "$pfx - dirty status indicator - dirty index and worktree" '
 		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_red}*${c_green}+${c_clear}):AFTER\\nmaster" >expected &&
 		echo "dirty index" >file &&
 		test_when_finished "git reset --hard" &&
@@ -527,7 +529,7 @@ run_pcmode_tests () {
 		test_cmp expected "$actual"
 	'
 
-	test_expect_success 'prompt - bash color pc mode - dirty status indicator - before root commit' '
+	test_expect_success "$pfx - dirty status indicator - before root commit" '
 		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_green}#${c_clear}):AFTER\\nmaster" >expected &&
 		(
 			GIT_PS1_SHOWDIRTYSTATE=y &&
@@ -539,7 +541,7 @@ run_pcmode_tests () {
 		test_cmp expected "$actual"
 	'
 
-	test_expect_success 'prompt - bash color pc mode - inside .git directory' '
+	test_expect_success "$pfx - inside .git directory" '
 		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear}):AFTER\\nGIT_DIR!" >expected &&
 		echo "dirty" >file &&
 		test_when_finished "git reset --hard" &&
@@ -553,7 +555,7 @@ run_pcmode_tests () {
 		test_cmp expected "$actual"
 	'
 
-	test_expect_success 'prompt - bash color pc mode - stash status indicator' '
+	test_expect_success "$pfx - stash status indicator" '
 		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_lblue}\$${c_clear}):AFTER\\nmaster" >expected &&
 		echo 2 >file &&
 		git stash &&
@@ -567,7 +569,7 @@ run_pcmode_tests () {
 		test_cmp expected "$actual"
 	'
 
-	test_expect_success 'prompt - bash color pc mode - untracked files status indicator' '
+	test_expect_success "$pfx - untracked files status indicator" '
 		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_red}%%${c_clear}):AFTER\\nmaster" >expected &&
 		(
 			GIT_PS1_SHOWUNTRACKEDFILES=y &&
-- 
2.0.0
Richard Hansen· Jun 4, 2014, 21:01 UTC · re: Richard Hansen · lore

[PATCH v2 04/11] t9903: run pc mode tests again with PS1 expansion disabled

Bash has a shell option that makes it possible to disable parameter expansion in PS1. Test __git_ps1's ability to detect and react to disabled PS1 expansion by running the "pc mode" tests twice: once with PS1 parameter expansion enabled and once with it disabled.

Signed-off-by: Richard Hansen <rhansen@bbn.com>
---
 t/lib-bash.sh          |  3 ++
 t/t9903-bash-prompt.sh | 96 ++++++++++++++++++++++++++++++++++++++------------
 2 files changed, 76 insertions(+), 23 deletions(-)
Show changes to 2 files +76 −23

t/lib-bash.sh, t/t9903-bash-prompt.sh

diff --git a/t/lib-bash.sh b/t/lib-bash.sh
index 2be955f..37a48fd 100644
--- a/t/lib-bash.sh
+++ b/t/lib-bash.sh
@@ -14,4 +14,7 @@ else
 	exit 0
 fi
 
+ps1_expansion_enable () { shopt -s promptvars; }
+ps1_expansion_disable () { shopt -u promptvars; }
+
 . ./test-lib.sh
diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh
index d29dd2b..eb5a167 100755
--- a/t/t9903-bash-prompt.sh
+++ b/t/t9903-bash-prompt.sh
@@ -451,57 +451,97 @@ test_expect_success 'prompt - format string starting with dash' '
 	test_cmp expected "$actual"
 '
 
-run_pcmode_tests () {
-	test_expect_success 'prompt - pc mode' '
-		printf "BEFORE: (\${__git_ps1_branch_name}):AFTER\\nmaster" >expected &&
+pcmode_expected () {
+	case $ps1expansion in
+	on) printf "$1" '${__git_ps1_branch_name}' "$2";;
+	off) printf "$1" "$2" "";;
+	esac >expected
+}
+
+pcmode_actual () {
+	case $ps1expansion in
+	on) printf %s\\n%s "$PS1" "${__git_ps1_branch_name}";;
+	off) printf %s\\n "$PS1";;
+	esac >"$actual"
+}
+
+set_ps1expansion () {
+	case $ps1expansion in
+	on) ps1_expansion_enable;;
+	off) ps1_expansion_disable;;
+	*) error "invalid argument to _run_pcmode_tests: $ps1expansion";;
+	esac
+}
+
+_run_pcmode_tests () {
+	ps1expansion=$1; shift
+
+	# Test whether the shell supports enabling/disabling PS1
+	# expansion by running set_ps1expansion.  If not, quietly skip
+	# this set of tests.
+	#
+	# Even though set_ps1expansion is run here, it must also be
+	# run inside each individual test case because the state of
+	# the shell might be reset in some fashion before executing
+	# the test code.  (Notably, Zsh shell emulation causes the
+	# PROMPT_SUBST option to be reset each time a test is run.)
+	set_ps1expansion || return 0
+
+	test_expect_success "prompt - pc mode (PS1 expansion $ps1expansion)" '
+		set_ps1expansion &&
+		pcmode_expected "BEFORE: (%s):AFTER\\n%s" master &&
 		printf "" >expected_output &&
 		(
 			__git_ps1 "BEFORE:" ":AFTER" >"$actual" &&
 			test_cmp expected_output "$actual" &&
-			printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+			pcmode_actual
 		) &&
 		test_cmp expected "$actual"
 	'
 
-	pfx="prompt - bash color pc mode"
+	pfx="prompt - bash color pc mode (PS1 expansion $ps1expansion)"
 
 	test_expect_success "$pfx - branch name" '
-		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear}):AFTER\\nmaster" >expected &&
+		set_ps1expansion &&
+		pcmode_expected "BEFORE: (${c_green}%s${c_clear}):AFTER\\n%s" master &&
 		(
 			GIT_PS1_SHOWCOLORHINTS=y &&
 			__git_ps1 "BEFORE:" ":AFTER" >"$actual"
-			printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+			pcmode_actual
 		) &&
 		test_cmp expected "$actual"
 	'
 
 	test_expect_success "$pfx - detached head" '
-		printf "BEFORE: (${c_red}\${__git_ps1_branch_name}${c_clear}):AFTER\\n(%s...)" $(git log -1 --format="%h" b1^) >expected &&
+		set_ps1expansion &&
+		pcmode_expected "BEFORE: (${c_red}%s${c_clear}):AFTER\\n%s" "($(git log -1 --format="%h" b1^)...)" &&
 		git checkout b1^ &&
 		test_when_finished "git checkout master" &&
 		(
 			GIT_PS1_SHOWCOLORHINTS=y &&
 			__git_ps1 "BEFORE:" ":AFTER" &&
-			printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+			pcmode_actual
 		) &&
 		test_cmp expected "$actual"
 	'
 
 	test_expect_success "$pfx - dirty status indicator - dirty worktree" '
-		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_red}*${c_clear}):AFTER\\nmaster" >expected &&
+		set_ps1expansion &&
+		pcmode_expected "BEFORE: (${c_green}%s${c_clear} ${c_red}*${c_clear}):AFTER\\n%s" master &&
 		echo "dirty" >file &&
 		test_when_finished "git reset --hard" &&
 		(
 			GIT_PS1_SHOWDIRTYSTATE=y &&
 			GIT_PS1_SHOWCOLORHINTS=y &&
 			__git_ps1 "BEFORE:" ":AFTER" &&
-			printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+			pcmode_actual
 		) &&
 		test_cmp expected "$actual"
 	'
 
 	test_expect_success "$pfx - dirty status indicator - dirty index" '
-		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_green}+${c_clear}):AFTER\\nmaster" >expected &&
+		set_ps1expansion &&
+		pcmode_expected "BEFORE: (${c_green}%s${c_clear} ${c_green}+${c_clear}):AFTER\\n%s" master &&
 		echo "dirty" >file &&
 		test_when_finished "git reset --hard" &&
 		git add -u &&
@@ -509,13 +549,14 @@ run_pcmode_tests () {
 			GIT_PS1_SHOWDIRTYSTATE=y &&
 			GIT_PS1_SHOWCOLORHINTS=y &&
 			__git_ps1 "BEFORE:" ":AFTER" &&
-			printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+			pcmode_actual
 		) &&
 		test_cmp expected "$actual"
 	'
 
 	test_expect_success "$pfx - dirty status indicator - dirty index and worktree" '
-		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_red}*${c_green}+${c_clear}):AFTER\\nmaster" >expected &&
+		set_ps1expansion &&
+		pcmode_expected "BEFORE: (${c_green}%s${c_clear} ${c_red}*${c_green}+${c_clear}):AFTER\\n%s" master &&
 		echo "dirty index" >file &&
 		test_when_finished "git reset --hard" &&
 		git add -u &&
@@ -524,25 +565,27 @@ run_pcmode_tests () {
 			GIT_PS1_SHOWCOLORHINTS=y &&
 			GIT_PS1_SHOWDIRTYSTATE=y &&
 			__git_ps1 "BEFORE:" ":AFTER" &&
-			printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+			pcmode_actual
 		) &&
 		test_cmp expected "$actual"
 	'
 
 	test_expect_success "$pfx - dirty status indicator - before root commit" '
-		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_green}#${c_clear}):AFTER\\nmaster" >expected &&
+		set_ps1expansion &&
+		pcmode_expected "BEFORE: (${c_green}%s${c_clear} ${c_green}#${c_clear}):AFTER\\n%s" master &&
 		(
 			GIT_PS1_SHOWDIRTYSTATE=y &&
 			GIT_PS1_SHOWCOLORHINTS=y &&
 			cd otherrepo &&
 			__git_ps1 "BEFORE:" ":AFTER" &&
-			printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+			pcmode_actual
 		) &&
 		test_cmp expected "$actual"
 	'
 
 	test_expect_success "$pfx - inside .git directory" '
-		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear}):AFTER\\nGIT_DIR!" >expected &&
+		set_ps1expansion &&
+		pcmode_expected "BEFORE: (${c_green}%s${c_clear}):AFTER\\n%s" "GIT_DIR!" &&
 		echo "dirty" >file &&
 		test_when_finished "git reset --hard" &&
 		(
@@ -550,13 +593,14 @@ run_pcmode_tests () {
 			GIT_PS1_SHOWCOLORHINTS=y &&
 			cd .git &&
 			__git_ps1 "BEFORE:" ":AFTER" &&
-			printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+			pcmode_actual
 		) &&
 		test_cmp expected "$actual"
 	'
 
 	test_expect_success "$pfx - stash status indicator" '
-		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_lblue}\$${c_clear}):AFTER\\nmaster" >expected &&
+		set_ps1expansion &&
+		pcmode_expected "BEFORE: (${c_green}%s${c_clear} ${c_lblue}\$${c_clear}):AFTER\\n%s" master &&
 		echo 2 >file &&
 		git stash &&
 		test_when_finished "git stash drop" &&
@@ -564,23 +608,29 @@ run_pcmode_tests () {
 			GIT_PS1_SHOWSTASHSTATE=y &&
 			GIT_PS1_SHOWCOLORHINTS=y &&
 			__git_ps1 "BEFORE:" ":AFTER" &&
-			printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+			pcmode_actual
 		) &&
 		test_cmp expected "$actual"
 	'
 
 	test_expect_success "$pfx - untracked files status indicator" '
-		printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_red}%%${c_clear}):AFTER\\nmaster" >expected &&
+		set_ps1expansion &&
+		pcmode_expected "BEFORE: (${c_green}%s${c_clear} ${c_red}%%${c_clear}):AFTER\\n%s" master &&
 		(
 			GIT_PS1_SHOWUNTRACKEDFILES=y &&
 			GIT_PS1_SHOWCOLORHINTS=y &&
 			__git_ps1 "BEFORE:" ":AFTER" &&
-			printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
+			pcmode_actual
 		) &&
 		test_cmp expected "$actual"
 	'
 }
 
+run_pcmode_tests () {
+	_run_pcmode_tests on
+	_run_pcmode_tests off
+}
+
 run_pcmode_tests
 
 test_done
-- 
2.0.0
Richard Hansen· Jun 4, 2014, 21:01 UTC · re: Richard Hansen · lore

[PATCH v2 05/11] t9903: include "Bash" in test names via new $shellname var

Define a new 'shellname' variable in lib-bash.sh and use it in the prompt test names. This is a step toward moving the shell prompt tests to a separate library file so that they can be reused to test prompting in Zsh.

Signed-off-by: Richard Hansen <rhansen@bbn.com>
---
 t/lib-bash.sh          |  2 ++
 t/t9903-bash-prompt.sh | 86 ++++++++++++++++++++++++++------------------------
 2 files changed, 46 insertions(+), 42 deletions(-)
Show changes to 2 files +46 −42

t/lib-bash.sh, t/t9903-bash-prompt.sh

diff --git a/t/lib-bash.sh b/t/lib-bash.sh
index 37a48fd..a0f4e16 100644
--- a/t/lib-bash.sh
+++ b/t/lib-bash.sh
@@ -14,6 +14,8 @@ else
 	exit 0
 fi
 
+shellname=Bash
+
 ps1_expansion_enable () { shopt -s promptvars; }
 ps1_expansion_disable () { shopt -u promptvars; }
 
diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh
index eb5a167..27135a1 100755
--- a/t/t9903-bash-prompt.sh
+++ b/t/t9903-bash-prompt.sh
@@ -15,7 +15,7 @@ c_green='\\[\\e[32m\\]'
 c_lblue='\\[\\e[1;34m\\]'
 c_clear='\\[\\e[0m\\]'
 
-test_expect_success 'setup for prompt tests' '
+test_expect_success "setup for $shellname prompt tests" '
 	git init otherrepo &&
 	echo 1 >file &&
 	git add file &&
@@ -38,13 +38,15 @@ test_expect_success 'setup for prompt tests' '
 	git checkout master
 '
 
-test_expect_success 'prompt - branch name' '
+pfx="$shellname prompt"
+
+test_expect_success "$pfx - branch name" '
 	printf " (master)" >expected &&
 	__git_ps1 >"$actual" &&
 	test_cmp expected "$actual"
 '
 
-test_expect_success SYMLINKS 'prompt - branch name - symlink symref' '
+test_expect_success SYMLINKS "$pfx - branch name - symlink symref" '
 	printf " (master)" >expected &&
 	test_when_finished "git checkout master" &&
 	test_config core.preferSymlinkRefs true &&
@@ -53,7 +55,7 @@ test_expect_success SYMLINKS 'prompt - branch name - symlink symref' '
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - unborn branch' '
+test_expect_success "$pfx - unborn branch" '
 	printf " (unborn)" >expected &&
 	git checkout --orphan unborn &&
 	test_when_finished "git checkout master" &&
@@ -72,7 +74,7 @@ else
 	say 'Your filesystem does not allow newlines in filenames.'
 fi
 
-test_expect_success FUNNYNAMES 'prompt - with newline in path' '
+test_expect_success FUNNYNAMES "$pfx - with newline in path" '
 	printf " (master)" >expected &&
 	git init "$repo_with_newline" &&
 	test_when_finished "rm -rf \"$repo_with_newline\"" &&
@@ -84,7 +86,7 @@ test_expect_success FUNNYNAMES 'prompt - with newline in path' '
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - detached head' '
+test_expect_success "$pfx - detached head" '
 	printf " ((%s...))" $(git log -1 --format="%h" --abbrev=13 b1^) >expected &&
 	test_config core.abbrev 13 &&
 	git checkout b1^ &&
@@ -93,7 +95,7 @@ test_expect_success 'prompt - detached head' '
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - describe detached head - contains' '
+test_expect_success "$pfx - describe detached head - contains" '
 	printf " ((t2~1))" >expected &&
 	git checkout b1^ &&
 	test_when_finished "git checkout master" &&
@@ -104,7 +106,7 @@ test_expect_success 'prompt - describe detached head - contains' '
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - describe detached head - branch' '
+test_expect_success "$pfx - describe detached head - branch" '
 	printf " ((b1~1))" >expected &&
 	git checkout b1^ &&
 	test_when_finished "git checkout master" &&
@@ -115,7 +117,7 @@ test_expect_success 'prompt - describe detached head - branch' '
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - describe detached head - describe' '
+test_expect_success "$pfx - describe detached head - describe" '
 	printf " ((t1-1-g%s))" $(git log -1 --format="%h" b1^) >expected &&
 	git checkout b1^ &&
 	test_when_finished "git checkout master" &&
@@ -126,7 +128,7 @@ test_expect_success 'prompt - describe detached head - describe' '
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - describe detached head - default' '
+test_expect_success "$pfx - describe detached head - default" '
 	printf " ((t2))" >expected &&
 	git checkout --detach b1 &&
 	test_when_finished "git checkout master" &&
@@ -134,7 +136,7 @@ test_expect_success 'prompt - describe detached head - default' '
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - inside .git directory' '
+test_expect_success "$pfx - inside .git directory" '
 	printf " (GIT_DIR!)" >expected &&
 	(
 		cd .git &&
@@ -143,7 +145,7 @@ test_expect_success 'prompt - inside .git directory' '
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - deep inside .git directory' '
+test_expect_success "$pfx - deep inside .git directory" '
 	printf " (GIT_DIR!)" >expected &&
 	(
 		cd .git/refs/heads &&
@@ -152,7 +154,7 @@ test_expect_success 'prompt - deep inside .git directory' '
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - inside bare repository' '
+test_expect_success "$pfx - inside bare repository" '
 	printf " (BARE:master)" >expected &&
 	git init --bare bare.git &&
 	test_when_finished "rm -rf bare.git" &&
@@ -163,7 +165,7 @@ test_expect_success 'prompt - inside bare repository' '
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - interactive rebase' '
+test_expect_success "$pfx - interactive rebase" '
 	printf " (b1|REBASE-i 2/3)" >expected
 	write_script fake_editor.sh <<-\EOF &&
 		echo "exec echo" >"$1"
@@ -180,7 +182,7 @@ test_expect_success 'prompt - interactive rebase' '
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - rebase merge' '
+test_expect_success "$pfx - rebase merge" '
 	printf " (b2|REBASE-m 1/3)" >expected &&
 	git checkout b2 &&
 	test_when_finished "git checkout master" &&
@@ -190,7 +192,7 @@ test_expect_success 'prompt - rebase merge' '
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - rebase' '
+test_expect_success "$pfx - rebase" '
 	printf " (b2|REBASE 1/3)" >expected &&
 	git checkout b2 &&
 	test_when_finished "git checkout master" &&
@@ -200,7 +202,7 @@ test_expect_success 'prompt - rebase' '
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - merge' '
+test_expect_success "$pfx - merge" '
 	printf " (b1|MERGING)" >expected &&
 	git checkout b1 &&
 	test_when_finished "git checkout master" &&
@@ -210,7 +212,7 @@ test_expect_success 'prompt - merge' '
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - cherry-pick' '
+test_expect_success "$pfx - cherry-pick" '
 	printf " (master|CHERRY-PICKING)" >expected &&
 	test_must_fail git cherry-pick b1 &&
 	test_when_finished "git reset --hard" &&
@@ -218,7 +220,7 @@ test_expect_success 'prompt - cherry-pick' '
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - bisect' '
+test_expect_success "$pfx - bisect" '
 	printf " (master|BISECTING)" >expected &&
 	git bisect start &&
 	test_when_finished "git bisect reset" &&
@@ -226,7 +228,7 @@ test_expect_success 'prompt - bisect' '
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - dirty status indicator - clean' '
+test_expect_success "$pfx - dirty status indicator - clean" '
 	printf " (master)" >expected &&
 	(
 		GIT_PS1_SHOWDIRTYSTATE=y &&
@@ -235,7 +237,7 @@ test_expect_success 'prompt - dirty status indicator - clean' '
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - dirty status indicator - dirty worktree' '
+test_expect_success "$pfx - dirty status indicator - dirty worktree" '
 	printf " (master *)" >expected &&
 	echo "dirty" >file &&
 	test_when_finished "git reset --hard" &&
@@ -246,7 +248,7 @@ test_expect_success 'prompt - dirty status indicator - dirty worktree' '
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - dirty status indicator - dirty index' '
+test_expect_success "$pfx - dirty status indicator - dirty index" '
 	printf " (master +)" >expected &&
 	echo "dirty" >file &&
 	test_when_finished "git reset --hard" &&
@@ -258,7 +260,7 @@ test_expect_success 'prompt - dirty status indicator - dirty index' '
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - dirty status indicator - dirty index and worktree' '
+test_expect_success "$pfx - dirty status indicator - dirty index and worktree" '
 	printf " (master *+)" >expected &&
 	echo "dirty index" >file &&
 	test_when_finished "git reset --hard" &&
@@ -271,7 +273,7 @@ test_expect_success 'prompt - dirty status indicator - dirty index and worktree'
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - dirty status indicator - before root commit' '
+test_expect_success "$pfx - dirty status indicator - before root commit" '
 	printf " (master #)" >expected &&
 	(
 		GIT_PS1_SHOWDIRTYSTATE=y &&
@@ -281,7 +283,7 @@ test_expect_success 'prompt - dirty status indicator - before root commit' '
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - dirty status indicator - shell variable unset with config disabled' '
+test_expect_success "$pfx - dirty status indicator - shell variable unset with config disabled" '
 	printf " (master)" >expected &&
 	echo "dirty" >file &&
 	test_when_finished "git reset --hard" &&
@@ -293,7 +295,7 @@ test_expect_success 'prompt - dirty status indicator - shell variable unset with
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - dirty status indicator - shell variable unset with config enabled' '
+test_expect_success "$pfx - dirty status indicator - shell variable unset with config enabled" '
 	printf " (master)" >expected &&
 	echo "dirty" >file &&
 	test_when_finished "git reset --hard" &&
@@ -305,7 +307,7 @@ test_expect_success 'prompt - dirty status indicator - shell variable unset with
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - dirty status indicator - shell variable set with config disabled' '
+test_expect_success "$pfx - dirty status indicator - shell variable set with config disabled" '
 	printf " (master)" >expected &&
 	echo "dirty" >file &&
 	test_when_finished "git reset --hard" &&
@@ -317,7 +319,7 @@ test_expect_success 'prompt - dirty status indicator - shell variable set with c
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - dirty status indicator - shell variable set with config enabled' '
+test_expect_success "$pfx - dirty status indicator - shell variable set with config enabled" '
 	printf " (master *)" >expected &&
 	echo "dirty" >file &&
 	test_when_finished "git reset --hard" &&
@@ -329,7 +331,7 @@ test_expect_success 'prompt - dirty status indicator - shell variable set with c
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - dirty status indicator - not shown inside .git directory' '
+test_expect_success "$pfx - dirty status indicator - not shown inside .git directory" '
 	printf " (GIT_DIR!)" >expected &&
 	echo "dirty" >file &&
 	test_when_finished "git reset --hard" &&
@@ -341,7 +343,7 @@ test_expect_success 'prompt - dirty status indicator - not shown inside .git dir
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - stash status indicator - no stash' '
+test_expect_success "$pfx - stash status indicator - no stash" '
 	printf " (master)" >expected &&
 	(
 		GIT_PS1_SHOWSTASHSTATE=y &&
@@ -350,7 +352,7 @@ test_expect_success 'prompt - stash status indicator - no stash' '
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - stash status indicator - stash' '
+test_expect_success "$pfx - stash status indicator - stash" '
 	printf " (master $)" >expected &&
 	echo 2 >file &&
 	git stash &&
@@ -363,7 +365,7 @@ test_expect_success 'prompt - stash status indicator - stash' '
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - stash status indicator - not shown inside .git directory' '
+test_expect_success "$pfx - stash status indicator - not shown inside .git directory" '
 	printf " (GIT_DIR!)" >expected &&
 	echo 2 >file &&
 	git stash &&
@@ -376,7 +378,7 @@ test_expect_success 'prompt - stash status indicator - not shown inside .git dir
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - untracked files status indicator - no untracked files' '
+test_expect_success "$pfx - untracked files status indicator - no untracked files" '
 	printf " (master)" >expected &&
 	(
 		GIT_PS1_SHOWUNTRACKEDFILES=y &&
@@ -386,7 +388,7 @@ test_expect_success 'prompt - untracked files status indicator - no untracked fi
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - untracked files status indicator - untracked files' '
+test_expect_success "$pfx - untracked files status indicator - untracked files" '
 	printf " (master %%)" >expected &&
 	(
 		GIT_PS1_SHOWUNTRACKEDFILES=y &&
@@ -395,7 +397,7 @@ test_expect_success 'prompt - untracked files status indicator - untracked files
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - untracked files status indicator - shell variable unset with config disabled' '
+test_expect_success "$pfx - untracked files status indicator - shell variable unset with config disabled" '
 	printf " (master)" >expected &&
 	test_config bash.showUntrackedFiles false &&
 	(
@@ -405,7 +407,7 @@ test_expect_success 'prompt - untracked files status indicator - shell variable
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - untracked files status indicator - shell variable unset with config enabled' '
+test_expect_success "$pfx - untracked files status indicator - shell variable unset with config enabled" '
 	printf " (master)" >expected &&
 	test_config bash.showUntrackedFiles true &&
 	(
@@ -415,7 +417,7 @@ test_expect_success 'prompt - untracked files status indicator - shell variable
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - untracked files status indicator - shell variable set with config disabled' '
+test_expect_success "$pfx - untracked files status indicator - shell variable set with config disabled" '
 	printf " (master)" >expected &&
 	test_config bash.showUntrackedFiles false &&
 	(
@@ -425,7 +427,7 @@ test_expect_success 'prompt - untracked files status indicator - shell variable
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - untracked files status indicator - shell variable set with config enabled' '
+test_expect_success "$pfx - untracked files status indicator - shell variable set with config enabled" '
 	printf " (master %%)" >expected &&
 	test_config bash.showUntrackedFiles true &&
 	(
@@ -435,7 +437,7 @@ test_expect_success 'prompt - untracked files status indicator - shell variable
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - untracked files status indicator - not shown inside .git directory' '
+test_expect_success "$pfx - untracked files status indicator - not shown inside .git directory" '
 	printf " (GIT_DIR!)" >expected &&
 	(
 		GIT_PS1_SHOWUNTRACKEDFILES=y &&
@@ -445,7 +447,7 @@ test_expect_success 'prompt - untracked files status indicator - not shown insid
 	test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - format string starting with dash' '
+test_expect_success "$pfx - format string starting with dash" '
 	printf -- "-master" >expected &&
 	__git_ps1 "-%s" >"$actual" &&
 	test_cmp expected "$actual"
@@ -487,7 +489,7 @@ _run_pcmode_tests () {
 	# PROMPT_SUBST option to be reset each time a test is run.)
 	set_ps1expansion || return 0
 
-	test_expect_success "prompt - pc mode (PS1 expansion $ps1expansion)" '
+	test_expect_success "$shellname prompt - pc mode (PS1 expansion $ps1expansion)" '
 		set_ps1expansion &&
 		pcmode_expected "BEFORE: (%s):AFTER\\n%s" master &&
 		printf "" >expected_output &&
@@ -499,7 +501,7 @@ _run_pcmode_tests () {
 		test_cmp expected "$actual"
 	'
 
-	pfx="prompt - bash color pc mode (PS1 expansion $ps1expansion)"
+	pfx="$shellname prompt - color pc mode (PS1 expansion $ps1expansion)"
 
 	test_expect_success "$pfx - branch name" '
 		set_ps1expansion &&
-- 
2.0.0
Richard Hansen· Jun 4, 2014, 21:01 UTC · re: Richard Hansen · lore

[PATCH v2 06/11] t9903: move PS1 color code variable definitions to lib-bash.sh

Define a new 'set_ps1_format_vars' function in lib-bash.sh that sets the c_red, c_green, c_lblue, and c_clear variables. Call this function from run_pcmode_tests(). This is a step toward moving the shell prompt tests to a separate library file so that they can be reused to test prompting in Zsh.

Signed-off-by: Richard Hansen <rhansen@bbn.com>
---
 t/lib-bash.sh          | 6 ++++++
 t/t9903-bash-prompt.sh | 5 +----
 2 files changed, 7 insertions(+), 4 deletions(-)
Show changes to 2 files +7 −4

t/lib-bash.sh, t/t9903-bash-prompt.sh

diff --git a/t/lib-bash.sh b/t/lib-bash.sh
index a0f4e16..9d428bd 100644
--- a/t/lib-bash.sh
+++ b/t/lib-bash.sh
@@ -18,5 +18,11 @@ shellname=Bash
 
 ps1_expansion_enable () { shopt -s promptvars; }
 ps1_expansion_disable () { shopt -u promptvars; }
+set_ps1_format_vars () {
+	c_red='\\[\\e[31m\\]'
+	c_green='\\[\\e[32m\\]'
+	c_lblue='\\[\\e[1;34m\\]'
+	c_clear='\\[\\e[0m\\]'
+}
 
 . ./test-lib.sh
diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh
index 27135a1..fe60cf9 100755
--- a/t/t9903-bash-prompt.sh
+++ b/t/t9903-bash-prompt.sh
@@ -10,10 +10,7 @@ test_description='test git-specific bash prompt functions'
 . "$GIT_BUILD_DIR/contrib/completion/git-prompt.sh"
 
 actual="$TRASH_DIRECTORY/actual"
-c_red='\\[\\e[31m\\]'
-c_green='\\[\\e[32m\\]'
-c_lblue='\\[\\e[1;34m\\]'
-c_clear='\\[\\e[0m\\]'
+set_ps1_format_vars
 
 test_expect_success "setup for $shellname prompt tests" '
 	git init otherrepo &&
-- 
2.0.0
Richard Hansen· Jun 4, 2014, 21:01 UTC · re: Richard Hansen · lore

[PATCH v2 07/11] t9903: move prompt tests to a new lib-prompt-tests.sh file

This is a step toward creating a new test script that runs the same prompt tests as t9903 but with Zsh instead of Bash.

Signed-off-by: Richard Hansen <rhansen@bbn.com>
---
 t/lib-prompt-tests.sh  | 653 +++++++++++++++++++++++++++++++++++++++++++++++++
 t/t9903-bash-prompt.sh | 626 +----------------------------------------------
 2 files changed, 654 insertions(+), 625 deletions(-)
 create mode 100644 t/lib-prompt-tests.sh
Show changes to 2 files +654 −625

t/lib-prompt-tests.sh, t/t9903-bash-prompt.sh

diff --git a/t/lib-prompt-tests.sh b/t/lib-prompt-tests.sh
new file mode 100644
index 0000000..7aaeb8e
--- /dev/null
+++ b/t/lib-prompt-tests.sh
@@ -0,0 +1,653 @@
+# Copyright (c) 2012 SZEDER Gábor
+
+# To use this library:
+#   1. set the variable shellname to the name of the shell (e.g.,
+#      "Bash")
+#   2. define functions named ps1_expansion_enable and
+#      ps1_expansion_disable that, upon return, guarantee that the
+#      shell will and will not (respectively) perform parameter
+#      expansion on PS1, if supported by the shell.  If it is not
+#      possible to configure the shell to disable (enable) PS1
+#      expansion, ps1_expansion_enable should simply return 0
+#      (non-zero) and ps1_expansion_disable should simply return
+#      non-zero (0)
+#   3. define a function named set_ps1_format_vars that sets the
+#      variables c_red, c_green, c_lblue, and c_clear to the strings
+#      that __git_ps1 uses to add color to the prompt.  The values of
+#      these variables are used in the first argument to the printf
+#      command, so they must be escaped appropriately.
+#   4. source this library
+
+# sanity checks
+[ -n "$shellname" ] || error "shellname must be set to the name of the shell"
+for i in ps1_expansion_enable ps1_expansion_disable set_ps1_format_vars
+do
+	command -v "$i" >/dev/null 2>&1 || error "function $i not defined"
+done
+(ps1_expansion_enable || ps1_expansion_disable) \
+	|| error "either ps1_expansion_enable or ps1_expansion_disable must return true"
+
+. "$GIT_BUILD_DIR/contrib/completion/git-prompt.sh"
+
+actual="$TRASH_DIRECTORY/actual"
+set_ps1_format_vars
+
+test_expect_success "setup for $shellname prompt tests" '
+	git init otherrepo &&
+	echo 1 >file &&
+	git add file &&
+	test_tick &&
+	git commit -m initial &&
+	git tag -a -m msg1 t1 &&
+	git checkout -b b1 &&
+	echo 2 >file &&
+	git commit -m "second b1" file &&
+	echo 3 >file &&
+	git commit -m "third b1" file &&
+	git tag -a -m msg2 t2 &&
+	git checkout -b b2 master &&
+	echo 0 >file &&
+	git commit -m "second b2" file &&
+	echo 00 >file &&
+	git commit -m "another b2" file &&
+	echo 000 >file &&
+	git commit -m "yet another b2" file &&
+	git checkout master
+'
+
+pfx="$shellname prompt"
+
+test_expect_success "$pfx - branch name" '
+	printf " (master)" >expected &&
+	__git_ps1 >"$actual" &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success SYMLINKS "$pfx - branch name - symlink symref" '
+	printf " (master)" >expected &&
+	test_when_finished "git checkout master" &&
+	test_config core.preferSymlinkRefs true &&
+	git checkout master &&
+	__git_ps1 >"$actual" &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - unborn branch" '
+	printf " (unborn)" >expected &&
+	git checkout --orphan unborn &&
+	test_when_finished "git checkout master" &&
+	__git_ps1 >"$actual" &&
+	test_cmp expected "$actual"
+'
+
+repo_with_newline='repo
+with
+newline'
+
+if mkdir "$repo_with_newline" 2>/dev/null
+then
+	test_set_prereq FUNNYNAMES
+else
+	say 'Your filesystem does not allow newlines in filenames.'
+fi
+
+test_expect_success FUNNYNAMES "$pfx - with newline in path" '
+	printf " (master)" >expected &&
+	git init "$repo_with_newline" &&
+	test_when_finished "rm -rf \"$repo_with_newline\"" &&
+	mkdir "$repo_with_newline"/subdir &&
+	(
+		cd "$repo_with_newline/subdir" &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - detached head" '
+	printf " ((%s...))" $(git log -1 --format="%h" --abbrev=13 b1^) >expected &&
+	test_config core.abbrev 13 &&
+	git checkout b1^ &&
+	test_when_finished "git checkout master" &&
+	__git_ps1 >"$actual" &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - describe detached head - contains" '
+	printf " ((t2~1))" >expected &&
+	git checkout b1^ &&
+	test_when_finished "git checkout master" &&
+	(
+		GIT_PS1_DESCRIBE_STYLE=contains &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - describe detached head - branch" '
+	printf " ((b1~1))" >expected &&
+	git checkout b1^ &&
+	test_when_finished "git checkout master" &&
+	(
+		GIT_PS1_DESCRIBE_STYLE=branch &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - describe detached head - describe" '
+	printf " ((t1-1-g%s))" $(git log -1 --format="%h" b1^) >expected &&
+	git checkout b1^ &&
+	test_when_finished "git checkout master" &&
+	(
+		GIT_PS1_DESCRIBE_STYLE=describe &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - describe detached head - default" '
+	printf " ((t2))" >expected &&
+	git checkout --detach b1 &&
+	test_when_finished "git checkout master" &&
+	__git_ps1 >"$actual" &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - inside .git directory" '
+	printf " (GIT_DIR!)" >expected &&
+	(
+		cd .git &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - deep inside .git directory" '
+	printf " (GIT_DIR!)" >expected &&
+	(
+		cd .git/refs/heads &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - inside bare repository" '
+	printf " (BARE:master)" >expected &&
+	git init --bare bare.git &&
+	test_when_finished "rm -rf bare.git" &&
+	(
+		cd bare.git &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - interactive rebase" '
+	printf " (b1|REBASE-i 2/3)" >expected
+	write_script fake_editor.sh <<-\EOF &&
+		echo "exec echo" >"$1"
+		echo "edit $(git log -1 --format="%h")" >>"$1"
+		echo "exec echo" >>"$1"
+	EOF
+	test_when_finished "rm -f fake_editor.sh" &&
+	test_set_editor "$TRASH_DIRECTORY/fake_editor.sh" &&
+	git checkout b1 &&
+	test_when_finished "git checkout master" &&
+	git rebase -i HEAD^ &&
+	test_when_finished "git rebase --abort"
+	__git_ps1 >"$actual" &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - rebase merge" '
+	printf " (b2|REBASE-m 1/3)" >expected &&
+	git checkout b2 &&
+	test_when_finished "git checkout master" &&
+	test_must_fail git rebase --merge b1 b2 &&
+	test_when_finished "git rebase --abort" &&
+	__git_ps1 >"$actual" &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - rebase" '
+	printf " (b2|REBASE 1/3)" >expected &&
+	git checkout b2 &&
+	test_when_finished "git checkout master" &&
+	test_must_fail git rebase b1 b2 &&
+	test_when_finished "git rebase --abort" &&
+	__git_ps1 >"$actual" &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - merge" '
+	printf " (b1|MERGING)" >expected &&
+	git checkout b1 &&
+	test_when_finished "git checkout master" &&
+	test_must_fail git merge b2 &&
+	test_when_finished "git reset --hard" &&
+	__git_ps1 >"$actual" &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - cherry-pick" '
+	printf " (master|CHERRY-PICKING)" >expected &&
+	test_must_fail git cherry-pick b1 &&
+	test_when_finished "git reset --hard" &&
+	__git_ps1 >"$actual" &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - bisect" '
+	printf " (master|BISECTING)" >expected &&
+	git bisect start &&
+	test_when_finished "git bisect reset" &&
+	__git_ps1 >"$actual" &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - dirty status indicator - clean" '
+	printf " (master)" >expected &&
+	(
+		GIT_PS1_SHOWDIRTYSTATE=y &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - dirty status indicator - dirty worktree" '
+	printf " (master *)" >expected &&
+	echo "dirty" >file &&
+	test_when_finished "git reset --hard" &&
+	(
+		GIT_PS1_SHOWDIRTYSTATE=y &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - dirty status indicator - dirty index" '
+	printf " (master +)" >expected &&
+	echo "dirty" >file &&
+	test_when_finished "git reset --hard" &&
+	git add -u &&
+	(
+		GIT_PS1_SHOWDIRTYSTATE=y &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - dirty status indicator - dirty index and worktree" '
+	printf " (master *+)" >expected &&
+	echo "dirty index" >file &&
+	test_when_finished "git reset --hard" &&
+	git add -u &&
+	echo "dirty worktree" >file &&
+	(
+		GIT_PS1_SHOWDIRTYSTATE=y &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - dirty status indicator - before root commit" '
+	printf " (master #)" >expected &&
+	(
+		GIT_PS1_SHOWDIRTYSTATE=y &&
+		cd otherrepo &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - dirty status indicator - shell variable unset with config disabled" '
+	printf " (master)" >expected &&
+	echo "dirty" >file &&
+	test_when_finished "git reset --hard" &&
+	test_config bash.showDirtyState false &&
+	(
+		sane_unset GIT_PS1_SHOWDIRTYSTATE &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - dirty status indicator - shell variable unset with config enabled" '
+	printf " (master)" >expected &&
+	echo "dirty" >file &&
+	test_when_finished "git reset --hard" &&
+	test_config bash.showDirtyState true &&
+	(
+		sane_unset GIT_PS1_SHOWDIRTYSTATE &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - dirty status indicator - shell variable set with config disabled" '
+	printf " (master)" >expected &&
+	echo "dirty" >file &&
+	test_when_finished "git reset --hard" &&
+	test_config bash.showDirtyState false &&
+	(
+		GIT_PS1_SHOWDIRTYSTATE=y &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - dirty status indicator - shell variable set with config enabled" '
+	printf " (master *)" >expected &&
+	echo "dirty" >file &&
+	test_when_finished "git reset --hard" &&
+	test_config bash.showDirtyState true &&
+	(
+		GIT_PS1_SHOWDIRTYSTATE=y &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - dirty status indicator - not shown inside .git directory" '
+	printf " (GIT_DIR!)" >expected &&
+	echo "dirty" >file &&
+	test_when_finished "git reset --hard" &&
+	(
+		GIT_PS1_SHOWDIRTYSTATE=y &&
+		cd .git &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - stash status indicator - no stash" '
+	printf " (master)" >expected &&
+	(
+		GIT_PS1_SHOWSTASHSTATE=y &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - stash status indicator - stash" '
+	printf " (master $)" >expected &&
+	echo 2 >file &&
+	git stash &&
+	test_when_finished "git stash drop" &&
+	git pack-refs --all &&
+	(
+		GIT_PS1_SHOWSTASHSTATE=y &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - stash status indicator - not shown inside .git directory" '
+	printf " (GIT_DIR!)" >expected &&
+	echo 2 >file &&
+	git stash &&
+	test_when_finished "git stash drop" &&
+	(
+		GIT_PS1_SHOWSTASHSTATE=y &&
+		cd .git &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - untracked files status indicator - no untracked files" '
+	printf " (master)" >expected &&
+	(
+		GIT_PS1_SHOWUNTRACKEDFILES=y &&
+		cd otherrepo &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - untracked files status indicator - untracked files" '
+	printf " (master %%)" >expected &&
+	(
+		GIT_PS1_SHOWUNTRACKEDFILES=y &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - untracked files status indicator - shell variable unset with config disabled" '
+	printf " (master)" >expected &&
+	test_config bash.showUntrackedFiles false &&
+	(
+		sane_unset GIT_PS1_SHOWUNTRACKEDFILES &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - untracked files status indicator - shell variable unset with config enabled" '
+	printf " (master)" >expected &&
+	test_config bash.showUntrackedFiles true &&
+	(
+		sane_unset GIT_PS1_SHOWUNTRACKEDFILES &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - untracked files status indicator - shell variable set with config disabled" '
+	printf " (master)" >expected &&
+	test_config bash.showUntrackedFiles false &&
+	(
+		GIT_PS1_SHOWUNTRACKEDFILES=y &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - untracked files status indicator - shell variable set with config enabled" '
+	printf " (master %%)" >expected &&
+	test_config bash.showUntrackedFiles true &&
+	(
+		GIT_PS1_SHOWUNTRACKEDFILES=y &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - untracked files status indicator - not shown inside .git directory" '
+	printf " (GIT_DIR!)" >expected &&
+	(
+		GIT_PS1_SHOWUNTRACKEDFILES=y &&
+		cd .git &&
+		__git_ps1 >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success "$pfx - format string starting with dash" '
+	printf -- "-master" >expected &&
+	__git_ps1 "-%s" >"$actual" &&
+	test_cmp expected "$actual"
+'
+
+pcmode_expected () {
+	case $ps1expansion in
+	on) printf "$1" '${__git_ps1_branch_name}' "$2";;
+	off) printf "$1" "$2" "";;
+	esac >expected
+}
+
+pcmode_actual () {
+	case $ps1expansion in
+	on) printf %s\\n%s "$PS1" "${__git_ps1_branch_name}";;
+	off) printf %s\\n "$PS1";;
+	esac >"$actual"
+}
+
+set_ps1expansion () {
+	case $ps1expansion in
+	on) ps1_expansion_enable;;
+	off) ps1_expansion_disable;;
+	*) error "invalid argument to _run_pcmode_tests: $ps1expansion";;
+	esac
+}
+
+_run_pcmode_tests () {
+	ps1expansion=$1; shift
+
+	# Test whether the shell supports enabling/disabling PS1
+	# expansion by running set_ps1expansion.  If not, quietly skip
+	# this set of tests.
+	#
+	# Even though set_ps1expansion is run here, it must also be
+	# run inside each individual test case because the state of
+	# the shell might be reset in some fashion before executing
+	# the test code.  (Notably, Zsh shell emulation causes the
+	# PROMPT_SUBST option to be reset each time a test is run.)
+	set_ps1expansion || return 0
+
+	test_expect_success "$shellname prompt - pc mode (PS1 expansion $ps1expansion)" '
+		set_ps1expansion &&
+		pcmode_expected "BEFORE: (%s):AFTER\\n%s" master &&
+		printf "" >expected_output &&
+		(
+			__git_ps1 "BEFORE:" ":AFTER" >"$actual" &&
+			test_cmp expected_output "$actual" &&
+			pcmode_actual
+		) &&
+		test_cmp expected "$actual"
+	'
+
+	pfx="$shellname prompt - color pc mode (PS1 expansion $ps1expansion)"
+
+	test_expect_success "$pfx - branch name" '
+		set_ps1expansion &&
+		pcmode_expected "BEFORE: (${c_green}%s${c_clear}):AFTER\\n%s" master &&
+		(
+			GIT_PS1_SHOWCOLORHINTS=y &&
+			__git_ps1 "BEFORE:" ":AFTER" >"$actual"
+			pcmode_actual
+		) &&
+		test_cmp expected "$actual"
+	'
+
+	test_expect_success "$pfx - detached head" '
+		set_ps1expansion &&
+		pcmode_expected "BEFORE: (${c_red}%s${c_clear}):AFTER\\n%s" "($(git log -1 --format="%h" b1^)...)" &&
+		git checkout b1^ &&
+		test_when_finished "git checkout master" &&
+		(
+			GIT_PS1_SHOWCOLORHINTS=y &&
+			__git_ps1 "BEFORE:" ":AFTER" &&
+			pcmode_actual
+		) &&
+		test_cmp expected "$actual"
+	'
+
+	test_expect_success "$pfx - dirty status indicator - dirty worktree" '
+		set_ps1expansion &&
+		pcmode_expected "BEFORE: (${c_green}%s${c_clear} ${c_red}*${c_clear}):AFTER\\n%s" master &&
+		echo "dirty" >file &&
+		test_when_finished "git reset --hard" &&
+		(
+			GIT_PS1_SHOWDIRTYSTATE=y &&
+			GIT_PS1_SHOWCOLORHINTS=y &&
+			__git_ps1 "BEFORE:" ":AFTER" &&
+			pcmode_actual
+		) &&
+		test_cmp expected "$actual"
+	'
+
+	test_expect_success "$pfx - dirty status indicator - dirty index" '
+		set_ps1expansion &&
+		pcmode_expected "BEFORE: (${c_green}%s${c_clear} ${c_green}+${c_clear}):AFTER\\n%s" master &&
+		echo "dirty" >file &&
+		test_when_finished "git reset --hard" &&
+		git add -u &&
+		(
+			GIT_PS1_SHOWDIRTYSTATE=y &&
+			GIT_PS1_SHOWCOLORHINTS=y &&
+			__git_ps1 "BEFORE:" ":AFTER" &&
+			pcmode_actual
+		) &&
+		test_cmp expected "$actual"
+	'
+
+	test_expect_success "$pfx - dirty status indicator - dirty index and worktree" '
+		set_ps1expansion &&
+		pcmode_expected "BEFORE: (${c_green}%s${c_clear} ${c_red}*${c_green}+${c_clear}):AFTER\\n%s" master &&
+		echo "dirty index" >file &&
+		test_when_finished "git reset --hard" &&
+		git add -u &&
+		echo "dirty worktree" >file &&
+		(
+			GIT_PS1_SHOWCOLORHINTS=y &&
+			GIT_PS1_SHOWDIRTYSTATE=y &&
+			__git_ps1 "BEFORE:" ":AFTER" &&
+			pcmode_actual
+		) &&
+		test_cmp expected "$actual"
+	'
+
+	test_expect_success "$pfx - dirty status indicator - before root commit" '
+		set_ps1expansion &&
+		pcmode_expected "BEFORE: (${c_green}%s${c_clear} ${c_green}#${c_clear}):AFTER\\n%s" master &&
+		(
+			GIT_PS1_SHOWDIRTYSTATE=y &&
+			GIT_PS1_SHOWCOLORHINTS=y &&
+			cd otherrepo &&
+			__git_ps1 "BEFORE:" ":AFTER" &&
+			pcmode_actual
+		) &&
+		test_cmp expected "$actual"
+	'
+
+	test_expect_success "$pfx - inside .git directory" '
+		set_ps1expansion &&
+		pcmode_expected "BEFORE: (${c_green}%s${c_clear}):AFTER\\n%s" "GIT_DIR!" &&
+		echo "dirty" >file &&
+		test_when_finished "git reset --hard" &&
+		(
+			GIT_PS1_SHOWDIRTYSTATE=y &&
+			GIT_PS1_SHOWCOLORHINTS=y &&
+			cd .git &&
+			__git_ps1 "BEFORE:" ":AFTER" &&
+			pcmode_actual
+		) &&
+		test_cmp expected "$actual"
+	'
+
+	test_expect_success "$pfx - stash status indicator" '
+		set_ps1expansion &&
+		pcmode_expected "BEFORE: (${c_green}%s${c_clear} ${c_lblue}\$${c_clear}):AFTER\\n%s" master &&
+		echo 2 >file &&
+		git stash &&
+		test_when_finished "git stash drop" &&
+		(
+			GIT_PS1_SHOWSTASHSTATE=y &&
+			GIT_PS1_SHOWCOLORHINTS=y &&
+			__git_ps1 "BEFORE:" ":AFTER" &&
+			pcmode_actual
+		) &&
+		test_cmp expected "$actual"
+	'
+
+	test_expect_success "$pfx - untracked files status indicator" '
+		set_ps1expansion &&
+		pcmode_expected "BEFORE: (${c_green}%s${c_clear} ${c_red}%%${c_clear}):AFTER\\n%s" master &&
+		(
+			GIT_PS1_SHOWUNTRACKEDFILES=y &&
+			GIT_PS1_SHOWCOLORHINTS=y &&
+			__git_ps1 "BEFORE:" ":AFTER" &&
+			pcmode_actual
+		) &&
+		test_cmp expected "$actual"
+	'
+}
+
+run_pcmode_tests () {
+	_run_pcmode_tests on
+	_run_pcmode_tests off
+}
+
+run_pcmode_tests
diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh
index fe60cf9..b698fe9 100755
--- a/t/t9903-bash-prompt.sh
+++ b/t/t9903-bash-prompt.sh
@@ -6,630 +6,6 @@
 test_description='test git-specific bash prompt functions'
 
 . ./lib-bash.sh
-
-. "$GIT_BUILD_DIR/contrib/completion/git-prompt.sh"
-
-actual="$TRASH_DIRECTORY/actual"
-set_ps1_format_vars
-
-test_expect_success "setup for $shellname prompt tests" '
-	git init otherrepo &&
-	echo 1 >file &&
-	git add file &&
-	test_tick &&
-	git commit -m initial &&
-	git tag -a -m msg1 t1 &&
-	git checkout -b b1 &&
-	echo 2 >file &&
-	git commit -m "second b1" file &&
-	echo 3 >file &&
-	git commit -m "third b1" file &&
-	git tag -a -m msg2 t2 &&
-	git checkout -b b2 master &&
-	echo 0 >file &&
-	git commit -m "second b2" file &&
-	echo 00 >file &&
-	git commit -m "another b2" file &&
-	echo 000 >file &&
-	git commit -m "yet another b2" file &&
-	git checkout master
-'
-
-pfx="$shellname prompt"
-
-test_expect_success "$pfx - branch name" '
-	printf " (master)" >expected &&
-	__git_ps1 >"$actual" &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success SYMLINKS "$pfx - branch name - symlink symref" '
-	printf " (master)" >expected &&
-	test_when_finished "git checkout master" &&
-	test_config core.preferSymlinkRefs true &&
-	git checkout master &&
-	__git_ps1 >"$actual" &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - unborn branch" '
-	printf " (unborn)" >expected &&
-	git checkout --orphan unborn &&
-	test_when_finished "git checkout master" &&
-	__git_ps1 >"$actual" &&
-	test_cmp expected "$actual"
-'
-
-repo_with_newline='repo
-with
-newline'
-
-if mkdir "$repo_with_newline" 2>/dev/null
-then
-	test_set_prereq FUNNYNAMES
-else
-	say 'Your filesystem does not allow newlines in filenames.'
-fi
-
-test_expect_success FUNNYNAMES "$pfx - with newline in path" '
-	printf " (master)" >expected &&
-	git init "$repo_with_newline" &&
-	test_when_finished "rm -rf \"$repo_with_newline\"" &&
-	mkdir "$repo_with_newline"/subdir &&
-	(
-		cd "$repo_with_newline/subdir" &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - detached head" '
-	printf " ((%s...))" $(git log -1 --format="%h" --abbrev=13 b1^) >expected &&
-	test_config core.abbrev 13 &&
-	git checkout b1^ &&
-	test_when_finished "git checkout master" &&
-	__git_ps1 >"$actual" &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - describe detached head - contains" '
-	printf " ((t2~1))" >expected &&
-	git checkout b1^ &&
-	test_when_finished "git checkout master" &&
-	(
-		GIT_PS1_DESCRIBE_STYLE=contains &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - describe detached head - branch" '
-	printf " ((b1~1))" >expected &&
-	git checkout b1^ &&
-	test_when_finished "git checkout master" &&
-	(
-		GIT_PS1_DESCRIBE_STYLE=branch &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - describe detached head - describe" '
-	printf " ((t1-1-g%s))" $(git log -1 --format="%h" b1^) >expected &&
-	git checkout b1^ &&
-	test_when_finished "git checkout master" &&
-	(
-		GIT_PS1_DESCRIBE_STYLE=describe &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - describe detached head - default" '
-	printf " ((t2))" >expected &&
-	git checkout --detach b1 &&
-	test_when_finished "git checkout master" &&
-	__git_ps1 >"$actual" &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - inside .git directory" '
-	printf " (GIT_DIR!)" >expected &&
-	(
-		cd .git &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - deep inside .git directory" '
-	printf " (GIT_DIR!)" >expected &&
-	(
-		cd .git/refs/heads &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - inside bare repository" '
-	printf " (BARE:master)" >expected &&
-	git init --bare bare.git &&
-	test_when_finished "rm -rf bare.git" &&
-	(
-		cd bare.git &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - interactive rebase" '
-	printf " (b1|REBASE-i 2/3)" >expected
-	write_script fake_editor.sh <<-\EOF &&
-		echo "exec echo" >"$1"
-		echo "edit $(git log -1 --format="%h")" >>"$1"
-		echo "exec echo" >>"$1"
-	EOF
-	test_when_finished "rm -f fake_editor.sh" &&
-	test_set_editor "$TRASH_DIRECTORY/fake_editor.sh" &&
-	git checkout b1 &&
-	test_when_finished "git checkout master" &&
-	git rebase -i HEAD^ &&
-	test_when_finished "git rebase --abort"
-	__git_ps1 >"$actual" &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - rebase merge" '
-	printf " (b2|REBASE-m 1/3)" >expected &&
-	git checkout b2 &&
-	test_when_finished "git checkout master" &&
-	test_must_fail git rebase --merge b1 b2 &&
-	test_when_finished "git rebase --abort" &&
-	__git_ps1 >"$actual" &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - rebase" '
-	printf " (b2|REBASE 1/3)" >expected &&
-	git checkout b2 &&
-	test_when_finished "git checkout master" &&
-	test_must_fail git rebase b1 b2 &&
-	test_when_finished "git rebase --abort" &&
-	__git_ps1 >"$actual" &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - merge" '
-	printf " (b1|MERGING)" >expected &&
-	git checkout b1 &&
-	test_when_finished "git checkout master" &&
-	test_must_fail git merge b2 &&
-	test_when_finished "git reset --hard" &&
-	__git_ps1 >"$actual" &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - cherry-pick" '
-	printf " (master|CHERRY-PICKING)" >expected &&
-	test_must_fail git cherry-pick b1 &&
-	test_when_finished "git reset --hard" &&
-	__git_ps1 >"$actual" &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - bisect" '
-	printf " (master|BISECTING)" >expected &&
-	git bisect start &&
-	test_when_finished "git bisect reset" &&
-	__git_ps1 >"$actual" &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - dirty status indicator - clean" '
-	printf " (master)" >expected &&
-	(
-		GIT_PS1_SHOWDIRTYSTATE=y &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - dirty status indicator - dirty worktree" '
-	printf " (master *)" >expected &&
-	echo "dirty" >file &&
-	test_when_finished "git reset --hard" &&
-	(
-		GIT_PS1_SHOWDIRTYSTATE=y &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - dirty status indicator - dirty index" '
-	printf " (master +)" >expected &&
-	echo "dirty" >file &&
-	test_when_finished "git reset --hard" &&
-	git add -u &&
-	(
-		GIT_PS1_SHOWDIRTYSTATE=y &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - dirty status indicator - dirty index and worktree" '
-	printf " (master *+)" >expected &&
-	echo "dirty index" >file &&
-	test_when_finished "git reset --hard" &&
-	git add -u &&
-	echo "dirty worktree" >file &&
-	(
-		GIT_PS1_SHOWDIRTYSTATE=y &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - dirty status indicator - before root commit" '
-	printf " (master #)" >expected &&
-	(
-		GIT_PS1_SHOWDIRTYSTATE=y &&
-		cd otherrepo &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - dirty status indicator - shell variable unset with config disabled" '
-	printf " (master)" >expected &&
-	echo "dirty" >file &&
-	test_when_finished "git reset --hard" &&
-	test_config bash.showDirtyState false &&
-	(
-		sane_unset GIT_PS1_SHOWDIRTYSTATE &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - dirty status indicator - shell variable unset with config enabled" '
-	printf " (master)" >expected &&
-	echo "dirty" >file &&
-	test_when_finished "git reset --hard" &&
-	test_config bash.showDirtyState true &&
-	(
-		sane_unset GIT_PS1_SHOWDIRTYSTATE &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - dirty status indicator - shell variable set with config disabled" '
-	printf " (master)" >expected &&
-	echo "dirty" >file &&
-	test_when_finished "git reset --hard" &&
-	test_config bash.showDirtyState false &&
-	(
-		GIT_PS1_SHOWDIRTYSTATE=y &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - dirty status indicator - shell variable set with config enabled" '
-	printf " (master *)" >expected &&
-	echo "dirty" >file &&
-	test_when_finished "git reset --hard" &&
-	test_config bash.showDirtyState true &&
-	(
-		GIT_PS1_SHOWDIRTYSTATE=y &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - dirty status indicator - not shown inside .git directory" '
-	printf " (GIT_DIR!)" >expected &&
-	echo "dirty" >file &&
-	test_when_finished "git reset --hard" &&
-	(
-		GIT_PS1_SHOWDIRTYSTATE=y &&
-		cd .git &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - stash status indicator - no stash" '
-	printf " (master)" >expected &&
-	(
-		GIT_PS1_SHOWSTASHSTATE=y &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - stash status indicator - stash" '
-	printf " (master $)" >expected &&
-	echo 2 >file &&
-	git stash &&
-	test_when_finished "git stash drop" &&
-	git pack-refs --all &&
-	(
-		GIT_PS1_SHOWSTASHSTATE=y &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - stash status indicator - not shown inside .git directory" '
-	printf " (GIT_DIR!)" >expected &&
-	echo 2 >file &&
-	git stash &&
-	test_when_finished "git stash drop" &&
-	(
-		GIT_PS1_SHOWSTASHSTATE=y &&
-		cd .git &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - untracked files status indicator - no untracked files" '
-	printf " (master)" >expected &&
-	(
-		GIT_PS1_SHOWUNTRACKEDFILES=y &&
-		cd otherrepo &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - untracked files status indicator - untracked files" '
-	printf " (master %%)" >expected &&
-	(
-		GIT_PS1_SHOWUNTRACKEDFILES=y &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - untracked files status indicator - shell variable unset with config disabled" '
-	printf " (master)" >expected &&
-	test_config bash.showUntrackedFiles false &&
-	(
-		sane_unset GIT_PS1_SHOWUNTRACKEDFILES &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - untracked files status indicator - shell variable unset with config enabled" '
-	printf " (master)" >expected &&
-	test_config bash.showUntrackedFiles true &&
-	(
-		sane_unset GIT_PS1_SHOWUNTRACKEDFILES &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - untracked files status indicator - shell variable set with config disabled" '
-	printf " (master)" >expected &&
-	test_config bash.showUntrackedFiles false &&
-	(
-		GIT_PS1_SHOWUNTRACKEDFILES=y &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - untracked files status indicator - shell variable set with config enabled" '
-	printf " (master %%)" >expected &&
-	test_config bash.showUntrackedFiles true &&
-	(
-		GIT_PS1_SHOWUNTRACKEDFILES=y &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - untracked files status indicator - not shown inside .git directory" '
-	printf " (GIT_DIR!)" >expected &&
-	(
-		GIT_PS1_SHOWUNTRACKEDFILES=y &&
-		cd .git &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - format string starting with dash" '
-	printf -- "-master" >expected &&
-	__git_ps1 "-%s" >"$actual" &&
-	test_cmp expected "$actual"
-'
-
-pcmode_expected () {
-	case $ps1expansion in
-	on) printf "$1" '${__git_ps1_branch_name}' "$2";;
-	off) printf "$1" "$2" "";;
-	esac >expected
-}
-
-pcmode_actual () {
-	case $ps1expansion in
-	on) printf %s\\n%s "$PS1" "${__git_ps1_branch_name}";;
-	off) printf %s\\n "$PS1";;
-	esac >"$actual"
-}
-
-set_ps1expansion () {
-	case $ps1expansion in
-	on) ps1_expansion_enable;;
-	off) ps1_expansion_disable;;
-	*) error "invalid argument to _run_pcmode_tests: $ps1expansion";;
-	esac
-}
-
-_run_pcmode_tests () {
-	ps1expansion=$1; shift
-
-	# Test whether the shell supports enabling/disabling PS1
-	# expansion by running set_ps1expansion.  If not, quietly skip
-	# this set of tests.
-	#
-	# Even though set_ps1expansion is run here, it must also be
-	# run inside each individual test case because the state of
-	# the shell might be reset in some fashion before executing
-	# the test code.  (Notably, Zsh shell emulation causes the
-	# PROMPT_SUBST option to be reset each time a test is run.)
-	set_ps1expansion || return 0
-
-	test_expect_success "$shellname prompt - pc mode (PS1 expansion $ps1expansion)" '
-		set_ps1expansion &&
-		pcmode_expected "BEFORE: (%s):AFTER\\n%s" master &&
-		printf "" >expected_output &&
-		(
-			__git_ps1 "BEFORE:" ":AFTER" >"$actual" &&
-			test_cmp expected_output "$actual" &&
-			pcmode_actual
-		) &&
-		test_cmp expected "$actual"
-	'
-
-	pfx="$shellname prompt - color pc mode (PS1 expansion $ps1expansion)"
-
-	test_expect_success "$pfx - branch name" '
-		set_ps1expansion &&
-		pcmode_expected "BEFORE: (${c_green}%s${c_clear}):AFTER\\n%s" master &&
-		(
-			GIT_PS1_SHOWCOLORHINTS=y &&
-			__git_ps1 "BEFORE:" ":AFTER" >"$actual"
-			pcmode_actual
-		) &&
-		test_cmp expected "$actual"
-	'
-
-	test_expect_success "$pfx - detached head" '
-		set_ps1expansion &&
-		pcmode_expected "BEFORE: (${c_red}%s${c_clear}):AFTER\\n%s" "($(git log -1 --format="%h" b1^)...)" &&
-		git checkout b1^ &&
-		test_when_finished "git checkout master" &&
-		(
-			GIT_PS1_SHOWCOLORHINTS=y &&
-			__git_ps1 "BEFORE:" ":AFTER" &&
-			pcmode_actual
-		) &&
-		test_cmp expected "$actual"
-	'
-
-	test_expect_success "$pfx - dirty status indicator - dirty worktree" '
-		set_ps1expansion &&
-		pcmode_expected "BEFORE: (${c_green}%s${c_clear} ${c_red}*${c_clear}):AFTER\\n%s" master &&
-		echo "dirty" >file &&
-		test_when_finished "git reset --hard" &&
-		(
-			GIT_PS1_SHOWDIRTYSTATE=y &&
-			GIT_PS1_SHOWCOLORHINTS=y &&
-			__git_ps1 "BEFORE:" ":AFTER" &&
-			pcmode_actual
-		) &&
-		test_cmp expected "$actual"
-	'
-
-	test_expect_success "$pfx - dirty status indicator - dirty index" '
-		set_ps1expansion &&
-		pcmode_expected "BEFORE: (${c_green}%s${c_clear} ${c_green}+${c_clear}):AFTER\\n%s" master &&
-		echo "dirty" >file &&
-		test_when_finished "git reset --hard" &&
-		git add -u &&
-		(
-			GIT_PS1_SHOWDIRTYSTATE=y &&
-			GIT_PS1_SHOWCOLORHINTS=y &&
-			__git_ps1 "BEFORE:" ":AFTER" &&
-			pcmode_actual
-		) &&
-		test_cmp expected "$actual"
-	'
-
-	test_expect_success "$pfx - dirty status indicator - dirty index and worktree" '
-		set_ps1expansion &&
-		pcmode_expected "BEFORE: (${c_green}%s${c_clear} ${c_red}*${c_green}+${c_clear}):AFTER\\n%s" master &&
-		echo "dirty index" >file &&
-		test_when_finished "git reset --hard" &&
-		git add -u &&
-		echo "dirty worktree" >file &&
-		(
-			GIT_PS1_SHOWCOLORHINTS=y &&
-			GIT_PS1_SHOWDIRTYSTATE=y &&
-			__git_ps1 "BEFORE:" ":AFTER" &&
-			pcmode_actual
-		) &&
-		test_cmp expected "$actual"
-	'
-
-	test_expect_success "$pfx - dirty status indicator - before root commit" '
-		set_ps1expansion &&
-		pcmode_expected "BEFORE: (${c_green}%s${c_clear} ${c_green}#${c_clear}):AFTER\\n%s" master &&
-		(
-			GIT_PS1_SHOWDIRTYSTATE=y &&
-			GIT_PS1_SHOWCOLORHINTS=y &&
-			cd otherrepo &&
-			__git_ps1 "BEFORE:" ":AFTER" &&
-			pcmode_actual
-		) &&
-		test_cmp expected "$actual"
-	'
-
-	test_expect_success "$pfx - inside .git directory" '
-		set_ps1expansion &&
-		pcmode_expected "BEFORE: (${c_green}%s${c_clear}):AFTER\\n%s" "GIT_DIR!" &&
-		echo "dirty" >file &&
-		test_when_finished "git reset --hard" &&
-		(
-			GIT_PS1_SHOWDIRTYSTATE=y &&
-			GIT_PS1_SHOWCOLORHINTS=y &&
-			cd .git &&
-			__git_ps1 "BEFORE:" ":AFTER" &&
-			pcmode_actual
-		) &&
-		test_cmp expected "$actual"
-	'
-
-	test_expect_success "$pfx - stash status indicator" '
-		set_ps1expansion &&
-		pcmode_expected "BEFORE: (${c_green}%s${c_clear} ${c_lblue}\$${c_clear}):AFTER\\n%s" master &&
-		echo 2 >file &&
-		git stash &&
-		test_when_finished "git stash drop" &&
-		(
-			GIT_PS1_SHOWSTASHSTATE=y &&
-			GIT_PS1_SHOWCOLORHINTS=y &&
-			__git_ps1 "BEFORE:" ":AFTER" &&
-			pcmode_actual
-		) &&
-		test_cmp expected "$actual"
-	'
-
-	test_expect_success "$pfx - untracked files status indicator" '
-		set_ps1expansion &&
-		pcmode_expected "BEFORE: (${c_green}%s${c_clear} ${c_red}%%${c_clear}):AFTER\\n%s" master &&
-		(
-			GIT_PS1_SHOWUNTRACKEDFILES=y &&
-			GIT_PS1_SHOWCOLORHINTS=y &&
-			__git_ps1 "BEFORE:" ":AFTER" &&
-			pcmode_actual
-		) &&
-		test_cmp expected "$actual"
-	'
-}
-
-run_pcmode_tests () {
-	_run_pcmode_tests on
-	_run_pcmode_tests off
-}
-
-run_pcmode_tests
+. "$TEST_DIRECTORY"/lib-prompt-tests.sh
 
 test_done
-- 
2.0.0
Richard Hansen· Jun 4, 2014, 21:01 UTC · re: Richard Hansen · lore

[PATCH v2 08/11] lib-prompt-tests.sh: put all tests inside a function

Modify lib-prompt-tests.sh so that it does nothing when sourced except define a function for running the prompt tests (plus some "private" helper functions).

Signed-off-by: Richard Hansen <rhansen@bbn.com>
---
 t/lib-prompt-tests.sh  | 802 ++++++++++++++++++++++++-------------------------
 t/t9903-bash-prompt.sh |   2 +
 2 files changed, 403 insertions(+), 401 deletions(-)
Show changes to 2 files +403 −401

t/lib-prompt-tests.sh, t/t9903-bash-prompt.sh

diff --git a/t/lib-prompt-tests.sh b/t/lib-prompt-tests.sh
index 7aaeb8e..ba22acc 100644
--- a/t/lib-prompt-tests.sh
+++ b/t/lib-prompt-tests.sh
@@ -17,6 +17,7 @@
 #      these variables are used in the first argument to the printf
 #      command, so they must be escaped appropriately.
 #   4. source this library
+#   5. invoke the run_prompt_tests function
 
 # sanity checks
 [ -n "$shellname" ] || error "shellname must be set to the name of the shell"
@@ -27,448 +28,445 @@ done
 (ps1_expansion_enable || ps1_expansion_disable) \
 	|| error "either ps1_expansion_enable or ps1_expansion_disable must return true"
 
-. "$GIT_BUILD_DIR/contrib/completion/git-prompt.sh"
+_run_non_pcmode_tests () {
+	test_expect_success "setup for $shellname prompt tests" '
+		git init otherrepo &&
+		echo 1 >file &&
+		git add file &&
+		test_tick &&
+		git commit -m initial &&
+		git tag -a -m msg1 t1 &&
+		git checkout -b b1 &&
+		echo 2 >file &&
+		git commit -m "second b1" file &&
+		echo 3 >file &&
+		git commit -m "third b1" file &&
+		git tag -a -m msg2 t2 &&
+		git checkout -b b2 master &&
+		echo 0 >file &&
+		git commit -m "second b2" file &&
+		echo 00 >file &&
+		git commit -m "another b2" file &&
+		echo 000 >file &&
+		git commit -m "yet another b2" file &&
+		git checkout master
+	'
 
-actual="$TRASH_DIRECTORY/actual"
-set_ps1_format_vars
+	pfx="$shellname prompt"
 
-test_expect_success "setup for $shellname prompt tests" '
-	git init otherrepo &&
-	echo 1 >file &&
-	git add file &&
-	test_tick &&
-	git commit -m initial &&
-	git tag -a -m msg1 t1 &&
-	git checkout -b b1 &&
-	echo 2 >file &&
-	git commit -m "second b1" file &&
-	echo 3 >file &&
-	git commit -m "third b1" file &&
-	git tag -a -m msg2 t2 &&
-	git checkout -b b2 master &&
-	echo 0 >file &&
-	git commit -m "second b2" file &&
-	echo 00 >file &&
-	git commit -m "another b2" file &&
-	echo 000 >file &&
-	git commit -m "yet another b2" file &&
-	git checkout master
-'
+	test_expect_success "$pfx - branch name" '
+		printf " (master)" >expected &&
+		__git_ps1 >"$actual" &&
+		test_cmp expected "$actual"
+	'
 
-pfx="$shellname prompt"
+	test_expect_success SYMLINKS "$pfx - branch name - symlink symref" '
+		printf " (master)" >expected &&
+		test_when_finished "git checkout master" &&
+		test_config core.preferSymlinkRefs true &&
+		git checkout master &&
+		__git_ps1 >"$actual" &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - branch name" '
-	printf " (master)" >expected &&
-	__git_ps1 >"$actual" &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - unborn branch" '
+		printf " (unborn)" >expected &&
+		git checkout --orphan unborn &&
+		test_when_finished "git checkout master" &&
+		__git_ps1 >"$actual" &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success SYMLINKS "$pfx - branch name - symlink symref" '
-	printf " (master)" >expected &&
-	test_when_finished "git checkout master" &&
-	test_config core.preferSymlinkRefs true &&
-	git checkout master &&
-	__git_ps1 >"$actual" &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success "$pfx - unborn branch" '
-	printf " (unborn)" >expected &&
-	git checkout --orphan unborn &&
-	test_when_finished "git checkout master" &&
-	__git_ps1 >"$actual" &&
-	test_cmp expected "$actual"
-'
-
-repo_with_newline='repo
+	repo_with_newline='repo
 with
 newline'
 
-if mkdir "$repo_with_newline" 2>/dev/null
-then
-	test_set_prereq FUNNYNAMES
-else
-	say 'Your filesystem does not allow newlines in filenames.'
-fi
+	if mkdir "$repo_with_newline" 2>/dev/null
+	then
+		test_set_prereq FUNNYNAMES
+	else
+		say 'Your filesystem does not allow newlines in filenames.'
+	fi
 
-test_expect_success FUNNYNAMES "$pfx - with newline in path" '
-	printf " (master)" >expected &&
-	git init "$repo_with_newline" &&
-	test_when_finished "rm -rf \"$repo_with_newline\"" &&
-	mkdir "$repo_with_newline"/subdir &&
-	(
-		cd "$repo_with_newline/subdir" &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success FUNNYNAMES "$pfx - with newline in path" '
+		printf " (master)" >expected &&
+		git init "$repo_with_newline" &&
+		test_when_finished "rm -rf \"$repo_with_newline\"" &&
+		mkdir "$repo_with_newline"/subdir &&
+		(
+			cd "$repo_with_newline/subdir" &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - detached head" '
-	printf " ((%s...))" $(git log -1 --format="%h" --abbrev=13 b1^) >expected &&
-	test_config core.abbrev 13 &&
-	git checkout b1^ &&
-	test_when_finished "git checkout master" &&
-	__git_ps1 >"$actual" &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - detached head" '
+		printf " ((%s...))" $(git log -1 --format="%h" --abbrev=13 b1^) >expected &&
+		test_config core.abbrev 13 &&
+		git checkout b1^ &&
+		test_when_finished "git checkout master" &&
+		__git_ps1 >"$actual" &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - describe detached head - contains" '
-	printf " ((t2~1))" >expected &&
-	git checkout b1^ &&
-	test_when_finished "git checkout master" &&
-	(
-		GIT_PS1_DESCRIBE_STYLE=contains &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - describe detached head - contains" '
+		printf " ((t2~1))" >expected &&
+		git checkout b1^ &&
+		test_when_finished "git checkout master" &&
+		(
+			GIT_PS1_DESCRIBE_STYLE=contains &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - describe detached head - branch" '
-	printf " ((b1~1))" >expected &&
-	git checkout b1^ &&
-	test_when_finished "git checkout master" &&
-	(
-		GIT_PS1_DESCRIBE_STYLE=branch &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - describe detached head - branch" '
+		printf " ((b1~1))" >expected &&
+		git checkout b1^ &&
+		test_when_finished "git checkout master" &&
+		(
+			GIT_PS1_DESCRIBE_STYLE=branch &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - describe detached head - describe" '
-	printf " ((t1-1-g%s))" $(git log -1 --format="%h" b1^) >expected &&
-	git checkout b1^ &&
-	test_when_finished "git checkout master" &&
-	(
-		GIT_PS1_DESCRIBE_STYLE=describe &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - describe detached head - describe" '
+		printf " ((t1-1-g%s))" $(git log -1 --format="%h" b1^) >expected &&
+		git checkout b1^ &&
+		test_when_finished "git checkout master" &&
+		(
+			GIT_PS1_DESCRIBE_STYLE=describe &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - describe detached head - default" '
-	printf " ((t2))" >expected &&
-	git checkout --detach b1 &&
-	test_when_finished "git checkout master" &&
-	__git_ps1 >"$actual" &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - describe detached head - default" '
+		printf " ((t2))" >expected &&
+		git checkout --detach b1 &&
+		test_when_finished "git checkout master" &&
+		__git_ps1 >"$actual" &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - inside .git directory" '
-	printf " (GIT_DIR!)" >expected &&
-	(
-		cd .git &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - inside .git directory" '
+		printf " (GIT_DIR!)" >expected &&
+		(
+			cd .git &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - deep inside .git directory" '
-	printf " (GIT_DIR!)" >expected &&
-	(
-		cd .git/refs/heads &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - deep inside .git directory" '
+		printf " (GIT_DIR!)" >expected &&
+		(
+			cd .git/refs/heads &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - inside bare repository" '
-	printf " (BARE:master)" >expected &&
-	git init --bare bare.git &&
-	test_when_finished "rm -rf bare.git" &&
-	(
-		cd bare.git &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - inside bare repository" '
+		printf " (BARE:master)" >expected &&
+		git init --bare bare.git &&
+		test_when_finished "rm -rf bare.git" &&
+		(
+			cd bare.git &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - interactive rebase" '
-	printf " (b1|REBASE-i 2/3)" >expected
-	write_script fake_editor.sh <<-\EOF &&
-		echo "exec echo" >"$1"
-		echo "edit $(git log -1 --format="%h")" >>"$1"
-		echo "exec echo" >>"$1"
-	EOF
-	test_when_finished "rm -f fake_editor.sh" &&
-	test_set_editor "$TRASH_DIRECTORY/fake_editor.sh" &&
-	git checkout b1 &&
-	test_when_finished "git checkout master" &&
-	git rebase -i HEAD^ &&
-	test_when_finished "git rebase --abort"
-	__git_ps1 >"$actual" &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - interactive rebase" '
+		printf " (b1|REBASE-i 2/3)" >expected
+		write_script fake_editor.sh <<-\EOF &&
+			echo "exec echo" >"$1"
+			echo "edit $(git log -1 --format="%h")" >>"$1"
+			echo "exec echo" >>"$1"
+		EOF
+		test_when_finished "rm -f fake_editor.sh" &&
+		test_set_editor "$TRASH_DIRECTORY/fake_editor.sh" &&
+		git checkout b1 &&
+		test_when_finished "git checkout master" &&
+		git rebase -i HEAD^ &&
+		test_when_finished "git rebase --abort"
+		__git_ps1 >"$actual" &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - rebase merge" '
-	printf " (b2|REBASE-m 1/3)" >expected &&
-	git checkout b2 &&
-	test_when_finished "git checkout master" &&
-	test_must_fail git rebase --merge b1 b2 &&
-	test_when_finished "git rebase --abort" &&
-	__git_ps1 >"$actual" &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - rebase merge" '
+		printf " (b2|REBASE-m 1/3)" >expected &&
+		git checkout b2 &&
+		test_when_finished "git checkout master" &&
+		test_must_fail git rebase --merge b1 b2 &&
+		test_when_finished "git rebase --abort" &&
+		__git_ps1 >"$actual" &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - rebase" '
-	printf " (b2|REBASE 1/3)" >expected &&
-	git checkout b2 &&
-	test_when_finished "git checkout master" &&
-	test_must_fail git rebase b1 b2 &&
-	test_when_finished "git rebase --abort" &&
-	__git_ps1 >"$actual" &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - rebase" '
+		printf " (b2|REBASE 1/3)" >expected &&
+		git checkout b2 &&
+		test_when_finished "git checkout master" &&
+		test_must_fail git rebase b1 b2 &&
+		test_when_finished "git rebase --abort" &&
+		__git_ps1 >"$actual" &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - merge" '
-	printf " (b1|MERGING)" >expected &&
-	git checkout b1 &&
-	test_when_finished "git checkout master" &&
-	test_must_fail git merge b2 &&
-	test_when_finished "git reset --hard" &&
-	__git_ps1 >"$actual" &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - merge" '
+		printf " (b1|MERGING)" >expected &&
+		git checkout b1 &&
+		test_when_finished "git checkout master" &&
+		test_must_fail git merge b2 &&
+		test_when_finished "git reset --hard" &&
+		__git_ps1 >"$actual" &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - cherry-pick" '
-	printf " (master|CHERRY-PICKING)" >expected &&
-	test_must_fail git cherry-pick b1 &&
-	test_when_finished "git reset --hard" &&
-	__git_ps1 >"$actual" &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - cherry-pick" '
+		printf " (master|CHERRY-PICKING)" >expected &&
+		test_must_fail git cherry-pick b1 &&
+		test_when_finished "git reset --hard" &&
+		__git_ps1 >"$actual" &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - bisect" '
-	printf " (master|BISECTING)" >expected &&
-	git bisect start &&
-	test_when_finished "git bisect reset" &&
-	__git_ps1 >"$actual" &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - bisect" '
+		printf " (master|BISECTING)" >expected &&
+		git bisect start &&
+		test_when_finished "git bisect reset" &&
+		__git_ps1 >"$actual" &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - dirty status indicator - clean" '
-	printf " (master)" >expected &&
-	(
-		GIT_PS1_SHOWDIRTYSTATE=y &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - dirty status indicator - clean" '
+		printf " (master)" >expected &&
+		(
+			GIT_PS1_SHOWDIRTYSTATE=y &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - dirty status indicator - dirty worktree" '
-	printf " (master *)" >expected &&
-	echo "dirty" >file &&
-	test_when_finished "git reset --hard" &&
-	(
-		GIT_PS1_SHOWDIRTYSTATE=y &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - dirty status indicator - dirty worktree" '
+		printf " (master *)" >expected &&
+		echo "dirty" >file &&
+		test_when_finished "git reset --hard" &&
+		(
+			GIT_PS1_SHOWDIRTYSTATE=y &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - dirty status indicator - dirty index" '
-	printf " (master +)" >expected &&
-	echo "dirty" >file &&
-	test_when_finished "git reset --hard" &&
-	git add -u &&
-	(
-		GIT_PS1_SHOWDIRTYSTATE=y &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - dirty status indicator - dirty index" '
+		printf " (master +)" >expected &&
+		echo "dirty" >file &&
+		test_when_finished "git reset --hard" &&
+		git add -u &&
+		(
+			GIT_PS1_SHOWDIRTYSTATE=y &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - dirty status indicator - dirty index and worktree" '
-	printf " (master *+)" >expected &&
-	echo "dirty index" >file &&
-	test_when_finished "git reset --hard" &&
-	git add -u &&
-	echo "dirty worktree" >file &&
-	(
-		GIT_PS1_SHOWDIRTYSTATE=y &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - dirty status indicator - dirty index and worktree" '
+		printf " (master *+)" >expected &&
+		echo "dirty index" >file &&
+		test_when_finished "git reset --hard" &&
+		git add -u &&
+		echo "dirty worktree" >file &&
+		(
+			GIT_PS1_SHOWDIRTYSTATE=y &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - dirty status indicator - before root commit" '
-	printf " (master #)" >expected &&
-	(
-		GIT_PS1_SHOWDIRTYSTATE=y &&
-		cd otherrepo &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - dirty status indicator - before root commit" '
+		printf " (master #)" >expected &&
+		(
+			GIT_PS1_SHOWDIRTYSTATE=y &&
+			cd otherrepo &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - dirty status indicator - shell variable unset with config disabled" '
-	printf " (master)" >expected &&
-	echo "dirty" >file &&
-	test_when_finished "git reset --hard" &&
-	test_config bash.showDirtyState false &&
-	(
-		sane_unset GIT_PS1_SHOWDIRTYSTATE &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - dirty status indicator - shell variable unset with config disabled" '
+		printf " (master)" >expected &&
+		echo "dirty" >file &&
+		test_when_finished "git reset --hard" &&
+		test_config bash.showDirtyState false &&
+		(
+			sane_unset GIT_PS1_SHOWDIRTYSTATE &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - dirty status indicator - shell variable unset with config enabled" '
-	printf " (master)" >expected &&
-	echo "dirty" >file &&
-	test_when_finished "git reset --hard" &&
-	test_config bash.showDirtyState true &&
-	(
-		sane_unset GIT_PS1_SHOWDIRTYSTATE &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - dirty status indicator - shell variable unset with config enabled" '
+		printf " (master)" >expected &&
+		echo "dirty" >file &&
+		test_when_finished "git reset --hard" &&
+		test_config bash.showDirtyState true &&
+		(
+			sane_unset GIT_PS1_SHOWDIRTYSTATE &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - dirty status indicator - shell variable set with config disabled" '
-	printf " (master)" >expected &&
-	echo "dirty" >file &&
-	test_when_finished "git reset --hard" &&
-	test_config bash.showDirtyState false &&
-	(
-		GIT_PS1_SHOWDIRTYSTATE=y &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - dirty status indicator - shell variable set with config disabled" '
+		printf " (master)" >expected &&
+		echo "dirty" >file &&
+		test_when_finished "git reset --hard" &&
+		test_config bash.showDirtyState false &&
+		(
+			GIT_PS1_SHOWDIRTYSTATE=y &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - dirty status indicator - shell variable set with config enabled" '
-	printf " (master *)" >expected &&
-	echo "dirty" >file &&
-	test_when_finished "git reset --hard" &&
-	test_config bash.showDirtyState true &&
-	(
-		GIT_PS1_SHOWDIRTYSTATE=y &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - dirty status indicator - shell variable set with config enabled" '
+		printf " (master *)" >expected &&
+		echo "dirty" >file &&
+		test_when_finished "git reset --hard" &&
+		test_config bash.showDirtyState true &&
+		(
+			GIT_PS1_SHOWDIRTYSTATE=y &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - dirty status indicator - not shown inside .git directory" '
-	printf " (GIT_DIR!)" >expected &&
-	echo "dirty" >file &&
-	test_when_finished "git reset --hard" &&
-	(
-		GIT_PS1_SHOWDIRTYSTATE=y &&
-		cd .git &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - dirty status indicator - not shown inside .git directory" '
+		printf " (GIT_DIR!)" >expected &&
+		echo "dirty" >file &&
+		test_when_finished "git reset --hard" &&
+		(
+			GIT_PS1_SHOWDIRTYSTATE=y &&
+			cd .git &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - stash status indicator - no stash" '
-	printf " (master)" >expected &&
-	(
-		GIT_PS1_SHOWSTASHSTATE=y &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - stash status indicator - no stash" '
+		printf " (master)" >expected &&
+		(
+			GIT_PS1_SHOWSTASHSTATE=y &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - stash status indicator - stash" '
-	printf " (master $)" >expected &&
-	echo 2 >file &&
-	git stash &&
-	test_when_finished "git stash drop" &&
-	git pack-refs --all &&
-	(
-		GIT_PS1_SHOWSTASHSTATE=y &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - stash status indicator - stash" '
+		printf " (master $)" >expected &&
+		echo 2 >file &&
+		git stash &&
+		test_when_finished "git stash drop" &&
+		git pack-refs --all &&
+		(
+			GIT_PS1_SHOWSTASHSTATE=y &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - stash status indicator - not shown inside .git directory" '
-	printf " (GIT_DIR!)" >expected &&
-	echo 2 >file &&
-	git stash &&
-	test_when_finished "git stash drop" &&
-	(
-		GIT_PS1_SHOWSTASHSTATE=y &&
-		cd .git &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - stash status indicator - not shown inside .git directory" '
+		printf " (GIT_DIR!)" >expected &&
+		echo 2 >file &&
+		git stash &&
+		test_when_finished "git stash drop" &&
+		(
+			GIT_PS1_SHOWSTASHSTATE=y &&
+			cd .git &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - untracked files status indicator - no untracked files" '
-	printf " (master)" >expected &&
-	(
-		GIT_PS1_SHOWUNTRACKEDFILES=y &&
-		cd otherrepo &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - untracked files status indicator - no untracked files" '
+		printf " (master)" >expected &&
+		(
+			GIT_PS1_SHOWUNTRACKEDFILES=y &&
+			cd otherrepo &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - untracked files status indicator - untracked files" '
-	printf " (master %%)" >expected &&
-	(
-		GIT_PS1_SHOWUNTRACKEDFILES=y &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - untracked files status indicator - untracked files" '
+		printf " (master %%)" >expected &&
+		(
+			GIT_PS1_SHOWUNTRACKEDFILES=y &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - untracked files status indicator - shell variable unset with config disabled" '
-	printf " (master)" >expected &&
-	test_config bash.showUntrackedFiles false &&
-	(
-		sane_unset GIT_PS1_SHOWUNTRACKEDFILES &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - untracked files status indicator - shell variable unset with config disabled" '
+		printf " (master)" >expected &&
+		test_config bash.showUntrackedFiles false &&
+		(
+			sane_unset GIT_PS1_SHOWUNTRACKEDFILES &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - untracked files status indicator - shell variable unset with config enabled" '
-	printf " (master)" >expected &&
-	test_config bash.showUntrackedFiles true &&
-	(
-		sane_unset GIT_PS1_SHOWUNTRACKEDFILES &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - untracked files status indicator - shell variable unset with config enabled" '
+		printf " (master)" >expected &&
+		test_config bash.showUntrackedFiles true &&
+		(
+			sane_unset GIT_PS1_SHOWUNTRACKEDFILES &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - untracked files status indicator - shell variable set with config disabled" '
-	printf " (master)" >expected &&
-	test_config bash.showUntrackedFiles false &&
-	(
-		GIT_PS1_SHOWUNTRACKEDFILES=y &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - untracked files status indicator - shell variable set with config disabled" '
+		printf " (master)" >expected &&
+		test_config bash.showUntrackedFiles false &&
+		(
+			GIT_PS1_SHOWUNTRACKEDFILES=y &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - untracked files status indicator - shell variable set with config enabled" '
-	printf " (master %%)" >expected &&
-	test_config bash.showUntrackedFiles true &&
-	(
-		GIT_PS1_SHOWUNTRACKEDFILES=y &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - untracked files status indicator - shell variable set with config enabled" '
+		printf " (master %%)" >expected &&
+		test_config bash.showUntrackedFiles true &&
+		(
+			GIT_PS1_SHOWUNTRACKEDFILES=y &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - untracked files status indicator - not shown inside .git directory" '
-	printf " (GIT_DIR!)" >expected &&
-	(
-		GIT_PS1_SHOWUNTRACKEDFILES=y &&
-		cd .git &&
-		__git_ps1 >"$actual"
-	) &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - untracked files status indicator - not shown inside .git directory" '
+		printf " (GIT_DIR!)" >expected &&
+		(
+			GIT_PS1_SHOWUNTRACKEDFILES=y &&
+			cd .git &&
+			__git_ps1 >"$actual"
+		) &&
+		test_cmp expected "$actual"
+	'
 
-test_expect_success "$pfx - format string starting with dash" '
-	printf -- "-master" >expected &&
-	__git_ps1 "-%s" >"$actual" &&
-	test_cmp expected "$actual"
-'
+	test_expect_success "$pfx - format string starting with dash" '
+		printf -- "-master" >expected &&
+		__git_ps1 "-%s" >"$actual" &&
+		test_cmp expected "$actual"
+	'
+}
 
 pcmode_expected () {
 	case $ps1expansion in
@@ -645,9 +643,11 @@ _run_pcmode_tests () {
 	'
 }
 
-run_pcmode_tests () {
+run_prompt_tests () {
+	. "$GIT_BUILD_DIR/contrib/completion/git-prompt.sh"
+	actual="$TRASH_DIRECTORY/actual"
+	set_ps1_format_vars
+	_run_non_pcmode_tests
 	_run_pcmode_tests on
 	_run_pcmode_tests off
 }
-
-run_pcmode_tests
diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh
index b698fe9..858f0cd 100755
--- a/t/t9903-bash-prompt.sh
+++ b/t/t9903-bash-prompt.sh
@@ -8,4 +8,6 @@ test_description='test git-specific bash prompt functions'
 . ./lib-bash.sh
 . "$TEST_DIRECTORY"/lib-prompt-tests.sh
 
+run_prompt_tests
+
 test_done
-- 
2.0.0
Richard Hansen· Jun 4, 2014, 21:01 UTC · re: Richard Hansen · lore

[PATCH v2 09/11] lib-prompt-tests.sh: add variable for string that encodes percent in PS1

To add a literal percent character to a Zsh prompt, the string "%%" is used in PS1. Bash and POSIX shells simply use "%". To accommodate this difference, use ${percent} where a percent character is expected and define the percent variable in the set_ps1_format_vars function.

Signed-off-by: Richard Hansen <rhansen@bbn.com>
---
 t/lib-bash.sh         |  1 +
 t/lib-prompt-tests.sh | 15 ++++++++-------
 2 files changed, 9 insertions(+), 7 deletions(-)
Show changes to 2 files +9 −7

t/lib-bash.sh, t/lib-prompt-tests.sh

diff --git a/t/lib-bash.sh b/t/lib-bash.sh
index 9d428bd..8a030ac 100644
--- a/t/lib-bash.sh
+++ b/t/lib-bash.sh
@@ -19,6 +19,7 @@ shellname=Bash
 ps1_expansion_enable () { shopt -s promptvars; }
 ps1_expansion_disable () { shopt -u promptvars; }
 set_ps1_format_vars () {
+	percent='%%'
 	c_red='\\[\\e[31m\\]'
 	c_green='\\[\\e[32m\\]'
 	c_lblue='\\[\\e[1;34m\\]'
diff --git a/t/lib-prompt-tests.sh b/t/lib-prompt-tests.sh
index ba22acc..c6226b1 100644
--- a/t/lib-prompt-tests.sh
+++ b/t/lib-prompt-tests.sh
@@ -12,10 +12,11 @@
 #      (non-zero) and ps1_expansion_disable should simply return
 #      non-zero (0)
 #   3. define a function named set_ps1_format_vars that sets the
-#      variables c_red, c_green, c_lblue, and c_clear to the strings
-#      that __git_ps1 uses to add color to the prompt.  The values of
-#      these variables are used in the first argument to the printf
-#      command, so they must be escaped appropriately.
+#      variables percent, c_red, c_green, c_lblue, and c_clear to the
+#      strings that __git_ps1 uses to add percent characters and color
+#      to the prompt.  The values of these variables are used in the
+#      first argument to the printf command, so they must be escaped
+#      appropriately.
 #   4. source this library
 #   5. invoke the run_prompt_tests function
 
@@ -403,7 +404,7 @@ newline'
 	'
 
 	test_expect_success "$pfx - untracked files status indicator - untracked files" '
-		printf " (master %%)" >expected &&
+		printf " (master ${percent})" >expected &&
 		(
 			GIT_PS1_SHOWUNTRACKEDFILES=y &&
 			__git_ps1 >"$actual"
@@ -442,7 +443,7 @@ newline'
 	'
 
 	test_expect_success "$pfx - untracked files status indicator - shell variable set with config enabled" '
-		printf " (master %%)" >expected &&
+		printf " (master ${percent})" >expected &&
 		test_config bash.showUntrackedFiles true &&
 		(
 			GIT_PS1_SHOWUNTRACKEDFILES=y &&
@@ -632,7 +633,7 @@ _run_pcmode_tests () {
 
 	test_expect_success "$pfx - untracked files status indicator" '
 		set_ps1expansion &&
-		pcmode_expected "BEFORE: (${c_green}%s${c_clear} ${c_red}%%${c_clear}):AFTER\\n%s" master &&
+		pcmode_expected "BEFORE: (${c_green}%s${c_clear} ${c_red}${percent}${c_clear}):AFTER\\n%s" master &&
 		(
 			GIT_PS1_SHOWUNTRACKEDFILES=y &&
 			GIT_PS1_SHOWCOLORHINTS=y &&
-- 
2.0.0
Richard Hansen· Jun 4, 2014, 21:01 UTC · re: Richard Hansen · lore

[PATCH v2 10/11] test-lib: make it possible to override how test code is eval'd

If a command named 'test_eval_override' exists, use it instead of 'eval' to run the test code.

This is needed to support zsh test cases: test-lib.sh must be sourced in sh emulation mode due to fundamental incompatibilities between the POSIX sh language and the zsh language. When a function is defined while zsh is emulating some shell, zsh notes the shell that is being emulated and records it along with the function definition. This enables zsh to temporarily re-enable the shell emulation mode whenever the function is called, allowing zsh scripts to mix and match code written for different shell languages. (This description of zsh shell emulation is not completely accurate, but it's close enough.)

Because test_eval_ is defined while zsh is in sh emulation mode, the shell code passed as an argument to test_expect_success would normally be evaluated in sh emulation mode. However, with this change, it is now possible to evaluate the test code in zsh mode by adding the following line to a zsh-based test script:

    emulate -R zsh -c 'test_eval_override () { eval "$*"; }'

With test_eval_override defined in zsh emulation mode, the call to test_eval_override from test_eval_ will temporarily cause zsh to switch from sh emulation mode to zsh emulation mode.

Signed-off-by: Richard Hansen <rhansen@bbn.com>
---
 t/test-lib.sh | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)
Show changes to t/test-lib.sh +6 −1
diff --git a/t/test-lib.sh b/t/test-lib.sh
index c081668..3779634 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -414,7 +414,12 @@ maybe_setup_valgrind () {
 test_eval_ () {
 	# This is a separate function because some tests use
 	# "return" to end a test_expect_success block early.
-	eval </dev/null >&3 2>&4 "$*"
+	if command -v test_eval_override >/dev/null 2>&1
+	then
+		test_eval_override "$*"
+	else
+		eval "$*"
+	fi </dev/null >&3 2>&4
 }
 
 test_run_ () {
-- 
2.0.0
Junio C Hamano· Jun 5, 2014, 21:11 UTC · re: Richard Hansen · lore

Re: [PATCH v2 10/11] test-lib: make it possible to override how test code is eval'd

Richard Hansen <rhansen@bbn.com> writes:
Show 11 quoted lines
> Because test_eval_ is defined while zsh is in sh emulation mode, the
> shell code passed as an argument to test_expect_success would normally
> be evaluated in sh emulation mode.  However, with this change, it is
> now possible to evaluate the test code in zsh mode by adding the
> following line to a zsh-based test script:
>
>     emulate -R zsh -c 'test_eval_override () { eval "$*"; }'
>
> With test_eval_override defined in zsh emulation mode, the call to
> test_eval_override from test_eval_ will temporarily cause zsh to
> switch from sh emulation mode to zsh emulation mode.
Micronit: aren't all "zsh emulation mode"s above "zsh native mode"s?

In any case, the above explanation confuses me somewhat. test_eval_ is fed a scriptlet defined for various test_expect_success tests, and they are written in POSIX shells, not zsh, so wouldn't it be wrong to run them as if they are zsh native scripts, following non-POSIX shell syntax rules?

Puzzled...
Show 23 quoted lines
> Signed-off-by: Richard Hansen <rhansen@bbn.com>
> ---
>  t/test-lib.sh | 7 ++++++-
>  1 file changed, 6 insertions(+), 1 deletion(-)
>
> diff --git a/t/test-lib.sh b/t/test-lib.sh
> index c081668..3779634 100644
> --- a/t/test-lib.sh
> +++ b/t/test-lib.sh
> @@ -414,7 +414,12 @@ maybe_setup_valgrind () {
>  test_eval_ () {
>  	# This is a separate function because some tests use
>  	# "return" to end a test_expect_success block early.
> -	eval </dev/null >&3 2>&4 "$*"
> +	if command -v test_eval_override >/dev/null 2>&1
> +	then
> +		test_eval_override "$*"
> +	else
> +		eval "$*"
> +	fi </dev/null >&3 2>&4
>  }
>  
>  test_run_ () {
Richard Hansen· Jun 6, 2014, 01:00 UTC · re: Junio C Hamano · lore

Re: [PATCH v2 10/11] test-lib: make it possible to override how test code is eval'd

On 2014-06-05 17:11, Junio C Hamano wrote:
Show 15 quoted lines
> Richard Hansen <rhansen@bbn.com> writes:
> 
>> Because test_eval_ is defined while zsh is in sh emulation mode, the
>> shell code passed as an argument to test_expect_success would normally
>> be evaluated in sh emulation mode.  However, with this change, it is
>> now possible to evaluate the test code in zsh mode by adding the
>> following line to a zsh-based test script:
>>
>>     emulate -R zsh -c 'test_eval_override () { eval "$*"; }'
>>
>> With test_eval_override defined in zsh emulation mode, the call to
>> test_eval_override from test_eval_ will temporarily cause zsh to
>> switch from sh emulation mode to zsh emulation mode.
> 
> Micronit: aren't all "zsh emulation mode"s above "zsh native mode"s?

Sort of... Zsh's emulation is under-documented, confusing, and awkwardly implemented (in my opinion) so it's hard to precisely describe what's going on in a few words.

I'll explain it here more precisely (apologies for the length) and you can let me know if I should add more detail to the commit message and/or code comments:

As far as I understand it, there isn't really such a thing as an "emulation mode" in Zsh -- the phrase "emulation mode" is just a convenient way to refer to the mechanism. Zsh "emulates" other shells by simply toggling a particular collection of shell options. For example, 'emulate sh' turns on the option that controls field splitting and turns off the option that updates $0 whenever a function is called (among other option changes).

One might expect 'emulate <shell> -c <code>' to be equivalent to 'emulate <shell>; <code>; emulate <previous_shell>' but it's not -- there's a significant behavior difference. When '-c <code>' is used, and only when '-c <code>' is used, a function defined inside <code> gets "sticky options": The shell remembers the state of some (all?) options and saves them along with the function definition. Whenever the function is called, those sticky options are temporarily restored for the duration of the function call. (This is not entirely accurate -- there are some relatively unimportant cases where the sticky options aren't restored -- but I don't understand the subtleties of the mechanism well enough to provide a perfectly accurate description.)

If a function with sticky options calls a function without sticky options, the sticky options remain in effect -- Zsh doesn't temporarily revert the sticky options while it calls the non-sticky function.

Thus, if a function foo() is declared inside 'emulate sh -c <code>', and it calls a function bar() that was declared outside of 'emulate', the options are still set for POSIX sh during bar()'s execution. This might cause bar() to misbehave. To work around this, bar() must be declared like 'emulate zsh -c "bar() { stuff... }"' so that bar() gets its own sticky options.

This commit makes it possible to declare a function that will get sticky options so that we can control the state of the shell when the scriptlet is executed.

Show 6 quoted lines
> 
> In any case, the above explanation confuses me somewhat.  test_eval_
> is fed a scriptlet defined for various test_expect_success tests,
> and they are written in POSIX shells, not zsh, so wouldn't it be
> wrong to run them as if they are zsh native scripts, following
> non-POSIX shell syntax rules?

The scriptlets in lib-prompt-tests.sh are not actually written for POSIX sh -- they are written in a common subset of the zsh and bash languages (I should document this in lib-prompt-tests.sh).

We want to test how the __git_ps1 code behaves when interpreted in "native" zsh mode (default options), because that's how it will be used in the wild, so the scriptlets must be valid zsh code. We also want to test how __git_ps1 behaves in native bash mode, so the scriptlets must also be valid bash code. (Fortunately the similarities between the shells make this easy to do.)

An alternative to this commit -- and I kinda like this idea so I'm tempted to rewrite the series -- would be to do change the form of the tests in lib-prompt-tests.sh to something like this:

    test_expect_success 'name of test here' '
        run_in_native_shell_mode '\''
            scriptlet code here
        '\''
    '
Then lib-zsh.sh would do this:
    run_in_native_shell_mode () { emulate -R zsh -c "$*"; }
and lib-bash.sh would do this:
    run_in_native_shell_mode () { eval "$*"; }

This approach makes it clear to others that the scriptlet code is not actually POSIX shell code, but a common subset of multiple shell languages.

What do you think?

Ignoring t9902 for a moment, we could even stop doing that messy 'exec $SHELL "$0" "$@"' stuff in lib-*sh.sh and let t9903 and t9904 run under /bin/sh. Then run_in_native_shell_mode() would be defined as follows:

  for zsh:
    # wrap the scriptlet in a function in case it does 'return'
    run_in_native_shell_mode () { zsh -c "foo () { $*; }; foo"; }
  for bash:
    # wrap the scriptlet in a function in case it does 'return'
    run_in_native_shell_mode () { bash -c "foo () { $*; }; foo"; }

Running the scriptlets in a separate process like this would cause its own mess -- the separate processes wouldn't be able to use those convenience shell functions I added (pcmode_expected(), etc.), so either there'd be a lot of code copy+paste or each scriptlet would have to source a helper library.

-Richard
Show 26 quoted lines
> 
> Puzzled...
> 
>> Signed-off-by: Richard Hansen <rhansen@bbn.com>
>> ---
>>  t/test-lib.sh | 7 ++++++-
>>  1 file changed, 6 insertions(+), 1 deletion(-)
>>
>> diff --git a/t/test-lib.sh b/t/test-lib.sh
>> index c081668..3779634 100644
>> --- a/t/test-lib.sh
>> +++ b/t/test-lib.sh
>> @@ -414,7 +414,12 @@ maybe_setup_valgrind () {
>>  test_eval_ () {
>>  	# This is a separate function because some tests use
>>  	# "return" to end a test_expect_success block early.
>> -	eval </dev/null >&3 2>&4 "$*"
>> +	if command -v test_eval_override >/dev/null 2>&1
>> +	then
>> +		test_eval_override "$*"
>> +	else
>> +		eval "$*"
>> +	fi </dev/null >&3 2>&4
>>  }
>>  
>>  test_run_ () {
Junio C Hamano· Jun 6, 2014, 16:53 UTC · re: Richard Hansen · lore

Re: [PATCH v2 10/11] test-lib: make it possible to override how test code is eval'd

Richard Hansen <rhansen@bbn.com> writes:
Show 18 quoted lines
> On 2014-06-05 17:11, Junio C Hamano wrote:
> ...
>> In any case, the above explanation confuses me somewhat.  test_eval_
>> is fed a scriptlet defined for various test_expect_success tests,
>> and they are written in POSIX shells, not zsh, so wouldn't it be
>> wrong to run them as if they are zsh native scripts, following
>> non-POSIX shell syntax rules?
>
> The scriptlets in lib-prompt-tests.sh are not actually written for POSIX
> sh -- they are written in a common subset of the zsh and bash languages
> (I should document this in lib-prompt-tests.sh).
>
> We want to test how the __git_ps1 code behaves when interpreted in
> "native" zsh mode (default options), because that's how it will be used
> in the wild, so the scriptlets must be valid zsh code.  We also want to
> test how __git_ps1 behaves in native bash mode, so the scriptlets must
> also be valid bash code.  (Fortunately the similarities between the
> shells make this easy to do.)

OK. The above all makes sense, but I think we would prefer a solution with changes limited to lib-prompt-tests and lib-zsh without touching lib-test-functions at all if that is the case.

Show 9 quoted lines
> An alternative to this commit -- and I kinda like this idea so I'm
> tempted to rewrite the series -- would be to do change the form of the
> tests in lib-prompt-tests.sh to something like this:
>
>     test_expect_success 'name of test here' '
>         run_in_native_shell_mode '\''
>             scriptlet code here
>         '\''
>     '
Yeah, or even:
	prompt_test_expect success 'name of test' '
        	scriptlet code here
	'

with a helper prompt_test_expect that wraps whatever logic you will have in run-in-native-shell-mode.

Show 5 quoted lines
> ...
> This approach makes it clear to others that the scriptlet code is not
> actually POSIX shell code, but a common subset of multiple shell languages.
>
> What do you think?
;-)
> Ignoring t9902 for a moment, we could even stop doing that messy 'exec
> $SHELL "$0" "$@"' stuff in lib-*sh.sh and let t9903 and t9904 run under
> /bin/sh.  Then run_in_native_shell_mode() would be defined as follows:

No, let's not go there (and you stated the reason why we do not want to yourself already ;-).

Richard Hansen· Jun 4, 2014, 21:01 UTC · re: Richard Hansen · lore

[PATCH v2 11/11] t9904: new __git_ps1 tests for Zsh

These are the same tests as in t9903, but run in zsh instead of bash.
Signed-off-by: Richard Hansen <rhansen@bbn.com>
---
 t/lib-zsh.sh          | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++
 t/t9904-zsh-prompt.sh | 10 ++++++++++
 2 files changed, 62 insertions(+)
 create mode 100644 t/lib-zsh.sh
 create mode 100755 t/t9904-zsh-prompt.sh
Show changes to 2 files +62 −0

t/lib-zsh.sh, t/t9904-zsh-prompt.sh

diff --git a/t/lib-zsh.sh b/t/lib-zsh.sh
new file mode 100644
index 0000000..1fd69fd
--- /dev/null
+++ b/t/lib-zsh.sh
@@ -0,0 +1,52 @@
+# Shell library sourced instead of ./test-lib.sh by tests that need to
+# run under Zsh; primarily intended for tests of the git-prompt.sh
+# script.
+
+if test -n "$ZSH_VERSION" && test -z "$POSIXLY_CORRECT" && [[ ! -o FUNCTION_ARGZERO ]]; then
+	true
+elif command -v zsh >/dev/null 2>&1; then
+	unset POSIXLY_CORRECT
+	# Run Zsh with the FUNCTION_ARGZERO option disabled so that
+	# test-lib.sh sees the test script pathname when it examines
+	# $0 instead of "./lib-zsh.sh".  (This works around a Zsh
+	# limitation: 'emulate sh -c' does not restore $0 to the value
+	# specified by POSIX.)
+	exec zsh +o FUNCTION_ARGZERO "$0" "$@"
+else
+	echo '1..0 #SKIP skipping Zsh-specific tests; zsh not available'
+	exit 0
+fi
+
+# ensure that we are in full-on Zsh mode.  note: this re-enables the
+# FUNCTION_ARGZERO option
+emulate -R zsh || exit 1
+
+shellname=Zsh
+
+ps1_expansion_enable () { setopt PROMPT_SUBST; }
+ps1_expansion_disable () { unsetopt PROMPT_SUBST; }
+set_ps1_format_vars () {
+	percent='%%%%'
+	c_red='%%F{red}'
+	c_green='%%F{green}'
+	c_lblue='%%F{blue}'
+	c_clear='%%f'
+}
+
+# Due to language incompatibilities between POSIX sh and Zsh,
+# test-lib.sh must be sourced in sh emulation mode.
+#
+# Note: Although the FUNCTION_ARGZERO option is currently enabled, sh
+# emulation mode temporarily turns it off ($0 is left alone when
+# sourcing test-lib.sh)
+emulate -R sh -c '. ./test-lib.sh'
+
+# Ensure that the test code is run in Zsh mode.  Because test_eval_()
+# was defined by test-lib.sh inside the above 'emulate sh -c', the Zsh
+# shell options that implement sh emulation will be temporarily
+# toggled when test_eval_() executes.  Normally this would cause the
+# test code to run in sh emulation mode, not Zsh mode.  By defining
+# test_eval_override() in zsh emulation mode, the options are
+# temporarily toggled back to the Zsh defaults when evaluating the
+# test code.
+emulate -R zsh -c 'test_eval_override () { eval "$*"; }'
diff --git a/t/t9904-zsh-prompt.sh b/t/t9904-zsh-prompt.sh
new file mode 100755
index 0000000..a38a3fd
--- /dev/null
+++ b/t/t9904-zsh-prompt.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+test_description='test git-specific Zsh prompt functions'
+
+. ./lib-zsh.sh
+. "$TEST_DIRECTORY"/lib-prompt-tests.sh
+
+run_prompt_tests
+
+test_done
-- 
2.0.0
Torsten Bögershausen· Jun 10, 2014, 20:06 UTC · re: Richard Hansen · lore

Re: [PATCH v2 00/11] Zsh prompt tests

On 2014-06-04 23.01, Richard Hansen wrote: [] I haven't digged too deep, but this is what I get on pu:

./t9904-zsh-prompt.sh ./lib-zsh.sh:emulate:42: too many arguments ./lib-zsh.sh:emulate:52: too many arguments /lib-prompt-tests.sh:.:6: no such file or directory: /lib-prompt-tests.sh ##

 zsh --version
zsh 4.3.9 (i386-apple-darwin10.0)
I'm not a zsh expert, but I can offer to do more tests/debugging if needed 
Richard Hansen· Jun 10, 2014, 20:28 UTC · re: Torsten Bögershausen · lore

Re: [PATCH v2 00/11] Zsh prompt tests

On 2014-06-10 16:06, Torsten Bögershausen wrote:
Show 9 quoted lines
> On 2014-06-04 23.01, Richard Hansen wrote:
> []
> I haven't digged too deep, but this is what I get on pu:
> 
> ./t9904-zsh-prompt.sh 
> ./lib-zsh.sh:emulate:42: too many arguments
> ./lib-zsh.sh:emulate:52: too many arguments
> /lib-prompt-tests.sh:.:6: no such file or directory: /lib-prompt-tests.sh
> ##
Thank you for trying these patches and reporting your findings!
> 
>  zsh --version
> zsh 4.3.9 (i386-apple-darwin10.0)

zsh 4.3.9 is over 5 years old (2008-11-03). Is that young enough that we should still try to support it?

> 
> I'm not a zsh expert, but I can offer to do more tests/debugging if needed 

I'm not a zsh expert either (I just got a crash course over the past couple of weeks), but if I were to guess I'd say that the 'emulate -c' option is new to zsh 5.0 (2012-07-21). When I next have time I'll try testing with a bunch of different versions of zsh.

Thanks, Richard

brian m. carlson· Jun 11, 2014, 01:16 UTC · re: Richard Hansen · lore

Re: [PATCH v2 00/11] Zsh prompt tests

On Tue, Jun 10, 2014 at 04:28:46PM -0400, Richard Hansen wrote:
Show 19 quoted lines
> On 2014-06-10 16:06, Torsten Bögershausen wrote:
> > On 2014-06-04 23.01, Richard Hansen wrote:
> > []
> > I haven't digged too deep, but this is what I get on pu:
> > 
> > ./t9904-zsh-prompt.sh 
> > ./lib-zsh.sh:emulate:42: too many arguments
> > ./lib-zsh.sh:emulate:52: too many arguments
> > /lib-prompt-tests.sh:.:6: no such file or directory: /lib-prompt-tests.sh
> > ##
> 
> Thank you for trying these patches and reporting your findings!
> 
> > 
> >  zsh --version
> > zsh 4.3.9 (i386-apple-darwin10.0)
> 
> zsh 4.3.9 is over 5 years old (2008-11-03).  Is that young enough that
> we should still try to support it?

zsh 4.3.10 is the version in RHEL/CentOS 6, and people are still using CentOS 5. At $DAYJOB we build git on both, so it would be nice if at least the tests didn't fail. Skipping them on older versions (maybe using is-at-least) would be fine by me.

-- 
brian m. carlson / brian with sandals: Houston, Texas, US
+1 832 623 2791 | http://www.crustytoothpaste.net/~bmc | My opinion only
OpenPGP: RSA v4 4096b: 88AC E9B2 9196 305B A994 7552 F1BA 225C 0223 B187
Richard Hansen· Jun 11, 2014, 15:27 UTC · re: brian m. carlson · lore

Re: [PATCH v2 00/11] Zsh prompt tests

On 2014-06-10 21:16, brian m. carlson wrote:
Show 11 quoted lines
> On Tue, Jun 10, 2014 at 04:28:46PM -0400, Richard Hansen wrote:
>> On 2014-06-10 16:06, Torsten Bögershausen wrote:
>>>  zsh --version
>>> zsh 4.3.9 (i386-apple-darwin10.0)
>>
>> zsh 4.3.9 is over 5 years old (2008-11-03).  Is that young enough that
>> we should still try to support it?
> 
> zsh 4.3.10 is the version in RHEL/CentOS 6, and people are still using
> CentOS 5.  At $DAYJOB we build git on both, so it would be nice if at
> least the tests didn't fail.

Looks like CentOS 5 has zsh 4.2.6, so when I rewrite the patch series I'll test zsh versions that are at least that old.

> Skipping them on older versions (maybe using is-at-least) would be
> fine by me.

I don't think we'll need to skip the tests on old zsh versions -- running 'zsh -c' should still work even if 'emulate zsh -c' doesn't.

Thanks, Richard

brian m. carlson· Jun 11, 2014, 23:46 UTC · re: Richard Hansen · lore

Re: [PATCH v2 00/11] Zsh prompt tests

On Wed, Jun 11, 2014 at 11:27:23AM -0400, Richard Hansen wrote:
Show 6 quoted lines
> On 2014-06-10 21:16, brian m. carlson wrote:
> > Skipping them on older versions (maybe using is-at-least) would be
> > fine by me.
> 
> I don't think we'll need to skip the tests on old zsh versions --
> running 'zsh -c' should still work even if 'emulate zsh -c' doesn't.

Okay. I honestly don't support CentOS 5 in my personal projects because it's very old, so I didn't want you to have to do a lot of extra work if you felt that the age didn't justify it. Most git users on CentOS 5 already deal with some minor reduced functionality anyways.

-- 
brian m. carlson / brian with sandals: Houston, Texas, US
+1 832 623 2791 | http://www.crustytoothpaste.net/~bmc | My opinion only
OpenPGP: RSA v4 4096b: 88AC E9B2 9196 305B A994 7552 F1BA 225C 0223 B187

← back to recent threads