RSpec::Support.require_rspec_core"formatters/helpers"moduleRSpec::Core# Notifications are value objects passed to formatters to provide them# with information about a particular event of interest.moduleNotifications# @privateclassNullColorizerdefwrap(line)lineendend# The `StartNotification` represents a notification sent by the reporter# when the suite is started. It contains the expected amount of examples# to be executed, and the load time of RSpec.## @attr count [Fixnum] the number counted# @attr load_time [Float] the number of seconds taken to boot RSpec# and load the spec filesStartNotification=Struct.new(:count,:load_time)# The `ExampleNotification` represents notifications sent by the reporter# which contain information about the current (or soon to be) example.# It is used by formatters to access information about that example.## @example# def example_started(notification)# puts "Hey I started #{notification.example.description}"# end## @attr example [RSpec::Core::Example] the current exampleExampleNotification=Struct.new(:example)do# @privatedefself.for(example)ifexample.execution_result.pending_fixed?PendingExampleFixedNotification.new(example)elsifexample.execution_result.status==:failedFailedExampleNotification.new(example)elsenew(example)endendprivate_class_method:newend# The `ExamplesNotification` represents notifications sent by the reporter# which contain information about the suites examples.## @example# def stop(notification)# puts "Hey I ran #{notification.examples.size}"# end#classExamplesNotificationdefinitialize(reporter)@reporter=reporterend# @return [Array(RSpec::Core::Example)] list of examplesdefexamples@reporter.examplesend# @return [Array(RSpec::Core::Example)] list of failed examplesdeffailed_examples@reporter.failed_examplesend# @return [Array(RSpec::Core::Example)] list of pending examplesdefpending_examples@reporter.pending_examplesend# @return [Array(Rspec::Core::Notifications::ExampleNotification]# returns examples as notificationsdefnotifications@notifications||=format(examples)end# @return [Array(Rspec::Core::Notifications::FailedExampleNotification]# returns failed examples as notificationsdeffailure_notifications@failed_notifications||=format(failed_examples)end# @return [String] The list of failed examples, fully formatted in the way that# RSpec's built-in formatters emit.deffully_formatted_failed_examples(colorizer=::RSpec::Core::Formatters::ConsoleCodes)formatted="\nFailures:\n"failure_notifications.each_with_indexdo|failure,index|formatted<<failure.fully_formatted(index.next,colorizer)endformattedend# @return [String] The list of pending examples, fully formatted in the way that# RSpec's built-in formatters emit.deffully_formatted_pending_examples(colorizer=::RSpec::Core::Formatters::ConsoleCodes)formatted="\nPending:\n"pending_examples.eachdo|example|formatted_caller=RSpec.configuration.backtrace_formatter.backtrace_line(example.location)formatted<<" #{colorizer.wrap(example.full_description,:pending)}\n"<<" # #{colorizer.wrap(example.execution_result.pending_message,:detail)}\n"<<" # #{colorizer.wrap(formatted_caller,:detail)}\n"endformattedendprivatedefformat(examples)examples.mapdo|example|ExampleNotification.for(example)endendend# The `FailedExampleNotification` extends `ExampleNotification` with# things useful for failed specs.## @example# def example_failed(notification)# puts "Hey I failed :("# puts "Here's my stack trace"# puts notification.exception.backtrace.join("\n")# end## @attr [RSpec::Core::Example] example the current example# @see ExampleNotificationclassFailedExampleNotification<ExampleNotificationpublic_class_method:new# @return [Exception] The example failuredefexceptionexample.execution_result.exceptionend# @return [String] The example descriptiondefdescriptionexample.full_descriptionend# Returns the message generated for this failure line by line.## @return [Array(String)] The example failure messagedefmessage_linesadd_shared_group_line(failure_lines,NullColorizer)end# Returns the message generated for this failure colorized line by line.## @param colorizer [#wrap] An object to colorize the message_lines by# @return [Array(String)] The example failure message colorizeddefcolorized_message_lines(colorizer=::RSpec::Core::Formatters::ConsoleCodes)add_shared_group_line(failure_lines,colorizer).mapdo|line|colorizer.wrapline,RSpec.configuration.failure_colorendend# Returns the failures formatted backtrace.## @return [Array(String)] the examples backtrace linesdefformatted_backtracebacktrace_formatter.format_backtrace(exception.backtrace,example.metadata)end# Returns the failures colorized formatted backtrace.## @param colorizer [#wrap] An object to colorize the message_lines by# @return [Array(String)] the examples colorized backtrace linesdefcolorized_formatted_backtrace(colorizer=::RSpec::Core::Formatters::ConsoleCodes)formatted_backtrace.mapdo|backtrace_info|colorizer.wrap"# #{backtrace_info}",RSpec.configuration.detail_colorendend# @return [String] The failure information fully formatted in the way that# RSpec's built-in formatters emit.deffully_formatted(failure_number,colorizer=::RSpec::Core::Formatters::ConsoleCodes)formatted="\n#{failure_number}) #{description}\n"colorized_message_lines(colorizer).eachdo|line|formatted<<" #{line}\n"endcolorized_formatted_backtrace(colorizer).eachdo|line|formatted<<" #{line}\n"endformattedendprivatedefbacktrace_formatterRSpec.configuration.backtrace_formatterenddefexception_class_namename=exception.class.name.to_sname="(anonymous error class)"ifname==''nameenddeffailure_lines@failure_lines||=beginlines=["Failure/Error: #{read_failed_line.strip}"]lines<<"#{exception_class_name}:"unlessexception_class_name=~/RSpec/exception.message.to_s.split("\n").eachdo|line|lines<<" #{line}"ifexception.messageendlinesendenddefadd_shared_group_line(lines,colorizer)unlessshared_group_line==""lines<<colorizer.wrap(shared_group_line,RSpec.configuration.default_color)endlinesenddefshared_group@shared_group||=group_and_parent_groups.find{|group|group.metadata[:shared_group_name]}enddefshared_group_line@shared_group_line||=ifshared_group"Shared Example Group: \"#{shared_group.metadata[:shared_group_name]}\""+" called from #{backtrace_formatter.backtrace_line(shared_group.location)}"else""endenddefgroup_and_parent_groupsexample.example_group.parent_groups+[example.example_group]enddefread_failed_lineunlessmatching_line=find_failed_linereturn"Unable to find matching line from backtrace"endfile_path,line_number=matching_line.match(/(.+?):(\d+)(|:\d+)/)[1..2]ifFile.exist?(file_path)File.readlines(file_path)[line_number.to_i-1]||"Unable to find matching line in #{file_path}"else"Unable to find #{file_path} to read failed line"endrescueSecurityError"Unable to read failed line"enddeffind_failed_linepath=File.expand_path(example.file_path)exception.backtrace.detectdo|line|match=line.match(/(.+?):(\d+)(|:\d+)/)match&&match[1].downcase==path.downcaseendendend# The `PendingExampleFixedNotification` extends `ExampleNotification` with# things useful for specs that pass when they are expected to fail.## @attr [RSpec::Core::Example] example the current example# @see ExampleNotificationclassPendingExampleFixedNotification<FailedExampleNotificationpublic_class_method:new# Returns the examples description## @return [String] The example descriptiondefdescription"#{example.full_description} FIXED"end# Returns the message generated for this failure line by line.## @return [Array(String)] The example failure messagedefmessage_lines["Expected pending '#{example.execution_result.pending_message}' to fail. No Error was raised."]end# Returns the message generated for this failure colorized line by line.## @param colorizer [#wrap] An object to colorize the message_lines by# @return [Array(String)] The example failure message colorizeddefcolorized_message_lines(colorizer=::RSpec::Core::Formatters::ConsoleCodes)message_lines.map{|line|colorizer.wrap(line,RSpec.configuration.fixed_color)}endend# The `GroupNotification` represents notifications sent by the reporter which# contain information about the currently running (or soon to be) example group# It is used by formatters to access information about that group.## @example# def example_group_started(notification)# puts "Hey I started #{notification.group.description}"# end# @attr group [RSpec::Core::ExampleGroup] the current groupGroupNotification=Struct.new(:group)# The `MessageNotification` encapsulates generic messages that the reporter# sends to formatters.## @attr message [String] the messageMessageNotification=Struct.new(:message)# The `SeedNotification` holds the seed used to randomize examples and# wether that seed has been used or not.## @attr seed [Fixnum] the seed used to randomize ordering# @attr used [Boolean] wether the seed has been used or notSeedNotification=Struct.new(:seed,:used)do# @api# @return [Boolean] has the seed been used?defseed_used?!!usedendprivate:used# @return [String] The seed information fully formatted in the way that# RSpec's built-in formatters emit.deffully_formatted"\nRandomized with seed #{seed}\n\n"endend# The `SummaryNotification` holds information about the results of running# a test suite. It is used by formatters to provide information at the end# of the test run.## @attr duration [Float] the time taken (in seconds) to run the suite# @attr examples [Array(RSpec::Core::Example)] the examples run# @attr failed_examples [Array(RSpec::Core::Example)] the failed examples# @attr pending_examples [Array(RSpec::Core::Example)] the pending examples# @attr load_time [Float] the number of seconds taken to boot RSpec# and load the spec filesSummaryNotification=Struct.new(:duration,:examples,:failed_examples,:pending_examples,:load_time)do# @api# @return [Fixnum] the number of examples rundefexample_count@example_count||=examples.sizeend# @api# @return [Fixnum] the number of failed examplesdeffailure_count@failure_count||=failed_examples.sizeend# @api# @return [Fixnum] the number of pending examplesdefpending_count@pending_count||=pending_examples.sizeend# @api# @return [String] A line summarising the result totals of the spec run.deftotals_linesummary=Formatters::Helpers.pluralize(example_count,"example")summary<<", "<<Formatters::Helpers.pluralize(failure_count,"failure")summary<<", #{pending_count} pending"ifpending_count>0summaryend# @api public## Wraps the results line with colors based on the configured# colors for failure, pending, and success. Defaults to red,# yellow, green accordingly.## @param colorizer [#wrap] An object which supports wrapping text with# specific colors.# @return [String] A colorized results line.defcolorized_totals_line(colorizer=::RSpec::Core::Formatters::ConsoleCodes)iffailure_count>0colorizer.wrap(totals_line,RSpec.configuration.failure_color)elsifpending_count>0colorizer.wrap(totals_line,RSpec.configuration.pending_color)elsecolorizer.wrap(totals_line,RSpec.configuration.success_color)endend# @api public## Formats failures into a rerunable command format.## @param colorizer [#wrap] An object which supports wrapping text with# specific colors.# @return [String] A colorized summary line.defcolorized_rerun_commands(colorizer=::RSpec::Core::Formatters::ConsoleCodes)"\nFailed examples:\n\n"+failed_examples.mapdo|example|colorizer.wrap("rspec #{example.location}",RSpec.configuration.failure_color)+" "+colorizer.wrap("# #{example.full_description}",RSpec.configuration.detail_color)end.join("\n")end# @return [String] a formatted version of the time it took to run the suitedefformatted_durationFormatters::Helpers.format_duration(duration)end# @return [String] a formatted version of the time it took to boot RSpec and# load the spec filesdefformatted_load_timeFormatters::Helpers.format_duration(load_time)end# @return [String] The summary information fully formatted in the way that# RSpec's built-in formatters emit.deffully_formatted(colorizer=::RSpec::Core::Formatters::ConsoleCodes)formatted="\nFinished in #{formatted_duration} "\"(files took #{formatted_load_time} to load)\n"\"#{colorized_totals_line(colorizer)}\n"unlessfailed_examples.empty?formatted<<colorized_rerun_commands(colorizer)<<"\n"endformattedendend# The `ProfileNotification` holds information about the results of running# a test suite when profiling is enabled. It is used by formatters to provide# information at the end of the test run for profiling information.## @attr duration [Float] the time taken (in seconds) to run the suite# @attr examples [Array(RSpec::Core::Example)] the examples run# @attr number_of_examples [Fixnum] the number of examples to profileProfileNotification=Struct.new(:duration,:examples,:number_of_examples)do# @return [Array(RSpec::Core::Example)] the slowest examplesdefslowest_examples@slowest_examples||=examples.sort_bydo|example|-example.execution_result.run_timeend.first(number_of_examples)end# @return [Float] the time taken (in seconds) to run the slowest examplesdefslow_duration@slow_duration||=slowest_examples.inject(0.0)do|i,e|i+e.execution_result.run_timeendend# @return [String] the percentage of total time takendefpercentage@percentage||=begintime_taken=slow_duration/duration'%.1f'%((time_taken.nan??0.0:time_taken)*100)endend# @return [Array(RSpec::Core::Example)] the slowest example groupsdefslowest_groups@slowest_groups||=calculate_slowest_groupsendprivatedefcalculate_slowest_groupsexample_groups={}examples.eachdo|example|location=example.example_group.parent_groups.last.metadata[:location]location_hash=example_groups[location]||=Hash.new(0)location_hash[:total_time]+=example.execution_result.run_timelocation_hash[:count]+=1unlesslocation_hash.has_key?(:description)location_hash[:description]=example.example_group.top_level_descriptionendend# stop if we've only one example groupreturn{}ifexample_groups.keys.length<=1example_groups.each_valuedo|hash|hash[:average]=hash[:total_time].to_f/hash[:count]endexample_groups.sort_by{|_,hash|-hash[:average]}.first(number_of_examples)endend# The `DeprecationNotification` is issued by the reporter when a deprecated# part of RSpec is encountered. It represents information about the deprecated# call site.## @attr message [String] A custom message about the deprecation# @attr deprecated [String] A custom message about the deprecation (alias of message)# @attr replacement [String] An optional replacement for the deprecation# @attr call_site [String] An optional call site from which the deprecation was issuedDeprecationNotification=Struct.new(:deprecated,:message,:replacement,:call_site)doprivate_class_method:new# @api# Convenience way to initialize the notificationdefself.from_hash(data)newdata[:deprecated],data[:message],data[:replacement],data[:call_site]endend# `NullNotification` represents a placeholder value for notifications that# currently require no information, but we may wish to extend in future.classNullNotificationendendend