Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
14f3f24
! r Extract function: main
MichaelRWolf Oct 13, 2023
22abf52
! r Extract function: compare_and_approve
MichaelRWolf Oct 13, 2023
9ae742e
. r Whitespace
MichaelRWolf Oct 13, 2023
9db16f1
! r Extract functions: pass(), fail()
MichaelRWolf Oct 13, 2023
3c9ffd5
! r Remove duplicate (dead) code
MichaelRWolf Oct 13, 2023
2c15f86
! r Extract function: fail_and_diff()
MichaelRWolf Oct 13, 2023
54c93dd
! r Extract function: pass_and_rm()
MichaelRWolf Oct 13, 2023
ebf764c
! r Extract function: warn()
MichaelRWolf Oct 13, 2023
f8b73af
^ r Rename function: pass_and_rm_received()
MichaelRWolf Oct 13, 2023
b7de348
^ f Default test_name to 'unspecified_test_name" if not specified
MichaelRWolf Oct 20, 2023
ed14b51
! f Logic to choose difftool from 'git difftool' or 'code'
MichaelRWolf Jan 21, 2025
4d68d33
! f Allow variable to be unbound: 'test_name'
MichaelRWolf Jan 21, 2025
6532a99
@ t New test: diff_tool_selection
MichaelRWolf Feb 3, 2025
b37b4f3
. @ New file: TODO.md
MichaelRWolf Feb 3, 2025
d3e3c2d
. r Extract tests into separate functions.
MichaelRWolf Feb 4, 2025
94f576e
. f Whitespace
MichaelRWolf Feb 4, 2025
d49fd56
_ _ Approve all tests
MichaelRWolf Feb 4, 2025
2ec8ad5
! r Convert short-circuit logic to if/then/else.
MichaelRWolf Feb 4, 2025
2faef7d
_ _ Quotes to preserve whitespace in variable expansion.
MichaelRWolf Feb 4, 2025
0a99a75
_ _ Add Emacs apheleia-mode local variables notes to TODO
MichaelRWolf Mar 27, 2026
d709837
Merge upstream CI workflow changes
MichaelRWolf Mar 27, 2026
9c85ccc
Ignore bash/failing-test.approved
MichaelRWolf Mar 27, 2026
da93380
Add macOS clutter to .gitignore
MichaelRWolf Apr 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,17 @@
*.received*
bash/failing-test.approved

# macOS
.DS_Store
.DS_Store?
._*
.AppleDouble
.LSOverride
.Spotlight-V100
.Trashes
.DocumentRevisions-V100
.fseventsd
.TemporaryItems
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
__MACOSX/
55 changes: 55 additions & 0 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Todo

- diff-tool selection is consistent with `git help difftool`
- Command line args are consistent with `git dif`
Use -t and --tool, NOT -d
See: git difftool --tool-help

# Starter code

```bash
get_git_difftool() {
# Check if --tool argument is provided in command line arguments
while [[ $# -gt 0 ]]; do
case "$1" in
-t|--tool)
echo "$2"
return 0
;;
*) shift ;;
esac
done

# Check if GIT_DIFF_TOOL environment variable is set
if [[ -n "$GIT_DIFF_TOOL" ]]; then
echo "$GIT_DIFF_TOOL"
return 0
fi

# Fallback to the configured diff.tool in git config
git_config_tool="$(git config --get diff.tool)"
if [[ -n "$git_config_tool" ]]; then
echo "$git_config_tool"
return 0
fi

# Default return if no tool is set (to avoid empty return)
echo "default-diff-tool" # Replace this with the actual default tool if needed
}
```

## Emacs

;; Local Variables:
;; eval: (apheleia-mode nil)
;; eval: (apheleia-mode nil)

;; eval: (apheleia-mode +1)
;; eval: (apheleia-mode 0)
;; eval: (apheleia-mode -1)

;; eval: (bound-and-true-p aphelia-mode)
;; eval: (apheleia-mode)

;; eval: (apheleia-mode-with-elisp-conventions nil)
;; End:
48 changes: 42 additions & 6 deletions bash/test.sh
Original file line number Diff line number Diff line change
@@ -1,10 +1,46 @@
#!/bin/bash

echo "test: pass"
./verify.sh -t test1 <<< "test 1 approves this message
function test1() {
test_name="${FUNCNAME[0]}"
echo "test: pass"
./verify.sh -t "$test_name" <<<"test 1 approves this message
line 2"
echo ""
echo ""
}

echo "test: fails and triggers diff tool"
./verify.sh -t test3 -d diff <<< "test 3 receives this input"
echo ""
function test2() {
true
}

