diff --git a/lib/cucumber/configuration.rb b/lib/cucumber/configuration.rb index f460ee872..2efd24a18 100644 --- a/lib/cucumber/configuration.rb +++ b/lib/cucumber/configuration.rb @@ -285,6 +285,10 @@ def id_generator @id_generator ||= Cucumber::Messages::Helpers::IdGenerator::UUID.new end + def test_run_started_id + @test_run_started_id ||= id_generator.new_id + end + private def default_features_paths diff --git a/lib/cucumber/formatter/message_builder.rb b/lib/cucumber/formatter/message_builder.rb index 86a0e9204..d40e29b0e 100644 --- a/lib/cucumber/formatter/message_builder.rb +++ b/lib/cucumber/formatter/message_builder.rb @@ -16,7 +16,7 @@ def initialize(config) @repository = Cucumber::Repository.new @query = Cucumber::Query.new(@repository) - @test_run_started_id = config.id_generator.new_id + @test_run_started_id = config.test_run_started_id # Fake Query objects @test_case_by_step_id = {} @@ -202,43 +202,16 @@ def on_test_run_finished(event) output_envelope(message) end - def on_test_run_hook_started(event) - @current_test_run_hook_started_id = @config.id_generator.new_id - - message = Cucumber::Messages::Envelope.new( - test_run_hook_started: Cucumber::Messages::TestRunHookStarted.new( - id: @current_test_run_hook_started_id, - hook_id: event.hook.id, - test_run_started_id: @test_run_started_id, - timestamp: time_to_timestamp(Time.now) - ) - ) - - output_envelope(message) + def on_test_run_hook_started(_event) + :no_op + # This is now emitted at the required source (Single event listener - cucumber/glue/registry_and_more.rb#invoke_run_hook) + # It does not need to be emitted here as well end - def on_test_run_hook_finished(event) - result = event.test_result - result_message = result.to_message - - if result.failed? - result_message = Cucumber::Messages::TestStepResult.new( - status: result_message.status, - duration: result_message.duration, - message: create_error_message(result.exception), - exception: create_exception_object(result, result.exception) - ) - end - - message = Cucumber::Messages::Envelope.new( - test_run_hook_finished: Cucumber::Messages::TestRunHookFinished.new( - test_run_hook_started_id: @current_test_run_hook_started_id, - timestamp: time_to_timestamp(Time.now), - result: result_message - ) - ) - - output_envelope(message) + def on_test_run_hook_finished(_event) + :no_op + # This is now emitted at the required source (Single event listener - cucumber/glue/registry_and_more.rb#invoke_run_hook) + # It does not need to be emitted here as well end def on_test_step_created(event) diff --git a/lib/cucumber/glue/registry_and_more.rb b/lib/cucumber/glue/registry_and_more.rb index f042c027a..036c78975 100644 --- a/lib/cucumber/glue/registry_and_more.rb +++ b/lib/cucumber/glue/registry_and_more.rb @@ -4,6 +4,8 @@ require 'cucumber/cucumber_expressions/cucumber_expression' require 'cucumber/cucumber_expressions/regular_expression' require 'cucumber/cucumber_expressions/cucumber_expression_generator' +require 'cucumber/messages/helpers/time_conversion' + require 'cucumber/glue/dsl' require 'cucumber/glue/snippet' require 'cucumber/glue/hook' @@ -48,6 +50,8 @@ def initialize(first_proc, second_proc) class RegistryAndMore attr_reader :current_world, :step_definitions + include Cucumber::Messages::Helpers::TimeConversion + all_keywords = ::Gherkin::DIALECTS.keys.map do |dialect_name| dialect = ::Gherkin::Dialect.for(dialect_name) dialect.given_keywords + dialect.when_keywords + dialect.then_keywords + dialect.and_keywords + dialect.but_keywords @@ -182,16 +186,75 @@ def create_expression(string_or_regexp) def invoke_run_hook(hook, pseudo_method) @configuration.notify(:test_run_hook_started, hook) + + current_test_run_hook_started_id = @configuration.id_generator.new_id + started_envelope = test_run_hook_started_envelope(hook, current_test_run_hook_started_id) + @configuration.notify(:envelope, started_envelope) + timer = Core::Test::Timer.new.start begin hook.invoke(pseudo_method, []) @configuration.notify(:test_run_hook_finished, hook, Core::Test::Result::Passed.new(timer.duration)) + finished_envelope = test_run_hook_finished_envelope(hook, Core::Test::Result::Passed.new(timer.duration), current_test_run_hook_started_id) + @configuration.notify(:envelope, finished_envelope) rescue StandardError => e @configuration.notify(:test_run_hook_finished, hook, Core::Test::Result::Failed.new(timer.duration, e)) + finished_envelope = test_run_hook_finished_envelope(hook, Core::Test::Result::Failed.new(timer.duration, e), current_test_run_hook_started_id) + @configuration.notify(:envelope, finished_envelope) raise end end + def test_run_hook_started_envelope(hook, id) + Cucumber::Messages::Envelope.new( + test_run_hook_started: Cucumber::Messages::TestRunHookStarted.new( + id: id, + hook_id: hook.id, + test_run_started_id: @configuration.test_run_started_id, + timestamp: time_to_timestamp(Time.now) + ) + ) + end + + def test_run_hook_finished_envelope(_hook, test_result, test_run_hook_started_id) + result = test_result + result_message = result.to_message + + if result.failed? + result_message = Cucumber::Messages::TestStepResult.new( + status: result_message.status, + duration: result_message.duration, + message: create_error_message(result.exception), + exception: create_exception_object(result, result.exception) + ) + end + + Cucumber::Messages::Envelope.new( + test_run_hook_finished: Cucumber::Messages::TestRunHookFinished.new( + test_run_hook_started_id: test_run_hook_started_id, + timestamp: time_to_timestamp(Time.now), + result: result_message + ) + ) + end + + def create_error_message(message_element) + <<~ERROR_MESSAGE + #{message_element.message} (#{message_element.class}) + #{message_element.backtrace} + ERROR_MESSAGE + end + + def create_exception_object(result, message_element) + return unless result.failed? + + Cucumber::Messages::Exception.new( + type: message_element.class, + message: message_element.message, + stack_trace: message_element.backtrace.join("\n") + ) + end + def parameter_type_envelope(parameter_type) # TODO: should this be moved to Cucumber::Expression::ParameterType#to_envelope ?? # Note: that would mean that cucumber-expression would depend on cucumber-messages