Class: Rubydex::Linter::RuleTestCase

Inherits:
Minitest::Test
  • Object
show all
Defined in:
lib/rubydex/linter/rule_test_case.rb

Overview

Base test case for Rubydex linter rules.

Add expected diagnostics below the source:

assert_diagnostics(<<~RUBY)
  FOO = 123
  ^^^ Failure: FOO
RUBY

The carets mark the diagnostic range. Use ^{} for a zero-width range. Pass a hash to test multiple files. Repeat an annotation line for each message line. By default, FooTest tests the Foo rule.

Constant Summary collapse

DEFAULT_FILE =

: String

"test.rb"
ANNOTATION_PATTERN =

: Regexp

/\A(?<indent>\s*)(?<carets>(?<!\\)\^+|\^\{\})(?:\s(?<message>.*))?\z/

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name) ⇒ RuleTestCase

: (String) -> void



35
36
37
38
# File 'lib/rubydex/linter/rule_test_case.rb', line 35

def initialize(name)
  super
  @workspace_path = File.realpath(Dir.mktmpdir("rubydex-rule-test-")) #: String
end

Instance Attribute Details

#workspace_pathObject (readonly)

: String



32
33
34
# File 'lib/rubydex/linter/rule_test_case.rb', line 32

def workspace_path
  @workspace_path
end

Instance Method Details

#add_shared_source(sources) ⇒ Object

Registers sources that are loaded into the graph for every assertion. Shared sources must not contain caret annotations. Test-specific sources win on filename collision. : (Hash[String, String]) -> void



63
64
65
# File 'lib/rubydex/linter/rule_test_case.rb', line 63

def add_shared_source(sources)
  shared_sources.merge!(sources)
end

#assert_diagnostics(*args, &rule_builder) ⇒ Object

Runs the rule and compares every diagnostic location with the inline annotations in the corresponding source. : (*(String | Hash[String | Symbol, String])) ?{ (Graph) -> CustomRule } -> Array



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/rubydex/linter/rule_test_case.rb', line 81

def assert_diagnostics(*args, &rule_builder)
  sources = validated_shared_source.merge(normalize_sources(args))
  clean_per_file, expected_per_file = write_sources(sources)

  diagnostics = run_rule(&rule_builder)
  actual_per_file = annotations_for_diagnostics(diagnostics, clean_per_file)

  surprise_files = actual_per_file.keys - clean_per_file.keys - ignored_diagnostic_files
  refute_predicate(
    surprise_files,
    :any?,
    "Diagnostics in unexpected files: #{surprise_files.inspect}",
  )

  clean_per_file.each do |filename, clean|
    expected = render_annotated(clean, expected_per_file[filename] || [])
    actual = render_annotated(clean, actual_per_file[filename] || [])
    assert_equal(expected, actual, "Mismatch in #{filename}")
  end

  diagnostics
end

#assert_handles_missing_required_dependency(dependency, *args, after_excluding: [], &rule_builder) ⇒ Object

: ( | String, | *(String | Hash[String | Symbol, String]), | ?after_excluding: Array, | ) ?{ (Graph) -> CustomRule } -> void



123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/rubydex/linter/rule_test_case.rb', line 123

def assert_handles_missing_required_dependency(dependency, *args, after_excluding: [], &rule_builder)
  sources = validated_shared_source.dup #: Hash[String, String]
  after_excluding.each { |filename| sources.delete(filename) }
  sources.merge!(normalize_sources(args))
  write_sources(sources)

  error = assert_raises(MissingGraphDependencyError) do
    run_rule(&rule_builder)
  end

  expected_message = MissingGraphDependencyError.new(rule_class.rule_name, dependency).message
  assert_equal(expected_message, error.message)
end

#assert_no_diagnostics(*args, &rule_builder) ⇒ Object

Runs the rule and asserts no diagnostics are reported in the provided sources. Sources must not contain caret annotations. : (*(String | Hash[String | Symbol, String])) ?{ (Graph) -> CustomRule } -> Array



107
108
109
110
111
112
113
114
115
116
# File 'lib/rubydex/linter/rule_test_case.rb', line 107

def assert_no_diagnostics(*args, &rule_builder)
  sources = validated_shared_source.merge(normalize_sources(args))
  write_sources(sources)

  diagnostics = run_rule(&rule_builder)
  ignored = ignored_diagnostic_files
  reported = diagnostics.reject { |diagnostic| ignored.include?(diagnostic.location.to_file_path) }
  assert_empty(reported, "Expected no diagnostics, got #{reported.map(&:message).inspect}")
  diagnostics
end

#ignored_diagnostic_filesObject

Returns absolute file paths whose diagnostics should be ignored. : -> Array



69
70
71
# File 'lib/rubydex/linter/rule_test_case.rb', line 69

def ignored_diagnostic_files
  @ignored_diagnostic_files ||= [] #: Array[String]?
end

#rule_classObject

: -> singleton(CustomRule)



48
49
50
51
52
53
54
55
56
57
# File 'lib/rubydex/linter/rule_test_case.rb', line 48

def rule_class
  @rule_class ||= begin
    name = self.class.to_s.delete_suffix("Test")
    if name == self.class.to_s
      raise "Could not infer rule class from #{self.class}; override `#rule_class`"
    end

    Object.const_get(name) #: as singleton(CustomRule)
  end #: singleton(CustomRule)?
end

#rule_configObject

: -> LinterConfig



74
75
76
# File 'lib/rubydex/linter/rule_test_case.rb', line 74

def rule_config
  @rule_config ||= LinterConfig.new({}) #: LinterConfig?
end

#teardownObject

: -> void



41
42
43
44
45
# File 'lib/rubydex/linter/rule_test_case.rb', line 41

def teardown
  super
ensure
  FileUtils.remove_entry(workspace_path)
end