add _spinner_

This commit is contained in:
Nathaniel Landau
2021-11-14 23:32:59 -05:00
parent 6c54c7f902
commit 6fe9e43cb5
6 changed files with 237 additions and 129 deletions

View File

@@ -258,12 +258,14 @@ Miscellaneous functions
- **`_detectLinuxDistro_`** Detects the host computer's distribution of Linux
- **`_detectMacOSVersion_`** Detects the host computer's version of macOS
- **`_detectOS_`** Detect the the host computer's operating system
- **`_endspin_`** Clears output from the _spinner_
- **`_execute_`** Executes commands with safety and logging options. Respects `DRYRUN` and `VERBOSE` flags.
- **`_findBaseDir_`** Locates the real directory of the script being run. Similar to GNU readlink -n
- **`_generateUUID_`** Generates a unique UUID
- **`_makeProgressBar_`** Prints a progress bar within a for/while loop
- **`_progressBar_`** Prints a progress bar within a for/while loop
- **`_runAsRoot_`** Run the requested command as root (via sudo if requested)
- **`_seekConfirmation_`** Seek user input for yes/no question
- **`_spinner_`** Creates a spinner within a for/while loop.
- **`_trapCleanup_`** Cleans up after a trapped error.
## services.bash

View File

@@ -53,6 +53,9 @@ _trapCleanup_() {
local _script="${5:-}"
local _sourced="${6:-}"
# Replace the cursor in-case 'tput civis' has been used
tput cnorm
if declare -f "fatal" &>/dev/null && declare -f "_printFuncStack_" &>/dev/null; then
_funcstack="'$(printf "%s" "${_funcstack}" | sed -E 's/ / < /g')'"

View File

@@ -53,7 +53,7 @@ _setColors_() {
blue=$(tput setaf 38)
yellow=$(tput setaf 11)
green=$(tput setaf 82)
red=$(tput setaf 196)
red=$(tput setaf 9)
purple=$(tput setaf 171)
gray=$(tput setaf 250)
else
@@ -61,7 +61,7 @@ _setColors_() {
blue=$(tput setaf 38)
yellow=$(tput setaf 3)
green=$(tput setaf 2)
red=$(tput setaf 1)
red=$(tput setaf 9)
purple=$(tput setaf 13)
gray=$(tput setaf 7)
fi
@@ -307,6 +307,9 @@ _trapCleanup_() {
local _script="${5:-}"
local _sourced="${6:-}"
# Replace the cursor in-case 'tput civis' has been used
tput cnorm
if declare -f "fatal" &>/dev/null && declare -f "_printFuncStack_" &>/dev/null; then
_funcstack="'$(printf "%s" "${_funcstack}" | sed -E 's/ / < /g')'"

View File

@@ -11,122 +11,119 @@ SOURCEFILE="${ROOTDIR}/utilities/misc.bash"
ALERTS="${ROOTDIR}/utilities/alerts.bash"
if test -f "${SOURCEFILE}" >&2; then
source "${SOURCEFILE}"
source "${SOURCEFILE}"
else
echo "Sourcefile not found: ${SOURCEFILE}" >&2
printf "Can not run tests.\n" >&2
exit 1
echo "Sourcefile not found: ${SOURCEFILE}" >&2
printf "Can not run tests.\n" >&2
exit 1
fi
if test -f "${ALERTS}" >&2; then
source "${ALERTS}"
_setColors_ #Set color constants
source "${ALERTS}"
_setColors_ #Set color constants
else
echo "Sourcefile not found: ${ALERTS}" >&2
printf "Can not run tests.\n" >&2
exit 1
echo "Sourcefile not found: ${ALERTS}" >&2
printf "Can not run tests.\n" >&2
exit 1
fi
setup() {
TESTDIR="$(temp_make)"
curPath="${PWD}"
TESTDIR="$(temp_make)"
curPath="${PWD}"
BATSLIB_FILE_PATH_REM="#${TEST_TEMP_DIR}"
BATSLIB_FILE_PATH_ADD='<temp>'
BATSLIB_FILE_PATH_REM="#${TEST_TEMP_DIR}"
BATSLIB_FILE_PATH_ADD='<temp>'
pushd "${TESTDIR}" >&2
pushd "${TESTDIR}" >&2
######## DEFAULT FLAGS ########
LOGFILE="${TESTDIR}/logs/log.txt"
QUIET=false
LOGLEVEL=ERROR
VERBOSE=false
FORCE=false
DRYRUN=false
######## DEFAULT FLAGS ########
LOGFILE="${TESTDIR}/logs/log.txt"
QUIET=false
LOGLEVEL=ERROR
VERBOSE=false
FORCE=false
DRYRUN=false
set -o errtrace
set -o nounset
set -o pipefail
set -o errtrace
set -o nounset
set -o pipefail
}
teardown() {
set +o nounset
set +o errtrace
set +o pipefail
set +o nounset
set +o errtrace
set +o pipefail
popd >&2
temp_del "${TESTDIR}"
popd >&2
temp_del "${TESTDIR}"
}
######## RUN TESTS ########
@test "Sanity..." {
run true
assert_success
assert_output ""
run true
assert_success
assert_output ""
}
_testExecute_() {
@test "_execute_: Debug command" {
@test "_execute_: Debug command" {
DRYRUN=true
run _execute_ "rm testfile.txt"
assert_success
assert_output --partial "[ dryrun] rm testfile.txt"
}
}
@test "_execute_: No command" {
@test "_execute_: No command" {
run _execute_
assert_failure
assert_output --regexp "\[ fatal\] Missing required argument to _execute_"
}
}
@test "_execute_: Bad command" {
@test "_execute_: Bad command" {
run _execute_ "rm nonexistant.txt"
assert_failure
assert_output --partial "[warning] rm nonexistant.txt"
}
}
@test "_execute_ -e: Bad command" {
@test "_execute_ -e: Bad command" {
run _execute_ -e "rm nonexistant.txt"
assert_failure
assert_output "error: rm nonexistant.txt"
}
}
@test "_execute_ -p: Return 0 on bad command" {
@test "_execute_ -p: Return 0 on bad command" {
run _execute_ -p "rm nonexistant.txt"
assert_success
assert_output --partial "[warning] rm nonexistant.txt"
}
}
@test "_execute_: Good command" {
@test "_execute_: Good command" {
touch "testfile.txt"
run _execute_ "rm testfile.txt"
assert_success
assert_output --partial "[ info] rm testfile.txt"
assert_file_not_exist "testfile.txt"
}
}
@test "_execute_: Good command - no output" {
@test "_execute_: Good command - no output" {
touch "testfile.txt"
run _execute_ -q "rm testfile.txt"
assert_success
refute_output --partial "[ info] rm testfile.txt"
assert_file_not_exist "testfile.txt"
}
}
@test "_execute_ -s: Good command" {
@test "_execute_ -s: Good command" {
touch "testfile.txt"
run _execute_ -s "rm testfile.txt"
assert_success
assert_output --partial "[success] rm testfile.txt"
assert_file_not_exist "testfile.txt"
}
}
@test "_execute_ -v: Good command" {
@test "_execute_ -v: Good command" {
touch "testfile.txt"
run _execute_ -v "rm -v testfile.txt"
@@ -134,18 +131,18 @@ _testExecute_() {
assert_line --index 0 "removed 'testfile.txt'"
assert_line --index 1 --partial "[ info] rm -v testfile.txt"
assert_file_not_exist "testfile.txt"
}
}
@test "_execute_ -n: Good command" {
@test "_execute_ -n: Good command" {
touch "testfile.txt"
run _execute_ -n "rm -v testfile.txt"
assert_success
assert_line --index 0 --partial "[ notice] rm -v testfile.txt"
assert_file_not_exist "testfile.txt"
}
}
@test "_execute_ -ev: Good command" {
@test "_execute_ -ev: Good command" {
touch "testfile.txt"
run _execute_ -ve "rm -v testfile.txt"
@@ -153,67 +150,74 @@ _testExecute_() {
assert_line --index 0 "removed 'testfile.txt'"
assert_line --index 1 --partial "rm -v testfile.txt"
assert_file_not_exist "testfile.txt"
}
}
_testExecute_
@test "_findBaseDir_" {
run _findBaseDir_
assert_output --regexp "^/usr/local/Cellar/bats-core/[0-9]\.[0-9]\.[0-9]"
run _findBaseDir_
assert_success
if [ -d /usr/local/Cellar/ ]; then
assert_output --regexp "^/usr/local/Cellar/bats-core/[0-9]\.[0-9]\.[0-9]"
elif [ -d /opt/homebrew/Cellar ]; then
assert_output --regexp "^/opt/homebrew/Cellar/bats-core/[0-9]\.[0-9]\.[0-9]"
fi
}
@test "_generateUUID_" {
run _generateUUID_
assert_success
assert_output --regexp "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"
run _generateUUID_
assert_success
assert_output --regexp "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"
}
@test "_makeProgressBar_: verbose" {
verbose=true
run _makeProgressBar_ 100
assert_success
assert_output ""
verbose=false
@test "_spinner_: verbose" {
verbose=true
run _spinner_
assert_success
assert_output ""
}
@test "_makeProgressBar_: quiet" {
quiet=true
run _makeProgressBar_ 100
@test "_spinner_: quiet" {
quiet=true
run _spinner_
assert_success
assert_output ""
}
assert_success
assert_output ""
quiet=false
@test "_progressBar_: verbose" {
verbose=true
run _progressBar_ 100
assert_success
assert_output ""
}
@test "_progressBar_: quiet" {
quiet=true
run _progressBar_ 100
assert_success
assert_output ""
}
@test "_seekConfirmation_: yes" {
run _seekConfirmation_ 'test' <<<"y"
assert_success
assert_output --partial "[ input] test"
run _seekConfirmation_ 'test' <<<"y"
assert_success
assert_output --partial "[ input] test"
}
@test "_seekConfirmation_: no" {
run _seekConfirmation_ 'test' <<<"n"
assert_failure
assert_output --partial "[ input] test"
run _seekConfirmation_ 'test' <<<"n"
assert_failure
assert_output --partial "[ input] test"
}
@test "_seekConfirmation_: Force" {
FORCE=true
run _seekConfirmation_ "test"
assert_success
assert_output --partial "test"
FORCE=true
run _seekConfirmation_ "test"
assert_success
assert_output --partial "test"
}
@test "_seekConfirmation_: Quiet" {
QUIET=true
run _seekConfirmation_ 'test' <<<"y"
assert_success
refute_output --partial "test"
quiet=false
QUIET=true
run _seekConfirmation_ 'test' <<<"y"
assert_success
refute_output --partial "test"
}

View File

@@ -22,7 +22,7 @@ _setColors_() {
blue=$(tput setaf 38)
yellow=$(tput setaf 11)
green=$(tput setaf 82)
red=$(tput setaf 196)
red=$(tput setaf 9)
purple=$(tput setaf 171)
gray=$(tput setaf 250)
else
@@ -30,7 +30,7 @@ _setColors_() {
blue=$(tput setaf 38)
yellow=$(tput setaf 3)
green=$(tput setaf 2)
red=$(tput setaf 1)
red=$(tput setaf 9)
purple=$(tput setaf 13)
gray=$(tput setaf 7)
fi
@@ -312,7 +312,7 @@ _columnizeOutput_() {
done <<<"$(fold -w${_rightWrapLength} -s <<<"${_value}")"
}
_clearLine_() {
_clearLine_() (
# DESC:
# Clears output in the terminal on the specified line number.
# ARGS:
@@ -325,10 +325,12 @@ _clearLine_() {
! declare -f _isTerminal_ &>/dev/null && fatal "${FUNCNAME[0]} needs function _isTerminal_"
local _num="${1:-1}"
local i
if _isTerminal_; then
for ((i = 0; i < $1; i++)); do
for ((i = 0; i < _num; i++)); do
printf "\033[A\033[2K"
done
fi
}
)

View File

@@ -356,26 +356,28 @@ _generateUUID_() {
printf '\n'
}
_makeProgressBar_() {
_progressBar_() {
# DESC:
# Prints a progress bar within a for/while loop
# Prints a progress bar within a for/while loop. For this to work correctly you
# MUST know the exact number of iterations. If you don't know the exact number use _spinner_
# ARGS:
# $1 (Required) - The total number of items counted
# $2 (Optional) - The optional title of the progress bar
# OUTS: stdout: progress bar
# OUTS:
# stdout: progress bar
# USAGE:
# for number in $(seq 0 100); do
# sleep 1
# for i in $(seq 0 100); do
# sleep 0.1
# _makeProgressBar_ "100" "Counting numbers"
# done
[[ $# == 0 ]] && return # Do nothing if no arguments are passed
(${QUIET:-}) && return
(${VERBOSE:-}) && return
[[ $# == 0 ]] && return # Do nothing if no arguments are passed
(${QUIET:-}) && return # Do nothing in quiet mode
(${VERBOSE:-}) && return # Do nothing if verbose mode is enabled
[ ! -t 1 ] && return # Do nothing if the output is not a terminal
[[ ${1} == 1 ]] && return # Do nothing with a single element
local n="${1}"
local _n="${1}"
local _width=30
local _barCharacter="#"
local _percentage
@@ -383,40 +385,132 @@ _makeProgressBar_() {
local _bar
local _progressBarLine
local _barTitle="${2:-Running Process}"
local n
((n = n - 1))
((_n = _n - 1))
# Reset the count
[ -z "${progressBarProgress:-}" ] && progressBarProgress=0
tput civis # Hide the cursor
trap 'tput cnorm; exit 1' SIGINT
[ -z "${PROGRESS_BAR_PROGRESS:-}" ] && PROGRESS_BAR_PROGRESS=0
# Hide the cursor
tput civis
if [[ ! ${PROGRESS_BAR_PROGRESS} -eq ${_n} ]]; then
if [[ ! ${progressBarProgress} -eq ${n} ]]; then
#echo "progressBarProgress: $progressBarProgress"
# Compute the percentage.
_percentage=$((progressBarProgress * 100 / $1))
_percentage=$((PROGRESS_BAR_PROGRESS * 100 / $1))
# Compute the number of blocks to represent the percentage.
_num=$((progressBarProgress * _width / $1))
_num=$((PROGRESS_BAR_PROGRESS * _width / $1))
# Create the progress bar string.
_bar=""
if [[ ${_num} -gt 0 ]]; then
_bar=$(printf "%0.s${_barCharacter}" $(seq 1 "${_num}"))
fi
# Print the progress bar.
_progressBarLine=$(printf "%s [%-${_width}s] (%d%%)" " ${_barTitle}" "${_bar}" "${_percentage}")
printf "%s\r" "${_progressBarLine}"
# echo -ne "${_progressBarLine}\r"
progressBarProgress=$((progressBarProgress + 1))
else
# Clear the progress bar when complete
# echo -ne "\033[0K\r"
tput el # Clear the line
unset progressBarProgress
PROGRESS_BAR_PROGRESS=$((PROGRESS_BAR_PROGRESS + 1))
else
# Replace the cursor
tput cnorm
# Clear the progress bar when complete
printf "\r\033[0K"
unset PROGRESS_BAR_PROGRESS
fi
}
_spinner_() {
# DESC:
# Creates a spinner within a for/while loop.
# Don't forget to add _endspin_ at the end of the loop
# ARGS:
# $1 (Optional) - Text accompanying the spinner
# OUTS:
# stdout: progress bar
# USAGE:
# for i in $(seq 0 100); do
# sleep 0.1
# _spinner_ "Counting numbers"
# done
# _endspin_
(${QUIET:-}) && return # Do nothing in quiet mode
(${VERBOSE:-}) && return # Do nothing in verbose mode
[ ! -t 1 ] && return # Do nothing if the output is not a terminal
local _message
_message="${1:-Running process}"
# Hide the cursor
tput civis
[[ -z ${SPIN_NUM:-} ]] && SPIN_NUM=0
case ${SPIN_NUM:-} in
0) _glyph="█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁" ;;
1) _glyph="██▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁" ;;
2) _glyph="███▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁" ;;
3) _glyph="████▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁" ;;
4) _glyph="██████▁▁▁▁▁▁▁▁▁▁▁▁▁▁" ;;
5) _glyph="██████▁▁▁▁▁▁▁▁▁▁▁▁▁▁" ;;
6) _glyph="███████▁▁▁▁▁▁▁▁▁▁▁▁▁" ;;
7) _glyph="████████▁▁▁▁▁▁▁▁▁▁▁▁" ;;
8) _glyph="█████████▁▁▁▁▁▁▁▁▁▁▁" ;;
9) _glyph="█████████▁▁▁▁▁▁▁▁▁▁▁" ;;
10) _glyph="██████████▁▁▁▁▁▁▁▁▁▁" ;;
11) _glyph="███████████▁▁▁▁▁▁▁▁▁" ;;
12) _glyph="█████████████▁▁▁▁▁▁▁" ;;
13) _glyph="██████████████▁▁▁▁▁▁" ;;
14) _glyph="██████████████▁▁▁▁▁▁" ;;
15) _glyph="███████████████▁▁▁▁▁" ;;
16) _glyph="███████████████▁▁▁▁▁" ;;
17) _glyph="███████████████▁▁▁▁▁" ;;
18) _glyph="████████████████▁▁▁▁" ;;
19) _glyph="█████████████████▁▁▁" ;;
20) _glyph="█████████████████▁▁▁" ;;
21) _glyph="██████████████████▁▁" ;;
22) _glyph="██████████████████▁▁" ;;
23) _glyph="███████████████████▁" ;;
24) _glyph="███████████████████▁" ;;
25) _glyph="███████████████████▁" ;;
26) _glyph="████████████████████" ;;
27) _glyph="████████████████████" ;;
28) _glyph="█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁" ;;
esac
# shellcheck disable=SC2154
printf "\r${gray}[ info] %s... %s${reset}" "${_message}" "${_glyph}"
if [[ ${SPIN_NUM} -lt 28 ]]; then
((SPIN_NUM = SPIN_NUM + 1))
else
SPIN_NUM=0
fi
}
_endspin_() {
# DESC:
# Clears the line that showed the spinner and replaces the cursor. To be run after _spinner_
# ARGS:
# None
# OUTS:
# stdout: Removes previous line
# USAGE:
# _endspin_
# Clear the spinner
printf "\r\033[0K"
# Replace the cursor
tput cnorm
unset SPIN_NUM
}
_runAsRoot_() {