branchproof 0.9.0 → 0.10.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.
@@ -8,19 +8,18 @@ require "stringio"
8
8
  require "fileutils"
9
9
 
10
10
  module Branchproof
11
- # Runs one isolated Minitest worker and atomically exports its result.
11
+ # Runs one isolated test worker and atomically exports its result.
12
12
  module Worker
13
13
  module_function
14
14
 
15
15
  def child_process(config_path)
16
+ $stdout.sync = true
17
+ $stderr.sync = true
16
18
  payload = JSON.parse(File.binread(config_path))
17
19
  project = symbolize(payload.fetch("project"))
18
20
  prepend_load_paths(project)
19
21
  inventory = symbolize(payload.fetch("inventory"))
20
22
  limits = symbolize(payload.fetch("limits"))
21
- require "minitest"
22
- require "minitest/test"
23
- require_relative "minitest_adapter"
24
23
  evidence = Branchproof::Evidence.new(inventory: inventory, limits: limits, run_id: payload.fetch("run_id"))
25
24
  runtime = Branchproof::Runtime
26
25
  runtime.boot(evidence: evidence)
@@ -28,23 +27,38 @@ module Branchproof
28
27
  status = loader.install
29
28
  return write_failure(payload, "loader", status) unless status[:status].to_sym == :installed
30
29
 
31
- adapter = Branchproof::MinitestAdapter.new(runtime: runtime)
30
+ adapter = adapter_for(project, runtime)
31
+ guard_late_rspec_execution(adapter, payload) if project[:framework].to_s == "rspec"
32
32
  rails_metadata = nil
33
33
  ARGV.replace(payload.fetch("runner_args"))
34
- adapter.run(test_files: payload.fetch("test_files"), runner_args: payload.fetch("runner_args"),
35
- before_load: lambda {
36
- rails_metadata = boot_project(project)
37
- }, on_complete: lambda { |baseline|
38
- result = completion_result(baseline: baseline, project: project, rails_metadata: rails_metadata,
39
- evidence: runtime.snapshot, tests: adapter.tests.values,
40
- diagnostics: loader.diagnostics)
41
- write_completion(payload, result)
42
- })
34
+ callbacks = if project[:framework].to_s == "rspec"
35
+ { test_selection_explicit: payload.fetch("test_selection_explicit", false), after_load: lambda {
36
+ require_relative "rails_support"
37
+ rails_metadata = Branchproof::RailsSupport.validate_rspec!(project: project)
38
+ } }
39
+ else
40
+ {}
41
+ end
42
+ before_load = lambda do
43
+ rails_metadata = boot_project(project) unless project[:framework].to_s == "rspec"
44
+ end
45
+ on_complete = lambda do |baseline|
46
+ result = completion_result(baseline: baseline, project: project, rails_metadata: rails_metadata,
47
+ evidence: runtime.snapshot, tests: adapter.tests.values,
48
+ diagnostics: loader.diagnostics)
49
+ write_completion(payload, result)
50
+ end
51
+ exit_status = adapter.run(test_files: payload.fetch("test_files"), runner_args: payload.fetch("runner_args"),
52
+ before_load: before_load, on_complete: on_complete, **callbacks)
53
+ return exit_status if project[:framework].to_s == "rspec"
54
+
43
55
  adapter.validate_runner!
44
56
  Minitest.autorun
45
57
  nil
46
58
  rescue StandardError => e
47
- code = if e.message.include?("parallel")
59
+ code = if e.respond_to?(:diagnostic_code)
60
+ e.diagnostic_code
61
+ elsif e.message.match?(/parallel|runner|bisect|DRb|dry.run/)
48
62
  "unsupported_runner"
49
63
  elsif defined?(Branchproof::RailsSupport::Error) && e.is_a?(Branchproof::RailsSupport::Error)
50
64
  "rails_boot"
@@ -55,6 +69,44 @@ module Branchproof
55
69
  2
56
70
  end
57
71
 
