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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +86 -1
- data/README.md +1 -1
- data/lib/henitai/cli.rb +1 -1
- data/lib/henitai/configuration.rb +9 -2
- data/lib/henitai/dirty_source_detector.rb +53 -0
- data/lib/henitai/equivalence_detector/operand_predicates.rb +49 -0
- data/lib/henitai/equivalence_detector.rb +6 -23
- data/lib/henitai/excluded_test_filter.rb +47 -0
- data/lib/henitai/execution_engine.rb +5 -11
- data/lib/henitai/inherited_fd_registry.rb +66 -0
- data/lib/henitai/integration/base.rb +7 -2
- data/lib/henitai/integration/child_bootstrap.rb +27 -0
- data/lib/henitai/integration/child_debug_log.rb +135 -0
- data/lib/henitai/integration/child_runtime_control.rb +6 -18
- data/lib/henitai/integration/loaded_features.rb +38 -0
- data/lib/henitai/integration/mutant_run_support.rb +5 -5
- data/lib/henitai/integration/rspec_child_runner.rb +16 -15
- data/lib/henitai/integration/rspec_process_runner.rb +7 -2
- data/lib/henitai/integration.rb +10 -7
- data/lib/henitai/mutation_skip_directives.rb +7 -1
- data/lib/henitai/operators/return_value.rb +1 -1
- data/lib/henitai/orphan_watchdog.rb +93 -0
- data/lib/henitai/process_liveness.rb +41 -0
- data/lib/henitai/reports_directory_lock.rb +12 -11
- data/lib/henitai/result.rb +30 -3
- data/lib/henitai/runner.rb +41 -123
- data/lib/henitai/runner_dependencies.rb +75 -0
- data/lib/henitai/slot_scheduler/drain_verdict.rb +29 -0
- data/lib/henitai/slot_scheduler/draining.rb +7 -17
- data/lib/henitai/slot_scheduler/retry_policy.rb +21 -0
- data/lib/henitai/slot_scheduler/slot_deadline.rb +37 -0
- data/lib/henitai/slot_scheduler/slot_table.rb +75 -0
- data/lib/henitai/slot_scheduler/test_file_selection.rb +40 -0
- data/lib/henitai/slot_scheduler.rb +68 -80
- data/lib/henitai/source_file_selection.rb +76 -0
- data/lib/henitai/subject_selection.rb +33 -0
- data/lib/henitai/survivor_rerun_strategy.rb +7 -19
- data/lib/henitai/version.rb +1 -1
- data/lib/henitai.rb +8 -0
- data/sig/henitai.rbs +92 -38
- metadata +31 -9
- 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
|
-
|
|
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
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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)
|
data/lib/henitai/version.rb
CHANGED
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
|
-
|
|
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
|
|
83
|
-
def
|
|
84
|
-
|
|
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
|
|
90
|
-
def
|
|
91
|
-
def
|
|
92
|
-
def
|
|
93
|
-
def
|
|
94
|
-
def
|
|
95
|
-
def
|
|
96
|
-
def
|
|
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
|
-
|
|
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
|
|
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
|
|
1091
|
-
def
|
|
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
|
+
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: '
|
|
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: '
|
|
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
|
|
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/
|
|
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.
|
|
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:
|
|
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
|