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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## Unreleased

### Added
- `assert_within_delta <expected> <actual> <delta>` asserts a number is within a tolerance of another; supports floats (#744)
- `assert_array_length <n> <array>` asserts an array has exactly `n` elements (#743)
- `assert_file_permissions <mode> <file>` asserts a file's octal permission mode (`644`, `0755`); portable across GNU/BSD `stat` (#742)

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

## assert_within_delta
> `assert_within_delta "expected" "actual" "delta"`

Reports an error if `actual` is not within `delta` of `expected`
(i.e. `|actual - expected| > delta`). Supports floating-point values.
Useful for timing or measured values where exact equality is too strict.

::: code-group
```bash [Example]
function test_success() {
assert_within_delta "3.14159" "3.14" "0.01"
}

function test_failure() {
assert_within_delta "100" "105" "3"
}
```
:::

## assert_date_equals
> `assert_date_equals "expected" "actual"`

Expand Down
55 changes: 55 additions & 0 deletions src/assert.sh
Original file line number Diff line number Diff line change
Expand Up @@ -792,6 +792,61 @@ function assert_greater_or_equal_than() {
bashunit::state::add_assertions_passed
}

##
# Whether a value looks like a number (integer or decimal, optional sign).
# Returns: 0 when numeric, 1 otherwise.
##
function bashunit::assert::_is_numeric() {
local value="$1"
case "$value" in
'' | *[!0-9.+-]*) return 1 ;;
esac
# Must contain at least one digit (rejects ".", "-", "+").
case "$value" in
*[0-9]*) return 0 ;;
esac
return 1
}

##
# Asserts the actual value is within +/- delta of the expected value:
# |actual - expected| <= delta. Supports floats via bashunit::math::calculate.
# Arguments: $1 - expected, $2 - actual, $3 - delta
##
function assert_within_delta() {
bashunit::assert::should_skip && return 0

local expected="$1"
local actual="$2"
local delta="$3"
local label
label="$(bashunit::assert::label)"

if ! bashunit::assert::_is_numeric "$expected" ||
! bashunit::assert::_is_numeric "$actual" ||
! bashunit::assert::_is_numeric "$delta"; then
bashunit::assert::mark_failed
bashunit::console_results::print_failed_test \
"${label}" "${expected} ${actual} ${delta}" "to all be numeric" "but got a non-numeric value"
return
fi

local diff
diff="$(bashunit::math::calculate "$expected - $actual")"
case "$diff" in
-*) diff="${diff#-}" ;;
esac

if [ "$(bashunit::math::calculate "$diff <= $delta")" != "1" ]; then
bashunit::assert::mark_failed
bashunit::console_results::print_failed_test \
"${label}" "${actual}" "to be within ${delta} of" "${expected}"
return
fi

bashunit::state::add_assertions_passed
}

function assert_line_count() {
bashunit::assert::should_skip && return 0
local IFS=$' \t\n'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,15 @@ Reports an error if `actual` is not greater than or equal to `expected`.
- assert_less_or_equal_than is the inverse of this assertion and takes the same arguments.


## assert_within_delta
--------------
> `assert_within_delta "expected" "actual" "delta"`

Reports an error if `actual` is not within `delta` of `expected`
(i.e. `|actual - expected| > delta`). Supports floating-point values.
Useful for timing or measured values where exact equality is too strict.


## assert_date_equals
--------------
> `assert_date_equals "expected" "actual"`
Expand Down
27 changes: 27 additions & 0 deletions tests/unit/assert_numeric_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,30 @@ function test_unsuccessful_assert_greater_or_equal_than() {
"Unsuccessful assert greater or equal than" "1" "to be greater or equal than" "3")" \
"$(assert_greater_or_equal_than "3" "1")"
}

function test_successful_assert_within_delta() {
assert_empty "$(assert_within_delta "3.14159" "3.14" "0.01")"
}

function test_successful_assert_within_delta_with_a_negative_difference() {
assert_empty "$(assert_within_delta "100" "105" "10")"
}

function test_successful_assert_within_delta_when_equal() {
assert_empty "$(assert_within_delta "42" "42" "0")"
}

function test_unsuccessful_assert_within_delta() {
assert_same \
"$(bashunit::console_results::print_failed_test \
"Unsuccessful assert within delta" "105" "to be within 3 of" "100")" \
"$(assert_within_delta "100" "105" "3")"
}

function test_unsuccessful_assert_within_delta_with_a_non_numeric_value() {
assert_same \
"$(bashunit::console_results::print_failed_test \
"Unsuccessful assert within delta with a non numeric value" \
"abc 105 3" "to all be numeric" "but got a non-numeric value")" \
"$(assert_within_delta "abc" "105" "3")"
}
Loading