solargraph_test_coverage 0.2.7 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b7c7bd9d2a4a3b6184942c28ce619023f098a515ca356e8534788adb93f9cb96
4
- data.tar.gz: 11850e7e7184658995b6e15479e8c3c87527f4b945c8b8bc9ab446242c5dba32
3
+ metadata.gz: 5aba663e3f7dca496c3c527adc1be8024353932ef806cf6f9eed9c70bd44f120
4
+ data.tar.gz: d4a031ffbb4e97f2d13eff2552760d3beae56413b1666e6e84edd1c182919c1e
5
5
  SHA512:
6
- metadata.gz: d4901017237a8e5cfc49e8a0228776c48929304ebb043bdd9c89500e56db2c4d7349b01838c911a3080900ca1d657b07f2d29a921d0cc39b2da96efe517d1443
7
- data.tar.gz: c00ecc7e6c928c42a93abda3f976e3d0811934f21f21362a40711dbc449244a3e443d683a6c6204955ea3ec28989226f41d5b371344238594d78c86b3fb98987
6
+ metadata.gz: 71edec840ad30fb0e2852c0f6ad3928b961631f546d02060500001e4c8e6bc0ff9f5bb3f71f4e4261ed00d483d6d4c6c3e25fc7dca35cd970a386b77ba28df16
7
+ data.tar.gz: 1abf9451d0fdcaca5f5f4ec52a6ffb918391bfdc092a09c77778b76dcc137b41c0e17719d7141182a92f472f2505d56f2f9b93fc0547748f681a1f1ba226bd78
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- solargraph_test_coverage (0.2.6)
4
+ solargraph_test_coverage (0.3.0)
5
5
  solargraph (~> 0.40, > 0.40)
6
6
 
7
7
  GEM
@@ -41,7 +41,7 @@ GEM
41
41
  rubocop-ast (1.11.0)
42
42
  parser (>= 3.0.1.1)
43
43
  ruby-progressbar (1.11.0)
44
- solargraph (0.43.0)
44
+ solargraph (0.43.2)
45
45
  backport (~> 1.2)
46
46
  benchmark
47
47
  bundler (>= 1.17.2)
@@ -58,7 +58,7 @@ GEM
58
58
  yard (~> 0.9, >= 0.9.24)
59
59
  thor (1.1.0)
60
60
  tilt (2.0.10)
61
- unicode-display_width (2.0.0)
61
+ unicode-display_width (2.1.0)
62
62
  yard (0.9.26)
63
63
 
64
64
  PLATFORMS
@@ -6,12 +6,14 @@ module SolargraphTestCoverage
6
6
 
7
7
  DEFAULTS = {
8
8
  'preload_rails' => true, # can be true or false - performance optimization
9
+ 'debug' => false, # can be true or false - shows debug messages when ChildFailedError is raised
9
10
  'test_framework' => 'rspec', # can be 'rspec' or 'minitest'
10
11
  'coverage' => [ # All diagnostics are enabled by default
11
12
  'line', # Specifying an array with fewer diagnostics will overwrite this
12
13
  'branch',
13
14
  'test_failing',
14
- 'test_missing'
15
+ 'test_missing',
16
+ 'example_failing'
15
17
  ],
16
18
  'exclude_paths' => [ # don't attempt to find/run a spec for files that match these paths
17
19
  'app/controller',
@@ -19,6 +21,10 @@ module SolargraphTestCoverage
19
21
  ]
20
22
  }.freeze
21
23
 
24
+ def debug?
25
+ plugin_config['debug']
26
+ end
27
+
22
28
  def preload_rails?
23
29
  plugin_config['preload_rails']
24
30
  end
@@ -43,6 +49,10 @@ module SolargraphTestCoverage
43
49
  plugin_config['coverage'].include? 'test_missing'
44
50
  end
45
51
 
52
+ def example_failing_coverage?
53
+ plugin_config['coverage'].include? 'example_failing'
54
+ end
55
+
46
56
  def test_framework
47
57
  plugin_config['test_framework']
48
58
  end
@@ -118,7 +128,7 @@ module SolargraphTestCoverage
118
128
  end
119
129
 
120
130
  def test_path