72
+ def adapter_for(project, runtime)
73
+ if project[:framework].to_s == "rspec"
74
+ begin
75
+ require "rspec/core"
76
+ rescue LoadError => e
77
+ raise unless e.path == "rspec/core"
78
+
79
+ error = ArgumentError.new("RSpec is not available; add rspec to the application's test bundle")
80
+ def error.diagnostic_code = "rspec_missing"
81
+ raise error
82
+ end
83
+ require_relative "rspec_adapter"
84
+ Branchproof::RSpecAdapter.new(runtime: runtime)
85
+ else
86
+ require "minitest"
87
+ require "minitest/test"
88
+ require_relative "minitest_adapter"
89
+ Branchproof::MinitestAdapter.new(runtime: runtime)
90
+ end
91
+ end
92
+
93
+ # Installed before application hooks, so this runs after their at_exit work.
94
+ # A rescued second runner must still invalidate the already-exported result.
95
+ def guard_late_rspec_execution(adapter, payload)
96
+ at_exit do
97
+ error = adapter.late_execution_error
98
+ if error
99
+ result = symbolize(JSON.parse(File.binread(payload.fetch("result_path"))))
100
+ diagnostic = { code: "unsupported_runner", severity: "error", message: error.message }
101
+ result.merge!(status: "ERROR", finalized: false, exit_status: 2,
102
+ diagnostics: Array(result[:diagnostics]) + [diagnostic])
103
+ result[:evidence] = incomplete_evidence(result[:evidence], [diagnostic]) if result[:evidence]
104
+ write_completion(payload, result)
105
+ exit(2)
106
+ end
107
+ end
108
+ end
109
+
58
110
  def legacy_project
