mutineer-mongoid 2.0.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 +7 -0
- data/CHANGELOG.md +1194 -0
- data/LICENSE +21 -0
- data/README.md +659 -0
- data/bin/mutineer +6 -0
- data/lib/mutineer/baseline.rb +185 -0
- data/lib/mutineer/changed_lines.rb +136 -0
- data/lib/mutineer/child_stdout.rb +35 -0
- data/lib/mutineer/cli.rb +681 -0
- data/lib/mutineer/config.rb +372 -0
- data/lib/mutineer/coverage_map.rb +1366 -0
- data/lib/mutineer/daemon_backend.rb +351 -0
- data/lib/mutineer/daemon_client.rb +407 -0
- data/lib/mutineer/daemon_server.rb +351 -0
- data/lib/mutineer/external_backend.rb +288 -0
- data/lib/mutineer/file_swap.rb +194 -0
- data/lib/mutineer/isolation.rb +321 -0
- data/lib/mutineer/job_plan.rb +352 -0
- data/lib/mutineer/kill_channel.rb +284 -0
- data/lib/mutineer/kill_matrix.rb +88 -0
- data/lib/mutineer/minitest_integration/kill_recorder.rb +301 -0
- data/lib/mutineer/minitest_integration/outer_reporter.rb +126 -0
- data/lib/mutineer/minitest_integration/stop_at_first_failure.rb +145 -0
- data/lib/mutineer/minitest_integration.rb +137 -0
- data/lib/mutineer/mongoid_failures.rb +82 -0
- data/lib/mutineer/mongoid_worker_db.rb +97 -0
- data/lib/mutineer/mutant_id.rb +126 -0
- data/lib/mutineer/mutation.rb +27 -0
- data/lib/mutineer/mutator_registry.rb +109 -0
- data/lib/mutineer/mutators/arithmetic.rb +35 -0
- data/lib/mutineer/mutators/array_literal.rb +44 -0
- data/lib/mutineer/mutators/base.rb +105 -0
- data/lib/mutineer/mutators/boolean_connector.rb +52 -0
- data/lib/mutineer/mutators/boolean_literal.rb +56 -0
- data/lib/mutineer/mutators/chain_link.rb +132 -0
- data/lib/mutineer/mutators/collection_method.rb +44 -0
- data/lib/mutineer/mutators/comparison.rb +35 -0
- data/lib/mutineer/mutators/condition_forcing.rb +134 -0
- data/lib/mutineer/mutators/condition_negation.rb +49 -0
- data/lib/mutineer/mutators/literal_mutation.rb +51 -0
- data/lib/mutineer/mutators/negation_removal.rb +44 -0
- data/lib/mutineer/mutators/operand_removal.rb +58 -0
- data/lib/mutineer/mutators/operator_assignment.rb +113 -0
- data/lib/mutineer/mutators/range_literal.rb +54 -0
- data/lib/mutineer/mutators/regex_literal.rb +72 -0
- data/lib/mutineer/mutators/return_nil.rb +72 -0
- data/lib/mutineer/mutators/safe_navigation.rb +76 -0
- data/lib/mutineer/mutators/statement_removal.rb +33 -0
- data/lib/mutineer/mutators/string_literal.rb +34 -0
- data/lib/mutineer/orphan_guard.rb +28 -0
- data/lib/mutineer/pairing.rb +298 -0
- data/lib/mutineer/parser.rb +45 -0
- data/lib/mutineer/progress.rb +46 -0
- data/lib/mutineer/project.rb +506 -0
- data/lib/mutineer/project_path.rb +57 -0
- data/lib/mutineer/rails_worker_db.rb +212 -0
- data/lib/mutineer/reporter.rb +926 -0
- data/lib/mutineer/result.rb +240 -0
- data/lib/mutineer/runner.rb +418 -0
- data/lib/mutineer/statement_lines.rb +183 -0
- data/lib/mutineer/subject.rb +38 -0
- data/lib/mutineer/test_runners/minitest.rb +22 -0
- data/lib/mutineer/test_runners/rspec.rb +330 -0
- data/lib/mutineer/test_runners.rb +30 -0
- data/lib/mutineer/version.rb +6 -0
- data/lib/mutineer/worker_pool.rb +147 -0
- data/lib/mutineer.rb +48 -0
- metadata +143 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "result"
|
|
4
|
+
|
|
5
|
+
module Mutineer
|
|
6
|
+
# Fixed-size fork pool. `run` forks up to `size` children at once; each child
|
|
7
|
+
# runs the block on one work item, marshals its Result to a private pipe, and
|
|
8
|
+
# exits. The parent drains pipes with IO.select and reaps each finished child
|
|
9
|
+
# by known pid (never wait2(-1), which would steal the host suite's children),
|
|
10
|
+
# opening exactly one slot per reap, then refills. Results are returned in the
|
|
11
|
+
# SAME ORDER as `items` regardless of finish order, so verdicts are identical
|
|
12
|
+
# to a serial run and downstream output is stable.
|
|
13
|
+
#
|
|
14
|
+
# The block is run inside the child via `yield(*items[i])`; whatever it
|
|
15
|
+
# returns (a Result) is the marshaled payload. Per-mutant timeout is handled
|
|
16
|
+
# one level down by Isolation. The pool adds no separate wall clock.
|
|
17
|
+
class WorkerPool
|
|
18
|
+
# Builds a pool.
|
|
19
|
+
#
|
|
20
|
+
# @param size [Integer] desired pool size.
|
|
21
|
+
def initialize(size)
|
|
22
|
+
@size = [size.to_i, 1].max
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Runs the work items through the pool.
|
|
26
|
+
#
|
|
27
|
+
# @param items [Array<Array>] work items.
|
|
28
|
+
# @param stop_when [Proc, nil] called with each collected Result; when it
|
|
29
|
+
# returns truthy, no further items are scheduled and the run drains and
|
|
30
|
+
# returns early (--fail-fast). Unscheduled slots stay nil.
|
|
31
|
+
# @param on_result [Proc, nil] called in the parent with each collected
|
|
32
|
+
# Result as it is reaped, in finish order (progress reporting); its
|
|
33
|
+
# return value is ignored. Must be fast and non-blocking: it runs on the
|
|
34
|
+
# pool's single reap thread, so a slow callback stalls draining the other
|
|
35
|
+
# in-flight children's pipes (the #4 deadlock discipline).
|
|
36
|
+
# @yieldparam item [Array] one work item.
|
|
37
|
+
# @return [Array<Mutineer::Result>] results in input order (nil for any item
|
|
38
|
+
# left unscheduled by an early stop).
|
|
39
|
+
def run(items, stop_when: nil, on_result: nil)
|
|
40
|
+
results = Array.new(items.size)
|
|
41
|
+
queue = (0...items.size).to_a
|
|
42
|
+
running = {} # pid => [index, read_io, buffer]
|
|
43
|
+
stopping = false
|
|
44
|
+
|
|
45
|
+
until queue.empty? && running.empty?
|
|
46
|
+
fill(items, queue, running) { |*args| yield(*args) } unless stopping
|
|
47
|
+
result = reap(results, running)
|
|
48
|
+
on_result&.call(result) if result
|
|
49
|
+
if !stopping && stop_when && result && stop_when.call(result)
|
|
50
|
+
stopping = true
|
|
51
|
+
queue.clear # schedule no more; let in-flight workers drain
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
results
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
# The child must ALWAYS hard-exit. If yield raises, marshal an error Result
|
|
61
|
+
# and exit! in `ensure`. Otherwise the child unwinds normally and our
|
|
62
|
+
# Minitest at_exit autorun re-runs the parent suite inside the worker,
|
|
63
|
+
# losing the real error.
|
|
64
|
+
def fill(items, queue, running)
|
|
65
|
+
while running.size < @size && !queue.empty?
|
|
66
|
+
idx = queue.shift
|
|
67
|
+
rd, wr = IO.pipe
|
|
68
|
+
rd.binmode # Marshal output is binary: keep the pipe byte-exact
|
|
69
|
+
wr.binmode
|
|
70
|
+
begin
|
|
71
|
+
pid = fork do
|
|
72
|
+
rd.close
|
|
73
|
+
payload =
|
|
74
|
+
begin
|
|
75
|
+
yield(*items[idx])
|
|
76
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
77
|
+
Result.error("worker crashed: #{e.class}: #{e.message}")
|
|
78
|
+
end
|
|
79
|
+
begin
|
|
80
|
+
wr.write(Marshal.dump(payload))
|
|
81
|
+
rescue StandardError # rubocop:disable Lint/SuppressedException
|
|
82
|
+
# pipe gone; parent will record "no result"
|
|
83
|
+
ensure
|
|
84
|
+
wr.close
|
|
85
|
+
exit!(0)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
rescue Errno::EAGAIN
|
|
89
|
+
# Process table is full. Put the item back and reap before retrying;
|
|
90
|
+
# if nothing is running we cannot make progress, so re-raise.
|
|
91
|
+
rd.close
|
|
92
|
+
wr.close
|
|
93
|
+
raise if running.empty?
|
|
94
|
+
|
|
95
|
+
queue.unshift(idx)
|
|
96
|
+
return
|
|
97
|
+
end
|
|
98
|
+
wr.close
|
|
99
|
+
running[pid] = [idx, rd, +""] # idx, read end, accumulated bytes
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Drain pipes with IO.select and reap a child only on EOF. The old code
|
|
104
|
+
# reaped first and read after, but a child whose payload exceeds the OS pipe
|
|
105
|
+
# buffer (~64KB) blocks on `write` before it can exit, so it was never reaped
|
|
106
|
+
# and the pool deadlocked. Reading concurrently keeps the pipe drained so the
|
|
107
|
+
# child can finish and exit; EOF means it closed its write end (done writing).
|
|
108
|
+
# We waitpid only OUR known pids (never wait2(-1), which would steal the host
|
|
109
|
+
# suite's children).
|
|
110
|
+
def reap(results, running)
|
|
111
|
+
return if running.empty?
|
|
112
|
+
|
|
113
|
+
loop do
|
|
114
|
+
rds = running.values.map { |v| v[1] }
|
|
115
|
+
ready, = IO.select(rds, nil, nil)
|
|
116
|
+
ready.each do |rd|
|
|
117
|
+
pid, (idx, _io, buf) = running.find { |_, v| v[1].equal?(rd) }
|
|
118
|
+
chunk = rd.read_nonblock(65_536, exception: false)
|
|
119
|
+
next if chunk == :wait_readable
|
|
120
|
+
|
|
121
|
+
if chunk.nil? # EOF: child closed wr; it is done writing and exiting
|
|
122
|
+
rd.close
|
|
123
|
+
Process.waitpid(pid) # reap the now-finished child (no zombie)
|
|
124
|
+
running.delete(pid)
|
|
125
|
+
# Return the collected Result so the caller's stop_when can see it.
|
|
126
|
+
return results[idx] = decode(buf)
|
|
127
|
+
end
|
|
128
|
+
buf << chunk
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# A partial/garbage Marshal stream (dead worker) must not crash the pool.
|
|
134
|
+
# Degrade to an error Result. The payload loads frozen, which also shares
|
|
135
|
+
# equal strings across payloads (the test names of a --matrix run repeat
|
|
136
|
+
# in every row).
|
|
137
|
+
# @param data [String] marshaled payload.
|
|
138
|
+
# @return [Mutineer::Result] decoded result or error result.
|
|
139
|
+
def decode(data)
|
|
140
|
+
return Result.error("worker produced no result") if data.empty?
|
|
141
|
+
|
|
142
|
+
Marshal.load(data, freeze: true)
|
|
143
|
+
rescue StandardError => e
|
|
144
|
+
Result.error("worker result unreadable: #{e.class}: #{e.message}")
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
data/lib/mutineer.rb
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "mutineer/version"
|
|
4
|
+
require_relative "mutineer/config"
|
|
5
|
+
require_relative "mutineer/parser"
|
|
6
|
+
require_relative "mutineer/subject"
|
|
7
|
+
require_relative "mutineer/mutation"
|
|
8
|
+
require_relative "mutineer/project"
|
|
9
|
+
require_relative "mutineer/pairing"
|
|
10
|
+
require_relative "mutineer/result"
|
|
11
|
+
require_relative "mutineer/coverage_map"
|
|
12
|
+
require_relative "mutineer/changed_lines"
|
|
13
|
+
require_relative "mutineer/isolation"
|
|
14
|
+
require_relative "mutineer/minitest_integration"
|
|
15
|
+
require_relative "mutineer/test_runners"
|
|
16
|
+
require_relative "mutineer/mutators/base"
|
|
17
|
+
require_relative "mutineer/mutators/arithmetic"
|
|
18
|
+
require_relative "mutineer/mutators/comparison"
|
|
19
|
+
require_relative "mutineer/mutators/boolean_connector"
|
|
20
|
+
require_relative "mutineer/mutators/boolean_literal"
|
|
21
|
+
require_relative "mutineer/mutators/statement_removal"
|
|
22
|
+
require_relative "mutineer/mutators/return_nil"
|
|
23
|
+
require_relative "mutineer/mutators/literal_mutation"
|
|
24
|
+
require_relative "mutineer/mutators/condition_negation"
|
|
25
|
+
require_relative "mutineer/mutators/string_literal"
|
|
26
|
+
require_relative "mutineer/mutators/regex_literal"
|
|
27
|
+
require_relative "mutineer/mutators/collection_method"
|
|
28
|
+
require_relative "mutineer/mutators/safe_navigation"
|
|
29
|
+
require_relative "mutineer/mutators/range_literal"
|
|
30
|
+
require_relative "mutineer/mutators/negation_removal"
|
|
31
|
+
require_relative "mutineer/mutators/chain_link"
|
|
32
|
+
require_relative "mutineer/mutators/operand_removal"
|
|
33
|
+
require_relative "mutineer/mutators/array_literal"
|
|
34
|
+
require_relative "mutineer/mutators/condition_forcing"
|
|
35
|
+
require_relative "mutineer/mutators/operator_assignment"
|
|
36
|
+
require_relative "mutineer/mutator_registry"
|
|
37
|
+
require_relative "mutineer/worker_pool"
|
|
38
|
+
require_relative "mutineer/progress"
|
|
39
|
+
require_relative "mutineer/job_plan"
|
|
40
|
+
require_relative "mutineer/runner"
|
|
41
|
+
require_relative "mutineer/kill_matrix"
|
|
42
|
+
require_relative "mutineer/reporter"
|
|
43
|
+
require_relative "mutineer/baseline"
|
|
44
|
+
require_relative "mutineer/cli"
|
|
45
|
+
|
|
46
|
+
# Mutineer is the top-level namespace for the mutation-testing library.
|
|
47
|
+
module Mutineer
|
|
48
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: mutineer-mongoid
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 2.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- David Teren
|
|
8
|
+
- Justin Allen Marsh
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: rake
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '13.0'
|
|
20
|
+
type: :development
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '13.0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: yard
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0.9'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0.9'
|
|
41
|
+
description: Mutineer mutates your source one change at a time and runs your Minitest
|
|
42
|
+
or RSpec suite against each mutant to find tests that don't actually test anything.
|
|
43
|
+
Prism-based, fork-isolated, zero runtime dependencies.
|
|
44
|
+
email:
|
|
45
|
+
- 48605824+justinallenmarsh@users.noreply.github.com
|
|
46
|
+
executables:
|
|
47
|
+
- mutineer
|
|
48
|
+
extensions: []
|
|
49
|
+
extra_rdoc_files: []
|
|
50
|
+
files:
|
|
51
|
+
- CHANGELOG.md
|
|
52
|
+
- LICENSE
|
|
53
|
+
- README.md
|
|
54
|
+
- bin/mutineer
|
|
55
|
+
- lib/mutineer.rb
|
|
56
|
+
- lib/mutineer/baseline.rb
|
|
57
|
+
- lib/mutineer/changed_lines.rb
|
|
58
|
+
- lib/mutineer/child_stdout.rb
|
|
59
|
+
- lib/mutineer/cli.rb
|
|
60
|
+
- lib/mutineer/config.rb
|
|
61
|
+
- lib/mutineer/coverage_map.rb
|
|
62
|
+
- lib/mutineer/daemon_backend.rb
|
|
63
|
+
- lib/mutineer/daemon_client.rb
|
|
64
|
+
- lib/mutineer/daemon_server.rb
|
|
65
|
+
- lib/mutineer/external_backend.rb
|
|
66
|
+
- lib/mutineer/file_swap.rb
|
|
67
|
+
- lib/mutineer/isolation.rb
|
|
68
|
+
- lib/mutineer/job_plan.rb
|
|
69
|
+
- lib/mutineer/kill_channel.rb
|
|
70
|
+
- lib/mutineer/kill_matrix.rb
|
|
71
|
+
- lib/mutineer/minitest_integration.rb
|
|
72
|
+
- lib/mutineer/minitest_integration/kill_recorder.rb
|
|
73
|
+
- lib/mutineer/minitest_integration/outer_reporter.rb
|
|
74
|
+
- lib/mutineer/minitest_integration/stop_at_first_failure.rb
|
|
75
|
+
- lib/mutineer/mongoid_failures.rb
|
|
76
|
+
- lib/mutineer/mongoid_worker_db.rb
|
|
77
|
+
- lib/mutineer/mutant_id.rb
|
|
78
|
+
- lib/mutineer/mutation.rb
|
|
79
|
+
- lib/mutineer/mutator_registry.rb
|
|
80
|
+
- lib/mutineer/mutators/arithmetic.rb
|
|
81
|
+
- lib/mutineer/mutators/array_literal.rb
|
|
82
|
+
- lib/mutineer/mutators/base.rb
|
|
83
|
+
- lib/mutineer/mutators/boolean_connector.rb
|
|
84
|
+
- lib/mutineer/mutators/boolean_literal.rb
|
|
85
|
+
- lib/mutineer/mutators/chain_link.rb
|
|
86
|
+
- lib/mutineer/mutators/collection_method.rb
|
|
87
|
+
- lib/mutineer/mutators/comparison.rb
|
|
88
|
+
- lib/mutineer/mutators/condition_forcing.rb
|
|
89
|
+
- lib/mutineer/mutators/condition_negation.rb
|
|
90
|
+
- lib/mutineer/mutators/literal_mutation.rb
|
|
91
|
+
- lib/mutineer/mutators/negation_removal.rb
|
|
92
|
+
- lib/mutineer/mutators/operand_removal.rb
|
|
93
|
+
- lib/mutineer/mutators/operator_assignment.rb
|
|
94
|
+
- lib/mutineer/mutators/range_literal.rb
|
|
95
|
+
- lib/mutineer/mutators/regex_literal.rb
|
|
96
|
+
- lib/mutineer/mutators/return_nil.rb
|
|
97
|
+
- lib/mutineer/mutators/safe_navigation.rb
|
|
98
|
+
- lib/mutineer/mutators/statement_removal.rb
|
|
99
|
+
- lib/mutineer/mutators/string_literal.rb
|
|
100
|
+
- lib/mutineer/orphan_guard.rb
|
|
101
|
+
- lib/mutineer/pairing.rb
|
|
102
|
+
- lib/mutineer/parser.rb
|
|
103
|
+
- lib/mutineer/progress.rb
|
|
104
|
+
- lib/mutineer/project.rb
|
|
105
|
+
- lib/mutineer/project_path.rb
|
|
106
|
+
- lib/mutineer/rails_worker_db.rb
|
|
107
|
+
- lib/mutineer/reporter.rb
|
|
108
|
+
- lib/mutineer/result.rb
|
|
109
|
+
- lib/mutineer/runner.rb
|
|
110
|
+
- lib/mutineer/statement_lines.rb
|
|
111
|
+
- lib/mutineer/subject.rb
|
|
112
|
+
- lib/mutineer/test_runners.rb
|
|
113
|
+
- lib/mutineer/test_runners/minitest.rb
|
|
114
|
+
- lib/mutineer/test_runners/rspec.rb
|
|
115
|
+
- lib/mutineer/version.rb
|
|
116
|
+
- lib/mutineer/worker_pool.rb
|
|
117
|
+
homepage: https://github.com/justinallenmarsh/mutineer-mongoid
|
|
118
|
+
licenses:
|
|
119
|
+
- MIT
|
|
120
|
+
metadata:
|
|
121
|
+
source_code_uri: https://github.com/justinallenmarsh/mutineer-mongoid
|
|
122
|
+
changelog_uri: https://github.com/justinallenmarsh/mutineer-mongoid/blob/main/CHANGELOG.md
|
|
123
|
+
bug_tracker_uri: https://github.com/justinallenmarsh/mutineer-mongoid/issues
|
|
124
|
+
documentation_uri: https://github.com/justinallenmarsh/mutineer-mongoid#readme
|
|
125
|
+
rubygems_mfa_required: 'true'
|
|
126
|
+
rdoc_options: []
|
|
127
|
+
require_paths:
|
|
128
|
+
- lib
|
|
129
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
130
|
+
requirements:
|
|
131
|
+
- - ">="
|
|
132
|
+
- !ruby/object:Gem::Version
|
|
133
|
+
version: '3.4'
|
|
134
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
135
|
+
requirements:
|
|
136
|
+
- - ">="
|
|
137
|
+
- !ruby/object:Gem::Version
|
|
138
|
+
version: '0'
|
|
139
|
+
requirements: []
|
|
140
|
+
rubygems_version: 3.6.9
|
|
141
|
+
specification_version: 4
|
|
142
|
+
summary: Mutation testing with parallel Mongoid/RSpec support, based on Mutineer.
|
|
143
|
+
test_files: []
|