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 "./config/*"
|
||||||
require "./node_filter"
|
require "./node_filter"
|
||||||
require "./example_group"
|
require "./example_group"
|
||||||
require "./example_iterator"
|
require "./filtered_example_iterator"
|
||||||
require "./formatting/formatter"
|
require "./formatting/formatter"
|
||||||
require "./run_flags"
|
require "./run_flags"
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ module Spectator
|
||||||
|
|
||||||
# Creates an iterator configured to select the filtered examples.
|
# Creates an iterator configured to select the filtered examples.
|
||||||
def iterator(group : ExampleGroup)
|
def iterator(group : ExampleGroup)
|
||||||
ExampleIterator.new(group).select(@node_filter)
|
FilteredExampleIterator.new(group, @node_filter)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Retrieves the configured random number generator.
|
# 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