121
- ReporterHelpers.test_path
131
+ FileHelpers.test_path
122
132
  end
123
133
  end
124
134
  end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolargraphTestCoverage
4
+ module DiagnosticMessages
5
+ def line_coverage_warning(source, line)
6
+ return unless Config.line_coverage?
7
+
8
+ {
9
+ range: range(line, 0, line, source.code.lines[line].length),
10
+ severity: Solargraph::Diagnostics::Severities::WARNING,
11
+ source: 'TestCoverage',
12
+ message: 'Line is missing test coverage'
13
+ }
14
+ end
15
+
16
+ def branch_coverage_warning(source, report)
17
+ return unless Config.branch_coverage?
18
+
19
+ {
20
+ range: range(report[:line] - 1, 0, report[:line] - 1, source.code.lines[report[:line] - 1].length),
21
+ severity: Solargraph::Diagnostics::Severities::WARNING,
22
+ source: 'TestCoverage',
23
+ message: "'#{report[:type].upcase}' branch is missing test coverage"
24
+ }
25
+ end
26
+
27
+ def test_failing_error(source)
28
+ return unless Config.test_failing_coverage?
29
+
30
+ {
31
+ range: range(0, 0, 0, source.code.lines[0].length),
32
+ severity: Solargraph::Diagnostics::Severities::ERROR,
33
+ source: 'TestCoverage',
34
+ message: 'Unit Test is currently failing.'
35
+ }
36
+ end
37
+
38
+ def test_missing_error(source)
39
+ return unless Config.test_missing_coverage?
40
+
41
+ {
42
+ range: range(0, 0, 0, source.code.lines[0].length),
43
+ severity: Solargraph::Diagnostics::Severities::HINT,
44
+ source: 'TestCoverage',
45
+ message: "No test file found at '#{FileHelpers.test_file(source).sub("#{Dir.pwd}/", '')}'"
46
+ }
47
+ end
48
+
49
+ def example_failing_error(example, source)
50
+ return unless Config.example_failing_coverage?
51
+
52
+ {
53
+ range: range(example[:line_number], 0, example[:line_number], source.code.lines[example[:line_number]].length),
54
+ severity: Solargraph::Diagnostics::Severities::ERROR,
55
+ source: 'ExampleStatus',
56
+ message: example[:message]
57
+ }
58
+ end
59
+
60
+ def debug_message(exception, source)
61
+ {
62
+ range: range(0, 0, 0, source.code.lines[0].length),
63
+ severity: Solargraph::Diagnostics::Severities::ERROR,
64
+ source: 'SolargraphTestCoverage Plugin',
65
+ message: "DEBUG: (ChildFailedError) #{exception.message}"
66
+ }
67
+ end
68
+
69
+ private
70
+
71
+ def range(start_line, start_column, end_line, end_column)
72
+ Solargraph::Range.from_to(start_line, start_column, end_line, end_column).to_hash
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ # example_status reporter for Solargraph
4
+ module SolargraphTestCoverage
5
+ class ExampleStatusReporter < Solargraph::Diagnostics::Base
6
+ include ReporterHelpers
7
+ include ReporterGuards
8
+ include DiagnosticMessages
9
+
10
+ def diagnose(source, _api_map)
11
+ if source.code.empty? || using_debugger?(source) || !is_test_file?(source) || is_test_support_file?(source)
12
+ return []
13
+ end
14
+
15
+ example_failing_errors(source, run_test(source, source.location.filename))
16
+ rescue ChildFailedError => e
17
+ Config.debug? ? [debug_message(e, source)] : []
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolargraphTestCoverage
4
+ module FileHelpers
5
+ extend self
6
+
7
+ def test_file(source)
8
+ relative_filepath = source.location.filename.sub(Dir.pwd, '').split('/').reject(&:empty?)
9
+
10
+ if relative_filepath.first == Config.test_dir && relative_filepath.last.end_with?(Config.test_file_suffix)
11
+ return source.location.filename
12
+ end
13
+
14
+ relative_filepath[0] = Config.test_dir
15
+ File.join(Dir.pwd, relative_filepath.join('/')).sub('.rb', Config.test_file_suffix)
16
+ end
17
+
18
+ def test_path
19
+ File.join(Dir.pwd, Config.test_dir)
20
+ end
21
+ end
22
+ end
@@ -24,17 +24,20 @@ module SolargraphTestCoverage
24
24
  result = @read.read
