Allow metadata to be stored as nil

This commit is contained in:
Michael Miller 2022-11-29 23:22:42 -07:00
parent fbe877690d
commit 275b217c6c
No known key found for this signature in database
GPG key ID: 32B47AE8F388A1FF
13 changed files with 41 additions and 24 deletions

View file

@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- Simplify string representation of mock-related types.
- Remove unnecessary redefinitions of methods when adding stub functionality to a type.
- Allow metadata to be stored as nil to reduce overhead when tracking nodes without tags.
## [0.11.4]
### Added

View file

@ -6,6 +6,9 @@ module Spectator::DSL
private macro _spectator_metadata(name, source, *tags, **metadata)
private def self.{{name.id}}
%metadata = {{source.id}}.dup
{% unless tags.empty? && metadata.empty? %}
%metadata ||= ::Spectator::Metadata.new
{% end %}
{% for k in tags %}
%metadata[{{k.id.symbolize}}] = nil
{% end %}

View file

@ -40,7 +40,7 @@ module Spectator
# Note: The metadata will not be merged with the parent metadata.
def initialize(@context : Context, @entrypoint : self ->,
name : String? = nil, location : Location? = nil,
@group : ExampleGroup? = nil, metadata = Metadata.new)
@group : ExampleGroup? = nil, metadata = nil)
super(name, location, metadata)
# Ensure group is linked.
@ -58,7 +58,7 @@ module Spectator
# Note: The metadata will not be merged with the parent metadata.
def initialize(@context : Context, @entrypoint : self ->,
@name_proc : Example -> String, location : Location? = nil,
@group : ExampleGroup? = nil, metadata = Metadata.new)
@group : ExampleGroup? = nil, metadata = nil)
super(nil, location, metadata)
# Ensure group is linked.
@ -75,7 +75,7 @@ module Spectator
# A set of *metadata* can be used for filtering and modifying example behavior.
# Note: The metadata will not be merged with the parent metadata.
def initialize(name : String? = nil, location : Location? = nil,
@group : ExampleGroup? = nil, metadata = Metadata.new, &block : self ->)
@group : ExampleGroup? = nil, metadata = nil, &block : self ->)
super(name, location, metadata)
@context = NullContext.new
@ -93,9 +93,10 @@ module Spectator
# A set of *metadata* can be used for filtering and modifying example behavior.
# Note: The metadata will not be merged with the parent metadata.
def self.pending(name : String? = nil, location : Location? = nil,
group : ExampleGroup? = nil, metadata = Metadata.new, reason = nil)
group : ExampleGroup? = nil, metadata = nil, reason = nil)
# Add pending tag and reason if they don't exist.
metadata = metadata.merge({:pending => nil, :reason => reason}) { |_, v, _| v }
tags = {:pending => nil, :reason => reason}
metadata = metadata ? metadata.merge(tags) { |_, v, _| v } : tags
new(name, location, group, metadata) { nil }
end

View file

@ -15,7 +15,7 @@ module Spectator
# The *entrypoint* indicates the proc used to invoke the test code in the example.
# The *name*, *location*, and *metadata* will be applied to the `Example` produced by `#build`.
def initialize(@context_builder : -> Context, @entrypoint : Example ->,
@name : String? = nil, @location : Location? = nil, @metadata : Metadata = Metadata.new)
@name : String? = nil, @location : Location? = nil, @metadata : Metadata? = nil)
end
# Creates the builder.
@ -24,7 +24,7 @@ module Spectator
# The *name* is an interpolated string that runs in the context of the example.
# *location*, and *metadata* will be applied to the `Example` produced by `#build`.
def initialize(@context_builder : -> Context, @entrypoint : Example ->,
@name : Example -> String, @location : Location? = nil, @metadata : Metadata = Metadata.new)
@name : Example -> String, @location : Location? = nil, @metadata : Metadata? = nil)
end
# Constructs an example with previously defined attributes and context.

View file

@ -79,7 +79,7 @@ module Spectator
# This group will be assigned to the parent *group* if it is provided.
# A set of *metadata* can be used for filtering and modifying example behavior.
def initialize(@name : Label = nil, @location : Location? = nil,
@group : ExampleGroup? = nil, @metadata : Metadata = Metadata.new)
@group : ExampleGroup? = nil, @metadata : Metadata? = nil)
# Ensure group is linked.
group << self if group
end

View file

@ -28,7 +28,7 @@ module Spectator
# Creates the builder.
# Initially, the builder will have no children and no hooks.
# The *name*, *location*, and *metadata* will be applied to the `ExampleGroup` produced by `#build`.
def initialize(@name : Label = nil, @location : Location? = nil, @metadata : Metadata = Metadata.new)
def initialize(@name : Label = nil, @location : Location? = nil, @metadata : Metadata? = nil)
end
# Constructs an example group with previously defined attributes, children, and hooks.

View file

@ -18,7 +18,7 @@ module Spectator
# This group will be assigned to the parent *group* if it is provided.
# A set of *metadata* can be used for filtering and modifying example behavior.
def initialize(@item : T, name : Label = nil, location : Location? = nil,
group : ExampleGroup? = nil, metadata : Metadata = Metadata.new)
group : ExampleGroup? = nil, metadata : Metadata? = nil)
super(name, location, group, metadata)
end
end

