From e47e625016e6e67193feaab78e902346960808be Mon Sep 17 00:00:00 2001 From: Michael Miller Date: Fri, 7 May 2021 21:04:17 -0600 Subject: [PATCH] Remove circular dependency with Node and ExampleGroup --- src/spectator/example.cr | 43 +++++++++++++++++++++++++++------- src/spectator/example_group.cr | 40 +++++++++++++++++++++++++++++++ src/spectator/node.cr | 30 ++---------------------- 3 files changed, 76 insertions(+), 37 deletions(-) diff --git a/src/spectator/example.cr b/src/spectator/example.cr index 2ec9ea0..ac0dfa1 100644 --- a/src/spectator/example.cr +++ b/src/spectator/example.cr @@ -13,6 +13,14 @@ module Spectator # Currently running 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. getter? finished : Bool = false @@ -33,8 +41,11 @@ module Spectator # Note: The tags will not be merged with the parent tags. def initialize(@context : Context, @entrypoint : self ->, name : String? = nil, location : Location? = nil, - group : ExampleGroup? = nil, tags = Tags.new) - super(name, location, group, tags) + @group : ExampleGroup? = nil, tags = Tags.new) + super(name, location, tags) + + # Ensure group is linked. + group << self if group end # Creates a dynamic example. @@ -48,9 +59,13 @@ module Spectator # Note: The tags will not be merged with the parent tags. def initialize(name : String? = nil, location : Location? = nil, group : ExampleGroup? = nil, tags = Tags.new, &block : self ->) - super(name, location, group, tags) + super(name, location, tags) + @context = NullContext.new @entrypoint = block + + # Ensure group is linked. + group << self if group end # Executes the test case. @@ -87,10 +102,10 @@ module Spectator end private def run_internal - group?.try(&.call_before_each(self)) + @group.try(&.call_before_each(self)) @entrypoint.call(self) @finished = true - group?.try(&.call_after_each(self)) + @group.try(&.call_after_each(self)) end # Executes code within the example's test context. @@ -126,11 +141,21 @@ module Spectator # Constructs the full name or description of the example. # This prepends names of groups this example is part of. def to_s(io) - if name? - super - else - 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 # Exposes information about the example useful for debugging. diff --git a/src/spectator/example_group.cr b/src/spectator/example_group.cr index 29e9b46..45c4cf1 100644 --- a/src/spectator/example_group.cr +++ b/src/spectator/example_group.cr @@ -11,6 +11,14 @@ module Spectator @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| Log.trace { "Processing before_all hooks for #{self}" } @@ -63,6 +71,18 @@ module Spectator 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. # The node will be unassigned from this group. def delete(node : Node) @@ -88,6 +108,26 @@ module Spectator @nodes.all?(&.finished?) 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. # Assigns the node to this group. # If the node already belongs to a group, diff --git a/src/spectator/node.cr b/src/spectator/node.cr index 587af76..4afe273 100644 --- a/src/spectator/node.cr +++ b/src/spectator/node.cr @@ -26,27 +26,15 @@ module Spectator protected def name=(@name : String) end - # Group the node belongs to. - getter! group : ExampleGroup - # User-defined keywords used for filtering and behavior modification. 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. # 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. - # The node will be assigned to *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 + def initialize(@name : Label = nil, @location : Location? = nil, @tags : Tags = Tags.new) end # Indicates whether the node has completed. @@ -61,21 +49,7 @@ module Spectator # Constructs the full name or description of the node. # This prepends names of groups this node 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 - - name.to_s(io) + (@name || "").to_s(io) end end end