25
25
 
26
26
  Process.wait(pid)
27
- raise ChildFailedError if result.nil?
27
+ raise ChildFailedError, "Couldn't read pipe" if result.nil?
28
28
 
29
- Marshal.load(result).tap { |r| raise ChildFailedError if r.nil? }
29
+ Marshal.load(result).tap do |r|
30
+ raise ChildFailedError, "Marshal.load(result) returned nil" if r.nil?
31
+ raise ChildFailedError, r.message if r.is_a? Exception
32
+ end
30
33
  end
31
34
 
32
35
  private
33
36
 
34
37
  def run_block_with_timeout(&block)
35
- Timeout.timeout(30, &block)
36
- rescue StandardError
37
- nil
38
+ Timeout.timeout(30000, &block)
39
+ rescue StandardError => e
40
+ e
38
41
  end
39
42
  end
40
43
  end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolargraphTestCoverage
4
+ # Some guard functions for the diagnostics
5
+ module ReporterGuards
6
+ def has_test_file?(source)
7
+ File.file? FileHelpers.test_file(source)
8
+ end
9
+
10
+ def is_test_file?(source)
11
+ source.location.filename.start_with? FileHelpers.test_path
12
+ end
13
+
14
+ def is_test_support_file?(source)
15
+ is_test_file?(source) && !source.location.filename.end_with?(Config.test_file_suffix)
16
+ end
17
+
18
+ def exclude_file?(source)
19
+ Config.exclude_paths.any? { |path| source.location.filename.sub(Dir.pwd, '').include? path }
20
+ end
21
+
22
+ def using_debugger?(source)
23
+ source.code.include?('binding.pry') ||
24
+ source.code.include?('byebug') ||
25
+ source.code.include?('debugger')
26
+ end
27
+ end
28
+ end
@@ -3,42 +3,16 @@
3
3
  module SolargraphTestCoverage
4
4
  # Some helper functions for the diagnostics
5
5
  module ReporterHelpers
6
- def exclude_file?(source_filename)
7
- return true if source_filename.start_with? test_path
8
-
9
- Config.exclude_paths.any? { |path| source_filename.sub(Dir.pwd, '').include? path }
10
- end
11
-
12
- def using_debugger?(source)
13
- source.code.include?('binding.pry') ||
14
- source.code.include?('byebug') ||
15
- source.code.include?('debugger')
16
- end
17
-
18
- def test_file(source)
19
- relative_filepath = source.location.filename.sub(Dir.pwd, '').split('/').reject(&:empty?)
20
- relative_filepath[0] = Config.test_dir
21
-
22
- File.join(Dir.pwd, relative_filepath.join('/')).sub('.rb', Config.test_file_suffix)
23
- end
24
-
25
6
  # @return [Hash]
26
- def run_test(source)
7
+ def run_test(source, test_file)
27
8
  ForkProcess.call do
28
9
  Coverage.start(lines: true, branches: true)
29
- runner = TestRunner.with(test_file(source)).run!
30
- Coverage.result.fetch(source.location.filename, {}).merge({ test_status: runner.passed? })
31
- end
32
- end
10
+ runner = TestRunner.with(test_file).run!
33
11
 
34
- def messages(source, results)
35
- messages = [
36
- line_warnings(source, results),
37
- branch_warnings(source, results),
38
- test_passing_error(source, results)
39
- ]
40
-
41
- messages.flatten.compact
12
+ Coverage.result
13
+ .fetch(source.location.filename, {})
14
+ .merge({ test_status: runner.passed?, failed_examples: runner.failed_examples })
15
+ end
42
16
  end
43
17
 
44
18
  def line_warnings(source, results)
@@ -53,6 +27,11 @@ module SolargraphTestCoverage
53
27
  results[:test_status] ? [] : [test_failing_error(source)]
54
28
  end
55
29
 
30
+ def example_failing_errors(source, results)
31
+ results.fetch(:failed_examples, [])
32
+ .map { |example| example_failing_error(example, source) }
33
+ end
34
+
56
35
  # Adapted from SingleCov
