henitai 0.2.0 → 0.2.1

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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +26 -1
  3. data/README.md +15 -3
  4. data/assets/schema/henitai.schema.json +6 -0
  5. data/lib/henitai/cli/clean_command.rb +48 -0
  6. data/lib/henitai/cli/command_support.rb +51 -0
  7. data/lib/henitai/cli/init_command.rb +64 -0
  8. data/lib/henitai/cli/operator_command.rb +95 -0
  9. data/lib/henitai/cli/options.rb +120 -0
  10. data/lib/henitai/cli/run_command.rb +103 -0
  11. data/lib/henitai/cli.rb +16 -404
  12. data/lib/henitai/configuration.rb +2 -1
  13. data/lib/henitai/configuration_validator/rules.rb +143 -0
  14. data/lib/henitai/configuration_validator/scalars.rb +123 -0
  15. data/lib/henitai/configuration_validator.rb +12 -239
  16. data/lib/henitai/eager_load.rb +36 -5
  17. data/lib/henitai/execution_engine.rb +4 -3
  18. data/lib/henitai/integration/base.rb +171 -0
  19. data/lib/henitai/integration/child_debug_support.rb +115 -0
  20. data/lib/henitai/integration/child_runtime_control.rb +50 -0
  21. data/lib/henitai/integration/coverage_suppression.rb +43 -0
  22. data/lib/henitai/integration/minitest.rb +133 -0
  23. data/lib/henitai/integration/mutant_run_support.rb +77 -0
  24. data/lib/henitai/integration/rspec_child_runner.rb +61 -0
  25. data/lib/henitai/integration/rspec_test_selection.rb +135 -0
  26. data/lib/henitai/integration/scenario_log_support.rb +116 -0
  27. data/lib/henitai/integration.rb +22 -846
  28. data/lib/henitai/mutant/activator.rb +1 -79
  29. data/lib/henitai/mutant/parameter_source.rb +98 -0
  30. data/lib/henitai/mutant.rb +1 -0
  31. data/lib/henitai/mutant_history_store/sql.rb +72 -0
  32. data/lib/henitai/mutant_history_store.rb +5 -69
  33. data/lib/henitai/per_test_coverage_collector.rb +3 -1
  34. data/lib/henitai/process_worker_runner.rb +48 -334
  35. data/lib/henitai/reporter.rb +20 -8
  36. data/lib/henitai/result.rb +17 -15
  37. data/lib/henitai/runner.rb +59 -182
  38. data/lib/henitai/slot_scheduler/draining.rb +140 -0
  39. data/lib/henitai/slot_scheduler/process_control.rb +43 -0
  40. data/lib/henitai/slot_scheduler.rb +214 -0
  41. data/lib/henitai/survivor_rerun_strategy.rb +195 -0
  42. data/lib/henitai/unparse_helper.rb +5 -2
  43. data/lib/henitai/version.rb +1 -1
  44. data/lib/henitai.rb +2 -0
  45. data/sig/configuration_validator.rbs +46 -22
  46. data/sig/henitai.rbs +158 -73
  47. metadata +25 -2
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+
5
+ module Henitai
6
+ module Integration
7
+ # Shared helpers for capturing stdout/stderr from child test processes and
8
+ # for reading and combining the captured log files afterwards.
9
+ class ScenarioLogSupport
10
+ def capture_child_output(log_paths)
11
+ output_files = open_child_output(log_paths)
12
+ yield
13
+ ensure
14
+ close_child_output(output_files)
15
+ end
16
+
17
+ def with_coverage_dir(mutant_id)
18
+ original_coverage_dir = ENV.fetch("HENITAI_COVERAGE_DIR", nil)
19
+ ENV["HENITAI_COVERAGE_DIR"] = mutation_coverage_dir(mutant_id)
20
+ yield
21
+ ensure
22
+ if original_coverage_dir.nil?
23
+ ENV.delete("HENITAI_COVERAGE_DIR")
24
+ else
25
+ ENV["HENITAI_COVERAGE_DIR"] = original_coverage_dir
26
+ end
27
+ end
28
+
29
+ def open_child_output(log_paths)
30
+ FileUtils.mkdir_p(File.dirname(log_paths[:log_path]))
31
+ output_files = build_child_output_files(log_paths)
32
+ sync_child_output_files(output_files)
33
+ redirect_child_output(output_files)
34
+ output_files
35
+ end
36
+
37
+ def close_child_output(output_files)
38
+ return unless output_files
39
+
40
+ restore_child_output(output_files)
41
+ close_child_output_files(output_files)
42
+ end
43
+
44
+ def build_child_output_files(log_paths)
45
+ {
46
+ original_stdout: stdout_stream.dup,
47
+ original_stderr: stderr_stream.dup,
48
+ stdout_file: File.new(log_paths[:stdout_path], "w"),
49
+ stderr_file: File.new(log_paths[:stderr_path], "w")
50
+ }
51
+ end
52
+
53
+ def sync_child_output_files(output_files)
54
+ output_files[:stdout_file].sync = true
55
+ output_files[:stderr_file].sync = true
56
+ end
57
+
58
+ def redirect_child_output(output_files)
59
+ reopen_child_output_stream(stdout_stream, output_files[:stdout_file])
60
+ reopen_child_output_stream(stderr_stream, output_files[:stderr_file])
61
+ $stdout = stdout_stream
62
+ $stderr = stderr_stream
63
+ end
64
+
65
+ def restore_child_output(output_files)
66
+ reopen_child_output_stream(stdout_stream, output_files[:original_stdout])
67
+ reopen_child_output_stream(stderr_stream, output_files[:original_stderr])
68
+ $stdout = stdout_stream
69
+ $stderr = stderr_stream
70
+ end
71
+
72
+ def reopen_child_output_stream(stream, original_stream)
73
+ stream.reopen(original_stream) if original_stream
74
+ end
75
+
76
+ def close_child_output_files(output_files)
77
+ %i[stdout_file stderr_file original_stdout original_stderr].each do |key|
78
+ output_files[key]&.close
79
+ end
80
+ end
81
+
82
+ def read_log_file(path)
83
+ return "" unless File.exist?(path)
84
+
85
+ File.read(path)
86
+ end
87
+
88
+ def write_combined_log(path, stdout, stderr)
89
+ FileUtils.mkdir_p(File.dirname(path))
90
+ File.write(path, combined_log(stdout, stderr))
91
+ end
92
+
93
+ def combined_log(stdout, stderr)
94
+ [
95
+ (stdout.empty? ? nil : "stdout:\n#{stdout}"),
96
+ (stderr.empty? ? nil : "stderr:\n#{stderr}")
97
+ ].compact.join("\n")
98
+ end
99
+
100
+ private
101
+
102
+ def mutation_coverage_dir(mutant_id)
103
+ reports_dir = ENV.fetch("HENITAI_REPORTS_DIR", "reports")
104
+ File.join(reports_dir, "mutation-coverage", mutant_id.to_s)
105
+ end
106
+
107
+ def stdout_stream
108
+ @stdout_stream ||= IO.for_fd(1)
109
+ end
110
+
111
+ def stderr_stream
112
+ @stderr_stream ||= IO.for_fd(2)
113
+ end
114
+ end
115
+ end
116
+ end