active_mutator 0.1.1 → 0.2.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/README.md +103 -11
- data/lib/active_mutator/accepted_ledger.rb +22 -7
- data/lib/active_mutator/baseline_delta.rb +67 -1
- data/lib/active_mutator/cli.rb +16 -4
- data/lib/active_mutator/config.rb +2 -1
- data/lib/active_mutator/config_file.rb +89 -0
- data/lib/active_mutator/defined_constants.rb +48 -0
- data/lib/active_mutator/edit.rb +8 -2
- data/lib/active_mutator/engine.rb +15 -2
- data/lib/active_mutator/inserter.rb +6 -3
- data/lib/active_mutator/operators/base.rb +2 -1
- data/lib/active_mutator/operators/call_swap.rb +16 -0
- data/lib/active_mutator/operators/literal.rb +14 -2
- data/lib/active_mutator/reporter/github.rb +36 -0
- data/lib/active_mutator/reporter/json.rb +1 -0
- data/lib/active_mutator/reporter/operator_stats.rb +20 -0
- data/lib/active_mutator/reporter/stryker_json.rb +117 -0
- data/lib/active_mutator/reporter/terminal.rb +11 -0
- data/lib/active_mutator/runner.rb +119 -15
- data/lib/active_mutator/scheduler.rb +41 -5
- data/lib/active_mutator/source_location.rb +21 -0
- data/lib/active_mutator/subject.rb +7 -1
- data/lib/active_mutator/subject_finder.rb +46 -7
- data/lib/active_mutator/subject_matcher.rb +23 -0
- data/lib/active_mutator/timeout_calibrator.rb +75 -0
- data/lib/active_mutator/version.rb +1 -1
- data/lib/active_mutator/work_item.rb +8 -1
- data/lib/active_mutator/worker.rb +3 -0
- data/lib/active_mutator.rb +8 -0
- metadata +10 -2
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
module ActiveMutator
|
|
2
|
+
# Adaptive timeout budgets (#9). Static budgets derive from baseline times
|
|
3
|
+
# measured warm and unloaded; under parallel load they misclassify
|
|
4
|
+
# slow-but-honest kills as timeouts. The Scheduler feeds this with the
|
|
5
|
+
# observed wall time of every KILLED fork (errors exit artificially fast,
|
|
6
|
+
# survivors run their whole covering set — both would bias the median;
|
|
7
|
+
# timed-out forks have no known wall time at all). One instance per lane:
|
|
8
|
+
# parallel and serial run under different load regimes.
|
|
9
|
+
#
|
|
10
|
+
# Utilization is elapsed / the EFFECTIVE budget the fork ran under (the
|
|
11
|
+
# value budget_for returned at its spawn), not the static item.timeout —
|
|
12
|
+
# otherwise a pinned-high scale could never observe recovery (ratchet).
|
|
13
|
+
# Once WARMUP observations exist, remaining budgets' variable part is
|
|
14
|
+
# scaled by the clamped median utilization. The fixed part (timeout_floor
|
|
15
|
+
# + browser boot) is never scaled: fork boot cost does not shrink because
|
|
16
|
+
# examples run fast.
|
|
17
|
+
#
|
|
18
|
+
# The scale is grow-only (MIN_SCALE = 1.0): budgets may only extend beyond
|
|
19
|
+
# the static value, never fall below it. Downscaling has no recovery signal
|
|
20
|
+
# because timeouts are CENSORED samples — the scheduler skips them (they have
|
|
21
|
+
# no known wall time), so only killed forks feed the window. A low median
|
|
22
|
+
# utilization would shrink the budget, causing a legitimately slow kill to be
|
|
23
|
+
# reaped as a timeout; that censored kill never enters the window, so nothing
|
|
24
|
+
# ever pushes utilization back up. The shrink self-sustains — an asymmetric
|
|
25
|
+
# ratchet. Growing is safe (a too-large budget still records a real kill and
|
|
26
|
+
# relaxes); shrinking is not, so we forbid it.
|
|
27
|
+
class TimeoutCalibrator
|
|
28
|
+
WARMUP = 5
|
|
29
|
+
# A real sliding window, not full-run history: a 500-mutant run's early
|
|
30
|
+
# load regime must age out completely instead of anchoring the median
|
|
31
|
+
# forever. 30 samples is enough for a stable median and small enough to
|
|
32
|
+
# track load changes within a few dozen finishes.
|
|
33
|
+
WINDOW = 30
|
|
34
|
+
TARGET_UTILIZATION = 0.25
|
|
35
|
+
MIN_SCALE = 1.0
|
|
36
|
+
MAX_SCALE = 4.0
|
|
37
|
+
|
|
38
|
+
def initialize
|
|
39
|
+
@utilizations = []
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def record(elapsed_seconds, budget)
|
|
43
|
+
return unless budget.positive?
|
|
44
|
+
|
|
45
|
+
@utilizations << elapsed_seconds / budget
|
|
46
|
+
@utilizations.shift while @utilizations.size > WINDOW
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def warmed? = @utilizations.size >= WARMUP
|
|
50
|
+
|
|
51
|
+
def budget_for(item)
|
|
52
|
+
return item.timeout unless warmed?
|
|
53
|
+
|
|
54
|
+
fixed = item.timeout - item.variable
|
|
55
|
+
item.variable * scale + fixed
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def scale
|
|
59
|
+
# An empty window has no median; a caller invoking scale without warmed?
|
|
60
|
+
# (median of [] is nil) would otherwise raise. Neutral scale = no change.
|
|
61
|
+
return 1.0 if @utilizations.empty?
|
|
62
|
+
|
|
63
|
+
s = median(@utilizations) / TARGET_UTILIZATION
|
|
64
|
+
s.clamp(MIN_SCALE, MAX_SCALE)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
def median(values)
|
|
70
|
+
sorted = values.sort
|
|
71
|
+
mid = sorted.size / 2
|
|
72
|
+
sorted.size.odd? ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2.0
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
module ActiveMutator
|
|
2
2
|
# lane: :parallel (default pool) | :serial (browser-covered, one at a time)
|
|
3
|
-
|
|
3
|
+
# timeout: static total budget (variable + fixed), kept for --debug-plan and compat
|
|
4
|
+
# variable: the baseline-estimate-derived part (estimate * timeout_factor) — the
|
|
5
|
+
# only part the TimeoutCalibrator scales
|
|
6
|
+
WorkItem = Data.define(:mutation, :example_ids, :timeout, :lane, :variable) do
|
|
7
|
+
def initialize(mutation:, example_ids:, timeout:, lane:, variable: 0.0)
|
|
8
|
+
super
|
|
9
|
+
end
|
|
10
|
+
end
|
|
4
11
|
end
|
|
@@ -24,6 +24,9 @@ module ActiveMutator
|
|
|
24
24
|
devnull = File.open(File::NULL, "w")
|
|
25
25
|
runner = RSpec::Core::Runner.new(RSpec::Core::ConfigurationOptions.new(@example_ids))
|
|
26
26
|
runner.setup(devnull, devnull) # loads spec files -> loads the app
|
|
27
|
+
# One failure kills the mutant; running the rest of the covering set
|
|
28
|
+
# is pure waste inside the fork.
|
|
29
|
+
RSpec.configuration.fail_fast = 1
|
|
27
30
|
Inserter.new.insert(@mutation) # now the target constant exists
|
|
28
31
|
after_fork_hygiene
|
|
29
32
|
code = runner.run_specs(covering_groups)
|
data/lib/active_mutator.rb
CHANGED
|
@@ -11,6 +11,7 @@ require_relative "active_mutator/edit"
|
|
|
11
11
|
require_relative "active_mutator/splicer"
|
|
12
12
|
require_relative "active_mutator/subject"
|
|
13
13
|
require_relative "active_mutator/subject_finder"
|
|
14
|
+
require_relative "active_mutator/subject_matcher"
|
|
14
15
|
require_relative "active_mutator/operators/base"
|
|
15
16
|
require_relative "active_mutator/operators/conditional_boundary"
|
|
16
17
|
require_relative "active_mutator/operators/condition_forcing"
|
|
@@ -27,16 +28,23 @@ require_relative "active_mutator/atomic_file"
|
|
|
27
28
|
require_relative "active_mutator/coverage_map"
|
|
28
29
|
require_relative "active_mutator/baseline"
|
|
29
30
|
require_relative "active_mutator/baseline_delta"
|
|
31
|
+
require_relative "active_mutator/defined_constants"
|
|
30
32
|
require_relative "active_mutator/inserter"
|
|
31
33
|
require_relative "active_mutator/worker"
|
|
32
34
|
require_relative "active_mutator/result"
|
|
33
35
|
require_relative "active_mutator/work_item"
|
|
36
|
+
require_relative "active_mutator/timeout_calibrator"
|
|
34
37
|
require_relative "active_mutator/scheduler"
|
|
38
|
+
require_relative "active_mutator/reporter/operator_stats"
|
|
35
39
|
require_relative "active_mutator/reporter/terminal"
|
|
40
|
+
require_relative "active_mutator/reporter/github"
|
|
36
41
|
require_relative "active_mutator/reporter/json"
|
|
42
|
+
require_relative "active_mutator/source_location"
|
|
37
43
|
require_relative "active_mutator/since_filter"
|
|
38
44
|
require_relative "active_mutator/fingerprint"
|
|
39
45
|
require_relative "active_mutator/accepted_ledger"
|
|
46
|
+
require_relative "active_mutator/reporter/stryker_json"
|
|
40
47
|
require_relative "active_mutator/config"
|
|
41
48
|
require_relative "active_mutator/runner"
|
|
49
|
+
require_relative "active_mutator/config_file"
|
|
42
50
|
require_relative "active_mutator/cli"
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: active_mutator
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Daniel John
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-07-
|
|
11
|
+
date: 2026-07-20 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: prism
|
|
@@ -74,7 +74,9 @@ files:
|
|
|
74
74
|
- lib/active_mutator/baseline_hooks.rb
|
|
75
75
|
- lib/active_mutator/cli.rb
|
|
76
76
|
- lib/active_mutator/config.rb
|
|
77
|
+
- lib/active_mutator/config_file.rb
|
|
77
78
|
- lib/active_mutator/coverage_map.rb
|
|
79
|
+
- lib/active_mutator/defined_constants.rb
|
|
78
80
|
- lib/active_mutator/edit.rb
|
|
79
81
|
- lib/active_mutator/engine.rb
|
|
80
82
|
- lib/active_mutator/fingerprint.rb
|
|
@@ -89,15 +91,21 @@ files:
|
|
|
89
91
|
- lib/active_mutator/operators/logical_operator.rb
|
|
90
92
|
- lib/active_mutator/operators/negation_removal.rb
|
|
91
93
|
- lib/active_mutator/operators/statement_deletion.rb
|
|
94
|
+
- lib/active_mutator/reporter/github.rb
|
|
92
95
|
- lib/active_mutator/reporter/json.rb
|
|
96
|
+
- lib/active_mutator/reporter/operator_stats.rb
|
|
97
|
+
- lib/active_mutator/reporter/stryker_json.rb
|
|
93
98
|
- lib/active_mutator/reporter/terminal.rb
|
|
94
99
|
- lib/active_mutator/result.rb
|
|
95
100
|
- lib/active_mutator/runner.rb
|
|
96
101
|
- lib/active_mutator/scheduler.rb
|
|
97
102
|
- lib/active_mutator/since_filter.rb
|
|
103
|
+
- lib/active_mutator/source_location.rb
|
|
98
104
|
- lib/active_mutator/splicer.rb
|
|
99
105
|
- lib/active_mutator/subject.rb
|
|
100
106
|
- lib/active_mutator/subject_finder.rb
|
|
107
|
+
- lib/active_mutator/subject_matcher.rb
|
|
108
|
+
- lib/active_mutator/timeout_calibrator.rb
|
|
101
109
|
- lib/active_mutator/version.rb
|
|
102
110
|
- lib/active_mutator/work_item.rb
|
|
103
111
|
- lib/active_mutator/worker.rb
|