henitai 0.4.0 → 0.5.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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +86 -1
  3. data/README.md +1 -1
  4. data/lib/henitai/cli.rb +1 -1
  5. data/lib/henitai/configuration.rb +9 -2
  6. data/lib/henitai/dirty_source_detector.rb +53 -0
  7. data/lib/henitai/equivalence_detector/operand_predicates.rb +49 -0
  8. data/lib/henitai/equivalence_detector.rb +6 -23
  9. data/lib/henitai/excluded_test_filter.rb +47 -0
  10. data/lib/henitai/execution_engine.rb +5 -11
  11. data/lib/henitai/inherited_fd_registry.rb +66 -0
  12. data/lib/henitai/integration/base.rb +7 -2
  13. data/lib/henitai/integration/child_bootstrap.rb +27 -0
  14. data/lib/henitai/integration/child_debug_log.rb +135 -0
  15. data/lib/henitai/integration/child_runtime_control.rb +6 -18
  16. data/lib/henitai/integration/loaded_features.rb +38 -0
  17. data/lib/henitai/integration/mutant_run_support.rb +5 -5
  18. data/lib/henitai/integration/rspec_child_runner.rb +16 -15
  19. data/lib/henitai/integration/rspec_process_runner.rb +7 -2
  20. data/lib/henitai/integration.rb +10 -7
  21. data/lib/henitai/mutation_skip_directives.rb +7 -1
  22. data/lib/henitai/operators/return_value.rb +1 -1
  23. data/lib/henitai/orphan_watchdog.rb +93 -0
  24. data/lib/henitai/process_liveness.rb +41 -0
  25. data/lib/henitai/reports_directory_lock.rb +12 -11
  26. data/lib/henitai/result.rb +30 -3
  27. data/lib/henitai/runner.rb +41 -123
  28. data/lib/henitai/runner_dependencies.rb +75 -0
  29. data/lib/henitai/slot_scheduler/drain_verdict.rb +29 -0
  30. data/lib/henitai/slot_scheduler/draining.rb +7 -17
  31. data/lib/henitai/slot_scheduler/retry_policy.rb +21 -0
  32. data/lib/henitai/slot_scheduler/slot_deadline.rb +37 -0
  33. data/lib/henitai/slot_scheduler/slot_table.rb +75 -0
  34. data/lib/henitai/slot_scheduler/test_file_selection.rb +40 -0
  35. data/lib/henitai/slot_scheduler.rb +68 -80
  36. data/lib/henitai/source_file_selection.rb +76 -0
  37. data/lib/henitai/subject_selection.rb +33 -0
  38. data/lib/henitai/survivor_rerun_strategy.rb +7 -19
  39. data/lib/henitai/version.rb +1 -1
  40. data/lib/henitai.rb +8 -0
  41. data/sig/henitai.rbs +92 -38
  42. metadata +31 -9
  43. data/lib/henitai/integration/child_debug_support.rb +0 -119
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Henitai
4
+ # Turns a list of source files into the subjects a run mutates, applying any
5
+ # CLI subject patterns.
6
+ #
7
+ # With no patterns every resolved subject is kept. With patterns, each is
8
+ # applied independently and the results concatenated, so overlapping patterns
9
+ # can name the same subject twice — hence the de-duplication on
10
+ # `[expression, source_file]` rather than on object identity. Expression alone
11
+ # is not enough: the same expression can legitimately appear in two files.
12
+ class SubjectSelection
13
+ def initialize(subject_resolver:, patterns:)
14
+ @subject_resolver = subject_resolver
15
+ @patterns = patterns
16
+ end
17
+
18
+ def resolve(source_files)
19
+ subjects = @subject_resolver.resolve_from_files(source_files)
20
+ return subjects if pattern_expressions.empty?
21
+
22
+ unique(pattern_expressions.flat_map { |expression| @subject_resolver.apply_pattern(subjects, expression) })
23
+ end
24
+
25
+ def unique(subjects)
26
+ subjects.uniq { |subject| [subject.expression, subject.source_file] }
27
+ end
28
+
29
+ private
30
+
31
+ def pattern_expressions = Array(@patterns).map(&:expression)
32
+ end
33
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "dirty_source_detector"
4
+
3
5
  module Henitai