57
36
  # Coverage returns nil for untestable lines (like 'do', 'end', 'if' keywords)
58
37
  # otherwise returns int showing how many times a line was called
@@ -71,17 +50,5 @@ module SolargraphTestCoverage
71
50
  def uncovered_branches(results)
72
51
  Branch.build_from(results).reject(&:covered?)
73
52
  end
74
-
75
- def range(start_line, start_column, end_line, end_column)
76
- Solargraph::Range.from_to(start_line, start_column, end_line, end_column).to_hash
77
- end
78
-
79
- def self.test_path
80
- File.join(Dir.pwd, Config.test_dir)
81
- end
82
-
83
- def test_path
84
- ReporterHelpers.test_path
85
- end
86
53
  end
87
54
  end
@@ -4,61 +4,22 @@
4
4
  module SolargraphTestCoverage
5
5
  class TestCoverageReporter < Solargraph::Diagnostics::Base
6
6
  include ReporterHelpers
7
+ include ReporterGuards
8
+ include DiagnosticMessages
7
9
 
8
10
  def diagnose(source, _api_map)
9
- return [] if source.code.empty? || using_debugger?(source) || exclude_file?(source.location.filename)
10
- return [test_missing_error(source)] unless File.file?(test_file(source))
11
-
12
- results = run_test(source)
13
- messages(source, results)
14
- rescue ChildFailedError
15
- []
16
- end
17
-
18
- private
19
-
20
- def line_coverage_warning(source, line)
21
- return unless Config.line_coverage?
22
-
23
- {
24
- range: range(line, 0, line, source.code.lines[line].length),
25
- severity: Solargraph::Diagnostics::Severities::WARNING,
26
- source: 'TestCoverage',
27
- message: 'Line is missing test coverage'
28
- }
29
- end
30
-
31
- def branch_coverage_warning(source, report)
32
- return unless Config.branch_coverage?
33
-
34
- {
35
- range: range(report[:line] - 1, 0, report[:line] - 1, source.code.lines[report[:line] - 1].length),
36
- severity: Solargraph::Diagnostics::Severities::WARNING,
37
- source: 'TestCoverage',
38
- message: "'#{report[:type].upcase}' branch is missing test coverage"
39
- }
40
- end
41
-
42
- def test_failing_error(source)
43
- return unless Config.test_failing_coverage?
44
-
45
- {
46
- range: range(0, 0, 0, source.code.lines[0].length),
47
- severity: Solargraph::Diagnostics::Severities::ERROR,
48
- source: 'TestCoverage',
49
- message: 'Unit Test is currently failing.'
50
- }
51
- end
52
-
53
- def test_missing_error(source)
54
- return unless Config.test_missing_coverage?
55
-
56
- {
57
- range: range(0, 0, 0, source.code.lines[0].length),
58
- severity: Solargraph::Diagnostics::Severities::HINT,
59
- source: 'TestCoverage',
60
- message: "No test file found at '#{test_file(source).sub("#{Dir.pwd}/", '')}'"
61
- }
11
+ return [] if source.code.empty? || using_debugger?(source) || exclude_file?(source) || is_test_file?(source)
12
+ return [test_missing_error(source)] unless has_test_file?(source)
13
+
14
+ results = run_test(source, FileHelpers.test_file(source))
15
+
16
+ [
17
+ line_warnings(source, results),
18
+ branch_warnings(source, results),
19
+ test_passing_error(source, results)
20
+ ].flatten.compact
21
+ rescue ChildFailedError => e
22
+ Config.debug? ? [debug_message(e, source)] : []
62
23
  end
63
24
  end
64
25
  end
@@ -15,14 +15,15 @@ module SolargraphTestCoverage
15
15
  def initialize(test_file)
16
16
  @test_file = test_file
17
17
  @result = nil
18
+ @output = StringIO.new
18
19
  end
19
20
 
20
21
  def run!
21
- @result = test_framework_runner.run(test_options)
22
+ @result = test_framework_runner.run(test_options, $stderr, @output)
22
23
  self
23
24
  end
24
25
 
25
- def test_options
26
+ def failed_examples
26
27
  raise NotImplementedError
