Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## Unreleased

### Added
- `assert_file_permissions <mode> <file>` asserts a file's octal permission mode (`644`, `0755`); portable across GNU/BSD `stat` (#742)

### Fixed
- `watch` subcommand failed with `bashunit::watch::run: command not found` in the released binary because `src/watch.sh` was missing from the build; it is now bundled (#735)

Expand Down
27 changes: 27 additions & 0 deletions docs/assertions.md
Original file line number Diff line number Diff line change
Expand Up @@ -830,6 +830,33 @@ function test_failure() {
```
:::

## assert_file_permissions
> `assert_file_permissions "mode" "file"`

Reports an error if `file` does not have the expected octal permission `mode`
(e.g. `644`, `0755`). A leading zero is optional (`0755` and `755` are equal).
Works on both Linux (GNU `stat`) and macOS (BSD `stat`).

::: code-group
```bash [Example]
function test_success() {
local file="/tmp/file-path.txt"
touch "$file"
chmod 600 "$file"

assert_file_permissions "600" "$file"
}

function test_failure() {
local file="/tmp/file-path.txt"
touch "$file"
chmod 644 "$file"

assert_file_permissions "600" "$file"
}
```
:::

## assert_is_file
> `assert_is_file "file"`

Expand Down
51 changes: 51 additions & 0 deletions src/assert_files.sh
Original file line number Diff line number Diff line change
Expand Up @@ -149,3 +149,54 @@ function assert_file_not_contains() {

bashunit::state::add_assertions_passed
}

##
# Normalizes an octal file mode to its decimal value, dropping leading zeros
# (so "0755" and "755" compare equal). Echoes nothing on invalid octal input.
# Arguments: $1 - octal mode string
##
function bashunit::assert::_octal_to_decimal() {
local mode="$1"
case "$mode" in
'' | *[!0-7]*) return 1 ;;
esac
printf '%d' "$((8#$mode))"
}

##
# Asserts a file has the expected octal permission mode (e.g. "644", "0755").
# Arguments: $1 - expected octal mode, $2 - file path
##
function assert_file_permissions() {
bashunit::assert::should_skip && return 0

local expected="$1"
local file="$2"
local test_fn
test_fn="$(bashunit::helper::find_test_function_name)"
local label
label="$(bashunit::helper::normalize_test_function_name "$test_fn")"

if [ ! -e "$file" ]; then
bashunit::assert::mark_failed
bashunit::console_results::print_failed_test \
"${label}" "${file}" "to have permissions ${expected}" "but the file does not exist"
return
fi

local actual
actual="$(stat -c '%a' "$file" 2>/dev/null || stat -f '%Lp' "$file" 2>/dev/null)"

local expected_dec actual_dec
expected_dec="$(bashunit::assert::_octal_to_decimal "$expected")"
actual_dec="$(bashunit::assert::_octal_to_decimal "$actual")"

if [ "$expected_dec" != "$actual_dec" ]; then
bashunit::assert::mark_failed
bashunit::console_results::print_failed_test \
"${label}" "${file}" "to have permissions ${expected}" "but got ${actual}"
return
fi

bashunit::state::add_assertions_passed
}
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,15 @@ Reports an error if `file` does not contains the search string.
- assert_file_not_contains is the inverse of this assertion and takes the same arguments.


## assert_file_permissions
--------------
> `assert_file_permissions "mode" "file"`

Reports an error if `file` does not have the expected octal permission `mode`
(e.g. `644`, `0755`). A leading zero is optional (`0755` and `755` are equal).
Works on both Linux (GNU `stat`) and macOS (BSD `stat`).


## assert_is_file
--------------
> `assert_is_file "file"`
Expand Down Expand Up @@ -589,3 +598,5 @@ Reports an error if `command` completes in `threshold_ms` milliseconds or less.

Unambiguously reports an error message. Useful for reporting specific message
when testing situations not covered by any `assert_*` functions.


59 changes: 59 additions & 0 deletions tests/unit/file_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -190,3 +190,62 @@ function test_fails_assert_file_not_contains() {

rm "$file"
}

# shellcheck disable=SC2155
function test_successful_assert_file_permissions() {
# Windows (Git Bash) fakes POSIX permissions, so chmod/stat are unreliable there.
if bashunit::check_os::is_windows; then
bashunit::skip && return
fi

local file="/tmp/test_successful_assert_file_permissions_$$"
touch "$file"
chmod 600 "$file"

assert_empty "$(assert_file_permissions "600" "$file")"

rm "$file"
}

# shellcheck disable=SC2155
function test_successful_assert_file_permissions_accepts_leading_zero() {
if bashunit::check_os::is_windows; then
bashunit::skip && return
fi

local file="/tmp/test_assert_file_permissions_leading_zero_$$"
touch "$file"
chmod 755 "$file"

assert_empty "$(assert_file_permissions "0755" "$file")"

rm "$file"
}

# shellcheck disable=SC2155
function test_unsuccessful_assert_file_permissions() {
if bashunit::check_os::is_windows; then
bashunit::skip && return
fi

local file="/tmp/test_unsuccessful_assert_file_permissions_$$"
touch "$file"
chmod 600 "$file"

assert_same \
"$(bashunit::console_results::print_failed_test \
"Unsuccessful assert file permissions" "$file" "to have permissions 644" "but got 600")" \
"$(assert_file_permissions "644" "$file")"

rm "$file"
}

function test_unsuccessful_assert_file_permissions_when_file_does_not_exist() {
local file="a_random_file_that_will_not_exist"

assert_same \
"$(bashunit::console_results::print_failed_test \
"Unsuccessful assert file permissions when file does not exist" \
"$file" "to have permissions 644" "but the file does not exist")" \
"$(assert_file_permissions "644" "$file")"
}
Loading