# frozen_string_literal: true require 'fileutils' require 'cucumber/configuration' require 'cucumber/deprecate' require 'cucumber/load_path' require 'cucumber/formatter/duration' require 'cucumber/file_specs' require 'cucumber/filters' require 'cucumber/formatter/fanout' require 'cucumber/gherkin/i18n' require 'cucumber/glue/registry_wrapper' require 'cucumber/step_match_search' require 'cucumber/messages' require 'cucumber/runtime/meta_message_builder' require 'sys/uname' module Cucumber module FixRuby21Bug9285 def message String(super).gsub('@ rb_sysopen ', '') end end class FileException < RuntimeError attr_reader :path def initialize(original_exception, path) @path = path super(original_exception) end end class FileNotFoundException < FileException end class FeatureFolderNotFoundException < RuntimeError def initialize(path) @path = path super end def message "No such file or directory - #{@path}" end end require 'cucumber/core' require 'cucumber/runtime/user_interface' require 'cucumber/runtime/support_code' class Runtime attr_reader :results, :support_code, :configuration include Cucumber::Core include Formatter::Duration include Runtime::UserInterface def initialize(configuration = Configuration.default) @configuration = Configuration.new(configuration) @support_code = SupportCode.new(self, @configuration) end # Allows you to take an existing runtime and change its configuration def configure(new_configuration) @configuration = Configuration.new(new_configuration) @support_code.configure(@configuration) end def run! @configuration.notify :envelope, Cucumber::Messages::Envelope.new( meta: MetaMessageBuilder.build_meta_message ) load_step_definitions fire_install_plugin_hook fire_before_all_hook unless dry_run? # TODO: can we remove this state? self.visitor = report receiver = Test::Runner.new(@configuration.event_bus) compile features, receiver, filters, @configuration.event_bus @configuration.notify :test_run_finished, !failure? fire_after_all_hook unless dry_run? end def features_paths @configuration.paths end def dry_run? @configuration.dry_run? end def unmatched_step_definitions @support_code.unmatched_step_definitions end def begin_scenario(test_case) @support_code.fire_hook(:begin_scenario, test_case) end def end_scenario(_scenario) @support_code.fire_hook(:end_scenario) end # Returns Ast::DocString for +string_without_triple_quotes+. # def doc_string(string_without_triple_quotes, content_type = '', _line_offset = 0) Core::Test::DocString.new(string_without_triple_quotes, content_type) end def failure? if @configuration.wip? summary_report.test_cases.total_passed.positive? else !summary_report.ok?(@configuration.strict) end end private def fire_install_plugin_hook # :nodoc: @support_code.fire_hook(:install_plugin, @configuration, registry_wrapper) end def fire_before_all_hook # :nodoc: @support_code.fire_hook(:before_all) end def fire_after_all_hook # :nodoc: @support_code.fire_hook(:after_all) end require 'cucumber/core/gherkin/document' def features @features ||= feature_files.map do |path| source = NormalisedEncodingFile.read(path) @configuration.notify :gherkin_source_read, path, source Cucumber::Core::Gherkin::Document.new(path, source) end end def feature_files filespecs.files end def filespecs @filespecs ||= FileSpecs.new(@configuration.feature_files) end class NormalisedEncodingFile COMMENT_OR_EMPTY_LINE_PATTERN = /^\s*#|^\s*$/.freeze # :nodoc: ENCODING_PATTERN = /^\s*#\s*encoding\s*:\s*([^\s]+)/.freeze # :nodoc: def self.read(path) new(path).read end def initialize(path) @file = File.new(path) set_encoding rescue Errno::EACCES => e raise FileNotFoundException.new(e, File.expand_path(path)) rescue Errno::ENOENT raise FeatureFolderNotFoundException, path end def read @file.read.encode('UTF-8') end private def set_encoding @file.each do |line| if ENCODING_PATTERN =~ line @file.set_encoding Regexp.last_match(1) break end break unless COMMENT_OR_EMPTY_LINE_PATTERN =~ line end @file.rewind end end require 'cucumber/formatter/ignore_missing_messages' require 'cucumber/formatter/fail_fast' require 'cucumber/formatter/publish_banner_printer' require 'cucumber/core/report/summary' def report return @report if @report reports = [summary_report] + formatters reports << fail_fast_report if @configuration.fail_fast? reports << publish_banner_printer unless @configuration.publish_quiet? @report ||= Formatter::Fanout.new(reports) end def summary_report @summary_report ||= Core::Report::Summary.new(@configuration.event_bus) end def fail_fast_report @fail_fast_report ||= Formatter::FailFast.new(@configuration) end def publish_banner_printer @publish_banner_printer ||= Formatter::PublishBannerPrinter.new(@configuration) end def formatters @formatters ||= @configuration.formatter_factories do |factory, formatter_options, path_or_io| create_formatter(factory, formatter_options, path_or_io) end end def create_formatter(factory, formatter_options, path_or_io) if accept_options?(factory) return factory.new(@configuration, formatter_options) if path_or_io.nil? factory.new(@configuration.with_options(out_stream: path_or_io), formatter_options) else return factory.new(@configuration) if path_or_io.nil? factory.new(@configuration.with_options(out_stream: path_or_io)) end end def accept_options?(factory) factory.instance_method(:initialize).arity > 1 end require 'cucumber/core/test/filters' def filters # rubocop:disable Metrics/AbcSize tag_expressions = @configuration.tag_expressions name_regexps = @configuration.name_regexps tag_limits = @configuration.tag_limits [].tap do |filters| filters << Filters::TagLimits.new(tag_limits) if tag_limits.any? filters << Cucumber::Core::Test::TagFilter.new(tag_expressions) filters << Cucumber::Core::Test::NameFilter.new(name_regexps) filters << Cucumber::Core::Test::LocationsFilter.new(filespecs.locations) filters << Filters::Randomizer.new(@configuration.seed) if @configuration.randomize? # TODO: can we just use Glue::RegistryAndMore's step definitions directly? step_match_search = StepMatchSearch.new(@support_code.registry.method(:step_matches), @configuration) filters << Filters::ActivateSteps.new(step_match_search, @configuration) @configuration.filters.each { |filter| filters << filter } unless configuration.dry_run? filters << Filters::ApplyAfterStepHooks.new(@support_code) filters << Filters::ApplyBeforeHooks.new(@support_code) filters << Filters::ApplyAfterHooks.new(@support_code) filters << Filters::ApplyAroundHooks.new(@support_code) end filters << Filters::BroadcastTestCaseReadyEvent.new(@configuration) unless configuration.dry_run? filters << Filters::BroadcastTestRunStartedEvent.new(@configuration) filters << Filters::Quit.new filters << Filters::Retry.new(@configuration) # need to do this last so it becomes the first test step filters << Filters::PrepareWorld.new(self) end end end def load_step_definitions files = @configuration.support_to_load + @configuration.step_defs_to_load @support_code.load_files!(files) end def registry_wrapper Cucumber::Glue::RegistryWrapper.new(@support_code.registry) end def log Cucumber.logger end end end