59
111
  { "kind" => "ruby", "root" => Dir.pwd,
60
112
  "load_paths" => [File.join(Dir.pwd, "lib"), File.join(Dir.pwd, "test")],
@@ -76,7 +128,14 @@ module Branchproof
76
128
  end
77
129
 
78
130
  def project_metadata(project, rails_metadata)
131
+ framework = project[:framework].to_s.empty? ? "minitest" : project[:framework].to_s
132
+ version = if framework == "rspec" && defined?(RSpec::Core::Version::STRING)
133
+ RSpec::Core::Version::STRING
134
+ elsif framework == "minitest" && defined?(Minitest::VERSION)
135
+ Minitest::VERSION
136
+ end
79
137
  metadata = { kind: project[:kind].to_s, root: project[:root].to_s,
138
+ framework: framework, framework_version: version,
80
139
  load_paths: Array(project[:load_paths]).map(&:to_s), serial_policy: { mode: "single_process", workers: 1 } }
81
140
  metadata.merge!(rails_metadata) if rails_metadata
82
141
  metadata
@@ -85,6 +144,7 @@ module Branchproof
85
144
  # This result boundary intentionally carries the complete worker payload.
86
145
  # rubocop:disable-next Metrics/ParameterLists
87
146
  def completion_result(baseline:, project:, rails_metadata:, evidence:, tests:, diagnostics:)
147
+ diagnostics = Array(baseline[:diagnostics]) + diagnostics
88
148
  result = baseline.merge(project: project_metadata(project, rails_metadata), evidence: evidence,
89
149
  tests: tests, diagnostics: diagnostics)
90
150
  return result unless diagnostics.any? { |diagnostic| diagnostic[:severity].to_s == "error" }
data/lib/branchproof.rb CHANGED
@@ -14,6 +14,7 @@ module Branchproof
14
14
  autoload :Loader, "branchproof/loader"
15
15
  autoload :Runtime, "branchproof/runtime"
16
16
  autoload :MinitestAdapter, "branchproof/minitest_adapter"
17
+ autoload :RSpecAdapter, "branchproof/rspec_adapter"
17
18
  autoload :Evidence, "branchproof/evidence"
18
19
  autoload :Analyzer, "branchproof/analyzer"
19
20
  autoload :Constraints, "branchproof/constraints"
data/llms.txt CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  | | |
4
4
  | --- | --- |
5
- | **Defined in** | lib/branchproof.rb, lib/branchproof/cli.rb, lib/branchproof/limits.rb, lib/branchproof/loader.rb, lib/branchproof/report.rb, lib/branchproof/source.rb, lib/branchproof/worker.rb, lib/branchproof/project.rb, lib/branchproof/records.rb, lib/branchproof/runtime.rb, lib/branchproof/version.rb, lib/branchproof/analyzer.rb, lib/branchproof/evidence.rb, lib/branchproof/minimizer.rb, lib/branchproof/comparison.rb, lib/branchproof/constraints.rb, lib/branchproof/instrumenter.rb, lib/branchproof/runtime_flow.rb, lib/branchproof/saved_report.rb, lib/branchproof/value_syntax.rb, lib/branchproof/rails_support.rb, lib/branchproof/value_runtime.rb, lib/branchproof/coverage_index.rb, lib/branchproof/decision_table.rb, lib/branchproof/default_syntax.rb, lib/branchproof/focused_report.rb, lib/branchproof/decision_syntax.rb, lib/branchproof/default_runtime.rb, lib/branchproof/exception_syntax.rb, lib/branchproof/iteration_syntax.rb, lib/branchproof/minitest_adapter.rb, lib/branchproof/comparison_report.rb, lib/branchproof/exception_runtime.rb, lib/branchproof/iteration_runtime.rb, lib/branchproof/flow_instrumentation.rb, lib/branchproof/value_instrumentation.rb, lib/branchproof/default_instrumentation.rb, lib/branchproof/exception_instrumentation.rb, lib/branchproof/iteration_instrumentation.rb, lib/branchproof/extended_alternative_runtime.rb |
5
+ | **Defined in** | lib/branchproof.rb, lib/branchproof/cli.rb, lib/branchproof/limits.rb, lib/branchproof/loader.rb, lib/branchproof/report.rb, lib/branchproof/source.rb, lib/branchproof/worker.rb, lib/branchproof/project.rb, lib/branchproof/records.rb, lib/branchproof/runtime.rb, lib/branchproof/version.rb, lib/branchproof/analyzer.rb, lib/branchproof/evidence.rb, lib/branchproof/minimizer.rb, lib/branchproof/comparison.rb, lib/branchproof/constraints.rb, lib/branchproof/instrumenter.rb, lib/branchproof/runtime_flow.rb, lib/branchproof/saved_report.rb, lib/branchproof/value_syntax.rb, lib/branchproof/rails_support.rb, lib/branchproof/rspec_adapter.rb, lib/branchproof/value_runtime.rb, lib/branchproof/coverage_index.rb, lib/branchproof/decision_table.rb, lib/branchproof/default_syntax.rb, lib/branchproof/focused_report.rb, lib/branchproof/decision_syntax.rb, lib/branchproof/default_runtime.rb, lib/branchproof/exception_syntax.rb, lib/branchproof/iteration_syntax.rb, lib/branchproof/minitest_adapter.rb, lib/branchproof/comparison_report.rb, lib/branchproof/exception_runtime.rb, lib/branchproof/iteration_runtime.rb, lib/branchproof/flow_instrumentation.rb, lib/branchproof/value_instrumentation.rb, lib/branchproof/default_instrumentation.rb, lib/branchproof/exception_instrumentation.rb, lib/branchproof/iteration_instrumentation.rb, lib/branchproof/extended_alternative_runtime.rb |
6
6
 
7
7
  Keep source-boundary and parameter-binding rules together for auditing.
8
8
  rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity,
@@ -43,6 +43,13 @@ Not documented.
43
43
  - [Branchproof/Minimizer.md](doc/Branchproof/Minimizer.md)
44
44
  - [Branchproof/MinitestAdapter.md](doc/Branchproof/MinitestAdapter.md)
45
45
  - [Branchproof/Project.md](doc/Branchproof/Project.md)
46
+ - [Branchproof/RSpecAdapter/ClassRunnerGuard.md](doc/Branchproof/RSpecAdapter/ClassRunnerGuard.md)
47
+ - [Branchproof/RSpecAdapter/ContextLifecycle.md](doc/Branchproof/RSpecAdapter/ContextLifecycle.md)
48
+ - [Branchproof/RSpecAdapter/DefaultDiscovery.md](doc/Branchproof/RSpecAdapter/DefaultDiscovery.md)
49
+ - [Branchproof/RSpecAdapter/ExampleLifecycle.md](doc/Branchproof/RSpecAdapter/ExampleLifecycle.md)
50
+ - [Branchproof/RSpecAdapter/RunnerGuard.md](doc/Branchproof/RSpecAdapter/RunnerGuard.md)
51
+ - [Branchproof/RSpecAdapter/UnsupportedRunner.md](doc/Branchproof/RSpecAdapter/UnsupportedRunner.md)
52
+ - [Branchproof/RSpecAdapter.md](doc/Branchproof/RSpecAdapter.md)
46
53
  - [Branchproof/RailsSupport/Error.md](doc/Branchproof/RailsSupport/Error.md)
47
54
  - [Branchproof/RailsSupport.md](doc/Branchproof/RailsSupport.md)
48
55
  - [Branchproof/Records.md](doc/Branchproof/Records.md)
data/sig/branchproof.rbs CHANGED
@@ -34,13 +34,15 @@ module Branchproof
34
34
 
35
35
  class Project
36
36
  MODES: Array[String]
37
+ FRAMEWORKS: Array[String]
37
38
  RAILS_ENVIRONMENT: record
38
- def initialize: (root: String, ?mode: String) -> void
39
+ def initialize: (root: String, ?mode: String, ?framework: String) -> void
39
40
  def to_h: () -> record
40
41
  end
41
42
 
42
43
  module RailsSupport
43
44
  def self.boot: (project: record) -> record
45
+ def self.validate_rspec!: (project: record) -> record?
44
46
  end
45
47
 
46
48
  class Instrumenter
@@ -57,6 +59,8 @@ module Branchproof
57
59
  end
58
60
 
59
61
  module Runtime
62
+ def self.register_test: (test: record) -> record?
63
+ def self.test_phase_counts: () -> Hash[String, Hash[String, Integer]]
60
64
  def self.boot: (evidence: Evidence) -> nil
61
65
  def self.enter: (String decision_id) -> nil
62
66
  def self.condition: (String decision_id, Integer index, untyped value) -> untyped
@@ -100,6 +104,7 @@ module Branchproof
100
104
  def register_test: (test: record) -> record
101
105
  def record: (execution: record) -> record
102
106
  def diagnose: (diagnostic: record) -> nil
107
+ def test_phase_counts: () -> Hash[String, Hash[String, Integer]]
103
108
  def snapshot: () -> record
104
109
  def merge: (snapshot: record) -> record
105
110
  end
@@ -164,6 +169,27 @@ module Branchproof
164
169
  def run: (test_files: Array[String], runner_args: Array[String], on_complete: ^(record) -> untyped, ?before_load: ^() -> untyped?) -> nil
165
170
  end
166
171
 
172
+ class RSpecAdapter
173
+ class UnsupportedRunner < ArgumentError
174
+ def diagnostic_code: () -> String
175
+ end
176
+
177
+ FORBIDDEN_OPTIONS: Hash[Symbol, String]
178
+ attr_reader tests: Hash[String, record]
179
+ def self.active_adapter: () -> RSpecAdapter?
180
+ def self.active_adapter=: (RSpecAdapter?) -> RSpecAdapter?
181
+ def self.runner_adapter: () -> RSpecAdapter?
182
+ def self.runner_adapter=: (RSpecAdapter?) -> RSpecAdapter?
183
+ attr_reader late_execution_error: ArgumentError?
184
+ def initialize: (runtime: untyped) -> void
185
+ def reject_execution!: (String message) -> bot
186
+ def enter_runner!: (untyped runner) -> bool
187
+ def example_started: (untyped notification) -> untyped
188
+ def example_finished: (untyped notification) -> untyped
189
+ def run: (test_files: Array[String], runner_args: Array[String], on_complete: ^(record) -> untyped, ?before_load: ^() -> untyped?, ?after_load: ^() -> untyped?, ?test_selection_explicit: bool) -> untyped
190
+ def validate_runner!: () -> nil
191
+ end
192
+
167
193
  class Report
168
194
  SCHEMA_VERSION: String
169
195
  CRITERION_VERSION: String
@@ -175,6 +201,7 @@ module Branchproof
175
201
  def decision_table_requirement: (untyped value) -> String?
176
202
  def decision_table_reachability: (document rule) -> String
177
203
  def decision_table_expected_heading: (document decision) -> String
204
+ def rerun_command: (String test_id) -> String?
178
205
  def write: (io: _WriteIO, format: Symbol | String) -> nil
179
206
  def exit_code: () -> Integer
180
207
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: branchproof
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lucian Ghinda
@@ -50,7 +50,7 @@ dependencies:
50
50
  - !ruby/object:Gem::Version
51
51
  version: '2'
52
52
  description: Inventory Ruby decisions and report masking MC/DC evidence from Minitest
53
- runs.
53
+ or RSpec runs.
54
54
  email:
55
55
  - lucianghinda@users.noreply.github.com
56
56
  executables:
@@ -95,6 +95,13 @@ files:
95
95
  - doc/Branchproof/Minimizer.md
96
96
  - doc/Branchproof/MinitestAdapter.md
97
97
  - doc/Branchproof/Project.md
98
+ - doc/Branchproof/RSpecAdapter.md
99
+ - doc/Branchproof/RSpecAdapter/ClassRunnerGuard.md
100
+ - doc/Branchproof/RSpecAdapter/ContextLifecycle.md
101
+ - doc/Branchproof/RSpecAdapter/DefaultDiscovery.md
102
+ - doc/Branchproof/RSpecAdapter/ExampleLifecycle.md
103
+ - doc/Branchproof/RSpecAdapter/RunnerGuard.md
104
+ - doc/Branchproof/RSpecAdapter/UnsupportedRunner.md
98
105
  - doc/Branchproof/RailsSupport.md
99
106
  - doc/Branchproof/RailsSupport/Error.md
100
107
  - doc/Branchproof/Records.md
@@ -142,6 +149,7 @@ files:
142
149
  - lib/branchproof/rails_support.rb
143
150
  - lib/branchproof/records.rb
144
151
  - lib/branchproof/report.rb
152
+ - lib/branchproof/rspec_adapter.rb
145
153
  - lib/branchproof/runtime.rb
146
154
  - lib/branchproof/runtime_flow.rb
147
155
  - lib/branchproof/saved_report.rb