4
6
  # Survivor-rerun fast path for {Runner}.
5
7
  #
@@ -154,27 +156,13 @@ module Henitai
154
156
  end
155
157
 
156
158
  def dirty_source_files?(dirty_worktree_files, git_sha: nil)
157
- return true if dirty_worktree_files.nil?
158
-
159
- all_changed = dirty_worktree_files + committed_changed_files(git_sha)
160
- include_roots = Array(@config.includes).map { |path| normalize_path(path) }
161
- all_changed.any? { |path| in_include_root?(normalize_path(path), include_roots) }
162
- rescue StandardError
163
- true
159
+ dirty_source_detector.dirty?(dirty_worktree_files, git_sha: git_sha)
164
160
  end
165
161
 
166
- def committed_changed_files(git_sha)
167
- return [] unless git_sha
168
-
169
- @git_diff_analyzer.changed_files(from: git_sha, to: "HEAD")
170
- end
171
-
172
- def in_include_root?(path, include_roots)
173
- include_roots.any? { |root| path == root || path.start_with?("#{root}/") }
174
- end
175
-
176
- def normalize_path(path)
177
- File.expand_path(path)
162
+ def dirty_source_detector
163
+ @dirty_source_detector ||= DirtySourceDetector.new(
164
+ includes: @config.includes, git_diff_analyzer: @git_diff_analyzer
165
+ )
178
166
  end
179
167
 
180
168
  def warn_survivor_drift(selector)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Henitai
4
- VERSION = "0.4.0"
4
+ VERSION = "0.5.0"
5
5
  end
data/lib/henitai.rb CHANGED
@@ -58,6 +58,9 @@ module Henitai
58
58
  autoload :SurvivorTestFilter, "henitai/survivor_test_filter"
59
59
  autoload :SurvivorActivationCache, "henitai/survivor_activation_cache"
60
60
  autoload :SurvivorRerunStrategy, "henitai/survivor_rerun_strategy"
61
+ autoload :DirtySourceDetector, "henitai/dirty_source_detector"
62
+ autoload :SourceFileSelection, "henitai/source_file_selection"
63
+ autoload :SubjectSelection, "henitai/subject_selection"
61
64
  autoload :ScenarioExecutionResult, "henitai/scenario_execution_result"
62
65
  autoload :CoverageFormatter, "henitai/coverage_formatter"
63
66
  autoload :MinitestCoverageReporter, "henitai/minitest_coverage_reporter"
@@ -65,12 +68,17 @@ module Henitai
65
68
  autoload :SyntaxValidator, "henitai/syntax_validator"
66
69
  autoload :SamplingStrategy, "henitai/sampling_strategy"
67
70
  autoload :TestPrioritizer, "henitai/test_prioritizer"
71
+ autoload :ExcludedTestFilter, "henitai/excluded_test_filter"
68
72
  autoload :TimeoutCalibrator, "henitai/timeout_calibrator"
69
73
  autoload :ExecutionEngine, "henitai/execution_engine"
70
74
  autoload :ProcessWorkerRunner, "henitai/process_worker_runner"
71
75
  autoload :SlotScheduler, "henitai/slot_scheduler"
72
76
  autoload :ProcessWakeup, "henitai/process_wakeup"
77
+ autoload :ProcessLiveness, "henitai/process_liveness"
78
+ autoload :InheritedFdRegistry, "henitai/inherited_fd_registry"
79
+ autoload :OrphanWatchdog, "henitai/orphan_watchdog"
73
80
  autoload :Runner, "henitai/runner"