function test3() {
test_name="${FUNCNAME[0]}"
echo "test: fails and triggers diff tool"
./verify.sh -t "$test_name" -d diff <<<"test 3 receives this input"
echo ""
}

function test4() {
test_name="${FUNCNAME[0]}"
./verify.sh -d meld <<<"Input to test without a name"
}

function test_diff_tool_selection() {
test_name="${FUNCNAME[0]}"
cat <<DIFF_TOOL_SELECTION | ./verify.sh -t "$test_name"
# Logic for diff tool
# 1. --tool argument takes top priority.
# 2. If no --tool is specified, GIT_DIFF_TOOL is checked next.
# 3. If GIT_DIFF_TOOL is unset, git config --get diff.tool is used.
# 4. If neither is set, Git falls back to its internal diff.
DIFF_TOOL_SELECTION
}

function test_all() {
test1
test2
test3
test4
test_diff_tool_selection
}

test_all
Empty file added bash/test5.approved
Empty file.
Empty file.
5 changes: 5 additions & 0 deletions bash/test_diff_tool_selection.approved
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Logic for diff tool
# 1. --tool argument takes top priority.
# 2. If no --tool is specified, GIT_DIFF_TOOL is checked next.
# 3. If GIT_DIFF_TOOL is unset, git config --get diff.tool is used.
# 4. If neither is set, Git falls back to its internal diff.
1 change: 1 addition & 0 deletions bash/unspecified_test_name.approved
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Input to test without a name
114 changes: 86 additions & 28 deletions bash/verify.sh
Original file line number Diff line number Diff line change
@@ -1,39 +1,97 @@
#!/bin/bash
set -euo pipefail

default_diff_tool="code --diff"
diff_tool=$default_diff_tool
true <<TODO
See:
- git config --get diff.tool
- git difftool
- git difftool --no-index
TODO

while getopts ":r:t:d:" opt; do
git_difftool="$(git config --get diff.tool)"
vs_code_path=$(command -v code)

if [[ -n "${git_difftool}" ]] && command -v "${git_difftool}"; then
difftool="git difftool --tool ${git_difftool} --no-index"
elif [[ -n "${vs_code_path}" ]]; then
difftool="${vs_code_path} --diff"
else
difftool=""
fi

function warn() {
echo "$*" >&2
}

while getopts ":r:t:d:D" opt; do
case $opt in
d) diff_tool=$OPTARG ;;
r) received_text=$OPTARG ;;
t) test_name=$OPTARG ;;
\?) echo "Invalid option: -$OPTARG" >&2 ;;
d) difftool=$OPTARG ;;
r) received_text=$OPTARG ;;
t) test_name=$OPTARG ;;
D) debug_without_compare_and_approve="true" ;;
\?) warn "Invalid option: -$OPTARG" ;;
esac
done

if [[ "${test_name=}" == '' ]]; then
test_name="unspecified_test_name"
fi

received="$test_name.received"
approved="$test_name.approved"

if [ "$received_text" == "" ]; then
cat - >"$received"
else
echo "$received_text" >"$received"
fi
function debug_arguments() {
cat <<REPORT
$0 -d '$difftool' -r '$received_text' -t '$test_name'
REPORT
}

function main() {
if [ "${received_text=}" == "" ]; then
cat - >"$received"
else
echo "$received_text" >"$received"
fi

touch "$approved"

if [[ -n "${debug_without_compare_and_approve=}" ]]; then
debug_arguments
else
compare_and_approve "$test_name"
fi
}

function pass() {
echo "${1:=unnamed-test} passed"
}

function fail() {
echo "${1:unnamed-test} failed"
}

function fail_and_diff() {
if [ -e /dev/tty ]; then
$difftool "$received" "$approved" </dev/tty
else
$difftool "$received" "$approved"
fi
false
}

function pass_and_rm_received() {
pass "$1"
rm "$received"
}

function compare_and_approve() {
# NB: Following are run in sub-shells to prevent their errors from
# terminating this shell. I'm not sure if that's still necessary.
if diff -q "$received" "$approved" >/dev/null; then
(pass_and_rm_received "$1");
else
(fail_and_diff "$1")
fi
}

touch "$approved"

diff -q "$received" "$approved" >/dev/null &&
(
echo "test passed"
rm "$received"
) ||
(
echo "test failed"
if [ -t 1 ]; then
$diff_tool "$received" "$approved" </dev/tty
else
$diff_tool "$received" "$approved"
fi
false
)
main
Empty file added unspecified_test_name.approved
Empty file.