module RuboCop::Minitest::AssertOffense
def _investigate(cop, processed_source)
def _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.offenses end
def assert_correction(correction, loop: true)
def assert_correction(correction, loop: true) raise '`assert_correction` must follow `assert_offense`' unless @processed_source iteration = 0 new_source = loop do iteration += 1 corrected_source = @last_corrector.rewrite break corrected_source unless loop break corrected_source if @last_corrector.empty? || corrected_source == @processed_source.buffer.source if iteration > RuboCop::Runner::MAX_ITERATIONS raise RuboCop::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) end assert_equal(correction, new_source) end
def assert_no_corrections
def assert_no_corrections raise '`assert_no_corrections` must follow `assert_offense`' unless @processed_source return if @last_corrector.empty? # This is just here for a pretty diff if the source actually got changed new_source = @last_corrector.rewrite assert_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. raise RuboCop::Runner::InfiniteCorrectionLoop.new(@processed_source.path, [@offenses]) end
def assert_no_offenses(source, file = nil)
def assert_no_offenses(source, file = nil) setup_assertion offenses = 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) end
def assert_offense(source, file = nil, **replacements)
def assert_offense(source, file = nil, **replacements) setup_assertion enable_autocorrect source = format_offense(source, **replacements) expected_annotations = RuboCop::RSpec::ExpectOffense::AnnotatedSource.parse(source) if expected_annotations.plain_source == source raise '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) end
def configuration
def configuration @configuration ||= if defined?(config) config else RuboCop::Config.new({}, "#{Dir.pwd}/.rubocop.yml") end end
def cop
def cop @cop ||= begin cop_name = self.class.to_s.delete_suffix('Test') raise "Cop not defined: #{cop_name}" unless RuboCop::Cop::Minitest.const_defined?(cop_name) RuboCop::Cop::Minitest.const_get(cop_name).new(configuration) end end
def enable_autocorrect
def enable_autocorrect cop.instance_variable_get(:@options)[:autocorrect] = true end
def format_offense(source, **replacements)
def format_offense(source, **replacements) replacements.each do |keyword, value| value = value.to_s source = source.gsub("%{#{keyword}}", value) .gsub("^{#{keyword}}", '^' * value.size) .gsub("_{#{keyword}}", ' ' * value.size) end source end
def inspect_source(source, cop, file = nil)
def inspect_source(source, cop, file = nil) processed_source = parse_source!(source, file) raise 'Error parsing example code' unless processed_source.valid_syntax? _investigate(cop, processed_source) end
def investigate(cop, processed_source)
def investigate(cop, processed_source) needed = Hash.new { |h, k| h[k] = [] } Array(cop.class.joining_forces).each { |force| needed[force] << cop } forces = needed.map do |force_class, joining_cops| force_class.new(joining_cops) end commissioner = RuboCop::Cop::Commissioner.new([cop], forces, raise_error: true) commissioner.investigate(processed_source) commissioner end
def parse_source!(source, file = nil)
def parse_source!(source, file = nil) if file.respond_to?(:write) file.write(source) file.rewind file = file.path end processed_source = RuboCop::ProcessedSource.new(source, ruby_version, file, parser_engine: parser_engine) processed_source.config = configuration processed_source.registry = registry processed_source end
def parser_engine
def parser_engine ENV.fetch('PARSER_ENGINE', :parser_whitequark).to_sym end
def registry
def registry @registry ||= begin cops = configuration.keys.map { |cop| RuboCop::Cop::Registry.global.find_by_cop_name(cop) } cops << cop_class if defined?(cop_class) && !cops.include?(cop_class) cops.compact! RuboCop::Cop::Registry.new(cops) end end
def ruby_version
def ruby_version # Prism is the default backend parser for Ruby 3.4+. ENV['PARSER_ENGINE'] == 'parser_prism' ? 3.4 : RuboCop::TargetRuby::DEFAULT_VERSION end
def setup_assertion
def setup_assertion RuboCop::Formatter::DisabledConfigFormatter.config_to_allow_offenses = {} RuboCop::Formatter::DisabledConfigFormatter.detected_styles = {} end