# frozen_string_literal: truerequire'cucumber/messages'require'cucumber/messages/helpers/time_conversion'moduleCucumbermoduleCoremoduleTestmoduleResultTYPES=%i[failed flaky skipped undefined pending passed unknown].freezeSTRICT_AFFECTED_TYPES=%i[flaky undefined pending].freezedefself.ok?(type,strict: StrictConfiguration.new)class_name=type.to_s.slice(0,1).capitalize+type.to_s.slice(1..-1)const_get(class_name).ok?(strict: strict.strict?(type))end# Defines to_sym on a result class for the given result type## Defines predicate methods on a result class with only the given one returning truedefself.query_methods(result_type)Module.newdodefine_method:to_symdoresult_typeendTYPES.eachdo|possible_result_type|define_method("#{possible_result_type}?")dopossible_result_type==to_symendendendend# Null object for results. Represents the state where we haven't run anything yetclassUnknownincludeResult.query_methods:unknowndefdescribe_to(_visitor,*_args)selfenddefwith_filtered_backtrace(_filter)selfenddefto_messageCucumber::Messages::TestStepResult.new(status: Cucumber::Messages::TestStepResultStatus::UNKNOWN,duration: UnknownDuration.new.to_message_duration)endendclassPassedincludeResult.query_methods:passedattr_accessor:durationdefself.ok?(*)trueenddefinitialize(duration)raiseArgumentErrorunlessduration@duration=durationenddefdescribe_to(visitor,*args)visitor.passed(*args)visitor.duration(duration,*args)selfenddefto_s'✓'enddefto_messageCucumber::Messages::TestStepResult.new(status: Cucumber::Messages::TestStepResultStatus::PASSED,duration: duration.to_message_duration)enddefok?(*)self.class.ok?enddefwith_appended_backtrace(_step)selfenddefwith_filtered_backtrace(_filter)selfendendclassFailedincludeResult.query_methods:failedattr_reader:duration,:exceptiondefself.ok?(*)falseenddefinitialize(duration,exception)raiseArgumentErrorunlessdurationraiseArgumentErrorunlessexception@duration=duration@exception=exceptionenddefdescribe_to(visitor,*args)visitor.failed(*args)visitor.duration(duration,*args)visitor.exception(exception,*args)ifexceptionselfenddefto_s'✗'enddefto_messagebeginmessage=exception.backtrace.join("\n")rescueNoMethodErrormessage=''endCucumber::Messages::TestStepResult.new(status: Cucumber::Messages::TestStepResultStatus::FAILED,duration: duration.to_message_duration,message: message)enddefok?(*)self.class.ok?enddefwith_duration(new_duration)self.class.new(new_duration,exception)enddefwith_appended_backtrace(step)exception.backtrace<<step.backtrace_lineifstep.respond_to?(:backtrace_line)selfenddefwith_filtered_backtrace(filter)self.class.new(duration,filter.new(exception.dup).exception)endend# Flaky is not used directly as an execution result, but is used as a# reporting result type for test cases that fails and the passes on# retry, therefore only the class method self.ok? is needed.classFlakydefself.ok?(strict: false)!strictendend# Base class for exceptions that can be raised in a step definition causing the step to have that result.classRaisable<StandardErrorattr_reader:message,:durationdefinitialize(message='',duration=UnknownDuration.new,backtrace=nil)@message=message@duration=durationsuper(message)set_backtrace(backtrace)ifbacktraceenddefwith_message(new_message)self.class.new(new_message,duration,backtrace)enddefwith_duration(new_duration)self.class.new(message,new_duration,backtrace)enddefwith_appended_backtrace(step)returnselfunlessstep.respond_to?(:backtrace_line)set_backtrace([])unlessbacktracebacktrace<<step.backtrace_lineselfenddefwith_filtered_backtrace(filter)returnselfunlessbacktracefilter.new(dup).exceptionenddefok?(strict: StrictConfiguration.new)self.class.ok?(strict: strict.strict?(to_sym))endendclassUndefined<RaisableincludeResult.query_methods:undefineddefself.ok?(strict: false)!strictenddefdescribe_to(visitor,*args)visitor.undefined(*args)visitor.duration(duration,*args)selfenddefto_s'?'enddefto_messageCucumber::Messages::TestStepResult.new(status: Cucumber::Messages::TestStepResultStatus::UNDEFINED,duration: duration.to_message_duration)endendclassSkipped<RaisableincludeResult.query_methods:skippeddefself.ok?(*)trueenddefdescribe_to(visitor,*args)visitor.skipped(*args)visitor.duration(duration,*args)selfenddefto_s'-'enddefto_messageCucumber::Messages::TestStepResult.new(status: Cucumber::Messages::TestStepResultStatus::SKIPPED,duration: duration.to_message_duration)endendclassPending<RaisableincludeResult.query_methods:pendingdefself.ok?(strict: false)!strictenddefdescribe_to(visitor,*args)visitor.pending(self,*args)visitor.duration(duration,*args)selfenddefto_s'P'enddefto_messageCucumber::Messages::TestStepResult.new(status: Cucumber::Messages::TestStepResultStatus::PENDING,duration: duration.to_message_duration)endend# Handles the strict settings for the result types that are# affected by the strict options (that is the STRICT_AFFECTED_TYPES).classStrictConfigurationattr_accessor:settingsprivate:settingsdefinitialize(strict_types=[])@settings=STRICT_AFFECTED_TYPES.to_h{|t|[t,:default]}strict_types.eachdo|type|set_strict(true,type)endenddefstrict?(type=nil)iftype.nil?settings.each_valuedo|value|returntrueifvalue==trueendfalseelsereturnfalseunlesssettings.key?(type)returnfalseunlessset?(type)settings[type]endenddefset_strict(setting,type=nil)iftype.nil?STRICT_AFFECTED_TYPES.each{|type|set_strict(setting,type)}elsesettings[type]=settingendenddefmerge!(other)settings.each_keydo|type|set_strict(other.strict?(type),type)ifother.set?(type)endselfenddefset?(type)settings[type]!=:defaultendend## An object that responds to the description protocol from the results and collects summary information.## e.g.# summary = Result::Summary.new# Result::Passed.new(0).describe_to(summary)# puts summary.total_passed# => 1#classSummaryattr_reader:exceptions,:durationsdefinitialize@totals=Hash.new{0}@exceptions=[]@durations=[]enddefmethod_missing(name,*_args)ifname=~/^total_/get_total(name)elseincrement_total(name)endenddefrespond_to_missing?(*)trueenddefok?(strict: StrictConfiguration.new)TYPES.eachdo|type|ifget_total(type)>0returnfalseunlessResult.ok?(type,strict: strict)endendtrueenddefexception(exception)@exceptions<<exceptionselfenddefduration(duration)@durations<<durationselfenddeftotal(for_status=nil)iffor_status@totals.fetch(for_status,0)else@totals.values.reduce(0){|total,count|total+count}endenddefdecrement_failed@totals[:failed]-=1endprivatedefget_total(method_name)status=method_name.to_s.gsub('total_','').to_sym@totals.fetch(status,0)enddefincrement_total(status)@totals[status]+=1selfendendclassDurationincludeCucumber::Messages::Helpers::TimeConversionattr_reader:nanosecondsdefinitialize(nanoseconds)@nanoseconds=nanosecondsenddefto_message_durationduration_hash=seconds_to_duration(nanoseconds.to_f/NANOSECONDS_PER_SECOND)duration_hash.transform_keys!do|key|key.to_symrescueErrorreturnkeyendCucumber::Messages::Duration.from_h(duration_hash)endendclassUnknownDurationincludeCucumber::Messages::Helpers::TimeConversiondeftapselfenddefnanosecondsraise'#nanoseconds only allowed to be used in #tap block'enddefto_message_durationCucumber::Messages::Duration.new(seconds: 0,nanos: 0)endendendendendend