81
+ autoload :RunnerDependencies, "henitai/runner_dependencies"
74
82
  autoload :Reporter, "henitai/reporter"
75
83
  autoload :Integration, "henitai/integration"
76
84
  autoload :Result, "henitai/result"
data/sig/henitai.rbs CHANGED
@@ -76,36 +76,56 @@ module Henitai
76
76
  module Operators
77
77
  end
78
78
 
79
- module Integration::ChildDebugSupport
79
+ class Integration::LoadedFeatures
80
+ def include?: (String) -> bool
81
+ def map: (Array[String]) -> Array[[String, bool]]
82
+
80
83
  private
81
84
 
82
- def debug_child_puts: (String) -> void
83
- def debug_child?: () -> bool
84
- def debug_child_rspec_trace: (
85
+ def candidates_for: (String) -> Array[String]
86
+ def normalize: (String) -> String
87
+ end
88
+
89
+ class Integration::ChildDebugLog
90
+ PREFIX: String
91
+
92
+ def initialize: (?io: IO?, ?loaded_features: Integration::LoadedFeatures) -> void
93
+ def enabled?: () -> bool
94
+ def write: (String) -> void
95
+ def rspec_trace: (
85
96
  test_files: Array[String],
86
97
  rspec_options: Array[String],
87
98
  rspec_argv: Array[String]
88
99
  ) -> void
89
- def debug_child_rspec_exit: (untyped) -> void
90
- def debug_child_example_count: (String) -> void
91
- def debug_child_activation_start: (String) -> void
92
- def debug_child_activation_end: (untyped, test_files: Array[String]) -> void
93
- def debug_child_mutant_meta: (Mutant) -> void
94
- def debug_child_activation_check: () -> void
95
- def loaded_feature_map: (Array[String]) -> Array[[String, bool]]
96
- def loaded_feature?: (String) -> bool
100
+ def rspec_exit: (untyped) -> void
101
+ def example_count: (String) -> void
102
+ def activation_start: (String) -> void
103
+ def activation_end: (untyped, test_files: Array[String]) -> void
104
+ def mutant_meta: (untyped) -> void
105
+ def activation_check: () -> void
106
+ def timeout_signal_sent: (Integer) -> void
107
+ def thread_dump: (String) -> void
97
108
  def rspec_world_example_count: () -> Integer?
98
- def pause: (Float) -> void
109
+
110
+ private
111
+
112
+ def dump_thread: (Thread, Integer) -> void
113
+ def value_of: (untyped, Symbol) -> untyped
114
+ def location_of: (untyped) -> String?
115
+ def subject_expression_of: (untyped) -> String?
116
+ def io: () -> IO
99
117
  end
100
118
 
101
119
  module Integration::ChildRuntimeControl
120
+ def child_debug_log: () -> Integration::ChildDebugLog
121
+
102
122
  private
103
123
 
104
124
  def suppress_simplecov!: () -> void
105
125
  def suppress_coverage!: () -> void
106
126
  def debug_child_timeout_dump: (Integer) -> void
107
127
  def install_debug_timeout_trap: () -> void
108
- def debug_child_thread_dump: (String) -> void
128
+ def pause: (Float) -> void
109
129
  end
110
130
 
111
131
  module Integration::RspecChildRunner
@@ -371,7 +391,6 @@ module Henitai
371
391
  def self.for: (String) -> untyped
372
392
 
373
393
  class Base
374
- include ChildDebugSupport
375
394
  include ChildRuntimeControl
376
395
 
377
396
  def select_tests: (Subject) -> Array[String]
@@ -383,6 +402,7 @@ module Henitai
383
402
  def wait_with_timeout: (Integer, Float) -> untyped
384
403
  def reap_child: (Integer) -> void
385
404
  def cleanup_process_group: (Integer) -> void
405
+ def child_debug_log: () -> ChildDebugLog
386
406
 
387
407
  private
388
408
 
@@ -392,18 +412,6 @@ module Henitai
392
412
  def cleanup_child_process: (Integer) -> void
393
413
  def suppress_simplecov!: () -> void
394
414
  def suppress_coverage!: () -> void
395
- def debug_child?: () -> bool
396
- def debug_child_puts: (String) -> void
397
- def debug_child_rspec_trace: (test_files: Array[String], rspec_options: Array[String], rspec_argv: Array[String]) -> void
398
- def debug_child_rspec_exit: (untyped status) -> void
399
- def debug_child_activation_start: (String mutant_id) -> void
400
- def debug_child_activation_end: (untyped activation_result, test_files: Array[String]) -> void
401
- def debug_child_mutant_meta: (Mutant) -> void
402
- def debug_child_activation_check: () -> void
403
- def debug_child_example_count: (String) -> void
404
- def loaded_feature_map: (Array[String]) -> Array[[String, bool]]
405
- def loaded_feature?: (String) -> bool
406
- def rspec_world_example_count: () -> Integer?
407
415
  def with_non_interactive_stdin: () { () -> untyped } -> untyped
408
416
  def subprocess_env: () -> Hash[String, String]
409
417
  def scenario_log_support: () -> ScenarioLogSupport
@@ -973,6 +981,8 @@ module Henitai
973
981
 
974
982
  SCHEMA_VERSION: String
975
983
  DEFAULT_THRESHOLDS: Hash[Symbol, Integer]
984
+ DEFAULT_COVERAGE_CRITERIA: Hash[Symbol, bool]
985
+ CRITERION_STATUSES: Hash[Symbol, Symbol]
976
986
 
977
987
  attr_reader mutants: Array[Mutant]
978
988
  attr_reader started_at: Time
@@ -982,8 +992,9 @@ module Henitai
982
992
  attr_reader session_id: String
983
993
  attr_reader git_sha: String?
984
994
  attr_reader since: String?
995
+ attr_reader coverage_criteria: Hash[Symbol, bool]
985
996
 
986
- def initialize: (mutants: Array[Mutant], started_at: Time, finished_at: Time, ?thresholds: Hash[Symbol, Integer]?, ?partial_rerun: bool, ?survivor_stats: Hash[Symbol, untyped]?, ?session_id: String, ?git_sha: String?, ?source_provider: ^(String) -> String, ?authoritative: bool, ?since: String?) -> void
997
+ def initialize: (mutants: Array[Mutant], started_at: Time, finished_at: Time, ?thresholds: Hash[Symbol, Integer]?, ?partial_rerun: bool, ?survivor_stats: Hash[Symbol, untyped]?, ?session_id: String, ?git_sha: String?, ?source_provider: ^(String) -> String, ?authoritative: bool, ?since: String?, ?coverage_criteria: Hash[Symbol, bool]?) -> void
987
998
  def partial_rerun?: () -> bool
988
999
  def authoritative?: () -> bool
989
1000
  def killed: () -> Integer
@@ -1000,6 +1011,7 @@ module Henitai
1000
1011
  private
1001
1012
 
1002
1013
  def detected_in: (Array[Mutant]) -> Integer
1014
+ def detected_statuses: () -> Array[Symbol]
1003
1015
  def mutation_score_for: (Array[Mutant]) -> Float?
1004
1016
  def mutation_score_indicator_for: (Array[Mutant]) -> Float?
1005
1017
  def base_schema: () -> Hash[Symbol, untyped]
@@ -1063,12 +1075,59 @@ module Henitai
1063
1075
  def self.write: (String, Hash[String, Hash[String, untyped]]) -> void
1064
1076
  end
1065
1077
 
1078
+ class RunnerDependencies
1079
+ def initialize: (config: untyped) -> void
1080
+ def subject_resolver: () -> untyped
1081
+ def git_diff_analyzer: () -> untyped
1082
+ def mutant_generator: () -> untyped
1083
+ def static_filter: () -> untyped
1084
+ def execution_engine: () -> untyped
1085
+ def coverage_bootstrapper: () -> CoverageBootstrapper
1086
+ def integration: () -> untyped
1087
+ def operators: () -> Array[Operator]
1088
+ def per_test_coverage: () -> PerTestCoverage
1089
+ def history_store: () -> untyped
1090
+ def progress_reporter: (full_run: bool) -> untyped
1091
+ def source_provider: () -> ^(String) -> String
1092
+ end
1093
+
1094
+ class SourceFileSelection
1095
+ def initialize: (
1096
+ config: untyped,
1097
+ since: String?,
1098
+ git_diff_analyzer: untyped,
1099
+ per_test_coverage: untyped
1100
+ ) -> void
1101
+ def call: () -> Array[String]
1102
+ def included_source_files: () -> Array[String]
1103
+ def reject_excluded: (Array[String]) -> Array[String]
1104
+ def filter_changed: (Array[String]) -> Array[String]
1105
+
1106
+ private
1107
+
1108
+ def excluded_source_files: () -> Array[String]
1109
+ def changed_paths_since: () -> Array[String]
1110
+ def covered_sources_for_changed_tests: (Array[String]) -> Array[String]
1111
+ def normalize_path: (String) -> String
1112
+ end
1113
+
1114
+ class SubjectSelection
1115
+ def initialize: (subject_resolver: untyped, patterns: Array[untyped]?) -> void
1116
+ def resolve: (Array[String]) -> Array[Subject]
1117
+ def unique: (Array[Subject]) -> Array[Subject]
1118
+
1119
+ private
1120
+
1121
+ def pattern_expressions: () -> Array[String]
1122
+ end
1123
+
1066
1124
  class Runner
1067
1125
  attr_reader config: Configuration
1068
1126
  attr_reader result: untyped
1069
1127
 
1070
- def initialize: (?config: Configuration, ?subjects: Array[Subject], ?since: String, ?survivors_from: String?, ?mode: Hash[Symbol, bool]) -> void
1128
+ def initialize: (?config: Configuration, ?subjects: Array[Subject], ?since: String, ?survivors_from: String?, ?mode: Hash[Symbol, bool], ?deps: RunnerDependencies?) -> void
1071
1129
  def run: () -> Result
1130
+ def deps: () -> RunnerDependencies
1072
1131
  def resolve_subjects: (?Array[String]) -> untyped
1073
1132
  def generate_mutants: (untyped) -> untyped
1074
1133
  def filter_mutants: (untyped) -> untyped
@@ -1087,15 +1146,8 @@ module Henitai
1087
1146
  def per_test_coverage: () -> PerTestCoverage
1088
1147
  def history_store_path: () -> String
1089
1148
  def source_files: () -> Array[String]
1090
- def included_source_files: () -> Array[String]
1091
- def reject_excluded: (Array[String]) -> Array[String]
1092
- def excluded_source_files: () -> Array[String]
1093
- def filter_changed: (Array[String]) -> Array[String]
1094
- def changed_paths_since: () -> Array[String]
1095
- def covered_sources_for_changed_tests: (Array[String]) -> Array[String]
1096
- def pattern_subjects: () -> Array[Subject]
1097
- def unique_subjects: (Array[Subject]) -> Array[Subject]
1098
- def normalize_path: (String) -> String
1149
+ def source_file_selection: () -> SourceFileSelection
1150
+ def subject_selection: () -> SubjectSelection
1099
1151
 
1100
1152
  private
1101
1153
 
@@ -1111,6 +1163,8 @@ module Henitai
1111
1163
  def mutants_for: (Array[Subject], Array[String]) -> Array[Mutant]
1112
1164
  def with_reports_dir: () { () -> untyped } -> untyped
1113
1165
  def result_thresholds: () -> Hash[Symbol, Integer]?
1166
+ def result_coverage_criteria: () -> Hash[Symbol, bool]?
1167
+ def optional_config: (Symbol) -> untyped
1114
1168
  def survivor_rerun?: () -> bool
1115
1169
  def full_run?: () -> bool
1116
1170
  def survivor_strategy: () -> SurvivorRerunStrategy
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: henitai
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Martin Otten
@@ -55,16 +55,22 @@ dependencies:
55
55
  name: sqlite3
56
56
  requirement: !ruby/object:Gem::Requirement
57
57
  requirements:
58
- - - "~>"
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: 2.9.5
61
+ - - "<"
59
62
  - !ruby/object:Gem::Version
60
- version: '1.7'
63
+ version: '3'
61
64
  type: :runtime
62
65
  prerelease: false
63
66
  version_requirements: !ruby/object:Gem::Requirement
64
67
  requirements:
65
- - - "~>"
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: 2.9.5
71
+ - - "<"
66
72
  - !ruby/object:Gem::Version
67
- version: '1.7'
73
+ version: '3'
68
74
  - !ruby/object:Gem::Dependency
69
75
  name: unparser
70
76
  requirement: !ruby/object:Gem::Requirement
@@ -80,7 +86,7 @@ dependencies:
80
86
  - !ruby/object:Gem::Version
81
87
  version: '0.6'
82
88
  description: |
83
- Hen'i-tai (変異体) is a mutation testing framework for Ruby 4+.
89
+ Hen'i-tai (変異体) is a mutation testing framework for Ruby 3.3.6+.
84
90
  It produces Stryker-compatible mutation-testing-report-schema JSON,
85
91
  integrates with the Stryker Dashboard, and ships with a standalone
86
92
  HTML report powered by mutation-testing-elements.
@@ -122,18 +128,24 @@ files:
122
128
  - lib/henitai/coverage_bootstrapper.rb
123
129
  - lib/henitai/coverage_formatter.rb
124
130
  - lib/henitai/coverage_report_reader.rb
131
+ - lib/henitai/dirty_source_detector.rb
125
132
  - lib/henitai/eager_load.rb
126
133
  - lib/henitai/equivalence_detector.rb
134
+ - lib/henitai/equivalence_detector/operand_predicates.rb
135
+ - lib/henitai/excluded_test_filter.rb
127
136
  - lib/henitai/execution_engine.rb
128
137
  - lib/henitai/execution_engine/env_scope.rb
129
138
  - lib/henitai/generated_artifacts.rb
130
139
  - lib/henitai/git_diff_analyzer.rb
131
140
  - lib/henitai/incremental_filter.rb
141
+ - lib/henitai/inherited_fd_registry.rb
132
142
  - lib/henitai/integration.rb
133
143
  - lib/henitai/integration/base.rb
134
- - lib/henitai/integration/child_debug_support.rb
144
+ - lib/henitai/integration/child_bootstrap.rb
145
+ - lib/henitai/integration/child_debug_log.rb
135
146
  - lib/henitai/integration/child_runtime_control.rb
136
147
  - lib/henitai/integration/coverage_suppression.rb
148
+ - lib/henitai/integration/loaded_features.rb
137
149
  - lib/henitai/integration/minitest.rb
138
150
  - lib/henitai/integration/minitest_load_path.rb
139
151
  - lib/henitai/integration/minitest_suite_command.rb
@@ -179,10 +191,12 @@ files:
179
191
  - lib/henitai/operators/string_literal.rb
180
192
  - lib/henitai/operators/unary_operator.rb
181
193
  - lib/henitai/operators/update_operator.rb
194
+ - lib/henitai/orphan_watchdog.rb
182
195
  - lib/henitai/parser_current.rb
183
196
  - lib/henitai/per_test_coverage.rb
184
197
  - lib/henitai/per_test_coverage_collector.rb
185
198
  - lib/henitai/per_test_coverage_selector.rb
199
+ - lib/henitai/process_liveness.rb
186
200
  - lib/henitai/process_wakeup.rb
187
201
  - lib/henitai/process_worker_runner.rb
188
202
  - lib/henitai/reporter.rb
@@ -191,16 +205,24 @@ files:
191
205
  - lib/henitai/result.rb
192
206
  - lib/henitai/rspec_coverage_formatter.rb
193
207
  - lib/henitai/runner.rb
208
+ - lib/henitai/runner_dependencies.rb
194
209
  - lib/henitai/sampling_strategy.rb
195
210
  - lib/henitai/scenario_execution_result.rb
196
211
  - lib/henitai/slot_scheduler.rb
212
+ - lib/henitai/slot_scheduler/drain_verdict.rb
197
213
  - lib/henitai/slot_scheduler/draining.rb
198
214
  - lib/henitai/slot_scheduler/process_control.rb
215
+ - lib/henitai/slot_scheduler/retry_policy.rb
216
+ - lib/henitai/slot_scheduler/slot_deadline.rb
217
+ - lib/henitai/slot_scheduler/slot_table.rb
218
+ - lib/henitai/slot_scheduler/test_file_selection.rb
219
+ - lib/henitai/source_file_selection.rb
199
220
  - lib/henitai/source_parser.rb
200
221
  - lib/henitai/static_filter.rb
201
222
  - lib/henitai/stillborn_filter.rb
202
223
  - lib/henitai/subject.rb
203
224
  - lib/henitai/subject_resolver.rb
225
+ - lib/henitai/subject_selection.rb
204
226
  - lib/henitai/survivor_activation_cache.rb
205
227
  - lib/henitai/survivor_loader.rb
206
228
  - lib/henitai/survivor_rerun_strategy.rb
@@ -224,7 +246,7 @@ metadata:
224
246
  changelog_uri: https://github.com/martinotten/henitai/blob/main/CHANGELOG.md
225
247
  documentation_uri: https://github.com/martinotten/henitai/blob/main/README.md
226
248
  homepage_uri: https://github.com/martinotten/henitai
227
- source_code_uri: https://github.com/martinotten/henitai/tree/v0.4.0
249
+ source_code_uri: https://github.com/martinotten/henitai/tree/v0.5.0
228
250
  rubygems_mfa_required: 'true'
229
251
  rdoc_options: []
230
252
  require_paths:
@@ -233,7 +255,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
233
255
  requirements:
234
256
  - - ">="
235
257
  - !ruby/object:Gem::Version
236
- version: 4.0.0
258
+ version: 3.3.6
237
259
  required_rubygems_version: !ruby/object:Gem::Requirement
238
260
  requirements:
239
261
  - - ">="
@@ -1,119 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Henitai
4
- module Integration
5
- # Shared debug helpers for child-run diagnostics.
6
- # Debug helpers are intentionally grouped here so the child-run diagnostics
7
- # stay isolated from the main integration flow.
8
- module ChildDebugSupport
9
- private
10
-
11
- def debug_child? = ENV["HENITAI_DEBUG_CHILD"] == "1"
12
-
13
- # Gated here (not only at call sites) so no unguarded caller can leak
14
- # debug lines into every child log by default.
15
- def debug_child_puts(message)
16
- return unless debug_child?
17
-
18
- $stdout.puts(message)
19
- $stdout.flush
20
- end
21
-
22
- def debug_child_rspec_trace(test_files:, rspec_options:, rspec_argv:)
23
- return unless debug_child?
24
-
25
- files_exist = test_files.map { |f| [f, File.exist?(f)] }.inspect
26
- loaded_features = loaded_feature_map(test_files).inspect # steep:ignore Ruby::NoMethod
27
-
28
- debug_child_puts(
29
- "[henitai-debug-child] cwd=#{Dir.pwd}\n" \
30
- "[henitai-debug-child] files_exist=#{files_exist}\n" \
31
- "[henitai-debug-child] loaded_features_check=#{loaded_features}\n" \
32
- "[henitai-debug-child] test_files=#{test_files.inspect}\n" \
33
- "[henitai-debug-child] rspec_options=#{rspec_options.inspect}\n" \
34
- "[henitai-debug-child] rspec_argv=#{rspec_argv.inspect}"
35
- )
36
- end
37
-
38
- def debug_child_rspec_exit(status)
39
- return unless debug_child?
40
-
41
- debug_child_puts("[henitai-debug-child] RSpec result=#{status.inspect}")
42
- end
43
-
44
- def debug_child_example_count(stage) # steep:ignore Ruby::UndeclaredMethodDefinition
45
- return unless debug_child?
46
-
47
- count = rspec_world_example_count
48
- debug_child_puts(
49
- "[henitai-debug-child] rspec_world_example_count_#{stage}=#{count.inspect}"
50
- )
51
- end
52
-
53
- def debug_child_activation_start(mutant_id)
54
- return unless debug_child?
55
-
56
- debug_child_puts("[henitai-debug-child] activate_start mutant=#{mutant_id}")
57
- end
58
-
59
- def debug_child_activation_end(activation_result, test_files:)
60
- return unless debug_child?
61
-
62
- debug_child_puts(
63
- "[henitai-debug-child] activate_end result=#{activation_result.inspect}\n" \
64
- "[henitai-debug-child] run_tests_start test_files=#{test_files.inspect}"
65
- )
66
- end
67
-
68
- def debug_child_mutant_meta(mutant)
69
- stable_id = mutant.respond_to?(:stable_id) ? mutant.stable_id : nil
70
- operator = mutant.respond_to?(:operator) ? mutant.operator : nil
71
- has_subject_expression =
72
- mutant.respond_to?(:subject) && mutant.subject.respond_to?(:expression)
73
- subject_expression = has_subject_expression ? mutant.subject.expression : nil
74
- location = mutant.respond_to?(:location) ? mutant.location.inspect : nil
75
-
76
- debug_child_puts(
77
- "[henitai-debug-child] mutant_meta stableId=#{stable_id}\n" \
78
- "[henitai-debug-child] mutant_meta operator=#{operator}\n" \
79
- "[henitai-debug-child] mutant_meta subject=#{subject_expression}\n" \
80
- "[henitai-debug-child] mutant_meta location=#{location}\n"
81
- )
82
- end
83
-
84
- def debug_child_activation_check
85
- location = begin
86
- Henitai::Runner.instance_method(:resolve_subjects).source_location&.join(":") # henitai:disable
87
- rescue StandardError
88
- nil
89
- end
90
-
91
- debug_child_puts(
92
- "[henitai-debug-child] activation_check resolve_subjects_location=#{location}\n"
93
- )
94
- end
95
-
96
- def loaded_feature_map(test_files) = test_files.map { |file| [file, loaded_feature?(file)] } # steep:ignore Ruby::UndeclaredMethodDefinition
97
-
98
- def loaded_feature?(file) # steep:ignore Ruby::UndeclaredMethodDefinition
99
- expanded = File.expand_path(file)
100
- candidates = [expanded, "#{expanded}.rb", file, "#{file}.rb"].uniq
101
- $LOADED_FEATURES.any? do |feature|
102
- normalized = begin
103
- File.expand_path(feature)
104
- rescue StandardError
105
- feature
106
- end
107
- candidates.include?(feature) || candidates.include?(normalized)
108
- end
109
- end
110
-
111
- def rspec_world_example_count # steep:ignore Ruby::UndeclaredMethodDefinition
112
- world = ::RSpec.world
113
- world.example_count
114
- rescue StandardError
115
- nil
116
- end
117
- end
118
- end
119
- end