Compare commits

...

9 Commits

Author SHA1 Message Date
Michael Miller 287758e6af
Update README to point at v0.12.0 2024-02-03 08:05:02 -07:00
Michael Miller f39ceb8eba
Release v0.12.0 2024-02-03 07:56:57 -07:00
Michael Miller 9b1d400ee1
Update CHANGELOG 2024-01-27 11:29:11 -07:00
Michael Miller edb20e5b2f
Additional handling when comparing ranges against unexpected types 2024-01-27 11:25:59 -07:00
Michael Miller 526a998e41
Shorten compare_values case statements 2024-01-27 11:25:25 -07:00
Michael Miller 556d4783bf
Support case equality of tuples, arrays, named tuples, and hashes in stub argument matching 2024-01-27 11:18:10 -07:00
Michael Miller b5fbc96195
Allow matchers to be used in case equality 2024-01-27 11:17:19 -07:00
Michael Miller 5520999b6d
Add spec for GitHub issue 55
https://github.com/icy-arctic-fox/spectator/issues/55
2024-01-27 11:16:57 -07:00
Michael Miller 4a630b1ebf
Bump version to v0.11.7 2023-10-16 17:34:49 -06:00
7 changed files with 107 additions and 13 deletions

View File

@ -4,7 +4,15 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
## [0.12.0] - 2024-02-03
### Added
- Added ability to use matchers for case equality. [#55](https://github.com/icy-arctic-fox/spectator/issues/55)
- Added support for nested case equality when checking arguments with Array, Tuple, Hash, and NamedTuple.
### Fixed
- Fixed some issues with the `be_within` matcher when used with expected and union types.
## [0.11.7] - 2023-10-16
### Fixed
- Fix memoized value (`let`) with a union type causing segfault. [#81](https://gitlab.com/arctic-fox/spectator/-/issues/81)
@ -450,7 +458,9 @@ This has been changed so that it compiles and raises an error at runtime with a
First version ready for public use.
[Unreleased]: https://gitlab.com/arctic-fox/spectator/-/compare/v0.11.6...master
[Unreleased]: https://gitlab.com/arctic-fox/spectator/-/compare/v0.12.0...master
[0.12.0]: https://gitlab.com/arctic-fox/spectator/-/compare/v0.11.7...v0.12.0
[0.11.7]: https://gitlab.com/arctic-fox/spectator/-/compare/v0.11.6...v0.11.7
[0.11.6]: https://gitlab.com/arctic-fox/spectator/-/compare/v0.11.5...v0.11.6
[0.11.5]: https://gitlab.com/arctic-fox/spectator/-/compare/v0.11.4...v0.11.5
[0.11.4]: https://gitlab.com/arctic-fox/spectator/-/compare/v0.11.3...v0.11.4

View File

@ -25,7 +25,7 @@ Add this to your application's `shard.yml`:
development_dependencies:
spectator:
gitlab: arctic-fox/spectator
version: ~> 0.11.0
version: ~> 0.12.0
```
Usage

View File

@ -1,5 +1,5 @@
name: spectator
version: 0.11.6
version: 0.12.0
description: |
Feature-rich testing framework for Crystal inspired by RSpec.

View File

@ -0,0 +1,48 @@
require "../spec_helper"
Spectator.describe "GitHub Issue #55" do
GROUP_NAME = "CallCenter"
let(name) { "TimeTravel" }
let(source) { "my.time.travel.experiment" }
class Analytics(T)
property start_time = Time.local
property end_time = Time.local
def initialize(@brain_talker : T)
end
def instrument(*, name, source, &)
@brain_talker.send(payload: {
:group => GROUP_NAME,
:name => name,
:source => source,
:start => start_time,
:end => end_time,
}, action: "analytics")
end
end
double(:brain_talker, send: nil)
let(brain_talker) { double(:brain_talker) }
let(analytics) { Analytics.new(brain_talker) }
it "tracks the time it takes to run the block" do
analytics.start_time = expected_start_time = Time.local
expected_end_time = expected_start_time + 10.seconds
analytics.end_time = expected_end_time + 0.5.seconds # Offset to ensure non-exact match.
analytics.instrument(name: name, source: source) do
end
expect(brain_talker).to have_received(:send).with(payload: {
:group => GROUP_NAME,
:name => name,
:source => source,
:start => expected_start_time,
:end => be_within(1.second).of(expected_end_time),
}, action: "analytics")
end
end

View File

@ -1,3 +1,4 @@
require "../value"
require "./match_data"
module Spectator::Matchers
@ -22,6 +23,19 @@ module Spectator::Matchers
# A successful match with `#match` should normally fail for this method, and vice-versa.
abstract def negated_match(actual : Expression(T)) : MatchData forall T
# Compares a matcher against a value.
# Enables composable matchers.
def ===(actual : Expression(T)) : Bool
match(actual).matched?
end
# Compares a matcher against a value.
# Enables composable matchers.
def ===(other) : Bool
expression = Value.new(other)
match(expression).matched?
end
private def match_data_description(actual : Expression(T)) : String forall T
match_data_description(actual.label)
end

View File

@ -29,7 +29,26 @@ module Spectator::Matchers
# Checks whether the matcher is satisfied with the expression given to it.
private def match?(actual : Expression(T)) : Bool forall T
expected.value.includes?(actual.value)
actual_value = actual.value
expected_value = expected.value
if expected_value.is_a?(Range) && actual_value.is_a?(Comparable)
return match_impl?(expected_value, actual_value)
end
return false unless actual_value.is_a?(Comparable(typeof(expected_value.begin)))
expected_value.includes?(actual_value)
end
private def match_impl?(expected_value : Range(B, E), actual_value : Comparable(B)) : Bool forall B, E
expected_value.includes?(actual_value)
end
private def match_impl?(expected_value : Range(B, E), actual_value : T) : Bool forall B, E, T
return false unless actual_value.is_a?(B) || actual_value.is_a?(Comparable(B))
expected_value.includes?(actual_value)
end
private def match_impl?(expected_value : Range(Number, Number), actual_value : Number) : Bool
expected_value.includes?(actual_value)
end
# Message displayed when the matcher isn't satisfied.

View File

@ -7,7 +7,7 @@ module Spectator
end
# Utility method for comparing two tuples considering special types.
private def compare_tuples(a : Tuple, b : Tuple)
private def compare_tuples(a : Tuple | Array, b : Tuple | Array)
return false if a.size != b.size
a.zip(b) do |a_value, b_value|
@ -18,14 +18,14 @@ module Spectator
# Utility method for comparing two tuples considering special types.
# Supports nilable tuples (ideal for splats).
private def compare_tuples(a : Tuple?, b : Tuple?)
private def compare_tuples(a : Tuple? | Array?, b : Tuple? | Array?)
return false if a.nil? ^ b.nil?
compare_tuples(a.not_nil!, b.not_nil!)
end
# Utility method for comparing two named tuples ignoring order.
private def compare_named_tuples(a : NamedTuple, b : NamedTuple)
private def compare_named_tuples(a : NamedTuple | Hash, b : NamedTuple | Hash)
a.each do |k, v1|
v2 = b.fetch(k) { return false }
return false unless compare_values(v1, v2)
@ -45,11 +45,14 @@ module Spectator
when Range
# Ranges can only be matched against if their right side is comparable.
# Ensure the right side is comparable, otherwise compare directly.
if b.is_a?(Comparable(typeof(b)))
a === b
else
a == b
end
return a === b if b.is_a?(Comparable(typeof(b)))
a == b
when Tuple, Array
return compare_tuples(a, b) if b.is_a?(Tuple) || b.is_a?(Array)
a === b
when NamedTuple, Hash
return compare_named_tuples(a, b) if b.is_a?(NamedTuple) || b.is_a?(Hash)
a === b
else
a === b
end