# frozen_string_literal: true# Laziness copied from rubocop source coderequire'rubocop/rspec/expect_offense'require'rubocop/cop/legacy/corrector'moduleRuboCopmoduleMinitest# Mixin for `assert_offense` and `assert_no_offenses`## This mixin makes it easier to specify strict offense assertions# in a declarative and visual fashion. Just type out the code that# should generate an offense, annotate code by writing '^'s# underneath each character that should be highlighted, and follow# the carets with a string (separated by a space) that is the# message of the offense. You can include multiple offenses in# one code snippet.## @example Usage## assert_offense(<<~RUBY)# class FooTest < Minitest::Test# def test_do_something# assert_equal(nil, somestuff)# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer using `assert_nil(somestuff)`.# end# end# RUBY## Autocorrection can be tested using `assert_correction` after# `assert_offense`.## @example `assert_offense` and `assert_correction`## assert_offense(<<~RUBY)# class FooTest < Minitest::Test# def test_do_something# assert_equal(nil, somestuff)# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer using `assert_nil(somestuff)`.# end# end# RUBY## assert_correction(<<~RUBY)# class FooTest < Minitest::Test# def test_do_something# assert_nil(somestuff)# end# end# RUBY## If you do not want to specify an offense then use the# companion method `assert_no_offenses`. This method is a much# simpler assertion since it just inspects the source and checks# that there were no offenses. The `assert_offense` method has# to do more work by parsing out lines that contain carets.## If the code produces an offense that could not be autocorrected, you can# use `assert_no_corrections` after `assert_offense`.## @example `assert_offense` and `assert_no_corrections`## assert_offense(<<~RUBY)# class FooTest < Minitest::Test# def test_do_something# assert_equal(nil, somestuff)# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer using `assert_nil(somestuff)`.# end# end# RUBY## assert_no_corrections## rubocop:disable Metrics/ModuleLengthmoduleAssertOffenseprivatedefcop@cop||=begincop_name=self.class.to_s.delete_suffix('Test')raise"Cop not defined: #{cop_name}"unlessRuboCop::Cop::Minitest.const_defined?(cop_name)RuboCop::Cop::Minitest.const_get(cop_name).new(configuration)endenddefformat_offense(source,**replacements)replacements.eachdo|keyword,value|value=value.to_ssource=source.gsub("%{#{keyword}}",value).gsub("^{#{keyword}}",'^'*value.size).gsub("_{#{keyword}}",' '*value.size)endsourceenddefassert_no_offenses(source,file=nil)setup_assertionoffenses=inspect_source(source,cop,file)expected_annotations=RuboCop::RSpec::ExpectOffense::AnnotatedSource.parse(source)actual_annotations=expected_annotations.with_offense_annotations(offenses)assert_equal(source,actual_annotations.to_s)enddefassert_offense(source,file=nil,**replacements)setup_assertionenable_autocorrectsource=format_offense(source,**replacements)expected_annotations=RuboCop::RSpec::ExpectOffense::AnnotatedSource.parse(source)ifexpected_annotations.plain_source==sourceraise'Use `assert_no_offenses` to assert that no offenses are found'end@processed_source=parse_source!(expected_annotations.plain_source,file)@offenses=_investigate(cop,@processed_source)actual_annotations=expected_annotations.with_offense_annotations(@offenses)assert_equal(expected_annotations.to_s,actual_annotations.to_s)enddef_investigate(cop,processed_source)team=RuboCop::Cop::Team.new([cop],configuration,raise_error: true)report=team.investigate(processed_source)@last_corrector=report.correctors.first||RuboCop::Cop::Corrector.new(processed_source)report.offensesenddefenable_autocorrectcop.instance_variable_get(:@options)[:autocorrect]=trueenddefassert_correction(correction,loop: true)raise'`assert_correction` must follow `assert_offense`'unless@processed_sourceiteration=0new_source=loopdoiteration+=1corrected_source=@last_corrector.rewritebreakcorrected_sourceunlessloopbreakcorrected_sourceif@last_corrector.empty?||corrected_source==@processed_source.buffer.sourceifiteration>RuboCop::Runner::MAX_ITERATIONSraiseRuboCop::Runner::InfiniteCorrectionLoop.new(@processed_source.path,[@offenses])end# Prepare for next loop@processed_source=parse_source!(corrected_source,@processed_source.path)_investigate(cop,@processed_source)endassert_equal(correction,new_source)enddefassert_no_correctionsraise'`assert_no_corrections` must follow `assert_offense`'unless@processed_sourcereturnif@last_corrector.empty?# This is just here for a pretty diff if the source actually got changednew_source=@last_corrector.rewriteassert_equal(@processed_source.buffer.source,new_source)# There is an infinite loop if a corrector is present that did not make# any changes. It will cause the same offense/correction on the next loop.raiseRuboCop::Runner::InfiniteCorrectionLoop.new(@processed_source.path,[@offenses])enddefsetup_assertionRuboCop::Formatter::DisabledConfigFormatter.config_to_allow_offenses={}RuboCop::Formatter::DisabledConfigFormatter.detected_styles={}enddefinspect_source(source,cop,file=nil)processed_source=parse_source!(source,file)raise'Error parsing example code'unlessprocessed_source.valid_syntax?_investigate(cop,processed_source)enddefinvestigate(cop,processed_source)needed=Hash.new{|h,k|h[k]=[]}Array(cop.class.joining_forces).each{|force|needed[force]<<cop}forces=needed.mapdo|force_class,joining_cops|force_class.new(joining_cops)endcommissioner=RuboCop::Cop::Commissioner.new([cop],forces,raise_error: true)commissioner.investigate(processed_source)commissionerenddefparse_source!(source,file=nil)iffile.respond_to?(:write)file.write(source)file.rewindfile=file.pathendprocessed_source=RuboCop::ProcessedSource.new(source,ruby_version,file,parser_engine: parser_engine)processed_source.config=configurationprocessed_source.registry=registryprocessed_sourceenddefconfiguration@configuration||=ifdefined?(config)configelseRuboCop::Config.new({},"#{Dir.pwd}/.rubocop.yml")endenddefregistry@registry||=begincops=configuration.keys.map{|cop|RuboCop::Cop::Registry.global.find_by_cop_name(cop)}cops<<cop_classifdefined?(cop_class)&&!cops.include?(cop_class)cops.compact!RuboCop::Cop::Registry.new(cops)endenddefruby_version# Prism supports parsing Ruby 3.3+.ENV['PARSER_ENGINE']=='parser_prism'?3.3:RuboCop::TargetRuby::DEFAULT_VERSIONenddefparser_engineENV.fetch('PARSER_ENGINE',:parser_whitequark).to_symendend# rubocop:enable Metrics/ModuleLengthendend