aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitea/workflows/ci.yaml100
-rw-r--r--hyperterm/core/autodep.sh7
-rw-r--r--hyperterm/core/git.sh436
-rw-r--r--hyperterm/hyperterm.sha5124
-rw-r--r--tests/README.md84
-rw-r--r--tests/quick_test.sh176
-rw-r--r--tests/test_prompt.sh304
7 files changed, 778 insertions, 333 deletions
diff --git a/.gitea/workflows/ci.yaml b/.gitea/workflows/ci.yaml
index 4c181ca..a5e2ca9 100644
--- a/.gitea/workflows/ci.yaml
+++ b/.gitea/workflows/ci.yaml
@@ -1,12 +1,17 @@
name: CI Pipeline
-on: [push, pull_request]
+on:
+ push:
+ branches: [ main, master ]
+ pull_request:
+ branches: [ main, master ]
+ workflow_dispatch:
jobs:
shasums:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Run shasums script
run: |
cp -rv ./hyperterm/ "$HOME/.hyperterm/"
@@ -79,6 +84,11 @@ jobs:
shellcheck hyperterm/tools/sysinfo.sh
shellcheck hyperterm/tools/virtualenv.sh
+ - name: Run shellcheck on test scripts
+ run: |
+ shellcheck tests/test_prompt.sh
+ shellcheck tests/quick_test.sh
+
- name: Run shellcheck on install script
run: shellcheck install.sh
@@ -90,3 +100,89 @@ jobs:
- name: Run uninstall script
run: bash -x uninstall.sh -s
+
+ tests:
+ runs-on: ubuntu-latest
+ needs: build
+ env:
+ CI: true
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Set up git configuration for tests
+ run: |
+ git config --global user.name "CI Test User"
+ git config --global user.email "ci-test@example.com"
+
+ - name: Install dependencies
+ run: |
+ sudo apt-get update -y
+ sudo apt-get install -y git bash
+
+ - name: Run quick prompt test
+ run: |
+ echo "INFO: Running quick prompt test"
+ bash tests/quick_test.sh
+
+ - name: Run comprehensive prompt test
+ run: |
+ echo "INFO: Running comprehensive prompt test"
+ # Run in non-interactive mode for CI
+ bash tests/test_prompt.sh --non-interactive
+
+ - name: Test prompt in current repository
+ run: |
+ echo "INFO: Testing prompt in current git repository"
+ # Create test changes to verify git status detection
+ echo "test change for CI" >> README.md
+ echo "untracked CI test file" > ci_test_file.txt
+
+ # Test using our quick test (which already handles sourcing correctly)
+ echo "Running quick test to verify prompt works in CI environment..."
+ bash tests/quick_test.sh
+
+ - name: Validate optimizations
+ run: |
+ echo "INFO: Validating optimizations"
+
+ # Check that git_optimized.sh doesn't exist (should be integrated into git.sh)
+ if [[ -f "hyperterm/core/git_optimized.sh" ]]; then
+ echo "ERROR: git_optimized.sh should not exist (should be integrated)"
+ exit 1
+ fi
+
+ # Check that git.sh exists and is optimized
+ if [[ ! -f "hyperterm/core/git.sh" ]]; then
+ echo "ERROR: git.sh not found"
+ exit 1
+ fi
+
+ # Verify the optimized functions exist using bash
+ if ! bash -c 'source hyperterm/core/git.sh && command -v _get_git_status_fast >/dev/null 2>&1'; then
+ echo "ERROR: Optimized function _get_git_status_fast not found"
+ exit 1
+ fi
+
+ echo "SUCCESS: Optimizations validated"
+
+ performance:
+ runs-on: ubuntu-latest
+ needs: tests
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Set up git configuration
+ run: |
+ git config --global user.name "Performance Test"
+ git config --global user.email "perf-test@example.com"
+
+ - name: Performance benchmark
+ run: |
+ echo "INFO: Running performance benchmark using existing test suite"
+
+ # The comprehensive test already includes performance testing
+ # Run it again to get performance metrics in CI logs
+ bash tests/test_prompt.sh --non-interactive
+
+ echo "INFO: Performance benchmark completed"
+ echo "NOTE: Detailed performance metrics are included in the comprehensive test output above"
diff --git a/hyperterm/core/autodep.sh b/hyperterm/core/autodep.sh
index 8973100..26a1a3b 100644
--- a/hyperterm/core/autodep.sh
+++ b/hyperterm/core/autodep.sh
@@ -10,9 +10,10 @@ function install_package() {
msg "El paquete $pkg no se encontró. Procediendo a instalar..." \
"$pkg not found. Attempting to install..."
- local INSTALLER=""
- local SUDO=""
- local USER_CMD=$(command -v sudo || command -v doas)
+ local INSTALLER
+ local SUDO
+ local USER_CMD
+ USER_CMD=$(command -v sudo || command -v doas)=$(command -v sudo || command -v doas)
[[ "$(id -u)" -ne 0 ]] && SUDO="$USER_CMD"
diff --git a/hyperterm/core/git.sh b/hyperterm/core/git.sh
index 3ae80c2..4e437ef 100644
--- a/hyperterm/core/git.sh
+++ b/hyperterm/core/git.sh
@@ -1,373 +1,157 @@
#!/bin/bash
-# Set up symbols
-function _symbols() {
+# Cache symbols (generate once)
+declare -A GIT_SYMBOLS
+_init_git_symbols() {
+ [[ -n "${GIT_SYMBOLS[clean]:-}" ]] && return # Already initialized
- # Import colors
_colors_bash "$@"
- __ps="$(printf '%b%b%b' "${BOLD}${LEMON}" "\x7C" "${RESET}")" # |
- __ss="$(printf '%b%b' "${BOLD}${CYAN}" "\xE2\x9C\x94")" # ✔
- __dss="$(printf '%b%b' "${BOLD}${RED}" "\x2A")" # ∗
- __ahs="$(printf '%b%b' "${BOLD}${CYAN}" "\xE2\x86\x91")" # ↑
- __bhs="$(printf '%b%b' "${BOLD}${RED}" "\xE2\x86\x93")" # ↓
- __duphs="$(printf '%b%b' "${BOLD}${YELLOW}" "\xE2\x96\x82" )" # ▲
- __duplls="$(printf '%b%b' "${BOLD}${RED}" "\xE2\x96\xBC")" # ▼
- __duus="$(printf '%b%b%b' "${BOLD}${CYAN}" "\x64" "\x75")" # du
- __upulls="$(printf '%b%b' "${BOLD}${GREEN}" "\xE2\x96\xBD")" # ▽
- __sts="$(printf '%b%b%b' "${BOLD}${CYAN}" "\xE2\x86\x92" "\x4D")" # →M
- __usts="$(printf '%b%b%b' "${BOLD}${RED}" "\xE2\x86\x90" "\x4D")" # ←M
- __stusts="$(printf '%b%b%b%b' "${BOLD}${RED}" "\x3C" "\x4D" "\x3E")" # <M>
- __uts="$(printf '%b%b' "${BOLD}${RED}" "\x3F")" # ?
- __nfs="$(printf '%b%b' "${BOLD}${CYAN}" "\x2B")" # +
- __dfs="$(printf '%b%b' "${BOLD}${RED}" "\x44")" # D
- __rns="$(printf '%b%b%b' "${BOLD}${RED}" "\xE2\x8E\x87" "\x20")" # ⎇
+ GIT_SYMBOLS[pipe]="\x7C"
+ GIT_SYMBOLS[clean]="✔"
+ GIT_SYMBOLS[dirty]="∗"
+ GIT_SYMBOLS[ahead]="↑"
+ GIT_SYMBOLS[behind]="↓"
+ GIT_SYMBOLS[diverged]="⇅"
+ GIT_SYMBOLS[untracked]="?"
+ GIT_SYMBOLS[added]="+"
+ GIT_SYMBOLS[deleted]="D"
+ GIT_SYMBOLS[modified]="M"
+ GIT_SYMBOLS[renamed]="R"
+ GIT_SYMBOLS[staged]="●"
}
-function _get_git_branch() {
- # On branches, this will return the branch name
- # On non-branches, (no branch)
- ref="$(git symbolic-ref HEAD 2> /dev/null | sed -e 's/refs\/heads\///')"
- if [[ -n $ref ]]; then
- printf '%s' "$ref"
- else
- printf "(no branch)"
- fi
-}
-
-function _get_git_progress() {
- # Detect in-progress actions (e.g. merge, rebase)
- # https://github.com/git/git/blob/v1.9-rc2/wt-status.c#L1199-L1241
- git_dir="$(git rev-parse --git-dir)"
-
- # git merge
- if [[ -f "$git_dir/MERGE_HEAD" ]]; then
- printf " [merge]"
- elif [[ -d "$git_dir/rebase-apply" ]]; then
- # git am
- if [[ -f "$git_dir/rebase-apply/applying" ]]; then
- printf " [am]"
- # git rebase
- else
- printf " [rebase]"
- fi
- elif [[ -d "$git_dir/rebase-merge" ]]; then
- # git rebase --interactive/--merge
- printf " [rebase]"
- elif [[ -f "$git_dir/CHERRY_PICK_HEAD" ]]; then
- # git cherry-pick
- printf " [cherry-pick]"
- fi
- if [[ -f "$git_dir/BISECT_LOG" ]]; then
- # git bisect
- printf " [bisect]"
- fi
- if [[ -f "$git_dir/REVERT_HEAD" ]]; then
- # git revert --no-commit
- printf " [revert]"
- fi
+_get_git_branch() {
+ git symbolic-ref --short HEAD 2>/dev/null || echo "(no branch)"
}
-_prompt_is_branch1_behind_branch2 () {
- # $ git log origin/master..master -1
- # commit 4a633f715caf26f6e9495198f89bba20f3402a32
- # Author: Todd Wolfson <todd@twolfson.com>
- # Date: Sun Jul 7 22:12:17 2013 -0700
- #
- # Unsynced commit
+_get_git_progress() {
+ local git_dir
+ git_dir="$(git rev-parse --git-dir 2>/dev/null)" || return 0
- # Find the first log (if any) that is in branch1 but not branch2
- first_log="$(git log "$1..$2" -1 2> /dev/null)"
+ [[ -f "$git_dir/MERGE_HEAD" ]] && echo " [merge]" && return 0
+ [[ -d "$git_dir/rebase-apply" ]] && echo " [rebase]" && return 0
+ [[ -d "$git_dir/rebase-merge" ]] && echo " [rebase]" && return 0
+ [[ -f "$git_dir/CHERRY_PICK_HEAD" ]] && echo " [cherry-pick]" && return 0
+ [[ -f "$git_dir/BISECT_LOG" ]] && echo " [bisect]" && return 0
+ [[ -f "$git_dir/REVERT_HEAD" ]] && echo " [revert]" && return 0
- # Exit with 0 if there is a first log, 1 if there is not
- [[ -n "$first_log" ]]
+ return 0
}
-_prompt_branch_exists () {
- # List remote branches | # Find our branch and exit with 0 or 1 if found/not found
- git branch --remote 2> /dev/null | grep --quiet "$1"
-}
+# Single git status call - parse everything at once
+_parse_git_status() {
+ local status_output ahead_behind
-_prompt_parse_git_ahead () {
- # Grab the local and remote branch
- branch="$(_get_git_branch)"
- remote="$(git config --get "branch.${branch}.remote" || echo -n "origin")"
- remote_branch="$remote/$branch"
+ # Single git status call
+ status_output="$(git status --porcelain=v1 2>/dev/null)"
- # $ git log origin/master..master
- # commit 4a633f715caf26f6e9495198f89bba20f3402a32
- # Author: Todd Wolfson <todd@twolfson.com>
- # Date: Sun Jul 7 22:12:17 2013 -0700
- #
- # Unsynced commit
+ # Get ahead/behind counts in one call
+ if git rev-parse --abbrev-ref "@{upstream}" >/dev/null 2>&1; then
+ ahead_behind="$(git rev-list --left-right --count "@{upstream}"...HEAD 2>/dev/null)"
+ GIT_STATUS[behind]="${ahead_behind% *}"
+ GIT_STATUS[ahead]="${ahead_behind#* }"
+ else
+ GIT_STATUS[behind]=0
+ GIT_STATUS[ahead]=0
+ fi
- # If the remote branch is behind the local branch
- # or it has not been merged into origin (remote branch doesn't exist)
- if (_prompt_is_branch1_behind_branch2 "$remote_branch" "$branch" || ! _prompt_branch_exists "$remote_branch"); then echo -n "0"; else echo -n "1"; fi
-}
+ # Parse status output
+ GIT_STATUS[dirty]=0
+ GIT_STATUS[staged]=0
+ GIT_STATUS[untracked]=0
+ GIT_STATUS[modified]=0
+ GIT_STATUS[added]=0
+ GIT_STATUS[deleted]=0
+ GIT_STATUS[renamed]=0
-_prompt_parse_git_behind() {
- # Grab the branch
- branch="$(_get_git_branch)"
- remote="$(git config --get "branch.${branch}.remote" || echo -n "origin")"
- remote_branch="$remote/$branch"
+ [[ -z "$status_output" ]] && return
- # $ git log master..origin/master
- # commit 4a633f715caf26f6e9495198f89bba20f3402a32
- # Author: Todd Wolfson <todd@twolfson.com>
- # Date: Sun Jul 7 22:12:17 2013 -0700
- #
- # Unsynced commit
+ local line
+ while IFS= read -r line; do
+ [[ -z "$line" ]] && continue
- # If the local branch is behind the remote branch
- if _prompt_is_branch1_behind_branch2 "$branch" "$remote_branch"; then echo -n '0'; else echo -n '1'; fi
-}
+ local index_status="${line:0:1}"
+ local work_status="${line:1:1}"
-function _prompt_parse_git_dirty() {
- # If the git status has *any* changes (e.g. dirty), printf our character
- if [[ -n "$(git status --porcelain 2> /dev/null)" ]]; then echo -n '0'; else echo -n '1'; fi
-}
+ # Count changes
+ ((GIT_STATUS[dirty]++))
-# start counter on git
-function _git_dirty_count() {
- local _dirty_status
- local _git_status
- _dirty_status="$(_prompt_parse_git_dirty)"
- _git_status="$(git status --porcelain 2> /dev/null)"
- if [[ "$_dirty_status" == 0 ]]; then
- local change_count
- change_count="$(echo "$_git_status" | wc -l | tr -d '[:space:]')"
- case $change_count in
- 1) printf '%b\u2022%s' "${BOLD}${GREY}" "$change_count";;
- 2) printf '%b\u2236%s' "${BOLD}${GREY}" "$change_count";;
- 3) printf '%b\u2026%s' "${BOLD}${GREY}" "$change_count";;
- *) printf '%b\u00BB%s' "${BOLD}${GREY}" "$change_count";;
+ # Index (staged) changes
+ case "$index_status" in
+ [MADRC]) ((GIT_STATUS[staged]++)) ;;
esac
- fi
-}
-function _git_behind_count() {
- local __behind_count
- if git rev-parse --symbolic-full-name --abbrev-ref "@{upstream}" > /dev/null 2>&1; then
- __behind_count="$(git rev-list --left-right --count "@{upstream}"...HEAD | cut -f1 2> /dev/null)"
- case $__behind_count in
- 0) echo -n '';;
- *) echo -n "$__behind_count";;
+ # Working tree changes
+ case "$work_status" in
+ M) ((GIT_STATUS[modified]++)) ;;
+ D) ((GIT_STATUS[deleted]++)) ;;
esac
- fi
-}
-function _git_ahead_count() {
- local __ahead_count
- if git rev-parse --symbolic-full-name --abbrev-ref "@{upstream}" > /dev/null 2>&1; then
- __ahead_count="$(git rev-list --left-right --count "@{upstream}"...HEAD | cut -f2 2> /dev/null)"
- case $__ahead_count in
- 0) echo -n '';;
- *) echo -n "$__ahead_count";;
+ # Special cases
+ case "$line" in
+ "??*") ((GIT_STATUS[untracked]++)) ;;
+ "A "*) ((GIT_STATUS[added]++)) ;;
+ "R "*) ((GIT_STATUS[renamed]++)) ;;
esac
- fi
-}
-# ends counter on git
-
-function _prompt_parse_git_untracked() {
- local untracked
- local evaltask
- untracked="$(git status 2>&1 | tee)"
- grep -E 'Untracked files:' <<<"$untracked" &> /dev/null
- evaltask=$?
- if [ "$evaltask" -eq 0 ]; then echo -n '0'; else echo -n '1'; fi
-}
-function _prompt_parse_git_newfile() {
- local newfile
- local evaltask
- newfile="$(git status 2>&1 | tee)"
- grep -E 'new file:' <<<"$newfile" &> /dev/null
- evaltask=$?
- if [ "$evaltask" -eq 0 ]; then echo -n '0'; else echo -n '1'; fi
+ done <<< "$status_output"
}
-function _prompt_parse_git_deleted_file() {
- local deleted_file
- local evaltask
- deleted_file="$(git status 2>&1 | tee)"
- grep -E 'deleted:' <<<"$deleted_file" &> /dev/null
- evaltask=$?
- if [ "$evaltask" -eq 0 ]; then echo -n '0'; else echo -n '1'; fi
-}
+# Fast git status generation
+_get_git_status_fast() {
+ _init_git_symbols "$@"
-function _prompt_parse_git_renamed() {
- local renamed
- local evaltask
- renamed="$(git status 2>&1 | tee)"
- grep -E 'renamed:' <<<"$renamed" &> /dev/null
- evaltask=$?
- if [ "$evaltask" -eq 0 ]; then echo -n '0'; else echo -n '1'; fi
-}
+ declare -A GIT_STATUS
+ _parse_git_status
-function _prompt_parse_git_unstage() {
- local unstage
- local evaltask
- unstage="$(git status 2>&1 | tee)"
- grep -E 'not staged' <<<"$unstage" &> /dev/null
- evaltask=$?
- if [ "$evaltask" -eq 0 ]; then echo -n '0'; else echo -n '1'; fi
-}
-
-function _prompt_parse_git_stage() {
- local stage
- local evaltask
- stage="$(git status -s 2>&1 | tee)"
- grep -E 'M' <<<"$stage" &> /dev/null
- evaltask=$?
- if [ "$evaltask" -eq 0 ]; then echo -n '0'; else echo -n '1'; fi
-}
+ local output=""
-function _prompt_is_on_git() {
- git rev-parse 2> /dev/null
-}
-
-function _prompt_get_git_status() {
-
- _symbols "$@"
-
- # Grab the git dirty and git behind
- count_dirty="$(_git_dirty_count)"
- count_behind="$(_git_behind_count)"
- count_ahead="$(_git_ahead_count)"
- dirty_branch="$(_prompt_parse_git_dirty)"
- branch_ahead="$(_prompt_parse_git_ahead)"
- branch_behind="$(_prompt_parse_git_behind)"
- branch_stage="$(_prompt_parse_git_stage)"
- branch_unstage="$(_prompt_parse_git_unstage)"
- branch_untracked="$(_prompt_parse_git_untracked)"
- branch_newfile="$(_prompt_parse_git_newfile)"
- branch_deleted_file="$(_prompt_parse_git_deleted_file)"
- branch_renamed="$(_prompt_parse_git_renamed)"
-
- # Iterate through all the cases and if it matches, then printf
- case ${dirty_branch}${branch_ahead}${branch_behind}${branch_stage}${branch_unstage}${branch_newfile}${branch_untracked}${branch_deleted_file}${branch_renamed} in
- 111111111) printf '%s' "${__ss}";;
- 100111111) printf '%s' "${__ps}${__ahs}$count_ahead${__ps}${__bhs}$count_behind";;
- 110111111) printf '%s%s' "${__upulls}" "$count_behind";;
- 101111111) printf '%s%s' "${__ahs}" "$count_ahead";;
- 111001111) printf '%s%s' "${__ps}${__duus}${__stusts}" "$count_dirty";;
- 011111111) printf '%s%s' "${__dss}" "$count_dirty";;
- 010111111) printf '%s%s%s' "${__duplls}" "$count_behind" "$count_dirty";;
- 001111111) printf '%s%s%s' "${__duphs}" "$count_ahead" "$count_dirty";;
- 000111111) printf '%s%s%s' "${__duus}" "$count_behind-$count_ahead" "$count_dirty";;
-
- 000111011) printf '%s%s' "${__ps}${__ahs}$count_ahead${__ps}${__bhs}$count_behind${__ps}${__uts}${__ps}" "$count_dirty" ;;
+ # Clean repo
+ if [[ ${GIT_STATUS[dirty]} -eq 0 ]]; then
+ echo -n "${BOLD}${CYAN}${GIT_SYMBOLS[clean]}${RESET}"
+ return
+ fi
- 010111011) printf '%s%s' "${__ps}${__bhs}$count_behind${__ps}${__dss}${__uts}" "${__ps}$count_dirty";;
- 010110111) printf '%s%s' "${__ps}${__bhs}$count_behind${__ps}${__dss}${__nfs}" "${__ps}$count_dirty";;
- 010110011) printf '%s%s' "${__ps}${__bhs}$count_behind${__ps}${__dss}${__nfs}${__uts}" "${__ps}$count_dirty";;
- 010100001) printf '%s%s' "${__ps}${__bhs}$count_behind${__ps}${__dss}${__nfs}${__usts}${__uts}${__dfs}" "${__ps}$count_dirty";;
- 010000001) printf '%s%s' "${__ps}${__bhs}$count_behind${__ps}${__dss}${__nfs}${__stusts}${__uts}${__dfs}" "${__ps}$count_dirty";;
- 010010100) printf '%s%s' "${__ps}${__bhs}$count_behind${__ps}${__dss}${__nfs}${__dfs}${__rns}" "${__ps}${__sts}${__ps}$count_dirty";;
- 010010000) printf '%s%s' "${__ps}${__bhs}$count_behind${__ps}${__dss}${__nfs}${__dfs}${__rns}${__ps}${__uts}" "${__ps}${__sts}${__ps}$count_dirty";;
+ # Dirty count
+ output="${BOLD}${RED}${GIT_SYMBOLS[dirty]}${GIT_STATUS[dirty]}${RESET}"
- 011001111) printf '%s%s' "${__ps}${__dss}${__stusts}" "$count_dirty";;
- 011000111) printf '%s%s' "${__ps}${__dss}${__stusts}${__nfs}" "$count_dirty";;
- 011001101) printf '%s%s' "${__ps}${__dss}${__stusts}${__dfs}" "$count_dirty";;
- 011001011) printf '%s%s' "${__ps}${__dss}${__stusts}${__uts}" "$count_dirty" ;;
- 011001001) printf '%s%s' "${__ps}${__dss}${__stusts}${__uts}${__dfs}" "$count_dirty";;
- 011000101) printf '%s%s' "${__ps}${__dss}${__stusts}${__nfs}${__dfs}" "$count_dirty";;
- 011000001) printf '%s%s' "${__ps}${__dss}${__stusts}${__nfs}${__uts}${__dfs}" "$count_dirty";;
- 011011111) printf '%s%s' "${__ps}${__dss}${__sts}" "$count_dirty";;
- 011010111) printf '%s%s' "${__ps}${__dss}${__sts}${__nfs}" "$count_dirty";;
- 011010011) printf '%s%s' "${__ps}${__dss}${__sts}${__nfs}${__uts}" "$count_dirty";;
- 011010101) printf '%s%s' "${__ps}${__dss}${__sts}${__nfs}${__dfs}" "$count_dirty" ;;
- 011010001) printf '%s%s' "${__ps}${__dss}${__sts}${__nfs}${__uts}${__dfs}" "$count_dirty";;
- 011011011) printf '%s%s' "${__ps}${__dss}${__sts}${__uts}" "$count_dirty";;
- 011011101) printf '%s%s' "${__ps}${__dss}${__sts}${__dfs}" "$count_dirty";;
- 011110111) printf '%s%s' "${__ps}${__dss}${__nfs}" "$count_dirty";;
- 011110011) printf '%s%s' "${__ps}${__dss}${__nfs}${__uts}" "$count_dirty";;
- 011111011) printf '%s%s' "${__ps}${__dss}${__uts}" "$count_dirty";;
- 011101001) printf '%s%s' "${__ps}${__dss}${__usts}${__uts}${__dfs}" "$count_dirty";;
- 011111110) printf '%s%s' "${__ps}${__dss}${__rns}" "$count_dirty";;
- 011110110) printf '%s%s' "${__ps}${__dss}${__nfs}${__rns}" "$count_dirty";;
- 011110010) printf '%s%s' "${__ps}${__dss}${__nfs}${__uts}${__rns}" "$count_dirty";;
- 011011110) printf '%s%s' "${__ps}${__dss}${__sts}${__rns}" "$count_dirty";;
- 011010100) printf '%s%s' "${__ps}${__dss}${__sts}${__nfs}${__dfs}${__rns}" "$count_dirty" ;;
- 011010000) printf '%s%s' "${__ps}${__dss}${__sts}${__nfs}${__uts}${__dfs}${__rns}" "$count_dirty";;
- 011001010) printf '%s%s' "${__ps}${__dss}${__stusts}${__uts}${__rns}" "$count_dirty";;
- 011001000) printf '%s%s' "${__ps}${__dss}${__stusts}${__uts}${__dfs}${__rns}" "$count_dirty";;
- 011000011) printf '%s%s' "${__ps}${__dss}${__stusts}${__nfs}${__uts}" "$count_dirty";;
- 011000110) printf '%s%s' "${__ps}${__dss}${__stusts}${__nfs}${__rns}" "$count_dirty";;
- 011000010) printf '%s%s' "${__ps}${__dss}${__stusts}${__nfs}${__uts}${__rns}" "$count_dirty";;
- 011000000) printf '%s%s' "${__ps}${__dss}${__stusts}${__nfs}${__uts}${__dfs}${__rns}" "$count_dirty";;
- 011000100) printf '%s%s' "${__ps}${__dss}${__stusts}${__nfs}${__dfs}${__rns}" "$count_dirty";;
- 011010010) printf '%s%s' "${__ps}${__dss}${__sts}${__nfs}${__uts}${__rns}" "$count_dirty";;
- 011011010) printf '%s%s' "${__ps}${__dss}${__sts}${__uts}${__rns}" "$count_dirty";;
- 011111010) printf '%s%s' "${__ps}${__dss}${__uts}${__rns}" "$count_dirty";;
+ # Ahead/behind
+ if [[ ${GIT_STATUS[ahead]} -gt 0 && ${GIT_STATUS[behind]} -gt 0 ]]; then
+ output+="${BOLD}${YELLOW}${GIT_SYMBOLS[diverged]}${GIT_STATUS[ahead]}/${GIT_STATUS[behind]}${RESET}"
+ elif [[ ${GIT_STATUS[ahead]} -gt 0 ]]; then
+ output+="${BOLD}${GREEN}${GIT_SYMBOLS[ahead]}${GIT_STATUS[ahead]}${RESET}"
+ elif [[ ${GIT_STATUS[behind]} -gt 0 ]]; then
+ output+="${BOLD}${RED}${GIT_SYMBOLS[behind]}${GIT_STATUS[behind]}${RESET}"
+ fi
- 001001111) printf '%s%s' "${__ps}${__duus}${__stusts}" "$count_dirty";;
- 001000111) printf '%s%s' "${__ps}${__duus}${__stusts}${__nfs}" "$count_dirty";;
- 001001101) printf '%s%s' "${__ps}${__duus}${__stusts}${__dfs}" "$count_dirty";;
- 001001011) printf '%s%s' "${__ps}${__duus}${__stusts}${__uts}" "$count_dirty";;
- 001001001) printf '%s%s' "${__ps}${__duus}${__stusts}${__uts}${__dfs}" "$count_dirty";;
- 001000101) printf '%s%s' "${__ps}${__duus}${__stusts}${__nfs}${__dfs}" "$count_dirty";;
- 001000001) printf '%s%s' "${__ps}${__duus}${__stusts}${__nfs}${__uts}${__dfs}" "$count_dirty";;
- 001011111) printf '%s%s' "${__ps}${__duus}${__sts}" "$count_dirty";;
- 001010111) printf '%s%s' "${__ps}${__duus}${__sts}${__nfs}" "$count_dirty" ;;
- 001010011) printf '%s%s' "${__ps}${__duus}${__sts}${__nfs}${__uts}" "$count_dirty";;
- 001010101) printf '%s%s' "${__ps}${__duus}${__sts}${__nfs}${__dfs}" "$count_dirty" ;;
- 001010001) printf '%s%s' "${__ps}${__duus}${__sts}${__nfs}${__uts}${__dfs}" "$count_dirty";;
- 001011011) printf '%s%s' "${__ps}${__duus}${__sts}${__uts}" "$count_dirty";;
- 001011101) printf '%s%s' "${__ps}${__duus}${__sts}${__dfs}" "$count_dirty";;
- 001110111) printf '%s%s' "${__ps}${__duus}${__nfs}" "$count_dirty";;
- 001110011) printf '%s%s' "${__ps}${__duus}${__nfs}${__uts}" "$count_dirty";;
- 001111011) printf '%s%s' "${__ps}${__duus}${__uts}" "$count_dirty";;
- 001101001) printf '%s%s' "${__ps}${__duus}${__usts}${__uts}${__dfs}" "$count_dirty";;
- 001101101) printf '%s%s' "${__ps}${__duus}${__usts}${__dfs}" "$count_dirty";;
- 001111110) printf '%s%s' "${__ps}${__duus}${__rns}" "$count_dirty";;
- 001110110) printf '%s%s' "${__ps}${__duus}${__nfs}${__rns}" "$count_dirty";;
- 001110010) printf '%s%s' "${__ps}${__duus}${__nfs}${__uts}${__rns}" "$count_dirty";;
- 001011110) printf '%s%s' "${__ps}${__duus}${__sts}${__rns}" "$count_dirty";;
- 001010100) printf '%s%s' "${__ps}${__duus}${__sts}${__nfs}${__dfs}${__rns}" "$count_dirty" ;;
- 001010000) printf '%s%s' "${__ps}${__duus}${__sts}${__nfs}${__uts}${__dfs}${__rns}" "$count_dirty";;
- 001001010) printf '%s%s' "${__ps}${__duus}${__stusts}${__uts}${__rns}" "$count_dirty";;
- 001001000) printf '%s%s' "${__ps}${__duus}${__stusts}${__uts}${__dfs}${__rns}" "$count_dirty";;
- 001000011) printf '%s%s' "${__ps}${__duus}${__stusts}${__nfs}${__uts}" "$count_dirty";;
- 001000110) printf '%s%s' "${__ps}${__duus}${__stusts}${__nfs}${__rns}" "$count_dirty";;
- 001000010) printf '%s%s' "${__ps}${__duus}${__stusts}${__nfs}${__uts}${__rns}" "$count_dirty";;
- 001000000) printf '%s%s' "${__ps}${__duus}${__stusts}${__nfs}${__uts}${__dfs}${__rns}" "$count_dirty";;
- 001000100) printf '%s%s' "${__ps}${__duus}${__stusts}${__nfs}${__dfs}${__rns}" "$count_dirty";;
- 001010010) printf '%s%s' "${__ps}${__duus}${__sts}${__nfs}${__uts}${__rns}" "$count_dirty";;
- 001011010) printf '%s%s' "${__ps}${__duus}${__sts}${__uts}${__rns}" "$count_dirty";;
- 001111010) printf '%s%s' "${__ps}${__duus}${__uts}${__rns}" "$count_dirty";;
- *) echo -n "${__uts}" ;;
- esac
+ # File status indicators
+ [[ ${GIT_STATUS[staged]} -gt 0 ]] && output+="${BOLD}${GREEN}${GIT_SYMBOLS[staged]}${RESET}"
+ [[ ${GIT_STATUS[untracked]} -gt 0 ]] && output+="${BOLD}${RED}${GIT_SYMBOLS[untracked]}${RESET}"
+ [[ ${GIT_STATUS[added]} -gt 0 ]] && output+="${BOLD}${GREEN}${GIT_SYMBOLS[added]}${RESET}"
+ [[ ${GIT_STATUS[deleted]} -gt 0 ]] && output+="${BOLD}${RED}${GIT_SYMBOLS[deleted]}${RESET}"
+ [[ ${GIT_STATUS[renamed]} -gt 0 ]] && output+="${BOLD}${BLUE}${GIT_SYMBOLS[renamed]}${RESET}"
- #
- # dirty + unpushed = du stage + unstage = <M>
- # ∗ ↑ ↓ →M ←M + ? D ⎇
- # echo "${dirty_branch}${branch_ahead}${branch_behind}${branch_stage}${branch_unstage}${branch_newfile}${branch_untracked}${branch_deleted_file}${branch_renamed}"
- # 0 1 0 1 1 1 0 1 1
+ echo -n "$output"
}
-_prompt_get_git_info() {
- # Import colors
+_prompt_get_git_info_fast() {
_colors_bash "$@"
- # Grab branch
- branch=$(_get_git_branch)
+ local branch
+ branch="$(_get_git_branch)"
- # If there are any branches
- if [[ -n $branch ]]; then
- # Add on the git status
- output=$(_prompt_get_git_status "$@")
- # Printf our output
- printf '%b%s%b' "${BOLD}${LEMON}" "git:($branch$output" "${BOLD}${LEMON})"
+ if [[ -n "$branch" ]]; then
+ local status
+ status="$(_get_git_status_fast "$@")"
+ printf '%b%s%s%b' "${BOLD}${YELLOW}" "git:($branch" "$status" "${BOLD}${YELLOW})"
fi
}
__prompt_git() {
- if _prompt_is_on_git &> /dev/null; then
- echo -n "${BOLD}${WHITE} on $RESET" && \
- echo -n "$(_prompt_get_git_info "$@")" && \
- echo -n "${BOLD}${RED}$(_get_git_progress)" && \
- echo -n "$RESET"
+ if git rev-parse --git-dir >/dev/null 2>&1; then
+ echo -n "${BOLD}${WHITE} on ${RESET}"
+ _prompt_get_git_info_fast "$@"
+ echo -n "${BOLD}${RED}$(_get_git_progress)${RESET}"
fi
}
diff --git a/hyperterm/hyperterm.sha512 b/hyperterm/hyperterm.sha512
index 5733a0b..1ce287e 100644
--- a/hyperterm/hyperterm.sha512
+++ b/hyperterm/hyperterm.sha512
@@ -2,9 +2,9 @@ cdfe049ec07f02a1893cda29c13085d06709e09a30b0c2e1111585278315f03139d61080c883cb3f
f363606f41a2c2c8f1cc44110c64fe23b1c8feb4c788ee006222db0f5c7a3adeac2b0948626b313adc985e9b8d303a0b9ce1c5ba42746810accb54efddcd4b84 ./hyperterm.sh
b760a908a3f6222b974abc1f7464bde0f5427f120f1e7ef1c6d97ae61769e552ef3b5cb88e193e955da72a592f07eadb812413dd50a691cd3dbb33e3da581ea6 ./core/update.sh
1cfba599047d84a17ff92b695ebf527a505a30acc9ec21a2b9f410a7ea6dde4b23b5cf62e557d82f2fe9a8980649942424b879ca53baae4d4cb3057681baa7b6 ./core/colors.sh
-2036a79215a5434e31f3406bea3f2ffa7e94ffef86c2d1ceb8865db29f19fe7f342f9cab93288f57c75daed36ef146f85d15f8d633931a27d55c3983f55ef15b ./core/git.sh
+e3bacd715a327802c18de9b3c9f958f704eec4055295433403df284130e23f956f330bdf737c524bec3a89ef67a44ff0ab5dec40ffad5363aced3396bff54283 ./core/git.sh
f3e00b2aa8ab9f3ab44570adaa2520408ed66fd00f551654d60b64a4be3546ec781b7efa39bcd774937e654b6ffb4c7af3f21eeb36caf9c01f82f85cf28e2b4d ./core/languages.sh
-fdc570118a65a2b00571fd374be1aa983d41e9722bee6e604e2b00fcd2e1931df4dfd8951f15a2402dfcb3cbc76a5fe8d9d564a83fec73595dcaae2e38ac338a ./core/autodep.sh
+b205de01644af11ef1dc96230e4bf12087482e26b7c0472fb6a153bf94662e4dfc01b2da0c3ca0da4af93bce05faf0e33be5422fbee85e6b69ca1cccbe194cff ./core/autodep.sh
7447d3e167ab207d3ef4218e201a06bf5a3fc23281639f16f7f405f1d66b73923845d450fdb0a94672757866a9da0324f728564a1b61b2ed1678fe576eb565cf ./core/autocomplete.sh
99f9e937e39495f60495bc89f055f29e1bee25911c9bea5c8b2a7c6a5bdc417502f17da8cb9e63ea957eea9337c6fe1c63c208a3ff3ed33ef2ca8c1dbe328599 ./core/status.sh
65f850342f43bdfe68b2b0339a051a795c6640b4bd4f7c6b6b979ca214607f9a87d9cfdc6955785c5ca087d09294781ee8cb3a62dc0160cd8a105ff65cc82465 ./themes/minterm.sh
diff --git a/tests/README.md b/tests/README.md
new file mode 100644
index 0000000..024a679
--- /dev/null
+++ b/tests/README.md
@@ -0,0 +1,84 @@
+# HyperTerm Tests
+
+Test suite for HyperTerm prompt functionality and performance.
+
+## Test Files
+
+### test_prompt.sh
+Comprehensive test suite with full functionality testing.
+
+**Features:**
+- Individual function testing
+- Performance benchmarking
+- Git state testing
+- Interactive test mode (skipped in CI)
+- Non-interactive mode for CI/automation
+
+**Usage:**
+```bash
+cd tests
+bash test_prompt.sh # Interactive mode
+bash test_prompt.sh --non-interactive # Non-interactive mode (for CI)
+```
+
+### quick_test.sh
+Fast validation test for immediate feedback.
+
+**Features:**
+- Quick function validation
+- Basic performance check
+- Works in current directory or creates temp repo
+
+**Usage:**
+```bash
+cd tests
+bash quick_test.sh
+```
+
+## Test Environment
+
+Tests create isolated environments to avoid affecting your working directory:
+- Temporary git repositories in `/tmp/`
+- Various git states (clean, dirty, staged, untracked)
+- Automatic cleanup on exit
+
+## Performance Testing
+
+Both test scripts include performance measurements:
+- Multiple iterations for accurate timing
+- Average execution time per prompt call
+- Performance classification (Excellent < 10ms, Good < 50ms)
+
+## Git States Tested
+
+- Clean repository
+- Modified files
+- Untracked files
+- Staged files
+- Mixed states
+- Ahead/behind tracking
+
+## Requirements
+
+- Git installed and available in PATH
+- Bash 4.0 or later
+- HyperTerm core files in `../hyperterm/core/`
+
+## Logging
+
+All tests use structured logging:
+- `[INFO]` - General information
+- `[SUCCESS]` - Successful operations
+- `[WARN]` - Warnings
+- `[ERROR]` - Errors
+
+## Interactive Mode
+
+The full test suite includes an interactive mode with commands:
+- `status` - Show git status
+- `prompt` - Show full prompt
+- `perf` - Run performance test
+- `states` - Test different git states
+- `modify` - Create test modifications
+- `clean` - Clean working directory
+- `exit` - Exit interactive mode
diff --git a/tests/quick_test.sh b/tests/quick_test.sh
new file mode 100644
index 0000000..112796a
--- /dev/null
+++ b/tests/quick_test.sh
@@ -0,0 +1,176 @@
+#!/bin/bash
+# Quick Prompt Test - Fast validation
+# Simple test for immediate feedback
+# shellcheck disable=SC1090,SC2034,SC2155
+
+set -e
+
+# Logging
+log_info() { echo "[INFO] $*"; }
+log_error() { echo "[ERROR] $*"; }
+log_success() { echo "[SUCCESS] $*"; }
+
+# Configuration
+readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+readonly PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
+
+# Basic colors fallback
+setup_colors() {
+ RESET='\033[0m'
+ BOLD='\033[1m'
+ RED='\033[31m'
+ GREEN='\033[32m'
+ YELLOW='\033[33m'
+ BLUE='\033[34m'
+ CYAN='\033[36m'
+ WHITE='\033[37m'
+ GREY='\033[90m'
+}
+
+# Load functions
+load_functions() {
+ log_info "Loading HyperTerm functions"
+
+ # Load colors if available
+ local colors_file="$PROJECT_ROOT/hyperterm/core/colors.sh"
+ if [[ -f "$colors_file" ]]; then
+ source "$colors_file"
+ else
+ setup_colors
+ fi
+
+ # Load git functions
+ local git_file="$PROJECT_ROOT/hyperterm/core/git.sh"
+
+ if [[ -f "$git_file" ]]; then
+ source "$git_file"
+ log_success "Git functions loaded"
+ return 0
+ else
+ log_error "Git functions not found"
+ return 1
+ fi
+}
+
+# Test in current directory
+test_current_directory() {
+ if git rev-parse --git-dir >/dev/null 2>&1; then
+ log_success "Current directory is a git repository"
+
+ echo "Current git state:"
+ echo -n " Branch: "
+ _get_git_branch
+ echo ""
+
+ echo -n " Status: "
+ if command -v _get_git_status_fast >/dev/null 2>&1; then
+ _get_git_status_fast
+ else
+ _prompt_get_git_status
+ fi
+ echo ""
+
+ echo -n " Full prompt: "
+ __prompt_git
+ echo ""
+
+ return 0
+ else
+ log_info "Current directory is not a git repository"
+ return 1
+ fi
+}
+
+# Create temporary test
+test_with_temp_repo() {
+ log_info "Creating temporary git repository for testing"
+
+ local temp_dir="/tmp/quick_test_$$"
+ mkdir -p "$temp_dir"
+
+ (
+ cd "$temp_dir"
+ git init --quiet
+ git config user.name "Test User"
+ git config user.email "test@example.com"
+ echo "# Quick Test" > README.md
+ echo "test content" > file.txt
+ git add README.md
+ git commit -m "Initial commit" --quiet
+
+ # Create changes
+ echo "modified" >> file.txt
+ echo "untracked" > new.txt
+
+ log_success "Temporary repository created with changes"
+
+ echo "Test results:"
+ echo -n " Branch: "
+ _get_git_branch
+ echo ""
+
+ echo -n " Status: "
+ if command -v _get_git_status_fast >/dev/null 2>&1; then
+ _get_git_status_fast
+ else
+ _prompt_get_git_status
+ fi
+ echo ""
+
+ echo -n " Full prompt: "
+ __prompt_git
+ echo ""
+ )
+
+ rm -rf "$temp_dir"
+ log_info "Temporary repository cleaned up"
+}
+
+# Performance check
+quick_performance_check() {
+ log_info "Running quick performance check"
+
+ local iterations=10
+ local start_time end_time duration
+
+ start_time=$(date +%s%N)
+ for ((i=1; i<=iterations; i++)); do
+ __prompt_git >/dev/null 2>&1
+ done
+ end_time=$(date +%s%N)
+
+ duration=$(( (end_time - start_time) / 1000000 ))
+ local avg_duration=$(( duration / iterations ))
+
+ echo "Performance: ${avg_duration}ms average (${iterations} iterations)"
+
+ if [[ $avg_duration -lt 50 ]]; then
+ log_success "Performance is good"
+ elif [[ $avg_duration -lt 100 ]]; then
+ log_info "Performance is acceptable"
+ else
+ log_error "Performance may need optimization"
+ fi
+}
+
+# Main execution
+main() {
+ log_info "HyperTerm Quick Test"
+
+ if ! load_functions; then
+ exit 1
+ fi
+
+ if ! test_current_directory; then
+ test_with_temp_repo
+ fi
+
+ quick_performance_check
+
+ log_success "Quick test completed"
+}
+
+# Execute if run directly
+if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
+ main "$@"
+fi
diff --git a/tests/test_prompt.sh b/tests/test_prompt.sh
new file mode 100644
index 0000000..b4a002b
--- /dev/null
+++ b/tests/test_prompt.sh
@@ -0,0 +1,304 @@
+#!/bin/bash
+# HyperTerm Prompt Test Suite
+# Organized testing environment for prompt functionality
+# shellcheck disable=SC1090,SC2034,SC2155
+
+set -e
+
+# Logging functions
+log_info() { echo "[INFO] $*"; }
+log_warn() { echo "[WARN] $*"; }
+log_error() { echo "[ERROR] $*"; }
+log_success() { echo "[SUCCESS] $*"; }
+
+# Configuration
+readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+readonly PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
+readonly TEST_DIR="/tmp/hyperterm_test_$(date +%s)"
+
+# Cleanup function
+cleanup() {
+ if [[ -n "${TEST_DIR:-}" && -d "$TEST_DIR" ]]; then
+ rm -rf "$TEST_DIR"
+ log_info "Test directory cleaned: $TEST_DIR"
+ fi
+}
+
+# Setup test environment
+setup_test_env() {
+ log_info "Setting up test environment"
+
+ mkdir -p "$TEST_DIR"
+ cd "$TEST_DIR"
+
+ # Create test git repository
+ git init --quiet
+ git config user.name "Test User"
+ git config user.email "test@example.com"
+ echo "# Test Repository" > README.md
+ echo "test content" > test.txt
+ git add README.md
+ git commit -m "Initial commit" --quiet
+
+ # Create various git states for testing
+ echo "modified content" >> test.txt
+ echo "untracked file" > untracked.txt
+ echo "staged content" > staged.txt
+ git add staged.txt
+
+ log_success "Test environment created at: $TEST_DIR"
+ log_info "Git states created: modified, untracked, staged files"
+}
+
+# Load hyperterm functions
+load_hyperterm_functions() {
+ log_info "Loading HyperTerm functions"
+
+ # Load colors
+ local colors_file="$PROJECT_ROOT/hyperterm/core/colors.sh"
+ if [[ -f "$colors_file" ]]; then
+ source "$colors_file"
+ log_success "Colors loaded from: $colors_file"
+ else
+ log_warn "Colors file not found, using fallback"
+ # Fallback colors
+ RESET='\033[0m'
+ BOLD='\033[1m'
+ RED='\033[31m'
+ GREEN='\033[32m'
+ YELLOW='\033[33m'
+ BLUE='\033[34m'
+ CYAN='\033[36m'
+ WHITE='\033[37m'
+ GREY='\033[90m'
+ fi
+
+ # Load git functions
+ local git_file="$PROJECT_ROOT/hyperterm/core/git.sh"
+
+ if [[ -f "$git_file" ]]; then
+ source "$git_file"
+ log_success "Git functions loaded"
+ return 0
+ else
+ log_error "Git functions not found"
+ return 1
+ fi
+}
+
+# Test individual functions
+test_individual_functions() {
+ log_info "Testing individual functions"
+
+ echo "Branch detection:"
+ if command -v _get_git_branch >/dev/null 2>&1; then
+ local branch
+ branch="$(_get_git_branch)"
+ echo " Result: $branch"
+ log_success "Branch detection working"
+ else
+ log_error "Branch detection function not found"
+ fi
+
+ echo "Git progress detection:"
+ if command -v _get_git_progress >/dev/null 2>&1; then
+ local progress
+ progress="$(_get_git_progress)"
+ echo " Result: ${progress:-"(none)"}"
+ log_success "Progress detection working"
+ else
+ log_error "Progress detection function not found"
+ fi
+
+ echo "Git status:"
+ if command -v _get_git_status_fast >/dev/null 2>&1; then
+ local status
+ status="$(_get_git_status_fast)"
+ echo " Result: $status"
+ log_success "Fast git status working"
+ elif command -v _prompt_get_git_status >/dev/null 2>&1; then
+ local status
+ status="$(_prompt_get_git_status)"
+ echo " Result: $status"
+ log_success "Original git status working"
+ else
+ log_error "No git status function found"
+ fi
+}
+
+# Performance benchmark
+run_performance_test() {
+ log_info "Running performance benchmark"
+
+ if ! command -v __prompt_git >/dev/null 2>&1; then
+ log_error "Prompt function not available"
+ return 1
+ fi
+
+ local iterations=50
+ local start_time end_time duration
+
+ log_info "Running $iterations iterations"
+
+ start_time=$(date +%s%N)
+ for ((i=1; i<=iterations; i++)); do
+ __prompt_git >/dev/null 2>&1
+ done
+ end_time=$(date +%s%N)
+
+ duration=$(( (end_time - start_time) / 1000000 ))
+ local avg_duration=$(( duration / iterations ))
+
+ echo "Performance Results:"
+ echo " Total time: ${duration}ms"
+ echo " Average per call: ${avg_duration}ms"
+ echo " Iterations: $iterations"
+
+ if [[ $avg_duration -lt 10 ]]; then
+ log_success "Performance: Excellent (< 10ms per call)"
+ elif [[ $avg_duration -lt 50 ]]; then
+ log_success "Performance: Good (< 50ms per call)"
+ else
+ log_warn "Performance: Slow (>= 50ms per call)"
+ fi
+}
+
+# Test different git states
+test_git_states() {
+ log_info "Testing different git states"
+
+ # Clean state
+ git checkout -- . >/dev/null 2>&1
+ git clean -fd >/dev/null 2>&1
+ echo "Clean repository:"
+ __prompt_git
+ echo ""
+
+ # Modified files
+ echo "modified" >> test.txt
+ echo "Modified files:"
+ __prompt_git
+ echo ""
+
+ # Untracked files
+ echo "untracked" > new_file.txt
+ echo "With untracked files:"
+ __prompt_git
+ echo ""
+
+ # Staged files
+ git add new_file.txt
+ echo "With staged files:"
+ __prompt_git
+ echo ""
+
+ log_success "Git states testing completed"
+}
+
+# Interactive test mode
+interactive_mode() {
+ log_info "Entering interactive test mode"
+ echo "Available commands:"
+ echo " status - Show current git status"
+ echo " prompt - Show full prompt"
+ echo " perf - Run performance test"
+ echo " states - Test different git states"
+ echo " modify - Modify files for testing"
+ echo " clean - Clean working directory"
+ echo " help - Show this help"
+ echo " exit - Exit interactive mode"
+ echo ""
+
+ while true; do
+ echo -n "test> "
+ read -r command
+
+ case "$command" in
+ "status")
+ if command -v _get_git_status_fast >/dev/null 2>&1; then
+ _get_git_status_fast
+ else
+ _prompt_get_git_status
+ fi
+ echo ""
+ ;;
+ "prompt")
+ __prompt_git
+ echo ""
+ ;;
+ "perf")
+ run_performance_test
+ ;;
+ "states")
+ test_git_states
+ ;;
+ "modify")
+ echo "Creating test modifications"
+ echo "change $(date)" >> test.txt
+ echo "new file $(date)" > "file_$(date +%s).txt"
+ log_success "Files modified"
+ ;;
+ "clean")
+ git checkout -- . >/dev/null 2>&1
+ git clean -fd >/dev/null 2>&1
+ log_success "Working directory cleaned"
+ ;;
+ "help")
+ echo "Available commands: status, prompt, perf, states, modify, clean, help, exit"
+ ;;
+ "exit")
+ log_info "Exiting interactive mode"
+ break
+ ;;
+ "")
+ # Empty command, continue
+ ;;
+ *)
+ log_warn "Unknown command: $command (type 'help' for available commands)"
+ ;;
+ esac
+ done
+}
+
+# Main execution
+main() {
+ log_info "Starting HyperTerm Prompt Test Suite"
+
+ # Setup
+ setup_test_env
+
+ if ! load_hyperterm_functions; then
+ log_error "Failed to load HyperTerm functions"
+ exit 1
+ fi
+
+ # Run tests
+ test_individual_functions
+ echo ""
+ run_performance_test
+ echo ""
+ test_git_states
+ echo ""
+
+ # Interactive mode (only if not in CI or non-interactive mode)
+ if [[ "$1" != "--non-interactive" && "$1" != "-n" && -z "${CI:-}" ]]; then
+ log_info "All automated tests completed"
+ echo "Enter interactive mode? (y/N)"
+ read -r response
+ if [[ "$response" =~ ^[Yy]$ ]]; then
+ interactive_mode
+ fi
+ else
+ log_info "Running in non-interactive mode, skipping interactive prompt"
+ fi
+
+ log_success "Test suite completed successfully"
+}
+
+# Trap cleanup on exit
+trap cleanup EXIT
+
+# Execute main function if script is run directly
+if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
+ main "$@"
+fi