mirror of
https://gitea.invidious.io/iv-org/shard-spectator.git
synced 2024-08-15 00:53:35 +00:00
Implement an example filter that supports matching groups
Addresses https://gitlab.com/arctic-fox/spectator/-/issues/25 and https://github.com/icy-arctic-fox/spectator/issues/24
This commit is contained in:
parent
65799fdd3b
commit
94d5c96e7d
2 changed files with 87 additions and 2 deletions
|
@ -1,7 +1,7 @@
|
|||
require "./config/*"
|
||||
require "./node_filter"
|
||||
require "./example_group"
|
||||
require "./example_iterator"
|
||||
require "./filtered_example_iterator"
|
||||
require "./formatting/formatter"
|
||||
require "./run_flags"
|
||||
|
||||
|
@ -86,7 +86,7 @@ module Spectator
|
|||
|
||||
# Creates an iterator configured to select the filtered examples.
|
||||
def iterator(group : ExampleGroup)
|
||||
ExampleIterator.new(group).select(@node_filter)
|
||||
FilteredExampleIterator.new(group, @node_filter)
|
||||
end
|
||||
|
||||
# Retrieves the configured random number generator.
|
||||
|
|
85
src/spectator/filtered_example_iterator.cr
Normal file
85
src/spectator/filtered_example_iterator.cr
Normal file
|
@ -0,0 +1,85 @@
|
|||
require "./example"
|
||||
require "./node"
|
||||
require "./node_filter"
|
||||
require "./node_iterator"
|
||||
|
||||
module Spectator
|
||||
# Iterates through selected nodes in a group and its nested groups.
|
||||
# Nodes are iterated in pre-order.
|
||||
class FilteredExampleIterator
|
||||
include Iterator(Example)
|
||||
|
||||
# A stack is used to track where in the tree this iterator is.
|
||||
@stack = Deque(Node).new(1)
|
||||
|
||||
# A queue stores forced examples that have been matched by the a parent group.
|
||||
@queue = Deque(Example).new
|
||||
|
||||
# Creates a new iterator.
|
||||
# The *group* is the example group to iterate through.
|
||||
# The *filter* selects which examples (and groups) to iterate through.
|
||||
def initialize(@group : Node, @filter : NodeFilter)
|
||||
@stack.push(@group)
|
||||
end
|
||||
|
||||
# Retrieves the next selected `Example`.
|
||||
# If there are none left, then `Iterator::Stop` is returned.
|
||||
def next
|
||||
# Return items from the queue first before continuing to the stack.
|
||||
return @queue.shift unless @queue.empty?
|
||||
|
||||
# Keep going until either:
|
||||
# a. a suitable example is found.
|
||||
# b. the stack is empty.
|
||||
until @stack.empty?
|
||||
# Retrieve the next node.
|
||||
node = @stack.pop
|
||||
|
||||
# If the node is a group, conditionally traverse it.
|
||||
if node.is_a?(Indexable(Node))
|
||||
# To traverse, a child node or the group itself must match the filter.
|
||||
return node if node = next_group_match(node)
|
||||
elsif node.is_a?(Example) && @filter.includes?(node)
|
||||
return node
|
||||
end
|
||||
end
|
||||
|
||||
# Nothing left to iterate.
|
||||
stop
|
||||
end
|
||||
|
||||
# Restart the iterator at the beginning.
|
||||
def rewind
|
||||
@stack.clear
|
||||
@stack.push(@group)
|
||||
@queue.clear
|
||||
self
|
||||
end
|
||||
|
||||
# Attempts to find the next matching example in a group.
|
||||
# If any child in the group matches, then traversal on the stack (down the tree) continues.
|
||||
# However, if no children match, but the group itself does, then all examples in the group match.
|
||||
# In the latter scenario, the examples are added to the queue, and the next item from the queue returned.
|
||||
# Stack iteration should continue if nil is returned.
|
||||
private def next_group_match(group : Indexable(Node)) : Example?
|
||||
# Look for any children that match.
|
||||
iterator = NodeIterator.new(group)
|
||||
|
||||
# Check if any children match.
|
||||
# Skip first node because its the group being checked.
|
||||
if iterator.skip(1).any?(@filter)
|
||||
# Add the group's direct children to the queue
|
||||
# in reverse order so that the tree is traversed in pre-order.
|
||||
group.reverse_each { |node| @stack.push(node) }
|
||||
|
||||
# Check if the group matches, but no children match.
|
||||
elsif @filter.includes?(group)
|
||||
# Add examples from the group to the queue.
|
||||
# Return the next example from the queue.
|
||||
iterator.rewind.select(Example).each { |node| @queue.push(node) }
|
||||
@queue.shift unless @queue.empty?
|
||||
# If the queue is empty (group has no examples), go to next loop iteration of the stack.
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue