henitai 0.2.1 → 0.3.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 +80 -1
- data/README.md +123 -2
- data/assets/schema/henitai.schema.json +35 -0
- data/lib/henitai/available_cpu_count.rb +16 -23
- data/lib/henitai/canonical_report_merger.rb +107 -0
- data/lib/henitai/canonical_report_writer.rb +22 -0
- data/lib/henitai/checkpoint_reporter.rb +79 -0
- data/lib/henitai/cli/clean_command.rb +14 -8
- data/lib/henitai/cli/command_support.rb +2 -1
- data/lib/henitai/cli/operator_command.rb +2 -1
- data/lib/henitai/cli/options.rb +21 -57
- data/lib/henitai/cli/run_command.rb +36 -3
- data/lib/henitai/cli/run_options.rb +108 -0
- data/lib/henitai/cli.rb +12 -4
- data/lib/henitai/composite_progress_reporter.rb +42 -0
- data/lib/henitai/configuration.rb +41 -9
- data/lib/henitai/configuration_validator/rules.rb +18 -0
- data/lib/henitai/configuration_validator/scalars.rb +46 -0
- data/lib/henitai/configuration_validator.rb +8 -1
- data/lib/henitai/coverage_bootstrapper.rb +54 -5
- data/lib/henitai/coverage_formatter.rb +5 -1
- data/lib/henitai/coverage_report_reader.rb +28 -5
- data/lib/henitai/execution_engine/env_scope.rb +67 -0
- data/lib/henitai/execution_engine.rb +111 -50
- data/lib/henitai/generated_artifacts.rb +58 -0
- data/lib/henitai/incremental_filter.rb +100 -0
- data/lib/henitai/integration/child_debug_support.rb +6 -2
- data/lib/henitai/integration/coverage_suppression.rb +9 -0
- data/lib/henitai/integration/minitest.rb +11 -43
- data/lib/henitai/integration/minitest_load_path.rb +14 -0
- data/lib/henitai/integration/minitest_suite_command.rb +17 -0
- data/lib/henitai/integration/minitest_test_runner.rb +39 -0
- data/lib/henitai/integration/rails_environment_preloader.rb +14 -0
- data/lib/henitai/integration/rspec_child_runner.rb +3 -3
- data/lib/henitai/integration/rspec_test_selection.rb +3 -0
- data/lib/henitai/integration/scenario_log_support.rb +64 -20
- data/lib/henitai/minitest_coverage_reporter.rb +4 -1
- data/lib/henitai/mutant/activator.rb +14 -0
- data/lib/henitai/mutant.rb +13 -6
- data/lib/henitai/mutant_history_store/sql.rb +21 -3
- data/lib/henitai/mutant_history_store/verdict_cache.rb +84 -0
- data/lib/henitai/mutant_history_store.rb +62 -12
- data/lib/henitai/mutant_identity.rb +33 -2
- data/lib/henitai/mutation_skip_directives.rb +227 -0
- data/lib/henitai/operator.rb +3 -1
- data/lib/henitai/operators/equality_identity_operator.rb +51 -0
- data/lib/henitai/operators/equality_operator.rb +8 -3
- data/lib/henitai/operators.rb +1 -0
- data/lib/henitai/per_test_coverage.rb +66 -0
- data/lib/henitai/per_test_coverage_collector.rb +20 -5
- data/lib/henitai/per_test_coverage_selector.rb +11 -33
- data/lib/henitai/reporter/dashboard_metadata_provider.rb +113 -0
- data/lib/henitai/reporter.rb +238 -117
- data/lib/henitai/reports_directory_lock.rb +76 -0
- data/lib/henitai/result.rb +72 -9
- data/lib/henitai/runner.rb +82 -40
- data/lib/henitai/scenario_execution_result.rb +12 -0
- data/lib/henitai/slot_scheduler/draining.rb +21 -15
- data/lib/henitai/slot_scheduler.rb +73 -15
- data/lib/henitai/static_filter.rb +16 -6
- data/lib/henitai/survivor_rerun_strategy.rb +1 -1
- data/lib/henitai/survivor_test_filter.rb +1 -1
- data/lib/henitai/test_prioritizer.rb +28 -2
- data/lib/henitai/timeout_calibrator.rb +44 -0
- data/lib/henitai/verdict_fingerprint.rb +155 -0
- data/lib/henitai/version.rb +1 -1
- data/lib/henitai.rb +14 -1
- data/sig/henitai.rbs +230 -25
- metadata +22 -3
- data/lib/henitai/parallel_execution_runner.rb +0 -153
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Henitai
|
|
4
|
-
# Runs pending mutants across worker threads with signal and stdin handling.
|
|
5
|
-
class ParallelExecutionRunner
|
|
6
|
-
ParallelExecutionContext = Struct.new(
|
|
7
|
-
:queue, :integration, :config, :progress_reporter,
|
|
8
|
-
:mutex, :state, :old_handlers, :stdin_watcher
|
|
9
|
-
)
|
|
10
|
-
|
|
11
|
-
def initialize(worker_count:)
|
|
12
|
-
@worker_count = worker_count
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def run(mutants, integration, config, progress_reporter, options = {})
|
|
16
|
-
context = build_parallel_context(
|
|
17
|
-
mutants,
|
|
18
|
-
integration,
|
|
19
|
-
config,
|
|
20
|
-
progress_reporter
|
|
21
|
-
)
|
|
22
|
-
execute_parallel_execution(
|
|
23
|
-
context,
|
|
24
|
-
stdin_pipe: options.fetch(:stdin_pipe, false),
|
|
25
|
-
process_mutant: options.fetch(:process_mutant)
|
|
26
|
-
)
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def execute_parallel_execution(context, stdin_pipe:, process_mutant:)
|
|
30
|
-
install_parallel_signal_traps(context)
|
|
31
|
-
start_parallel_stdin_watcher(context, stdin_pipe)
|
|
32
|
-
parallel_workers(context, process_mutant).each(&:join)
|
|
33
|
-
ensure
|
|
34
|
-
teardown_parallel_execution(context)
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
private
|
|
38
|
-
|
|
39
|
-
attr_reader :worker_count
|
|
40
|
-
|
|
41
|
-
def teardown_parallel_execution(context)
|
|
42
|
-
stop_parallel_stdin_watcher(context)
|
|
43
|
-
restore_parallel_signal_traps(context)
|
|
44
|
-
emit_scheduler_diagnostics if Integration::SchedulerDiagnostics.enabled?
|
|
45
|
-
raise context.state[:error] if context&.state&.fetch(:error, nil)
|
|
46
|
-
raise Interrupt if context&.state&.fetch(:stopping, false)
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def emit_scheduler_diagnostics
|
|
50
|
-
summary = Integration::SchedulerDiagnostics.summary
|
|
51
|
-
warn "[henitai-scheduler] max_concurrent_children=#{summary[:max_concurrent]}"
|
|
52
|
-
warn "[henitai-scheduler] child_intervals=#{summary[:intervals].inspect}"
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
def build_parallel_queue(mutants)
|
|
56
|
-
Queue.new.tap { |queue| mutants.each { |mutant| queue << mutant } }
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
def build_parallel_context(mutants, integration, config, progress_reporter)
|
|
60
|
-
ParallelExecutionContext.new(
|
|
61
|
-
build_parallel_queue(mutants),
|
|
62
|
-
integration,
|
|
63
|
-
config,
|
|
64
|
-
progress_reporter,
|
|
65
|
-
Mutex.new,
|
|
66
|
-
{ stopping: false }
|
|
67
|
-
)
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
def install_parallel_signal_traps(context)
|
|
71
|
-
context.old_handlers = {
|
|
72
|
-
int: trap(:INT) { stop_parallel_execution(context) },
|
|
73
|
-
term: trap(:TERM) { stop_parallel_execution(context) },
|
|
74
|
-
hup: trap(:HUP) { stop_parallel_execution(context) }
|
|
75
|
-
}
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
def stop_parallel_execution(context)
|
|
79
|
-
context.state[:stopping] = true
|
|
80
|
-
context.queue.clear
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
def start_parallel_stdin_watcher(context, stdin_pipe)
|
|
84
|
-
return unless stdin_pipe
|
|
85
|
-
# CI runners expose stdin as a non-interactive pipe, so EOF there should
|
|
86
|
-
# not be treated as a user disconnect.
|
|
87
|
-
return if ci_environment?
|
|
88
|
-
|
|
89
|
-
context.stdin_watcher = Thread.new do
|
|
90
|
-
$stdin.read
|
|
91
|
-
stop_parallel_execution(context)
|
|
92
|
-
rescue IOError, Errno::EBADF
|
|
93
|
-
nil
|
|
94
|
-
end
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
def parallel_workers(context, process_mutant)
|
|
98
|
-
Array.new(worker_count) do
|
|
99
|
-
Thread.new { process_parallel_worker(context, process_mutant) }
|
|
100
|
-
end
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
def process_parallel_worker(context, process_mutant)
|
|
104
|
-
loop do
|
|
105
|
-
break if context.state[:stopping]
|
|
106
|
-
|
|
107
|
-
run_one_mutant(context, process_mutant)
|
|
108
|
-
rescue ThreadError
|
|
109
|
-
break
|
|
110
|
-
rescue StandardError => e
|
|
111
|
-
record_parallel_error(context, e)
|
|
112
|
-
break
|
|
113
|
-
end
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
def run_one_mutant(context, process_mutant)
|
|
117
|
-
mutant = context.queue.pop(true)
|
|
118
|
-
process_mutant.call(
|
|
119
|
-
mutant,
|
|
120
|
-
context.integration,
|
|
121
|
-
context.config,
|
|
122
|
-
context.progress_reporter,
|
|
123
|
-
context.mutex
|
|
124
|
-
)
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
def stop_parallel_stdin_watcher(context)
|
|
128
|
-
context&.stdin_watcher&.kill
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
def restore_parallel_signal_traps(context)
|
|
132
|
-
handlers = context&.old_handlers
|
|
133
|
-
return unless handlers
|
|
134
|
-
|
|
135
|
-
trap(:INT, handlers[:int] || "DEFAULT")
|
|
136
|
-
trap(:TERM, handlers[:term] || "DEFAULT")
|
|
137
|
-
trap(:HUP, handlers[:hup] || "DEFAULT")
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
def record_parallel_error(context, error)
|
|
141
|
-
context.mutex.synchronize do
|
|
142
|
-
context.state[:error] ||= error
|
|
143
|
-
context.state[:stopping] = true
|
|
144
|
-
context.queue.clear
|
|
145
|
-
end
|
|
146
|
-
end
|
|
147
|
-
|
|
148
|
-
def ci_environment?
|
|
149
|
-
value = ENV.fetch("CI", nil)
|
|
150
|
-
value && !%w[0 false].include?(value.downcase)
|
|
151
|
-
end
|
|
152
|
-
end
|
|
153
|
-
end
|