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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +19 -0
- data/README.md +77 -18
- data/doc/Branchproof/Evidence.md +3 -0
- data/doc/Branchproof/Project.md +4 -1
- data/doc/Branchproof/RSpecAdapter/ClassRunnerGuard.md +11 -0
- data/doc/Branchproof/RSpecAdapter/ContextLifecycle.md +14 -0
- data/doc/Branchproof/RSpecAdapter/DefaultDiscovery.md +11 -0
- data/doc/Branchproof/RSpecAdapter/ExampleLifecycle.md +11 -0
- data/doc/Branchproof/RSpecAdapter/RunnerGuard.md +11 -0
- data/doc/Branchproof/RSpecAdapter/UnsupportedRunner.md +12 -0
- data/doc/Branchproof/RSpecAdapter.md +47 -0
- data/doc/Branchproof/RailsSupport.md +35 -2
- data/doc/Branchproof/Report.md +3 -0
- data/doc/Branchproof/Runtime.md +3 -0
- data/doc/Branchproof/Worker.md +8 -1
- data/doc/Branchproof.md +8 -1
- data/doc/CHANGELOG.md +19 -0
- data/doc/README.md +77 -18
- data/lib/branchproof/cli.rb +74 -28
- data/lib/branchproof/comparison.rb +52 -6
- data/lib/branchproof/evidence.rb +4 -0
- data/lib/branchproof/focused_report.rb +5 -0
- data/lib/branchproof/project.rb +49 -2
- data/lib/branchproof/rails_support.rb +101 -0
- data/lib/branchproof/report.rb +21 -1
- data/lib/branchproof/rspec_adapter.rb +423 -0
- data/lib/branchproof/runtime.rb +4 -0
- data/lib/branchproof/saved_report.rb +9 -2
- data/lib/branchproof/version.rb +1 -1
- data/lib/branchproof/worker.rb +75 -15
- data/lib/branchproof.rb +1 -0
- data/llms.txt +8 -1
- data/sig/branchproof.rbs +28 -1
- metadata +10 -2
data/lib/branchproof/worker.rb
CHANGED
|
@@ -8,19 +8,18 @@ require "stringio"
|
|
|
8
8
|
require "fileutils"
|
|
9
9
|
|
|
10
10
|
module Branchproof
|
|
11
|
-
# Runs one isolated
|
|
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 =
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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.
|
|
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.
|
|
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
|