2021-01-16 23:28:33 +00:00
|
|
|
require "./example_procsy_hook"
|
2021-08-08 03:45:49 +00:00
|
|
|
require "./hooks"
|
2021-05-08 02:09:33 +00:00
|
|
|
require "./node"
|
2020-09-06 01:55:46 +00:00
|
|
|
|
2020-09-05 22:47:40 +00:00
|
|
|
module Spectator
|
|
|
|
# Collection of examples and sub-groups.
|
2021-05-08 02:09:33 +00:00
|
|
|
class ExampleGroup < Node
|
2021-08-08 03:45:49 +00:00
|
|
|
include Hooks
|
2021-08-17 18:54:36 +00:00
|
|
|
include Indexable(Node)
|
2020-09-05 22:47:40 +00:00
|
|
|
|
2021-05-08 02:09:33 +00:00
|
|
|
@nodes = [] of Node
|
2021-01-09 19:48:53 +00:00
|
|
|
|
2021-05-08 03:04:17 +00:00
|
|
|
# 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?
|
|
|
|
|
2021-08-08 03:45:49 +00:00
|
|
|
define_hook before_all : ExampleGroupHook do
|
2021-05-08 18:00:29 +00:00
|
|
|
Log.trace { "Processing before_all hooks for #{self}" }
|
|
|
|
|
2021-08-08 03:45:49 +00:00
|
|
|
@group.try &.call_before_all
|
|
|
|
before_all_hooks.each &.call_once
|
2021-05-08 18:00:29 +00:00
|
|
|
end
|
|
|
|
|
2021-08-08 16:27:34 +00:00
|
|
|
define_hook after_all : ExampleGroupHook, :prepend do
|
2021-05-08 18:00:29 +00:00
|
|
|
Log.trace { "Processing after_all hooks for #{self}" }
|
|
|
|
|
2021-08-08 03:45:49 +00:00
|
|
|
after_all_hooks.each &.call_once if finished?
|
|
|
|
if group = @group
|
|
|
|
group.call_after_all if group.finished?
|
|
|
|
end
|
2020-11-15 18:22:52 +00:00
|
|
|
end
|
|
|
|
|
2021-08-08 03:45:49 +00:00
|
|
|
define_hook before_each : ExampleHook do |example|
|
2021-01-09 18:33:29 +00:00
|
|
|
Log.trace { "Processing before_each hooks for #{self}" }
|
2020-11-15 18:22:52 +00:00
|
|
|
|
2021-08-08 03:45:49 +00:00
|
|
|
@group.try &.call_before_each(example)
|
|
|
|
before_each_hooks.each &.call(example)
|
2020-11-15 18:22:52 +00:00
|
|
|
end
|
|
|
|
|
2021-08-08 16:27:34 +00:00
|
|
|
define_hook after_each : ExampleHook, :prepend do |example|
|
2021-01-09 18:33:29 +00:00
|
|
|
Log.trace { "Processing after_each hooks for #{self}" }
|
2020-11-15 18:22:52 +00:00
|
|
|
|
2021-08-08 03:45:49 +00:00
|
|
|
after_each_hooks.each &.call(example)
|
|
|
|
@group.try &.call_after_each(example)
|
|
|
|
end
|
|
|
|
|
|
|
|
define_hook around_each : ExampleProcsyHook do |procsy|
|
|
|
|
Log.trace { "Processing around_each hooks for #{self}" }
|
|
|
|
|
|
|
|
around_each_hooks.reverse_each { |hook| procsy = hook.wrap(procsy) }
|
|
|
|
if group = @group
|
|
|
|
procsy = group.call_around_each(procsy)
|
|
|
|
end
|
|
|
|
procsy
|
2020-11-15 18:22:52 +00:00
|
|
|
end
|
2020-11-08 22:06:49 +00:00
|
|
|
|
2021-09-16 15:01:51 +00:00
|
|
|
define_hook pre_condition : ExampleHook do |example|
|
|
|
|
Log.trace { "Processing pre_condition hooks for #{self}" }
|
|
|
|
|
|
|
|
@group.try &.call_pre_condition(example)
|
|
|
|
pre_condition_hooks.each &.call(example)
|
|
|
|
end
|
|
|
|
|
|
|
|
define_hook post_condition : ExampleHook, :prepend do |example|
|
|
|
|
Log.trace { "Processing post_condition hooks for #{self}" }
|
|
|
|
|
|
|
|
post_condition_hooks.each &.call(example)
|
|
|
|
@group.try &.call_post_condition(example)
|
|
|
|
end
|
|
|
|
|
2021-05-08 03:04:17 +00:00
|
|
|
# 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.
|
2021-06-12 22:45:45 +00:00
|
|
|
# A set of *metadata* can be used for filtering and modifying example behavior.
|
2021-05-08 03:04:17 +00:00
|
|
|
def initialize(@name : Label = nil, @location : Location? = nil,
|
2021-06-12 22:45:45 +00:00
|
|
|
@group : ExampleGroup? = nil, @metadata : Metadata = Metadata.new)
|
2021-05-08 03:04:17 +00:00
|
|
|
# Ensure group is linked.
|
|
|
|
group << self if group
|
|
|
|
end
|
|
|
|
|
2021-08-17 18:54:36 +00:00
|
|
|
delegate size, unsafe_fetch, to: @nodes
|
|
|
|
|
2021-08-18 04:10:01 +00:00
|
|
|
# Yields this group and all parent groups.
|
|
|
|
def ascend
|
|
|
|
group = self
|
|
|
|
while group
|
|
|
|
yield group
|
|
|
|
group = group.group?
|
|
|
|
end
|
2021-06-13 20:45:01 +00:00
|
|
|
end
|
|
|
|
|
2020-09-05 22:47:40 +00:00
|
|
|
# Removes the specified *node* from the group.
|
|
|
|
# The node will be unassigned from this group.
|
2021-05-08 02:09:33 +00:00
|
|
|
def delete(node : Node)
|
2020-09-05 22:47:40 +00:00
|
|
|
# Only remove from the group if it is associated with this group.
|
|
|
|
return unless node.group == self
|
|
|
|
|
|
|
|
node.group = nil
|
|
|
|
@nodes.delete(node)
|
|
|
|
end
|
|
|
|
|
2020-09-12 22:02:11 +00:00
|
|
|
# Checks if all examples and sub-groups have finished.
|
|
|
|
def finished? : Bool
|
|
|
|
@nodes.all?(&.finished?)
|
|
|
|
end
|
|
|
|
|
2021-05-08 03:04:17 +00:00
|
|
|
# Constructs the full name or description of the example group.
|
|
|
|
# This prepends names of groups this group is part of.
|
2022-11-05 03:01:32 +00:00
|
|
|
def to_s(io : IO, *, nested = false) : Nil
|
|
|
|
unless parent = @group
|
|
|
|
# Display special string when called directly.
|
|
|
|
io << "<root>" unless nested
|
|
|
|
return
|
|
|
|
end
|
2021-05-17 01:37:45 +00:00
|
|
|
|
2022-11-05 03:01:32 +00:00
|
|
|
# Prefix with group's full name if the node belongs to a group.
|
|
|
|
parent.to_s(io, nested: true)
|
2021-05-08 03:04:17 +00:00
|
|
|
name = @name
|
|
|
|
|
2021-05-17 01:37:45 +00:00
|
|
|
# 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 !parent.name? || # ameba:disable Style/NegatedConditionsInUnless
|
|
|
|
(parent.name?.is_a?(Symbol) && name.is_a?(String) &&
|
|
|
|
(name.starts_with?('#') || name.starts_with?('.')))
|
2021-05-08 03:04:17 +00:00
|
|
|
|
2022-11-05 03:01:32 +00:00
|
|
|
super(io)
|
2021-05-08 03:04:17 +00:00
|
|
|
end
|
|
|
|
|
2020-09-05 22:47:40 +00:00
|
|
|
# Adds the specified *node* to the group.
|
|
|
|
# Assigns the node to this group.
|
|
|
|
# If the node already belongs to a group,
|
|
|
|
# it will be removed from the previous group before adding it to this group.
|
2021-05-08 02:09:33 +00:00
|
|
|
def <<(node : Node)
|
2020-09-05 22:47:40 +00:00
|
|
|
# Remove from existing group if the node is part of one.
|
|
|
|
if (previous = node.group?)
|
|
|
|
previous.delete(node)
|
|
|
|
end
|
|
|
|
|
|
|
|
# Add the node to this group and associate with it.
|
|
|
|
@nodes << node
|
|
|
|
node.group = self
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|