# encoding: utf-8# frozen_string_literal: truemoduleCucumbermoduleCoremoduleTestmoduleResultTYPES=[:failed,:flaky,:skipped,:undefined,:pending,:passed,:unknown].freezeSTRICT_AFFECTED_TYPES=[:flaky,:undefined,:pending].freezedefself.ok?(type,be_strict=StrictConfiguration.new)privateclass_name=type.to_s.slice(0,1).capitalize+type.to_s.slice(1..-1)const_get(class_name).ok?(be_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)selfendendclassPassedincludeResult.query_methods:passedattr_accessor:durationdefself.ok?(be_strict=false)trueenddefinitialize(duration)raiseArgumentErrorunlessduration@duration=durationenddefdescribe_to(visitor,*args)visitor.passed(*args)visitor.duration(duration,*args)selfenddefto_s"✓"enddefok?(be_strict=nil)self.class.ok?enddefwith_appended_backtrace(step)selfenddefwith_filtered_backtrace(filter)selfendendclassFailedincludeResult.query_methods:failedattr_reader:duration,:exceptiondefself.ok?(be_strict=false)falseenddefinitialize(duration,exception)raiseArgumentErrorunlessdurationraiseArgumentErrorunlessexception@duration=duration@exception=exceptionenddefdescribe_to(visitor,*args)visitor.failed(*args)visitor.duration(duration,*args)visitor.exception(exception,*args)ifexceptionselfenddefto_s"✗"enddefok?(be_strict=nil)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?(be_strict=false)!be_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,@duration=message,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?(be_strict=StrictConfiguration.new)self.class.ok?(be_strict.strict?(to_sym))endendclassUndefined<RaisableincludeResult.query_methods:undefineddefself.ok?(be_strict=false)!be_strictenddefdescribe_to(visitor,*args)visitor.undefined(*args)visitor.duration(duration,*args)selfenddefto_s"?"endendclassSkipped<RaisableincludeResult.query_methods:skippeddefself.ok?(be_strict=false)trueenddefdescribe_to(visitor,*args)visitor.skipped(*args)visitor.duration(duration,*args)selfenddefto_s"-"endendclassPending<RaisableincludeResult.query_methods:pendingdefself.ok?(be_strict=false)!be_strictenddefdescribe_to(visitor,*args)visitor.pending(self,*args)visitor.duration(duration,*args)selfenddefto_s"P"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=Hash[STRICT_AFFECTED_TYPES.map{|t|[t,:default]}]strict_types.eachdo|type|set_strict(true,type)endenddefstrict?(type=nil)iftype.nil?settings.eachdo|_key,value|returntrueifvalue==trueendfalseelsereturnfalseunlesssettings.key?(type)returnfalseunlessset?(type)settings[type]endenddefset_strict(setting,type=nil)iftype.nil?STRICT_AFFECTED_TYPES.eachdo|t|set_strict(setting,t)endelsesettings[type]=settingendenddefmerge!(other)settings.keys.eachdo|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)endenddefok?(be_strict=StrictConfiguration.new)TYPES.eachdo|type|ifget_total(type)>0returnfalseunlessResult.ok?(type,be_strict)endendtrueenddefexception(exception)@exceptions<<exceptionselfenddefduration(duration)@durations<<durationselfenddeftotal(for_status=nil)iffor_status@totals.fetch(for_status){0}else@totals.reduce(0){|total,status|total+=status[1]}endenddefdecrement_failed@totals[:failed]-=1endprivatedefget_total(method_name)status=method_name.to_s.gsub('total_','').to_symreturn@totals.fetch(status){0}enddefincrement_total(status)@totals[status]+=1selfendendclassDurationattr_reader:nanosecondsdefinitialize(nanoseconds)@nanoseconds=nanosecondsendendclassUnknownDurationdeftap(&block)selfenddefnanosecondsraise"#nanoseconds only allowed to be used in #tap block"endendendendendend