mirror of
				https://gitea.invidious.io/iv-org/shard-spectator.git
				synced 2024-08-15 00:53:35 +00:00 
			
		
		
		
	Logic for around_each hooks
This commit is contained in:
		
							parent
							
								
									5ea83f51bb
								
							
						
					
					
						commit
						36c2a5d368
					
				
					 3 changed files with 173 additions and 14 deletions
				
			
		|  | @ -46,25 +46,48 @@ module Spectator | |||
|     # Returns the result of the execution. | ||||
|     # The result will also be stored in `#result`. | ||||
|     def run : Result | ||||
|       @@current = self | ||||
|       Log.debug { "Running example #{self}" } | ||||
|       Log.warn { "Example #{self} already ran" } if @finished | ||||
|       @result = Harness.run do | ||||
|         if (parent = group?) | ||||
|           parent.call_once_before_all | ||||
|           parent.call_before_each(self) | ||||
|         end | ||||
| 
 | ||||
|         @entrypoint.call(self) | ||||
|       previous_example = @@current | ||||
|       @@current = self | ||||
| 
 | ||||
|       begin | ||||
|         @result = Harness.run do | ||||
|           if (parent = group?) | ||||
|             parent.call_around_each(self) { run_internal } | ||||
|           else | ||||
|             run_internal | ||||
|           end | ||||
|         end | ||||
|       ensure | ||||
|         @@current = previous_example | ||||
|         @finished = true | ||||
| 
 | ||||
|         if (parent = group?) | ||||
|           parent.call_after_each(self) | ||||
|           parent.call_once_after_all if parent.finished? | ||||
|         end | ||||
|       end | ||||
|     ensure | ||||
|       @@current = nil | ||||
|     end | ||||
| 
 | ||||
|     private def run_internal | ||||
|       run_before_hooks | ||||
|       run_test | ||||
|       run_after_hooks | ||||
|     end | ||||
| 
 | ||||
|     private def run_before_hooks : Nil | ||||
|       return unless (parent = group?) | ||||
| 
 | ||||
|       parent.call_once_before_all | ||||
|       parent.call_before_each(self) | ||||
|     end | ||||
| 
 | ||||
|     private def run_after_hooks : Nil | ||||
|       return unless (parent = group?) | ||||
| 
 | ||||
|       parent.call_after_each(self) | ||||
|       parent.call_once_after_all if parent.finished? | ||||
|     end | ||||
| 
 | ||||
|     private def run_test : Nil | ||||
|       @entrypoint.call(self) | ||||
|       @finished = true | ||||
|     end | ||||
| 
 | ||||
|  | @ -107,5 +130,37 @@ module Spectator | |||
| 
 | ||||
|       io << result | ||||
|     end | ||||
| 
 | ||||
|     # Wraps an example to behave like a `Proc`. | ||||
|     # This is typically used for an *around_each* hook. | ||||
|     # Invoking `#call` or `#run` will run the example. | ||||
|     struct Procsy | ||||
|       # Underlying example that will run. | ||||
|       getter example : Example | ||||
| 
 | ||||
|       # Creates the example proxy. | ||||
|       # The *example* should be run eventually. | ||||
|       # The *proc* defines the block of code to run when `#call` or `#run` is invoked. | ||||
|       def initialize(@example : Example, &@proc : ->) | ||||
|       end | ||||
| 
 | ||||
|       # Invokes the proc. | ||||
|       def call : Nil | ||||
|         @proc.call | ||||
|       end | ||||
| 
 | ||||
|       # Invokes the proc. | ||||
|       def run : Nil | ||||
|         @proc.call | ||||
|       end | ||||
| 
 | ||||
|       # Creates a new procsy for a block and the example from this instance. | ||||
|       def wrap(&block : ->) : self | ||||
|         self.class.new(@example, &block) | ||||
|       end | ||||
| 
 | ||||
|       # Allow instance to behave like an example. | ||||
|       forward_missing_to @example | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -1,5 +1,6 @@ | |||
| require "./events" | ||||
| require "./spec_node" | ||||
| require "./example_procsy_hook" | ||||
| 
 | ||||
| module Spectator | ||||
|   # Collection of examples and sub-groups. | ||||
|  | @ -101,5 +102,52 @@ module Spectator | |||
|       @nodes << node | ||||
|       node.group = self | ||||
|     end | ||||
| 
 | ||||
|     @around_hooks = [] of ExampleProcsyHook | ||||
| 
 | ||||