27
28
  end
28
29
 
@@ -30,21 +31,43 @@ module SolargraphTestCoverage
30
31
  raise NotImplementedError
31
32
  end
32
33
 
34
+ private
35
+
36
+ def test_options
37
+ raise NotImplementedError
38
+ end
39
+
33
40
  def test_framework_runner
34
41
  raise NotImplementedError
35
42
  end
43
+
44
+ def output
45
+ return if @output.string.empty?
46
+
47
+ JSON.parse @output.string
48
+ end
36
49
  end
37
50
 
38
51
  # Test Runner Subclass for RSpec
39
52
  class RSpecRunner < TestRunner
40
- def test_options
41
- [@test_file, '-o', '/dev/null']
53
+ def failed_examples
54
+ return unless output
55
+
56
+ output['examples']
57
+ .select { |example| example['status'] == 'failed' }
58
+ .map { |example| { line_number: example['line_number'] - 1, message: example.dig('exception', 'message') } }
42
59
  end
43
60
 
44
61
  def passed?
45
62
  @result&.zero?
46
63
  end
47
64
 
65
+ private
66
+
67
+ def test_options
68
+ [@test_file, '--format', 'json']
69
+ end
70
+
48
71
  def test_framework_runner
49
72
  RSpec::Core::Runner
50
73
  end
@@ -52,14 +75,21 @@ module SolargraphTestCoverage
52
75
 
53
76
  # Test Runner Subclass for Minitest
54
77
  class MinitestRunner < TestRunner
55
- def test_options
56
- [@test_file]
78
+ # TODO
79
+ def failed_examples
80
+ []
57
81
  end
58
82
 
59
83
  def passed?
60
84
  @result
61
85
  end
62
86
 
87
+ private
88
+
89
+ def test_options
90
+ [@test_file]
91
+ end
92
+
63
93
  def test_framework_runner
64
94
  Minitest
65
95
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SolargraphTestCoverage
4
- VERSION = '0.2.7'
4
+ VERSION = '0.3.0'
5
5
  end
@@ -4,9 +4,13 @@ require 'solargraph_test_coverage/version'
4
4
  require 'solargraph_test_coverage/branch'
5
5
  require 'solargraph_test_coverage/fork_process'
6
6
  require 'solargraph_test_coverage/reporter_helpers'
7
+ require 'solargraph_test_coverage/reporter_guards'
8
+ require 'solargraph_test_coverage/file_helpers'
7
9
  require 'solargraph_test_coverage/config'
8
10
  require 'solargraph_test_coverage/test_runner'
11
+ require 'solargraph_test_coverage/diagnostic_messages'
9
12
  require 'solargraph_test_coverage/test_coverage_reporter'
13
+ require 'solargraph_test_coverage/example_status_reporter'
10
14
 
11
15
  require 'solargraph'
12
16
  require 'coverage'
@@ -21,4 +25,5 @@ module SolargraphTestCoverage
21
25
  Config.preload_rails! if Config.preload_rails?
22
26
 
23
27
  Solargraph::Diagnostics.register 'test_coverage', TestCoverageReporter
28
+ Solargraph::Diagnostics.register 'example_status', ExampleStatusReporter
24
29
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solargraph_test_coverage
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.7
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cameron Kolkey
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-09-18 00:00:00.000000000 Z
11
+ date: 2021-09-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: solargraph
@@ -47,7 +47,11 @@ files:
47
47
  - lib/solargraph_test_coverage.rb
48
48
  - lib/solargraph_test_coverage/branch.rb
49
49
  - lib/solargraph_test_coverage/config.rb
50
+ - lib/solargraph_test_coverage/diagnostic_messages.rb
51
+ - lib/solargraph_test_coverage/example_status_reporter.rb
52
+ - lib/solargraph_test_coverage/file_helpers.rb
50
53
  - lib/solargraph_test_coverage/fork_process.rb
54
+ - lib/solargraph_test_coverage/reporter_guards.rb
51
55
  - lib/solargraph_test_coverage/reporter_helpers.rb
52
56
  - lib/solargraph_test_coverage/test_coverage_reporter.rb
53
57
  - lib/solargraph_test_coverage/test_runner.rb