View file

@ -15,7 +15,7 @@ module Spectator
# The *collection* is the set of items to create sub-nodes for.
# The *iterators* is a list of optional names given to items in the collection.
def initialize(@collection : Enumerable(T), name : String? = nil, @iterators : Array(String) = [] of String,
location : Location? = nil, metadata : Metadata = Metadata.new)
location : Location? = nil, metadata : Metadata? = nil)
super(name, location, metadata)
end

View file

@ -30,14 +30,16 @@ module Spectator
end
# User-defined tags and values used for filtering and behavior modification.
getter metadata : Metadata
def metadata : Metadata
@metadata ||= Metadata.new
end
# Creates the node.
# The *name* describes the purpose of the node.
# It can be a `Symbol` to describe a type.
# The *location* tracks where the node exists in source code.
# A set of *metadata* can be used for filtering and modifying example behavior.
def initialize(@name : Label = nil, @location : Location? = nil, @metadata : Metadata = Metadata.new)
def initialize(@name : Label = nil, @location : Location? = nil, @metadata : Metadata? = nil)
end
# Indicates whether the node has completed.
@ -46,17 +48,25 @@ module Spectator
# Checks if the node has been marked as pending.
# Pending items should be skipped during execution.
def pending?
metadata.has_key?(:pending) || metadata.has_key?(:skip)
return false unless md = @metadata
md.has_key?(:pending) || md.has_key?(:skip)
end
# Gets the reason the node has been marked as pending.
def pending_reason
metadata[:pending]? || metadata[:skip]? || metadata[:reason]? || DEFAULT_PENDING_REASON
return DEFAULT_PENDING_REASON unless md = @metadata
md[:pending]? || md[:skip]? || md[:reason]? || DEFAULT_PENDING_REASON
end
# Retrieves just the tag names applied to the node.
def tags
Tags.new(metadata.keys)
if md = @metadata
Tags.new(md.keys)
else
Tags.new
end
end
# Non-nil name used to show the node name.

View file

@ -11,7 +11,7 @@ module Spectator
# The *name*, *location*, and *metadata* will be applied to the `Example` produced by `#build`.
# A default *reason* can be given in case the user didn't provide one.
def initialize(@name : String? = nil, @location : Location? = nil,
@metadata : Metadata = Metadata.new, @reason : String? = nil)
@metadata : Metadata? = nil, @reason : String? = nil)
end
# Constructs an example with previously defined attributes.

View file

@ -60,7 +60,7 @@ module Spectator
#
# A set of *metadata* can be used for filtering and modifying example behavior.
# For instance, adding a "pending" tag will mark tests as pending and skip execution.
def start_group(name, location = nil, metadata = Metadata.new) : Nil
def start_group(name, location = nil, metadata = nil) : Nil
Log.trace { "Start group: #{name.inspect} @ #{location}; metadata: #{metadata}" }
builder = ExampleGroupBuilder.new(name, location, metadata)
@ -86,7 +86,7 @@ module Spectator
#
# A set of *metadata* can be used for filtering and modifying example behavior.
# For instance, adding a "pending" tag will mark tests as pending and skip execution.
def start_iterative_group(collection, name, iterator = nil, location = nil, metadata = Metadata.new) : Nil
def start_iterative_group(collection, name, iterator = nil, location = nil, metadata = nil) : Nil
Log.trace { "Start iterative group: #{name} (#{typeof(collection)}) @ #{location}; metadata: #{metadata}" }
builder = IterativeExampleGroupBuilder.new(collection, name, iterator, location, metadata)
@ -127,7 +127,7 @@ module Spectator
# It will be yielded two arguments - the example created by this method, and the *context* argument.
# The return value of the block is ignored.
# It is expected that the test code runs when the block is called.
def add_example(name, location, context_builder, metadata = Metadata.new, &block : Example -> _) : Nil
def add_example(name, location, context_builder, metadata = nil, &block : Example -> _) : Nil
Log.trace { "Add example: #{name} @ #{location}; metadata: #{metadata}" }
current << ExampleBuilder.new(context_builder, block, name, location, metadata)
end
@ -144,7 +144,7 @@ module Spectator
# A set of *metadata* can be used for filtering and modifying example behavior.
# For instance, adding a "pending" tag will mark the test as pending and skip execution.
# A default *reason* can be given in case the user didn't provide one.
def add_pending_example(name, location, metadata = Metadata.new, reason = nil) : Nil
def add_pending_example(name, location, metadata = nil, reason = nil) : Nil
Log.trace { "Add pending example: #{name} @ #{location}; metadata: #{metadata}" }
current << PendingExampleBuilder.new(name, location, metadata, reason)
end

View file

@ -10,7 +10,9 @@ module Spectator
# Checks whether the node satisfies the filter.
def includes?(node) : Bool
node.metadata.any? { |key, value| key.to_s == @tag && (!@value || value == @value) }
return false unless metadata = node.metadata
metadata.any? { |key, value| key.to_s == @tag && (!@value || value == @value) }
end
end
end

View file

@ -34,7 +34,7 @@ class SpectatorTestContext < SpectatorContext
# Initial metadata for tests.
# This method should be overridden by example groups and examples.
private def self.metadata
::Spectator::Metadata.new
private def self.metadata : ::Spectator::Metadata?
nil
end
end