|     # Adds a hook to be invoked when the *{{name.id}}* event occurs. | ||||
|     def add_around_each_hook(hook : ExampleProcsyHook) : Nil | ||||
|       @around_hooks << hook | ||||
|     end | ||||
| 
 | ||||
|     # Defines a hook for the *around_each* event. | ||||
|     # The block of code given to this method is invoked when the event occurs. | ||||
|     # The current example is provided as a block argument. | ||||
|     def around_each(&block : Example::Procsy ->) : Nil | ||||
|       hook = ExampleProcsyHook.new(label: "around_each", &block) | ||||
|       add_around_each_hook(hook) | ||||
|     end | ||||
| 
 | ||||
| 
 | ||||
|     # Signals that the *around_each* event has occurred. | ||||
|     # All hooks associated with the event will be called. | ||||
|     def call_around_each(example : Example, &block : -> _) : Nil | ||||
|       # Avoid overhead if there's no hooks. | ||||
|       return yield if @around_hooks.empty? | ||||
| 
 | ||||
|       # Start with a procsy that wraps the original code. | ||||
|       procsy = Example::Procsy.new(example, &block) | ||||
|       procsy = wrap_around_each(procsy) | ||||
|       procsy.call | ||||
|     end | ||||
| 
 | ||||
|     # Wraps a procsy with the *around_each* hooks from this example group. | ||||
|     # The returned procsy will call each hook then *procsy*. | ||||
|     protected def wrap_around_each(procsy : Example::Procsy) : Example::Procsy | ||||
|       # Avoid overhead if there's no hooks. | ||||
|       return procsy if @around_hooks.empty? | ||||
| 
 | ||||
|       # Wrap each hook with the next. | ||||
|       outer = procsy | ||||
|       @around_hooks.each do |hook| | ||||
|         outer = hook.wrap(outer) | ||||
|       end | ||||
| 
 | ||||
|       # If there's a parent, wrap the procsy with its hooks. | ||||
|       # Otherwise, return the outermost procsy. | ||||
|       return outer unless (parent = group?) | ||||
| 
 | ||||
|       parent.wrap_around_each(outer) | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
							
								
								
									
										56
									
								
								src/spectator/example_procsy_hook.cr
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								src/spectator/example_procsy_hook.cr
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,56 @@ | |||
| require "./label" | ||||
| require "./source" | ||||
| 
 | ||||
| module Spectator | ||||
|   # Information about a hook tied to an example and a proc to invoke it. | ||||
|   class ExampleProcsyHook | ||||
|     # Location of the hook in source code. | ||||
|     getter! source : Source | ||||
| 
 | ||||
|     # User-defined description of the hook. | ||||
|     getter! label : Label | ||||
| 
 | ||||
|     @proc : Example::Procsy -> | ||||
| 
 | ||||
|     # Creates the hook with a proc. | ||||
|     # The *proc* will be called when the hook is invoked. | ||||
|     # A *source* and *label* can be provided for debugging. | ||||
|     def initialize(@proc : (Example::Procsy ->), *, @source : Source? = nil, @label : Label = nil) | ||||
|     end | ||||
| 
 | ||||
|     # Creates the hook with a block. | ||||
|     # The block must take a single argument - the current example wrapped in a procsy. | ||||
|     # The block will be executed when the hook is invoked. | ||||
|     # A *source* and *label* can be provided for debugging. | ||||
|     def initialize(*, @source : Source? = nil, @label : Label = nil, &block : Example::Procsy -> _) | ||||
|       @proc = block | ||||
|     end | ||||
| 
 | ||||
|     # Invokes the hook. | ||||
|     # The *example* refers to the current example. | ||||
|     def call(procsy : Example::Procsy) : Nil | ||||
|       @proc.call(procsy) | ||||
|     end | ||||
| 
 | ||||
|     # Creates an example procsy that invokes this hook. | ||||
|     def wrap(procsy : Example::Procsy) : Example::Procsy | ||||
|       procsy.wrap { call(procsy) } | ||||
|     end | ||||
| 
 | ||||
|     # Produces the string representation of the hook. | ||||
|     # Includes the source and label if they're not nil. | ||||
|     def to_s(io) | ||||
|       io << "example hook" | ||||
| 
 | ||||
|       if (label = @label) | ||||
|         io << ' ' | ||||
|         io << label | ||||
|       end | ||||
| 
 | ||||
|       if (source = @source) | ||||
|         io << " @ " | ||||
|         io << source | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue