Remove circular dependency with Node and ExampleGroup

This commit is contained in:
Michael Miller 2021-05-07 21:04:17 -06:00
parent 6bea36d8b6
commit e47e625016
No known key found for this signature in database
GPG key ID: F9A0C5C65B162436
3 changed files with 76 additions and 37 deletions

View file

@ -13,6 +13,14 @@ module Spectator
# Currently running example. # Currently running example.
class_getter! current : Example class_getter! current : Example
# Group the node belongs to.
getter! group : ExampleGroup
# Assigns the node to the specified *group*.
# This is an internal method and should only be called from `ExampleGroup`.
# `ExampleGroup` manages the association of nodes to groups.
protected setter group : ExampleGroup?
# Indicates whether the example already ran. # Indicates whether the example already ran.
getter? finished : Bool = false getter? finished : Bool = false
@ -33,8 +41,11 @@ module Spectator
# Note: The tags will not be merged with the parent tags. # Note: The tags will not be merged with the parent tags.
def initialize(@context : Context, @entrypoint : self ->, def initialize(@context : Context, @entrypoint : self ->,
name : String? = nil, location : Location? = nil, name : String? = nil, location : Location? = nil,
group : ExampleGroup? = nil, tags = Tags.new) @group : ExampleGroup? = nil, tags = Tags.new)
super(name, location, group, tags) super(name, location, tags)
# Ensure group is linked.
group << self if group
end end
# Creates a dynamic example. # Creates a dynamic example.
@ -48,9 +59,13 @@ module Spectator
# Note: The tags will not be merged with the parent tags. # Note: The tags will not be merged with the parent tags.
def initialize(name : String? = nil, location : Location? = nil, group : ExampleGroup? = nil, def initialize(name : String? = nil, location : Location? = nil, group : ExampleGroup? = nil,
tags = Tags.new, &block : self ->) tags = Tags.new, &block : self ->)
super(name, location, group, tags) super(name, location, tags)
@context = NullContext.new @context = NullContext.new
@entrypoint = block @entrypoint = block
# Ensure group is linked.
group << self if group
end end
# Executes the test case. # Executes the test case.
@ -87,10 +102,10 @@ module Spectator
end end
private def run_internal private def run_internal
group?.try(&.call_before_each(self)) @group.try(&.call_before_each(self))
@entrypoint.call(self) @entrypoint.call(self)
@finished = true @finished = true
group?.try(&.call_after_each(self)) @group.try(&.call_after_each(self))
end end
# Executes code within the example's test context. # Executes code within the example's test context.
@ -126,11 +141,21 @@ module Spectator
# Constructs the full name or description of the example. # Constructs the full name or description of the example.
# This prepends names of groups this example is part of. # This prepends names of groups this example is part of.
def to_s(io) def to_s(io)
if name? name = @name
super
else # Prefix with group's full name if the node belongs to a group.
io << "<anonymous>" if (group = @group)
group.to_s(io)
# Add padding between the node names
# only if the names don't appear to be symbolic.
# Skip blank group names (like the root group).
io << ' ' unless !group.name? || # ameba:disable Style/NegatedConditionsInUnless
(group.name?.is_a?(Symbol) && name.is_a?(String) &&
(name.starts_with?('#') || name.starts_with?('.')))
end end
super
end end
# Exposes information about the example useful for debugging. # Exposes information about the example useful for debugging.

View file

@ -11,6 +11,14 @@ module Spectator
@nodes = [] of Node @nodes = [] of Node
# Parent group this group belongs to.
getter! group : ExampleGroup
# Assigns this group to the specified *group*.
# This is an internal method and should only be called from `ExampleGroup`.
# `ExampleGroup` manages the association of nodes to groups.
protected setter group : ExampleGroup?
group_event before_all do |hooks| group_event before_all do |hooks|
Log.trace { "Processing before_all hooks for #{self}" } Log.trace { "Processing before_all hooks for #{self}" }
@ -63,6 +71,18 @@ module Spectator
end end
end end
# Creates the example group.
# The *name* describes the purpose of the group.
# It can be a `Symbol` to describe a type.
# The *location* tracks where the group exists in source code.
# This group will be assigned to the parent *group* if it is provided.
# A set of *tags* can be used for filtering and modifying example behavior.
def initialize(@name : Label = nil, @location : Location? = nil,
@group : ExampleGroup? = nil, @tags : Tags = Tags.new)
# Ensure group is linked.
group << self if group
end
# Removes the specified *node* from the group. # Removes the specified *node* from the group.
# The node will be unassigned from this group. # The node will be unassigned from this group.
def delete(node : Node) def delete(node : Node)
@ -88,6 +108,26 @@ module Spectator
@nodes.all?(&.finished?) @nodes.all?(&.finished?)
end end
# Constructs the full name or description of the example group.
# This prepends names of groups this group is part of.
def to_s(io)
name = @name
# Prefix with group's full name if the node belongs to a group.
if (group = @group)
group.to_s(io)
# Add padding between the node names
# only if the names don't appear to be symbolic.
# Skip blank group names (like the root group).
io << ' ' unless !group.name? || # ameba:disable Style/NegatedConditionsInUnless
(group.name?.is_a?(Symbol) && name.is_a?(String) &&
(name.starts_with?('#') || name.starts_with?('.')))
end
super
end
# Adds the specified *node* to the group. # Adds the specified *node* to the group.
# Assigns the node to this group. # Assigns the node to this group.
# If the node already belongs to a group, # If the node already belongs to a group,

View file

@ -26,27 +26,15 @@ module Spectator
protected def name=(@name : String) protected def name=(@name : String)
end end
# Group the node belongs to.
getter! group : ExampleGroup
# User-defined keywords used for filtering and behavior modification. # User-defined keywords used for filtering and behavior modification.
getter tags : Tags getter tags : Tags
# Assigns the node to the specified *group*.
# This is an internal method and should only be called from `ExampleGroup`.
# `ExampleGroup` manages the association of nodes to groups.
protected setter group : ExampleGroup?
# Creates the node. # Creates the node.
# The *name* describes the purpose of the node. # The *name* describes the purpose of the node.
# It can be a `Symbol` to describe a type. # It can be a `Symbol` to describe a type.
# The *location* tracks where the node exists in source code. # The *location* tracks where the node exists in source code.
# The node will be assigned to *group* if it is provided.
# A set of *tags* can be used for filtering and modifying example behavior. # A set of *tags* can be used for filtering and modifying example behavior.
def initialize(@name : Label = nil, @location : Location? = nil, def initialize(@name : Label = nil, @location : Location? = nil, @tags : Tags = Tags.new)
group : ExampleGroup? = nil, @tags : Tags = Tags.new)
# Ensure group is linked.
group << self if group
end end
# Indicates whether the node has completed. # Indicates whether the node has completed.
@ -61,21 +49,7 @@ module Spectator
# Constructs the full name or description of the node. # Constructs the full name or description of the node.
# This prepends names of groups this node is part of. # This prepends names of groups this node is part of.
def to_s(io) def to_s(io)
name = @name (@name || "<anonymous>").to_s(io)
# Prefix with group's full name if the node belongs to a group.
if (group = @group)
group.to_s(io)
# Add padding between the node names
# only if the names don't appear to be symbolic.
# Skip blank group names (like the root group).
io << ' ' unless !group.name? || # ameba:disable Style/NegatedConditionsInUnless
(group.name?.is_a?(Symbol) && name.is_a?(String) &&
(name.starts_with?('#') || name.starts_with?('.')))
end
name.to_s(io)
end end
end end
end end