mirror of
https://gitea.invidious.io/iv-org/shard-spectator.git
synced 2024-08-15 00:53:35 +00:00
Add type capturing arguments
This commit is contained in:
parent
d7f8c2b958
commit
de7cd90d11
2 changed files with 231 additions and 0 deletions
162
spec/spectator/mocks/arguments_spec.cr
Normal file
162
spec/spectator/mocks/arguments_spec.cr
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
require "../../spec_helper"
|
||||||
|
|
||||||
|
Spectator.describe Spectator::Arguments do
|
||||||
|
subject(arguments) do
|
||||||
|
Spectator::Arguments.new(
|
||||||
|
args: {42, "foo"},
|
||||||
|
kwargs: {bar: "baz", qux: 123}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "stores the arguments" do
|
||||||
|
expect(arguments.args).to eq({42, "foo"})
|
||||||
|
end
|
||||||
|
|
||||||
|
it "stores the keyword arguments" do
|
||||||
|
expect(arguments.kwargs).to eq({bar: "baz", qux: 123})
|
||||||
|
end
|
||||||
|
|
||||||
|
describe ".capture" do
|
||||||
|
subject { Spectator::Arguments.capture(42, "foo", bar: "baz", qux: 123) }
|
||||||
|
|
||||||
|
it "stores the arguments and keyword arguments" do
|
||||||
|
is_expected.to have_attributes(args: {42, "foo"}, kwargs: {bar: "baz", qux: 123})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#to_s" do
|
||||||
|
subject { arguments.to_s }
|
||||||
|
|
||||||
|
it "formats the arguments" do
|
||||||
|
is_expected.to eq("(42, \"foo\", bar: \"baz\", qux: 123)")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#==" do
|
||||||
|
subject { arguments == other }
|
||||||
|
|
||||||
|
context "with equal arguments" do
|
||||||
|
let(other) { arguments }
|
||||||
|
|
||||||
|
it "returns true" do
|
||||||
|
is_expected.to be_true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with different arguments" do
|
||||||
|
let(other) do
|
||||||
|
Spectator::Arguments.new(
|
||||||
|
args: {123, :foo, "bar"},
|
||||||
|
kwargs: {opt: "foobar"}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns false" do
|
||||||
|
is_expected.to be_false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with the same kwargs in a different order" do
|
||||||
|
let(other) do
|
||||||
|
Spectator::Arguments.new(
|
||||||
|
args: arguments.args,
|
||||||
|
kwargs: {qux: 123, bar: "baz"}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns true" do
|
||||||
|
is_expected.to be_true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with a missing kwarg" do
|
||||||
|
let(other) do
|
||||||
|
Spectator::Arguments.new(
|
||||||
|
args: arguments.args,
|
||||||
|
kwargs: {bar: "baz"}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns false" do
|
||||||
|
is_expected.to be_false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#===" do
|
||||||
|
subject { pattern === arguments }
|
||||||
|
|
||||||
|
context "with equal arguments" do
|
||||||
|
let(pattern) { arguments }
|
||||||
|
|
||||||
|
it "returns true" do
|
||||||
|
is_expected.to be_true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with different arguments" do
|
||||||
|
let(pattern) do
|
||||||
|
Spectator::Arguments.new(
|
||||||
|
args: {123, :foo, "bar"},
|
||||||
|
kwargs: {opt: "foobar"}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns false" do
|
||||||
|
is_expected.to be_false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with the same kwargs in a different order" do
|
||||||
|
let(pattern) do
|
||||||
|
Spectator::Arguments.new(
|
||||||
|
args: arguments.args,
|
||||||
|
kwargs: {qux: 123, bar: "baz"}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns true" do
|
||||||
|
is_expected.to be_true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with a missing kwarg" do
|
||||||
|
let(pattern) do
|
||||||
|
Spectator::Arguments.new(
|
||||||
|
args: arguments.args,
|
||||||
|
kwargs: {bar: "baz"}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns false" do
|
||||||
|
is_expected.to be_false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with matching types and regex" do
|
||||||
|
let(pattern) do
|
||||||
|
Spectator::Arguments.new(
|
||||||
|
args: {Int32, /foo/},
|
||||||
|
kwargs: {bar: String, qux: 123}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns true" do
|
||||||
|
is_expected.to be_true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with different types and regex" do
|
||||||
|
let(pattern) do
|
||||||
|
Spectator::Arguments.new(
|
||||||
|
args: {Symbol, /bar/},
|
||||||
|
kwargs: {bar: String, qux: 42}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns false" do
|
||||||
|
is_expected.to be_false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
69
src/spectator/mocks/arguments.cr
Normal file
69
src/spectator/mocks/arguments.cr
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
module Spectator
|
||||||
|
# Arguments used in a method call.
|
||||||
|
#
|
||||||
|
# Can also be used to match arguments.
|
||||||
|
# *T* must be a `Tuple` type representing the positional arguments.
|
||||||
|
# *NT* must be a `NamedTuple` type representing the keyword arguments.
|
||||||
|
class Arguments(T, NT)
|
||||||
|
# Positional arguments.
|
||||||
|
getter args : T
|
||||||
|
|
||||||
|
# Keyword arguments.
|
||||||
|
getter kwargs : NT
|
||||||
|
|
||||||
|
# Creates arguments used in a method call.
|
||||||
|
def initialize(@args : T, @kwargs : NT)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Constructs an instance from literal arguments.
|
||||||
|
def self.capture(*args, **kwargs) : self
|
||||||
|
new(args, kwargs)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Constructs a string representation of the arguments.
|
||||||
|
def to_s(io : IO) : Nil
|
||||||
|
io << '('
|
||||||
|
|
||||||
|
# Add the positional arguments.
|
||||||
|
args.each_with_index do |arg, i|
|
||||||
|
io << ", " if i > 0
|
||||||
|
arg.inspect(io)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Add the keyword arguments.
|
||||||
|
size = args.size + kwargs.size
|
||||||
|
kwargs.each_with_index(args.size) do |k, v, i|
|
||||||
|
io << ", " if 0 < i < size
|
||||||
|
io << k << ": "
|
||||||
|
v.inspect(io)
|
||||||
|
end
|
||||||
|
|
||||||
|
io << ')'
|
||||||
|
end
|
||||||
|
|
||||||
|
# Checks if this set of arguments and another are equal.
|
||||||
|
def ==(other : Arguments)
|
||||||
|
args == other.args && kwargs == other.kwargs
|
||||||
|
end
|
||||||
|
|
||||||
|
# Checks if another set of arguments matches this set of arguments.
|
||||||
|
def ===(other : Arguments)
|
||||||
|
args === other.args && named_tuples_match?(kwargs, other.kwargs)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Checks if two named tuples match.
|
||||||
|
#
|
||||||
|
# Uses case equality (`===`) on every key-value pair.
|
||||||
|
# NamedTuple doesn't have a `===` operator, even though Tuple does.
|
||||||
|
private def named_tuples_match?(a : NamedTuple, b : NamedTuple)
|
||||||
|
return false if a.size != b.size
|
||||||
|
|
||||||
|
a.each do |k, v|
|
||||||
|
return false unless b.has_key?(k)
|
||||||
|
return false unless v === b[k]
|
||||||
|
end
|
||||||
|
|
||||||
|
true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue