sixth_sense 0.1.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.
Files changed (56) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +125 -0
  4. data/exe/sixth_sense +7 -0
  5. data/lib/sixth_sense/adapters/minitest.rb +145 -0
  6. data/lib/sixth_sense/adapters/rspec.rb +373 -0
  7. data/lib/sixth_sense/adapters/test_unit.rb +142 -0
  8. data/lib/sixth_sense/analysis_context.rb +35 -0
  9. data/lib/sixth_sense/analysis_runner.rb +141 -0
  10. data/lib/sixth_sense/analyzer.rb +85 -0
  11. data/lib/sixth_sense/analyzers/adequacy_checked_coverage.rb +39 -0
  12. data/lib/sixth_sense/analyzers/adequacy_coverage.rb +63 -0
  13. data/lib/sixth_sense/analyzers/adequacy_mutation.rb +141 -0
  14. data/lib/sixth_sense/analyzers/quality_assertion_density.rb +41 -0
  15. data/lib/sixth_sense/analyzers/quality_flakiness.rb +53 -0
  16. data/lib/sixth_sense/analyzers/quality_test_smells.rb +253 -0
  17. data/lib/sixth_sense/analyzers/redundancy_clone.rb +54 -0
  18. data/lib/sixth_sense/analyzers/redundancy_coverage.rb +39 -0
  19. data/lib/sixth_sense/analyzers/redundancy_mutation.rb +39 -0
  20. data/lib/sixth_sense/analyzers/redundancy_requirement.rb +70 -0
  21. data/lib/sixth_sense/changed_files.rb +45 -0
  22. data/lib/sixth_sense/cli.rb +229 -0
  23. data/lib/sixth_sense/config.rb +91 -0
  24. data/lib/sixth_sense/engines/mutant.rb +258 -0
  25. data/lib/sixth_sense/framework_adapter.rb +55 -0
  26. data/lib/sixth_sense/guardrail/baseline.rb +135 -0
  27. data/lib/sixth_sense/guardrail/evaluator.rb +69 -0
  28. data/lib/sixth_sense/model.rb +264 -0
  29. data/lib/sixth_sense/mutation_cache.rb +93 -0
  30. data/lib/sixth_sense/mutation_engine.rb +52 -0
  31. data/lib/sixth_sense/mutation_matrix_mutant_generator.rb +462 -0
  32. data/lib/sixth_sense/mutation_matrix_producer.rb +308 -0
  33. data/lib/sixth_sense/mutation_score_cache_writer.rb +75 -0
  34. data/lib/sixth_sense/rake_task.rb +16 -0
  35. data/lib/sixth_sense/reporters/console.rb +31 -0
  36. data/lib/sixth_sense/reporters/html.rb +62 -0
  37. data/lib/sixth_sense/reporters/json.rb +18 -0
  38. data/lib/sixth_sense/reporters/markdown.rb +34 -0
  39. data/lib/sixth_sense/reporters/sarif.rb +77 -0
  40. data/lib/sixth_sense/result.rb +86 -0
  41. data/lib/sixth_sense/runners/checked_coverage_estimator.rb +62 -0
  42. data/lib/sixth_sense/runners/checked_coverage_runner.rb +130 -0
  43. data/lib/sixth_sense/runners/checked_coverage_trace.rb +110 -0
  44. data/lib/sixth_sense/runners/coverage_runner.rb +220 -0
  45. data/lib/sixth_sense/runners/coverage_snapshot.rb +64 -0
  46. data/lib/sixth_sense/runners/minitest_checked_coverage_probe.rb +42 -0
  47. data/lib/sixth_sense/runners/minitest_coverage_probe.rb +49 -0
  48. data/lib/sixth_sense/runners/rspec_checked_coverage_probe.rb +26 -0
  49. data/lib/sixth_sense/runners/rspec_coverage_probe.rb +98 -0
  50. data/lib/sixth_sense/runners/test_unit_checked_coverage_probe.rb +43 -0
  51. data/lib/sixth_sense/runners/test_unit_coverage_probe.rb +51 -0
  52. data/lib/sixth_sense/scoring/aggregator.rb +117 -0
  53. data/lib/sixth_sense/source_location.rb +19 -0
  54. data/lib/sixth_sense/version.rb +5 -0
  55. data/lib/sixth_sense.rb +74 -0
  56. metadata +113 -0
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "checked_coverage_trace"
4
+
5
+ module SixthSense
6
+ module Runners
7
+ class MinitestCheckedCoverageProbe
8
+ class << self
9
+ def run(test_path, test_name)
10
+ status = CheckedCoverageTrace.capture do
11
+ require File.expand_path(test_path)
12
+ passed = run_test(test_name)
13
+ clear_minitest_runnables
14
+ passed ? 0 : 1
15
+ end
16
+ exit(status)
17
+ rescue LoadError => error
18
+ warn error.message
19
+ exit(127)
20
+ end
21
+
22
+ private
23
+
24
+ def run_test(test_name)
25
+ Minitest.seed ||= 0
26
+ klass = minitest_classes.find { |candidate| candidate.runnable_methods.include?(test_name) }
27
+ return false unless klass
28
+
29
+ klass.new(test_name).run.passed?
30
+ end
31
+
32
+ def minitest_classes
33
+ Minitest::Runnable.runnables.select { |candidate| candidate < Minitest::Test }
34
+ end
35
+
36
+ def clear_minitest_runnables
37
+ Minitest::Runnable.runnables.clear
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "coverage"
4
+ require "json"
5
+
6
+ require_relative "coverage_snapshot"
7
+
8
+ module SixthSense
9
+ module Runners
10
+ class MinitestCoverageProbe
11
+ class << self
12
+ def run(test_path, test_name)
13
+ Coverage.start(lines: true, branches: true)
14
+ require File.expand_path(test_path)
15
+ passed = run_test(test_name)
16
+ clear_minitest_runnables
17
+ result = Coverage.result
18
+ write_snapshot(result)
19
+ exit(passed ? 0 : 1)
20
+ rescue LoadError => error
21
+ warn error.message
22
+ exit(127)
23
+ end
24
+
25
+ private
26
+
27
+ def run_test(test_name)
28
+ Minitest.seed ||= 0
29
+ klass = minitest_classes.find { |candidate| candidate.runnable_methods.include?(test_name) }
30
+ return false unless klass
31
+
32
+ klass.new(test_name).run.passed?
33
+ end
34
+
35
+ def minitest_classes
36
+ Minitest::Runnable.runnables.select { |candidate| candidate < Minitest::Test }
37
+ end
38
+
39
+ def clear_minitest_runnables
40
+ Minitest::Runnable.runnables.clear
41
+ end
42
+
43
+ def write_snapshot(result)
44
+ File.write(ENV.fetch("SIXTH_SENSE_COVERAGE_OUTPUT"), JSON.generate(CoverageSnapshot.snapshot_for(result)))
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "stringio"
4
+
5
+ require_relative "checked_coverage_trace"
6
+
7
+ module SixthSense
8
+ module Runners
9
+ class RSpecCheckedCoverageProbe
10
+ class << self
11
+ def run(spec_path, line)
12
+ require "rspec/core"
13
+ require "rspec/expectations"
14
+
15
+ status = CheckedCoverageTrace.capture do
16
+ RSpec::Core::Runner.run(["--options", File::NULL, "#{spec_path}:#{line}"], StringIO.new, StringIO.new)
17
+ end
18
+ exit(status)
19
+ rescue LoadError => error
20
+ warn error.message
21
+ exit(127)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "coverage"
4
+ require "json"
5
+ require "stringio"
6
+
7
+ module SixthSense
8
+ module Runners
9
+ class RSpecCoverageProbe
10
+ class << self
11
+ def run(spec_path, line)
12
+ require "rspec/core"
13
+ require "rspec/expectations"
14
+
15
+ Coverage.start(lines: true, branches: true)
16
+ status = RSpec::Core::Runner.run(["--options", File::NULL, "#{spec_path}:#{line}"], StringIO.new, StringIO.new)
17
+ result = Coverage.result
18
+ write_snapshot(result)
19
+ exit(status)
20
+ rescue LoadError => error
21
+ warn error.message
22
+ exit(127)
23
+ end
24
+
25
+ private
26
+
27
+ def write_snapshot(result)
28
+ output = ENV.fetch("SIXTH_SENSE_COVERAGE_OUTPUT")
29
+ File.write(output, JSON.generate(snapshot_for(result)))
30
+ end
31
+
32
+ def snapshot_for(result)
33
+ lines = {}
34
+ executable_lines = {}
35
+ branches = {}
36
+ executable_branches = {}
37
+ result.each do |path, payload|
38
+ if payload.is_a?(Hash)
39
+ lines[path] = covered_lines(payload[:lines] || payload["lines"])
40
+ executable_lines[path] = executable_lines(payload[:lines] || payload["lines"])
41
+ branches[path] = covered_branches(payload[:branches] || payload["branches"])
42
+ executable_branches[path] = executable_branches(payload[:branches] || payload["branches"])
43
+ else
44
+ lines[path] = covered_lines(payload)
45
+ executable_lines[path] = executable_lines(payload)
46
+ end
47
+ end
48
+ {
49
+ lines: lines,
50
+ executable_lines: executable_lines,
51
+ branches: branches,
52
+ executable_branches: executable_branches
53
+ }
54
+ end
55
+
56
+ def covered_lines(line_counts)
57
+ Array(line_counts).each_with_index.filter_map do |count, index|
58
+ index + 1 if count.to_i.positive?
59
+ end
60
+ end
61
+
62
+ def executable_lines(line_counts)
63
+ Array(line_counts).each_with_index.filter_map do |count, index|
64
+ index + 1 unless count.nil?
65
+ end
66
+ end
67
+
68
+ def covered_branches(branches)
69
+ return [] unless branches.respond_to?(:each)
70
+
71
+ branches.each_with_index.filter_map do |branch, index|
72
+ "#{branch_key(branch)}:#{index}" if branch_executed?(branch)
73
+ end
74
+ end
75
+
76
+ def executable_branches(branches)
77
+ return [] unless branches.respond_to?(:each)
78
+
79
+ branches.each_with_index.map do |branch, index|
80
+ "#{branch_key(branch)}:#{index}"
81
+ end
82
+ end
83
+
84
+ def branch_key(branch)
85
+ branch.respond_to?(:first) ? branch.first.inspect : branch.inspect
86
+ end
87
+
88
+ def branch_executed?(branch)
89
+ values = branch.respond_to?(:last) ? branch.last : branch
90
+ return values.to_i.positive? if values.is_a?(Integer)
91
+ return values.values.any? { |count| count.to_i.positive? } if values.respond_to?(:values)
92
+
93
+ false
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "checked_coverage_trace"
4
+
5
+ module SixthSense
6
+ module Runners
7
+ class TestUnitCheckedCoverageProbe
8
+ class << self
9
+ def run(test_path, test_name)
10
+ status = CheckedCoverageTrace.capture do
11
+ require "test/unit"
12
+ require "test/unit/testresult"
13
+ require "test/unit/worker-context"
14
+ Test::Unit::AutoRunner.need_auto_run = false
15
+ require File.expand_path(test_path)
16
+ run_test(test_case_classes, test_name) ? 0 : 1
17
+ end
18
+ exit(status)
19
+ rescue LoadError => error
20
+ warn error.message
21
+ exit(127)
22
+ end
23
+
24
+ private
25
+
26
+ def run_test(classes, test_name)
27
+ klass = classes.find { |candidate| candidate.public_instance_methods.map(&:to_s).include?(test_name) }
28
+ return false unless klass
29
+
30
+ result = Test::Unit::TestResult.new
31
+ test = klass.new(test_name)
32
+ test.instance_variable_set(:@passed_assertions, [])
33
+ test.run(result) { |_event, *_args| }
34
+ result.passed?
35
+ end
36
+
37
+ def test_case_classes
38
+ ObjectSpace.each_object(Class).select { |klass| klass < Test::Unit::TestCase }
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "coverage"
4
+ require "json"
5
+
6
+ require_relative "coverage_snapshot"
7
+
8
+ module SixthSense
9
+ module Runners
10
+ class TestUnitCoverageProbe
11
+ class << self
12
+ def run(test_path, test_name)
13
+ Coverage.start(lines: true, branches: true)
14
+ require "test/unit"
15
+ require "test/unit/testresult"
16
+ require "test/unit/worker-context"
17
+ Test::Unit::AutoRunner.need_auto_run = false
18
+ require File.expand_path(test_path)
19
+ passed = run_test(test_case_classes, test_name)
20
+ result = Coverage.result
21
+ write_snapshot(result)
22
+ exit(passed ? 0 : 1)
23
+ rescue LoadError => error
24
+ warn error.message
25
+ exit(127)
26
+ end
27
+
28
+ private
29
+
30
+ def run_test(classes, test_name)
31
+ klass = classes.find { |candidate| candidate.public_instance_methods.map(&:to_s).include?(test_name) }
32
+ return false unless klass
33
+
34
+ result = Test::Unit::TestResult.new
35
+ test = klass.new(test_name)
36
+ test.instance_variable_set(:@passed_assertions, [])
37
+ test.run(result) { |_event, *_args| }
38
+ result.passed?
39
+ end
40
+
41
+ def test_case_classes
42
+ ObjectSpace.each_object(Class).select { |klass| klass < Test::Unit::TestCase }
43
+ end
44
+
45
+ def write_snapshot(result)
46
+ File.write(ENV.fetch("SIXTH_SENSE_COVERAGE_OUTPUT"), JSON.generate(CoverageSnapshot.snapshot_for(result)))
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../result"
4
+
5
+ module SixthSense
6
+ module Scoring
7
+ class Aggregator
8
+ AXES = %i[adequacy redundancy quality].freeze
9
+
10
+ def initialize(level:)
11
+ @level = level
12
+ end
13
+
14
+ def analyze(test_file, context)
15
+ analyzer_results = applicable_analyzers.map { |analyzer_class| analyzer_class.new.analyze(test_file, context) }
16
+ axis_scores = AXES.map { |axis| axis_score(axis, analyzer_results) }
17
+ measured_values = axis_scores.select(&:measured?).map(&:value)
18
+
19
+ Result::FileReport.new(
20
+ test_file: test_file,
21
+ axis_scores: axis_scores,
22
+ composite: measured_values.empty? ? nil : measured_values.min.round(2),
23
+ level: @level,
24
+ warnings: context.warnings
25
+ )
26
+ end
27
+
28
+ private
29
+
30
+ def applicable_analyzers
31
+ SixthSense.analyzers.select do |analyzer_class|
32
+ analyzer_class.level && analyzer_class.level <= @level
33
+ end
34
+ end
35
+
36
+ def axis_score(axis, analyzer_results)
37
+ results = analyzer_results.select { |item| item.axis == axis }
38
+ findings = results.flat_map(&:findings)
39
+ measured = results.select(&:measured?)
40
+
41
+ case axis
42
+ when :adequacy
43
+ adequacy_score(measured, findings)
44
+ when :redundancy
45
+ prioritized_score(axis, measured, findings, %w[redundancy/mutation_based redundancy/coverage_based redundancy/clone])
46
+ when :quality
47
+ minimum_score(axis, measured, findings)
48
+ else
49
+ unmeasured_axis(axis, findings)
50
+ end
51
+ end
52
+
53
+ # Paper basis: Just et al. 2014 and Papadakis et al. 2018 motivate the
54
+ # mutation-heavy weighting; Inozemtseva/Holmes 2014 and Schuler/Zeller
55
+ # 2011 keep coverage and checked coverage as lower-weight signals.
56
+ def adequacy_score(results, findings)
57
+ mutation = results.find { |item| item.analyzer_id == "adequacy/mutation" }
58
+ coverage = results.find { |item| item.analyzer_id == "adequacy/coverage" }
59
+ checked = results.find { |item| item.analyzer_id == "adequacy/checked_coverage" }
60
+ weighted = []
61
+ weighted << [mutation.score, 0.60] if mutation
62
+ weighted << [coverage.score, 0.25] if coverage
63
+ weighted << [checked.score, 0.15] if checked
64
+ return unmeasured_axis(:adequacy, findings) if weighted.empty?
65
+
66
+ total_weight = weighted.sum(&:last)
67
+ value = weighted.sum { |score, weight| score * (weight / total_weight) }
68
+ confidence = mutation ? :high : :medium
69
+ Result::AxisScore.new(
70
+ axis: :adequacy,
71
+ value: value.round(2),
72
+ confidence: confidence,
73
+ findings: findings,
74
+ measured: true
75
+ )
76
+ end
77
+
78
+ # Paper basis: Koochakzadeh/Garousi 2010 makes mutation requirements the
79
+ # more precise redundancy signal, with coverage minimization as fallback.
80
+ def prioritized_score(axis, results, findings, analyzer_ids)
81
+ selected = analyzer_ids.filter_map { |id| results.find { |item| item.analyzer_id == id } }.first
82
+ return unmeasured_axis(axis, findings) unless selected
83
+
84
+ Result::AxisScore.new(
85
+ axis: axis,
86
+ value: selected.score.round(2),
87
+ confidence: selected.confidence,
88
+ findings: selected.findings,
89
+ measured: true
90
+ )
91
+ end
92
+
93
+ def minimum_score(axis, results, findings)
94
+ return unmeasured_axis(axis, findings) if results.empty?
95
+
96
+ selected = results.min_by(&:score)
97
+ Result::AxisScore.new(
98
+ axis: axis,
99
+ value: selected.score.round(2),
100
+ confidence: selected.confidence,
101
+ findings: findings,
102
+ measured: true
103
+ )
104
+ end
105
+
106
+ def unmeasured_axis(axis, findings)
107
+ Result::AxisScore.new(
108
+ axis: axis,
109
+ value: nil,
110
+ confidence: :low,
111
+ findings: findings,
112
+ measured: false
113
+ )
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SixthSense
4
+ SourceLocation = Struct.new(:path, :line, :column, keyword_init: true) do
5
+ def to_h
6
+ {
7
+ path: path,
8
+ line: line,
9
+ column: column
10
+ }
11
+ end
12
+
13
+ def to_s
14
+ return path.to_s unless line
15
+
16
+ [path, line, column].compact.join(":")
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SixthSense
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "sixth_sense/version"
4
+
5
+ module SixthSense
6
+ class Error < StandardError; end
7
+
8
+ class Plugin
9
+ def adapter(name, adapter_class)
10
+ FrameworkAdapter.register(name, adapter_class)
11
+ end
12
+
13
+ def analyzer(analyzer_class)
14
+ SixthSense.register_analyzer(analyzer_class)
15
+ end
16
+ end
17
+
18
+ class << self
19
+ def analyzers
20
+ @analyzers ||= []
21
+ end
22
+
23
+ def register_analyzer(analyzer_class)
24
+ return if analyzers.include?(analyzer_class)
25
+
26
+ analyzers << analyzer_class
27
+ end
28
+
29
+ def plugin
30
+ registry = Plugin.new
31
+ yield registry if block_given?
32
+ registry
33
+ end
34
+ end
35
+ end
36
+
37
+ require_relative "sixth_sense/source_location"
38
+ require_relative "sixth_sense/model"
39
+ require_relative "sixth_sense/result"
40
+ require_relative "sixth_sense/framework_adapter"
41
+ require_relative "sixth_sense/analyzer"
42
+ require_relative "sixth_sense/analysis_context"
43
+ require_relative "sixth_sense/adapters/rspec"
44
+ require_relative "sixth_sense/adapters/test_unit"
45
+ require_relative "sixth_sense/adapters/minitest"
46
+ require_relative "sixth_sense/analyzers/quality_test_smells"
47
+ require_relative "sixth_sense/analyzers/quality_assertion_density"
48
+ require_relative "sixth_sense/analyzers/quality_flakiness"
49
+ require_relative "sixth_sense/analyzers/adequacy_coverage"
50
+ require_relative "sixth_sense/analyzers/adequacy_mutation"
51
+ require_relative "sixth_sense/analyzers/adequacy_checked_coverage"
52
+ require_relative "sixth_sense/analyzers/redundancy_coverage"
53
+ require_relative "sixth_sense/analyzers/redundancy_mutation"
54
+ require_relative "sixth_sense/analyzers/redundancy_clone"
55
+ require_relative "sixth_sense/scoring/aggregator"
56
+ require_relative "sixth_sense/reporters/console"
57
+ require_relative "sixth_sense/reporters/json"
58
+ require_relative "sixth_sense/reporters/markdown"
59
+ require_relative "sixth_sense/reporters/sarif"
60
+ require_relative "sixth_sense/reporters/html"
61
+ require_relative "sixth_sense/config"
62
+ require_relative "sixth_sense/analysis_runner"
63
+ require_relative "sixth_sense/mutation_engine"
64
+ require_relative "sixth_sense/mutation_cache"
65
+ require_relative "sixth_sense/mutation_matrix_mutant_generator"
66
+ require_relative "sixth_sense/mutation_matrix_producer"
67
+ require_relative "sixth_sense/mutation_score_cache_writer"
68
+ require_relative "sixth_sense/runners/coverage_runner"
69
+ require_relative "sixth_sense/runners/minitest_coverage_probe"
70
+ require_relative "sixth_sense/runners/test_unit_coverage_probe"
71
+ require_relative "sixth_sense/runners/minitest_checked_coverage_probe"
72
+ require_relative "sixth_sense/runners/test_unit_checked_coverage_probe"
73
+ require_relative "sixth_sense/runners/checked_coverage_runner"
74
+ require_relative "sixth_sense/runners/checked_coverage_estimator"
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sixth_sense
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Yudai Takada
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: prism
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0.24'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0.24'
26
+ description: Evaluates adequacy, redundancy, and maintainability of Ruby test suites
27
+ using coverage, checked coverage, mutation data, and test-smell analysis.
28
+ email:
29
+ - t.yudai92@gmail.com
30
+ executables:
31
+ - sixth_sense
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - LICENSE.txt
36
+ - README.md
37
+ - exe/sixth_sense
38
+ - lib/sixth_sense.rb
39
+ - lib/sixth_sense/adapters/minitest.rb
40
+ - lib/sixth_sense/adapters/rspec.rb
41
+ - lib/sixth_sense/adapters/test_unit.rb
42
+ - lib/sixth_sense/analysis_context.rb
43
+ - lib/sixth_sense/analysis_runner.rb
44
+ - lib/sixth_sense/analyzer.rb
45
+ - lib/sixth_sense/analyzers/adequacy_checked_coverage.rb
46
+ - lib/sixth_sense/analyzers/adequacy_coverage.rb
47
+ - lib/sixth_sense/analyzers/adequacy_mutation.rb
48
+ - lib/sixth_sense/analyzers/quality_assertion_density.rb
49
+ - lib/sixth_sense/analyzers/quality_flakiness.rb
50
+ - lib/sixth_sense/analyzers/quality_test_smells.rb
51
+ - lib/sixth_sense/analyzers/redundancy_clone.rb
52
+ - lib/sixth_sense/analyzers/redundancy_coverage.rb
53
+ - lib/sixth_sense/analyzers/redundancy_mutation.rb
54
+ - lib/sixth_sense/analyzers/redundancy_requirement.rb
55
+ - lib/sixth_sense/changed_files.rb
56
+ - lib/sixth_sense/cli.rb
57
+ - lib/sixth_sense/config.rb
58
+ - lib/sixth_sense/engines/mutant.rb
59
+ - lib/sixth_sense/framework_adapter.rb
60
+ - lib/sixth_sense/guardrail/baseline.rb
61
+ - lib/sixth_sense/guardrail/evaluator.rb
62
+ - lib/sixth_sense/model.rb
63
+ - lib/sixth_sense/mutation_cache.rb
64
+ - lib/sixth_sense/mutation_engine.rb
65
+ - lib/sixth_sense/mutation_matrix_mutant_generator.rb
66
+ - lib/sixth_sense/mutation_matrix_producer.rb
67
+ - lib/sixth_sense/mutation_score_cache_writer.rb
68
+ - lib/sixth_sense/rake_task.rb
69
+ - lib/sixth_sense/reporters/console.rb
70
+ - lib/sixth_sense/reporters/html.rb
71
+ - lib/sixth_sense/reporters/json.rb
72
+ - lib/sixth_sense/reporters/markdown.rb
73
+ - lib/sixth_sense/reporters/sarif.rb
74
+ - lib/sixth_sense/result.rb
75
+ - lib/sixth_sense/runners/checked_coverage_estimator.rb
76
+ - lib/sixth_sense/runners/checked_coverage_runner.rb
77
+ - lib/sixth_sense/runners/checked_coverage_trace.rb
78
+ - lib/sixth_sense/runners/coverage_runner.rb
79
+ - lib/sixth_sense/runners/coverage_snapshot.rb
80
+ - lib/sixth_sense/runners/minitest_checked_coverage_probe.rb
81
+ - lib/sixth_sense/runners/minitest_coverage_probe.rb
82
+ - lib/sixth_sense/runners/rspec_checked_coverage_probe.rb
83
+ - lib/sixth_sense/runners/rspec_coverage_probe.rb
84
+ - lib/sixth_sense/runners/test_unit_checked_coverage_probe.rb
85
+ - lib/sixth_sense/runners/test_unit_coverage_probe.rb
86
+ - lib/sixth_sense/scoring/aggregator.rb
87
+ - lib/sixth_sense/source_location.rb
88
+ - lib/sixth_sense/version.rb
89
+ homepage: https://github.com/ydah/sixth_sense
90
+ licenses:
91
+ - MIT
92
+ metadata:
93
+ homepage_uri: https://github.com/ydah/sixth_sense
94
+ source_code_uri: https://github.com/ydah/sixth_sense/tree/main
95
+ rubygems_mfa_required: 'true'
96
+ rdoc_options: []
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: 3.2.0
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ requirements: []
110
+ rubygems_version: 4.0.6
111
+ specification_version: 4
112
+ summary: Evidence-based test suite adequacy analysis for Ruby test files.
113
+ test_files: []