-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
lib/diff: Add module to log and resolve file diffs
Indirectly part of #184. In addition to being inspired by the `./go diff` command from mbland/dev-setup, `@go.diff_files` will be incorporated into the upcoming `@go.copy_files_safely` in `lib/fileutil`.
- Loading branch information
Showing
4 changed files
with
354 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
#! /usr/bin/env bash | ||
# | ||
# Tools for examining file differences | ||
# | ||
# Exports: | ||
# @go.diff_check_editor | ||
# Checks that `_GO_DIFF_EDITOR` is set | ||
# | ||
# @go.diff_files | ||
# Log differences between two files | ||
# | ||
# @go.diff_directories | ||
# Log differences between two directory structures | ||
|
||
export _GO_DIFF_EDITOR="${_GO_DIFF_EDITOR:-vimdiff}" | ||
|
||
. "$_GO_USE_MODULES" 'log' 'path' | ||
|
||
# Checks that `_GO_DIFF_EDITOR` is set | ||
# | ||
# Used by the functions in this module when the `--edit` flag is specified. | ||
# Fails with a stack trace if it isn't set. | ||
@go.diff_check_editor() { | ||
if [[ -z "$_GO_DIFF_EDITOR" ]]; then | ||
@go.log FATAL "_GO_DIFF_EDITOR not defined" | ||
elif ! command -v "$_GO_DIFF_EDITOR" >/dev/null; then | ||
@go.log FATAL "_GO_DIFF_EDITOR not installed: $_GO_DIFF_EDITOR" | ||
fi | ||
} | ||
|
||
# Log differences between two files | ||
# | ||
# Options: | ||
# --edit: Open `_GO_DIFF_EDITOR` on the files if they differ | ||
# | ||
# Arguments: | ||
# lhs: The "left-hand side" file | ||
# rhs: The "right-hand side" file | ||
@go.diff_files() { | ||
local lhs | ||
local rhs | ||
local edit | ||
|
||
if [[ "$1" == '--edit' ]]; then | ||
@go.diff_check_editor | ||
edit='true' | ||
shift | ||
fi | ||
|
||
lhs="$1" | ||
rhs="$2" | ||
|
||
if [[ ! -f "$lhs" ]]; then | ||
@go.log WARN "Left-hand side file $1 doesn't exist or isn't a regular file" | ||
elif [[ ! -f "$rhs" ]]; then | ||
@go.log WARN "Right-hand side file $2 doesn't exist or isn't a regular file" | ||
elif ! diff "$lhs" "$rhs" >/dev/null; then | ||
@go.log WARN "$lhs" differs from "$rhs" | ||
if [[ -n "$edit" ]]; then | ||
@go.log INFO "Editing $lhs and $rhs" | ||
"$_GO_DIFF_EDITOR" "$lhs" "$rhs" | ||
fi | ||
else | ||
return '0' | ||
fi | ||
return '1' | ||
} | ||
|
||
# Log differences between two directory structures | ||
# | ||
# Note that files and directories from `lhs_dir` that are missing in `rhs_dir` | ||
# are reported, but not the other way around. | ||
# | ||
# Options: | ||
# --edit: Open `_GO_DIFF_EDITOR` on any files that differ between the dirs | ||
# | ||
# Arguments: | ||
# lhs_dir: The "left-hand side" directory | ||
# rhs_dir: The "right-hand side" directory | ||
@go.diff_directories() { | ||
local __go_lhs_dir | ||
local __go_rhs_dir | ||
local __go_diff_files_args=() | ||
local __go_diff_directories_result='0' | ||
|
||
if [[ "$1" == '--edit' ]]; then | ||
__go_diff_files_args=('--edit') | ||
shift | ||
fi | ||
|
||
__go_lhs_dir="$1" | ||
__go_rhs_dir="$2" | ||
|
||
if [[ ! -d "$__go_lhs_dir" ]]; then | ||
@go.log WARN \ | ||
"Left-hand side directory $1 doesn't exist or isn't a directory" | ||
elif [[ ! -d "$__go_rhs_dir" ]]; then | ||
@go.log WARN \ | ||
"Right-hand side directory $2 doesn't exist or isn't a directory" | ||
else | ||
@go.walk_file_system [email protected]_directories_impl "$__go_lhs_dir" || : | ||
return "$__go_diff_directories_result" | ||
fi | ||
return '1' | ||
} | ||
|
||
# -------------------------------- | ||
# IMPLEMENTATION - HERE BE DRAGONS | ||
# | ||
# None of the functions below this line are part of the public interface. | ||
# -------------------------------- | ||
[email protected]_directories_impl() { | ||
local lhs="$1" | ||
local rhs="${__go_rhs_dir}${lhs#$__go_lhs_dir}" | ||
|
||
if [[ -f "$lhs" ]] && | ||
! @go.diff_files "${__go_diff_files_args[@]}" "$lhs" "$rhs"; then | ||
__go_diff_directories_result='1' | ||
fi | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
#! /usr/bin/env bats | ||
|
||
load ../environment | ||
|
||
setup() { | ||
test_filter | ||
@go.create_test_go_script \ | ||
'. "$_GO_USE_MODULES" "diff"'\ | ||
'@go.diff_check_editor' | ||
} | ||
|
||
teardown() { | ||
@go.remove_test_go_rootdir | ||
} | ||
|
||
@test "$SUITE: fails if __GO_DIFF_EDITOR not defined" { | ||
@go.create_test_go_script \ | ||
'. "$_GO_USE_MODULES" "diff"'\ | ||
'_GO_DIFF_EDITOR= @go.diff_check_editor' | ||
run "$TEST_GO_SCRIPT" | ||
assert_failure | ||
assert_output_matches 'FATAL.* _GO_DIFF_EDITOR not defined' | ||
} | ||
|
||
@test "$SUITE: fails if _GO_DIFF_EDITOR not found" { | ||
_GO_DIFF_EDITOR='nonexistent-editor' run "$TEST_GO_SCRIPT" | ||
assert_failure | ||
assert_output_matches \ | ||
'FATAL.* _GO_DIFF_EDITOR not installed: nonexistent-editor' | ||
} | ||
|
||
@test "$SUITE: does nothing if _GO_DIFF_EDITOR found" { | ||
stub_program_in_path 'vimdiff' | ||
_GO_DIFF_EDITOR='vimdiff' run "$TEST_GO_SCRIPT" | ||
restore_program_in_path 'vimdiff' | ||
assert_success '' | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
#! /usr/bin/env bats | ||
|
||
load ../environment | ||
|
||
LHS_DIR= | ||
RHS_DIR= | ||
|
||
setup() { | ||
test_filter | ||
@go.create_test_go_script \ | ||
'. "$_GO_USE_MODULES" "diff"' \ | ||
'@go.diff_directories "$@"' | ||
|
||
LHS_DIR="$TEST_GO_ROOTDIR/lhs" | ||
RHS_DIR="$TEST_GO_ROOTDIR/rhs" | ||
mkdir -p "$LHS_DIR" "$RHS_DIR" | ||
} | ||
|
||
teardown() { | ||
@go.remove_test_go_rootdir | ||
} | ||
|
||
@test "$SUITE: warns if LHS dir doesn't exist" { | ||
local lhs="$LHS_DIR" | ||
rmdir "$lhs" | ||
run "$TEST_GO_SCRIPT" "$lhs" "$RHS_DIR" | ||
assert_failure | ||
assert_output_matches \ | ||
"WARN.* Left-hand side directory $lhs doesn't exist or isn't a directory" | ||
} | ||
|
||
@test "$SUITE: warns if RHS dir doesn't exist" { | ||
local rhs="$RHS_DIR" | ||
rmdir "$rhs" | ||
run "$TEST_GO_SCRIPT" "$LHS_DIR" "$rhs" | ||
assert_failure | ||
assert_output_matches \ | ||
"WARN.* Right-hand side directory $rhs doesn't exist or isn't a directory" | ||
} | ||
|
||
@test "$SUITE: does nothing if first directory empty" { | ||
printf '%s\n' 'foo' >"$RHS_DIR/foo" | ||
run "$TEST_GO_SCRIPT" "$LHS_DIR" "$RHS_DIR" | ||
assert_success '' | ||
} | ||
|
||
@test "$SUITE: returns success if directories contain the same single file" { | ||
skip_if_system_missing 'diff' | ||
printf '%s\n' 'foo' >"$LHS_DIR/foo" | ||
printf '%s\n' 'foo' >"$RHS_DIR/foo" | ||
run "$TEST_GO_SCRIPT" "$LHS_DIR" "$RHS_DIR" | ||
assert_success '' | ||
} | ||
|
||
@test "$SUITE: returns success if rhs contains more files" { | ||
skip_if_system_missing 'diff' | ||
printf '%s\n' 'foo' >"$LHS_DIR/foo" | ||
printf '%s\n' 'foo' >"$RHS_DIR/foo" | ||
printf '%s\n' 'bar' >"$RHS_DIR/bar" | ||
run "$TEST_GO_SCRIPT" "$LHS_DIR" "$RHS_DIR" | ||
assert_success '' | ||
} | ||
|
||
@test "$SUITE: returns an error if rhs contains fewer files" { | ||
skip_if_system_missing 'diff' | ||
local missing="$RHS_DIR/bar" | ||
|
||
printf '%s\n' 'foo' >"$LHS_DIR/foo" | ||
printf '%s\n' 'bar' >"$LHS_DIR/bar" | ||
printf '%s\n' 'foo' >"$RHS_DIR/foo" | ||
|
||
run "$TEST_GO_SCRIPT" "$LHS_DIR" "$RHS_DIR" | ||
assert_failure | ||
assert_output_matches \ | ||
"WARN.* Right-hand side file $missing doesn't exist or isn't a regular file" | ||
} | ||
|
||
@test "$SUITE: warns and returns an error when files differ" { | ||
skip_if_system_missing 'diff' | ||
|
||
printf '%s\n' 'foo' >"$LHS_DIR/foo" | ||
printf '%s\n' 'bar' >"$RHS_DIR/foo" | ||
|
||
run "$TEST_GO_SCRIPT" "$LHS_DIR" "$RHS_DIR" | ||
assert_failure | ||
assert_output_matches "WARN.* $LHS_DIR/foo differs from $RHS_DIR/foo" | ||
} | ||
|
||
@test "$SUITE: --edit opens _GO_DIFF_EDITOR when files differ" { | ||
skip_if_system_missing 'diff' | ||
|
||
printf '%s\n' 'foo' >"$LHS_DIR/foo" | ||
printf '%s\n' 'bar' >"$RHS_DIR/foo" | ||
|
||
stub_program_in_path 'vimdiff' \ | ||
'printf "%s\n" "LHS: $1" "RHS: $2"' | ||
|
||
_GO_DIFF_EDITOR='vimdiff' run "$TEST_GO_SCRIPT" --edit "$LHS_DIR" "$RHS_DIR" | ||
restore_program_in_path 'vimdiff' | ||
|
||
assert_failure | ||
assert_lines_match \ | ||
"WARN.* $LHS_DIR/foo differs from $RHS_DIR/foo" \ | ||
"INFO.* Editing $LHS_DIR/foo and $RHS_DIR/foo" \ | ||
"LHS: $LHS_DIR/foo" \ | ||
"RHS: $RHS_DIR/foo" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
#! /usr/bin/env bats | ||
|
||
load ../environment | ||
|
||
setup() { | ||
test_filter | ||
@go.create_test_go_script \ | ||
'. "$_GO_USE_MODULES" "diff"'\ | ||
'@go.diff_files "$@"' | ||
} | ||
|
||
teardown() { | ||
@go.remove_test_go_rootdir | ||
} | ||
|
||
@test "$SUITE: warns if lhs file doesn't exist or isn't regular" { | ||
local lhs="$TEST_GO_ROOTDIR/foo" | ||
|
||
run "$TEST_GO_SCRIPT" "$lhs" | ||
assert_failure | ||
assert_output_matches \ | ||
"WARN.* Left-hand side file $lhs doesn't exist or isn't a regular file" | ||
} | ||
|
||
@test "$SUITE: warns if rhs file doesn't exist or isn't regular" { | ||
local lhs="$TEST_GO_ROOTDIR/foo" | ||
local rhs="$TEST_GO_ROOTDIR/bar" | ||
|
||
mkdir -p "$TEST_GO_ROOTDIR" | ||
printf '%s\n' 'foo' >"$lhs" | ||
mkdir "$rhs" | ||
|
||
run "$TEST_GO_SCRIPT" "$lhs" "$rhs" | ||
assert_failure | ||
assert_output_matches \ | ||
"WARN.* Right-hand side file $rhs doesn't exist or isn't a regular file" | ||
} | ||
|
||
@test "$SUITE: return success if the files match" { | ||
skip_if_system_missing 'diff' | ||
|
||
local lhs="$TEST_GO_ROOTDIR/foo" | ||
local rhs="$TEST_GO_ROOTDIR/bar" | ||
|
||
mkdir -p "$TEST_GO_ROOTDIR" | ||
printf '%s\n' 'foo' >"$lhs" | ||
printf '%s\n' 'foo' >"$rhs" | ||
|
||
run "$TEST_GO_SCRIPT" "$lhs" "$rhs" | ||
assert_success '' | ||
} | ||
|
||
@test "$SUITE: warns and return failure if the files differ" { | ||
skip_if_system_missing 'diff' | ||
|
||
local lhs="$TEST_GO_ROOTDIR/foo" | ||
local rhs="$TEST_GO_ROOTDIR/bar" | ||
|
||
mkdir -p "$TEST_GO_ROOTDIR" | ||
printf '%s\n' 'foo' >"$lhs" | ||
printf '%s\n' 'bar' >"$rhs" | ||
|
||
run "$TEST_GO_SCRIPT" "$lhs" "$rhs" | ||
assert_failure | ||
assert_output_matches "WARN.* $lhs differs from $rhs" | ||
} | ||
|
||
@test "$SUITE: --edit opens _GO_DIFF_EDITOR if the files differ" { | ||
skip_if_system_missing 'diff' | ||
|
||
local lhs="$TEST_GO_ROOTDIR/foo" | ||
local rhs="$TEST_GO_ROOTDIR/bar" | ||
|
||
mkdir -p "$TEST_GO_ROOTDIR" | ||
printf '%s\n' 'foo' >"$lhs" | ||
printf '%s\n' 'bar' >"$rhs" | ||
|
||
stub_program_in_path 'vimdiff' \ | ||
'printf "%s\n" "LHS: $1" "RHS: $2"' | ||
|
||
_GO_DIFF_EDITOR='vimdiff' run "$TEST_GO_SCRIPT" --edit "$lhs" "$rhs" | ||
restore_program_in_path 'vimdiff' | ||
|
||
assert_failure | ||
assert_lines_match \ | ||
"WARN.* $lhs differs from $rhs" \ | ||
"INFO.* Editing $lhs and $rhs" \ | ||
"LHS: $lhs" \ | ||
"RHS: $rhs" | ||
} |