lib/pbt/check/runner_methods.rb in pbt-0.2.0 vs lib/pbt/check/runner_methods.rb in pbt-0.3.0

- old
+ new

@@ -41,35 +41,52 @@ config[:num_runs].times do y.yield initial_values.next end end - suppress_exception_report_for_ractor(config) do + setup_for_ractor(config, property) do run_it(property, source_values, config).to_run_details(config) end end private - # If using Ractor, so many exception reports happen in Ractor and a console gets too messy. Suppress them to avoid that. + # If using Ractor, some extra configurations are available and they need to be set up. # + # - `:thread_report_on_exception` + # So many exception reports happen in Ractor and a console gets too messy. Suppress them to avoid that. + # - `:experimental_ractor_rspec_integration` + # Allow to use Ractor with RSpec. This is an experimental feature and it's not stable. + # # @param config [Hash] Configuration parameters. + # @param property [Property] # @param block [Proc] - def suppress_exception_report_for_ractor(config, &block) + def setup_for_ractor(config, property, &block) if config[:worker] == :ractor original_report_on_exception = Thread.report_on_exception Thread.report_on_exception = config[:thread_report_on_exception] + + if config[:experimental_ractor_rspec_integration] + require "pbt/check/rspec_adapter/integration" + class << property + include Pbt::Check::RSpecAdapter::PropertyExtension + end + property.setup_rspec_integration + end end yield ensure - Thread.report_on_exception = original_report_on_exception if config[:worker] == :ractor + if config[:worker] == :ractor + Thread.report_on_exception = original_report_on_exception + property.teardown_rspec_integration if config[:experimental_ractor_rspec_integration] + end end # Run the property test for each value. # - # @param property [Proc] Property to test. + # @param property [Property] Property to test. # @param source_values [Enumerator] Enumerator of values to test. # @param config [Hash] Configuration parameters. # @return [RunExecution] Result of the test. def run_it(property, source_values, config) runner = Check::RunnerIterator.new(source_values, property, config[:verbose]) @@ -86,11 +103,11 @@ end end runner.run_execution end - # @param property [Proc] + # @param property [Property] Property to test. # @param runner [RunnerIterator] # @return [void] def run_it_in_sequential(property, runner) runner.each_with_index do |val, index| c = Case.new(val:, index:) @@ -103,27 +120,42 @@ break # Ignore the rest of the cases. Just pick up the first failure. end end end - # @param property [Proc] + # @param property [Property] Property to test. # @param runner [RunnerIterator] # @return [void] def run_it_in_ractors(property, runner) runner.map.with_index { |val, index| Case.new(val:, index:, ractor: property.run_in_ractor(val)) }.each do |c| c.ractor.take runner.handle_result(c) rescue => e - c.exception = e.cause # Ractor error is wrapped in a Ractor::RemoteError. We need to get the cause. + handle_ractor_error(e.cause, c) runner.handle_result(c) break # Ignore the rest of the cases. Just pick up the first failure. end end - # @param property [Proc] + def handle_ractor_error(cause, c) + # Ractor error is wrapped in a Ractor::RemoteError. We need to get the cause. + unless defined?(Pbt::Check::RSpecAdapter) && cause.is_a?(Pbt::Check::RSpecAdapter::ExpectationNotMet) # Unknown error. + c.exception = cause + return + end + + # Convert Pbt's custom error to RSpec's error. + begin + RSpec::Expectations::ExpectationHelper.handle_failure(cause.matcher, cause.custom_message, cause.failure_message_method) + rescue RSpec::Expectations::ExpectationNotMetError => e # The class inherits Exception, not StandardError. + c.exception = e + end + end + + # @param property [Property] Property to test. # @param runner [RunnerIterator] # @return [void] def run_it_in_threads(property, runner) require_parallel @@ -140,10 +172,10 @@ break if c.exception # Ignore the rest of the cases. Just pick up the first failure. end end - # @param property [Proc] + # @param property [Property] Property to test. # @param runner [RunnerIterator] # @return [void] def run_it_in_processes(property, runner) require_parallel