-
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.
Merge pull request #204 from mbland/fileutil-module-#184
Add lib/fileutil file and directory tools module
- Loading branch information
Showing
5 changed files
with
296 additions
and
3 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,95 @@ | ||
#! /usr/bin/env bash | ||
# | ||
# File and directory management | ||
# | ||
# Exports: | ||
# @go.create_dirs | ||
# Creates a set of directories and any missing parents | ||
# | ||
# @go.collect_file_paths | ||
# Collects all the paths to regular files within a directory structure | ||
|
||
. "$_GO_USE_MODULES" 'log' 'path' | ||
|
||
# Creates a set of directories and any missing parents | ||
# | ||
# If the directory already exists, this will not update its permissions. | ||
# | ||
# Provides a bit more thorough error-checking and feedback than just using | ||
# `mkdir -p` and `chmod -R`. Errors are reported via `@go.log FATAL`, which will | ||
# show a stack trace and crash the program. | ||
# | ||
# Options: | ||
# --mode: Permissions for created directories | ||
# | ||
# Arguments: | ||
# $@: Paths of directories to create | ||
@go.create_dirs() { | ||
local dir | ||
local mode | ||
local __go_missing_parent | ||
|
||
if [[ "$1" == '--mode' ]]; then | ||
mode="$2" | ||
shift 2 | ||
fi | ||
|
||
for dir in "$@"; do | ||
if [[ -z "$dir" ]]; then | ||
@go.log FATAL 'The empty string is not a valid directory name' | ||
elif [[ ! -d "$dir" ]]; then | ||
@go.walk_path_forward '_@go.find_missing_parent_path' "$dir" || : | ||
|
||
if [[ -e "$__go_missing_parent" ]]; then | ||
@go.log FATAL "$__go_missing_parent exists and is not a directory" | ||
elif ! mkdir -p "$dir"; then | ||
@go.log FATAL "Could not create $dir in ${__go_missing_parent%/*}" | ||
elif [[ -n "$mode" ]] && ! chmod -R "$mode" "$__go_missing_parent"; then | ||
@go.log FATAL "Could not set permissions for $__go_missing_parent" | ||
fi | ||
fi | ||
done | ||
} | ||
|
||
# Collects all the paths to regular files within a directory structure | ||
# | ||
# Globals: | ||
# __go_collected_file_paths: Caller-defined array in which paths are stored | ||
# | ||
# Arguments: | ||
# $@: Paths from which to collect paths for regular files | ||
@go.collect_file_paths() { | ||
__go_collected_file_paths=() | ||
@go.walk_file_system _@go.collect_file_paths_impl "$@" | ||
} | ||
|
||
# -------------------------------- | ||
# IMPLEMENTATION - HERE BE DRAGONS | ||
# | ||
# None of the functions below this line are part of the public interface. | ||
# -------------------------------- | ||
|
||
# @go.walk_path_forwared helper to finds the first missing parent directory | ||
# | ||
# Arguments: | ||
# path: Path to examine whether | ||
_@go.find_missing_parent_path() { | ||
__go_missing_parent="$1" | ||
|
||
if [[ ! -d "$1" ]]; then | ||
return 1 | ||
fi | ||
} | ||
|
||
# Helper function for @go.collect_file_paths | ||
# | ||
# Globals: | ||
# __go_collected_file_paths: Caller-defined results array | ||
# | ||
# Arguments: | ||
# path: File system path passed in by @go.walk_file_system | ||
_@go.collect_file_paths_impl() { | ||
if [[ -f "$1" ]]; then | ||
__go_collected_file_paths+=("$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
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,53 @@ | ||
#! /usr/bin/env bats | ||
|
||
load ../environment | ||
|
||
setup() { | ||
test_filter | ||
@go.create_test_go_script \ | ||
'. "$_GO_USE_MODULES" "fileutil"' \ | ||
'declare __go_collected_file_paths' \ | ||
'@go.collect_file_paths "$1"' \ | ||
'printf "%s\n" "${__go_collected_file_paths[@]}"' | ||
} | ||
|
||
teardown() { | ||
@go.remove_test_go_rootdir | ||
} | ||
|
||
@test "$SUITE: empty argument list does nothing" { | ||
run "$TEST_GO_SCRIPT" | ||
assert_success '' | ||
} | ||
|
||
@test "$SUITE: nonexistent directory path does nothing" { | ||
run "$TEST_GO_SCRIPT" '' | ||
assert_success '' | ||
} | ||
|
||
@test "$SUITE: directory with no regular files returns nothing" { | ||
mkdir -p "$TEST_GO_ROOTDIR/foo/bar/baz" | ||
run "$TEST_GO_SCRIPT" "$TEST_GO_ROOTDIR/foo" | ||
assert_success '' | ||
} | ||
|
||
@test "$SUITE: directory with a single file" { | ||
mkdir -p "$TEST_GO_ROOTDIR/foo/bar" | ||
printf '%s\n' 'baz' >"$TEST_GO_ROOTDIR/foo/bar/baz" | ||
run "$TEST_GO_SCRIPT" "$TEST_GO_ROOTDIR/foo" | ||
assert_success "$TEST_GO_ROOTDIR/foo/bar/baz" | ||
} | ||
|
||
@test "$SUITE: directory with several files" { | ||
mkdir -p "$TEST_GO_ROOTDIR/foo/"{bar,baz,quux} | ||
printf '%s\n' 'xyzzy' >"$TEST_GO_ROOTDIR/foo/bar/xyzzy" | ||
printf '%s\n' 'plugh' >"$TEST_GO_ROOTDIR/foo/baz/plugh" | ||
printf '%s\n' 'frobozz' >"$TEST_GO_ROOTDIR/foo/quux/frobozz" | ||
printf '%s\n' 'frotz' >"$TEST_GO_ROOTDIR/foo/frotz" | ||
run "$TEST_GO_SCRIPT" "$TEST_GO_ROOTDIR/foo" | ||
assert_success \ | ||
"$TEST_GO_ROOTDIR/foo/bar/xyzzy" \ | ||
"$TEST_GO_ROOTDIR/foo/baz/plugh" \ | ||
"$TEST_GO_ROOTDIR/foo/frotz" \ | ||
"$TEST_GO_ROOTDIR/foo/quux/frobozz" | ||
} |
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,135 @@ | ||
#! /usr/bin/env bats | ||
|
||
load ../environment | ||
|
||
setup() { | ||
test_filter | ||
@go.create_test_go_script \ | ||
'. "$_GO_USE_MODULES" "fileutil"' \ | ||
'@go.create_dirs "$@"' | ||
} | ||
|
||
teardown() { | ||
@go.remove_test_go_rootdir | ||
} | ||
|
||
assert_dirs_created() { | ||
set "$DISABLE_BATS_SHELL_OPTIONS" | ||
local result='0' | ||
local missing_dirs=() | ||
local dir | ||
|
||
for dir in "$@"; do | ||
if [[ ! -d "$TEST_GO_ROOTDIR/$dir" ]]; then | ||
missing_dirs+=("$dir") | ||
fi | ||
done | ||
|
||
if [[ "${#missing_dirs[@]}" -ne '0' ]]; then | ||
printf 'The following directories were not created:\n' >&2 | ||
printf ' %s\n' "${missing_dirs[@]}" >&2 | ||
result='1' | ||
fi | ||
restore_bats_shell_options "$result" | ||
} | ||
|
||
@test "$SUITE: empty directory list does nothing" { | ||
run "$TEST_GO_SCRIPT" | ||
assert_success '' | ||
} | ||
|
||
@test "$SUITE: empty directory creates FATAL error" { | ||
run "$TEST_GO_SCRIPT" '' | ||
assert_failure | ||
assert_line_matches '0' \ | ||
'FATAL.* The empty string is not a valid directory name' | ||
} | ||
|
||
@test "$SUITE: existing directory does nothing" { | ||
run "$TEST_GO_SCRIPT" "$TEST_GO_ROOTDIR" | ||
assert_success '' | ||
} | ||
|
||
@test "$SUITE: new single directory" { | ||
run "$TEST_GO_SCRIPT" "$TEST_GO_ROOTDIR/foo" | ||
assert_success '' | ||
assert_dirs_created 'foo' | ||
} | ||
|
||
@test "$SUITE: permissions set for all new directories" { | ||
skip_if_cannot_trigger_file_permission_failure | ||
local existing_parent="$TEST_GO_ROOTDIR/foo" | ||
mkdir -p "$existing_parent" | ||
chmod 700 "$existing_parent" | ||
|
||
run "$TEST_GO_SCRIPT" --mode 723 "$TEST_GO_ROOTDIR/foo/bar/baz" | ||
assert_success '' | ||
assert_dirs_created 'foo' | ||
|
||
run ls -ld "$TEST_GO_ROOTDIR/"{foo,foo/bar,foo/bar/baz} | ||
assert_success | ||
assert_lines_match '^drwx------' '^drwx-w--wx' '^drwx-w--wx' | ||
} | ||
|
||
@test "$SUITE: multiple calls are idempotent" { | ||
run "$TEST_GO_SCRIPT" "$TEST_GO_ROOTDIR/foo" | ||
assert_success '' | ||
assert_dirs_created 'foo' | ||
|
||
run "$TEST_GO_SCRIPT" "$TEST_GO_ROOTDIR/foo" | ||
assert_success '' | ||
} | ||
|
||
@test "$SUITE: permissions don't change if directory already exists" { | ||
mkdir -p "$TEST_GO_ROOTDIR/foo" | ||
chmod 700 "$TEST_GO_ROOTDIR/foo" | ||
|
||
run "$TEST_GO_SCRIPT" --mode 723 "$TEST_GO_ROOTDIR/foo" | ||
assert_success '' | ||
|
||
run ls -ld "$TEST_GO_ROOTDIR/foo" | ||
assert_success | ||
assert_output_matches '^drwx------' | ||
} | ||
|
||
@test "$SUITE: multiple directories" { | ||
local dirs=('foo' 'bar/baz' 'bar/quux' 'xyzzy/plugh/frobozz') | ||
|
||
run "$TEST_GO_SCRIPT" "${dirs[@]#$TEST_GO_ROOTDIR/}" | ||
assert_success '' | ||
assert_dirs_created "${dirs[@]}" | ||
} | ||
|
||
@test "$SUITE: existing non-directory creates a FATAL error" { | ||
mkdir -p "$TEST_GO_ROOTDIR/foo" | ||
printf 'bar\n' >"$TEST_GO_ROOTDIR/foo/bar" | ||
|
||
run "$TEST_GO_SCRIPT" "$TEST_GO_ROOTDIR/foo/bar/baz" | ||
assert_failure | ||
assert_line_matches '0' \ | ||
"FATAL.* $TEST_GO_ROOTDIR/foo/bar exists and is not a directory" | ||
} | ||
|
||
@test "$SUITE: mkdir failure is a FATAL error" { | ||
local existing_parent="$TEST_GO_ROOTDIR/foo" | ||
mkdir -p "$existing_parent" | ||
stub_program_in_path 'mkdir' 'exit 1' | ||
|
||
run "$TEST_GO_SCRIPT" "$TEST_GO_ROOTDIR/foo/bar/baz" | ||
restore_program_in_path 'mkdir' | ||
assert_failure | ||
assert_line_matches '0' \ | ||
"FATAL.* Could not create $TEST_GO_ROOTDIR/foo/bar/baz in $existing_parent" | ||
} | ||
|
||
@test "$SUITE: chmod failure is a FATAL error" { | ||
local existing_parent="$TEST_GO_ROOTDIR/foo" | ||
mkdir -p "$existing_parent" | ||
stub_program_in_path 'chmod' 'exit 1' | ||
|
||
run "$TEST_GO_SCRIPT" --mode 700 "$TEST_GO_ROOTDIR/foo/bar/baz" | ||
restore_program_in_path 'chmod' | ||
assert_failure | ||
assert_line_matches '0' \ | ||
"FATAL.* Could not set permissions for $existing_parent/bar" | ||
} |
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