solargraph_test_coverage 0.2.7 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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