Class: Henitai::Integration::Rspec
- Inherits:
-
Base
- Object
- Base
- Henitai::Integration::Rspec
show all
- Defined in:
- lib/henitai/integration.rb
Overview
RSpec integration adapter.
Constant Summary
collapse
- DEFAULT_SUITE_TIMEOUT =
300.0
- REQUIRE_DIRECTIVE_PATTERN =
/
\A\s*
(require|require_relative)
\s*
(?:\(\s*)?
["']([^"']+)["']
\s*\)?
/x
Instance Method Summary
collapse
-
#build_result(wait_result, log_paths) ⇒ Object
-
#combined_log(stdout, stderr) ⇒ Object
-
#expand_candidates(base_path, required_path) ⇒ Object
-
#fallback_spec_files(subject) ⇒ Object
-
#per_test_coverage_supported? ⇒ Boolean
-
#read_log_file(path) ⇒ Object
-
#relative_candidates(spec_file, required_path) ⇒ Object
-
#require_candidates(spec_file, required_path) ⇒ Object
-
#required_files(spec_file) ⇒ Object
-
#requires_source_file?(spec_file, source_file) ⇒ Boolean
-
#requires_source_file_transitively?(spec_file, source_file, visited = []) ⇒ Boolean
-
#resolve_required_file(spec_file, method_name, required_path) ⇒ Object
-
#run_in_child(mutant:, test_files:, log_paths:) ⇒ Object
-
#run_mutant(mutant:, test_files:, timeout:) ⇒ Object
-
#run_suite(test_files, timeout: DEFAULT_SUITE_TIMEOUT) ⇒ Object
-
#run_tests(test_files) ⇒ Object
-
#scenario_log_paths(name) ⇒ Object
-
#select_tests(subject) ⇒ Object
-
#selection_patterns(subject) ⇒ Object
-
#spawn_suite_process(test_files, log_paths) ⇒ Object
-
#spec_files ⇒ Object
-
#suite_command(test_files) ⇒ Object
-
#test_files ⇒ Object
-
#write_combined_log(path, stdout, stderr) ⇒ Object
Methods inherited from Base
#cleanup_process_group, #reap_child, #wait_with_timeout
Instance Method Details
#build_result(wait_result, log_paths) ⇒ Object
316
317
318
319
320
321
322
323
324
325
326
327
|
# File 'lib/henitai/integration.rb', line 316
def build_result(wait_result, log_paths)
stdout = read_log_file(log_paths[:stdout_path])
stderr = read_log_file(log_paths[:stderr_path])
write_combined_log(log_paths[:log_path], stdout, stderr)
ScenarioExecutionResult.build(
wait_result:,
stdout:,
stderr:,
log_path: log_paths[:log_path]
)
end
|
#combined_log(stdout, stderr) ⇒ Object
445
446
447
448
449
450
|
# File 'lib/henitai/integration.rb', line 445
def combined_log(stdout, stderr)
[
(stdout.empty? ? nil : "stdout:\n#{stdout}"),
(stderr.empty? ? nil : "stderr:\n#{stderr}")
].compact.join("\n")
end
|
#expand_candidates(base_path, required_path) ⇒ Object
400
401
402
403
404
405
|
# File 'lib/henitai/integration.rb', line 400
def expand_candidates(base_path, required_path)
[
File.expand_path(required_path, base_path),
File.expand_path("#{required_path}.rb", base_path)
].uniq
end
|
#fallback_spec_files(subject) ⇒ Object
333
334
335
336
337
338
339
340
341
342
343
|
# File 'lib/henitai/integration.rb', line 333
def fallback_spec_files(subject)
return [] unless subject.source_file
matches = spec_files.select do |path|
requires_source_file_transitively?(path, subject.source_file)
rescue StandardError
false
end
matches.empty? ? spec_files : matches
end
|
#per_test_coverage_supported? ⇒ Boolean
280
281
282
|
# File 'lib/henitai/integration.rb', line 280
def per_test_coverage_supported?
true
end
|
#read_log_file(path) ⇒ Object
434
435
436
437
438
|
# File 'lib/henitai/integration.rb', line 434
def read_log_file(path)
return "" unless File.exist?(path)
File.read(path)
end
|
#relative_candidates(spec_file, required_path) ⇒ Object
390
391
392
|
# File 'lib/henitai/integration.rb', line 390
def relative_candidates(spec_file, required_path)
expand_candidates(File.dirname(spec_file), required_path)
end
|
#require_candidates(spec_file, required_path) ⇒ Object
394
395
396
397
398
|
# File 'lib/henitai/integration.rb', line 394
def require_candidates(spec_file, required_path)
([File.dirname(spec_file), Dir.pwd] + $LOAD_PATH).flat_map do |base_path|
expand_candidates(base_path, required_path)
end
end
|
#required_files(spec_file) ⇒ Object
370
371
372
373
374
375
376
377
|
# File 'lib/henitai/integration.rb', line 370
def required_files(spec_file)
File.read(spec_file).lines.filter_map do |line|
match = line.match(REQUIRE_DIRECTIVE_PATTERN)
next unless match
resolve_required_file(spec_file, match[1].to_s, match[2].to_s)
end
end
|
#requires_source_file?(spec_file, source_file) ⇒ Boolean
352
353
354
355
356
|
# File 'lib/henitai/integration.rb', line 352
def requires_source_file?(spec_file, source_file)
content = File.read(spec_file)
basename = File.basename(source_file, ".rb")
content.include?(basename) || content.include?(source_file)
end
|
#requires_source_file_transitively?(spec_file, source_file, visited = []) ⇒ Boolean
358
359
360
361
362
363
364
365
366
367
368
|
# File 'lib/henitai/integration.rb', line 358
def requires_source_file_transitively?(spec_file, source_file, visited = [])
normalized_spec_file = File.expand_path(spec_file)
return false if visited.include?(normalized_spec_file)
visited << normalized_spec_file
return true if requires_source_file?(spec_file, source_file)
required_files(spec_file).any? do |required_file|
requires_source_file_transitively?(required_file, source_file, visited)
end
end
|
#resolve_required_file(spec_file, method_name, required_path) ⇒ Object
379
380
381
382
383
384
385
386
387
388
|
# File 'lib/henitai/integration.rb', line 379
def resolve_required_file(spec_file, method_name, required_path)
candidates =
if method_name == "require_relative"
relative_candidates(spec_file, required_path)
else
require_candidates(spec_file, required_path)
end
candidates.find { |candidate| File.file?(candidate) }
end
|
#run_in_child(mutant:, test_files:, log_paths:) ⇒ Object
421
422
423
424
425
426
427
428
429
430
431
432
|
# File 'lib/henitai/integration.rb', line 421
def run_in_child(mutant:, test_files:, log_paths:)
Thread.report_on_exception = false
with_subprocess_env do
scenario_log_support.with_coverage_dir(mutant.id) do
scenario_log_support.capture_child_output(log_paths) do
return 2 if Mutant::Activator.activate!(mutant) == :compile_error
run_tests(test_files)
end
end
end
end
|
#run_mutant(mutant:, test_files:, timeout:) ⇒ Object
276
277
278
|
# File 'lib/henitai/integration.rb', line 276
def run_mutant(mutant:, test_files:, timeout:)
RspecProcessRunner.new.run_mutant(self, mutant:, test_files:, timeout:)
end
|
#run_suite(test_files, timeout: DEFAULT_SUITE_TIMEOUT) ⇒ Object
#run_tests(test_files) ⇒ Object
298
299
300
301
302
303
304
|
# File 'lib/henitai/integration.rb', line 298
def run_tests(test_files)
require "rspec/core"
status = RSpec::Core::Runner.run(test_files + rspec_options)
return status if status.is_a?(Integer)
status == true ? 0 : 1
end
|
#scenario_log_paths(name) ⇒ Object
306
307
308
309
310
311
312
313
314
|
# File 'lib/henitai/integration.rb', line 306
def scenario_log_paths(name)
reports_dir = ENV.fetch("HENITAI_REPORTS_DIR", "reports")
log_dir = File.join(reports_dir, "mutation-logs")
{
stdout_path: File.join(log_dir, "#{name}.stdout.log"),
stderr_path: File.join(log_dir, "#{name}.stderr.log"),
log_path: File.join(log_dir, "#{name}.log")
}
end
|
#select_tests(subject) ⇒ Object
259
260
261
262
263
264
265
266
267
268
269
270
|
# File 'lib/henitai/integration.rb', line 259
def select_tests(subject)
matches = spec_files.select do |path|
content = File.read(path)
selection_patterns(subject).any? { |pattern| content.include?(pattern) }
rescue StandardError
false
end
return matches unless matches.empty?
fallback_spec_files(subject)
end
|
#selection_patterns(subject) ⇒ Object
345
346
347
348
349
350
|
# File 'lib/henitai/integration.rb', line 345
def selection_patterns(subject)
[
subject.expression,
subject.namespace
].compact.uniq.sort_by(&:length).reverse
end
|
#spawn_suite_process(test_files, log_paths) ⇒ Object
407
408
409
410
411
412
413
414
415
416
417
418
419
|
# File 'lib/henitai/integration.rb', line 407
def spawn_suite_process(test_files, log_paths)
File.open(log_paths[:stdout_path], "w") do |stdout_file|
File.open(log_paths[:stderr_path], "w") do |stderr_file|
Process.spawn(
subprocess_env,
*suite_command(test_files),
out: stdout_file,
err: stderr_file,
pgroup: true
)
end
end
end
|
#spec_files ⇒ Object
329
330
331
|
# File 'lib/henitai/integration.rb', line 329
def spec_files
Dir.glob("spec/**/*_spec.rb")
end
|
#suite_command(test_files) ⇒ Object
288
289
290
291
292
293
294
295
296
|
# File 'lib/henitai/integration.rb', line 288
def suite_command(test_files)
[
"bundle", "exec", "ruby",
"-r", "henitai/rspec_coverage_formatter",
"-S", "rspec", *test_files,
"--format", "progress",
"--format", "Henitai::CoverageFormatter"
]
end
|
#test_files ⇒ Object
272
273
274
|
# File 'lib/henitai/integration.rb', line 272
def test_files
spec_files
end
|
#write_combined_log(path, stdout, stderr) ⇒ Object
440
441
442
443
|
# File 'lib/henitai/integration.rb', line 440
def write_combined_log(path, stdout, stderr)
FileUtils.mkdir_p(File.dirname(path))
File.write(path, combined_log(stdout, stderr))
end
|