Class: GeneratorHelper

Inherits:
Object show all
Defined in:
lib/ceedling/generators/generator_helper.rb

Instance Method Summary collapse

Instance Method Details

#log_test_results_crash(executable, shell_result, backtrace) ⇒ Object



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/ceedling/generators/generator_helper.rb', line 74

def log_test_results_crash(executable, shell_result, backtrace)
  runner = File.basename(executable)

  notice = "Test executable `#{runner}` seems to have crashed -- likely terminating early due to a bad code reference.\n"

  # Check for empty output
  if (shell_result[:output].nil? or shell_result[:output].strip.empty?)
    # Mirror style of generic tool_executor failure output
    notice += "> Produced no output (including no final test result counts).\n"

  # Check for no test results
  elsif ((shell_result[:output] =~ PATTERNS::TEST_STDOUT_STATISTICS).nil?)
    # Mirror style of generic tool_executor failure output
    notice += "> Produced some output but contains no final test result counts.\n"
  end
  
  notice += "> Causes can include: bad memory access, stack overflow, heap error, or bad branch in source or test code.\n"

  # Incorporate knowledge of the backtrace setting into a recommendation
  case backtrace
  when :simple
    notice += "> Consider configuring :project ↳ :use_backtrace to use the :gdb option to find the cause (see documentation).\n"
  when :none
    notice += "> Consider configuring :project ↳ :use_backtrace to help find the cause (see documentation).\n"
  end

  @loginator.log( notice, Verbosity::ERRORS, LogLabels::CRASH )
end

#test_crash?(test_filename, executable, shell_result) ⇒ Boolean

Returns:

  • (Boolean)


16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/ceedling/generators/generator_helper.rb', line 16

def test_crash?(test_filename, executable, shell_result)
  runner = File.basename(executable)

  crash = false

  # Any signal death is unconditionally a crash, whatever text made it out beforehand.
  # Broadened from a SIGSEGV(11)-only check: a sanitizer's halt_on_error (UBSan, ASan)
  # raises SIGABRT, not SIGSEGV, and there's no reason to trust a process that a
  # signal actually killed regardless of which one. Applies to Unix-like systems
  # including MSYS on Windows -- native (non-MSYS) Windows Ruby never reports
  # `signaled?` true at all, so this check is inert there rather than wrong; the
  # stats-footer and stderr checks below already carry Windows crash detection.
  if shell_result[:status] && shell_result[:status].signaled?
    @loginator.lazy( Verbosity::DEBUG, LogLabels::CRASH ) do
      "#{runner} process terminated by signal #{shell_result[:status].termsig}"
    end
    crash = true
  end

  # No test results found in test executable output
  stats_match = shell_result[:output].match( PATTERNS::TEST_STDOUT_STATISTICS )
  if stats_match.nil?
    # No debug logging here because we log this condition in the error log handling below
    crash = true
  end

  # A clean summary (0 failures) is not trustworthy if the real process still exited
  # nonzero afterward -- e.g. LeakSanitizer detecting a leak during teardown, after
  # every test case already printed and Unity's own summary already looked clean.
  # A nonzero exit consistent with Unity's own reported failures (its exit code
  # convention is one per failed test case) is an ordinary failing run, not a crash.
  if stats_match && shell_result[:status] && !shell_result[:status].success? &&
     !shell_result[:status].signaled? && stats_match[2].to_i == 0
    @loginator.lazy( Verbosity::DEBUG, LogLabels::CRASH ) do
      "#{runner} reported a clean summary but exited with real status #{shell_result[:status].exitstatus}"
    end
    crash = true
  end

  # Scan STDERR line by line for a segfault variant that is not attributed to the test file.
  # A line starting with test_filename is a Unity-reported test-case result, not an OS crash.
  # Checking each line individually avoids false negatives when attributed and bare segfault
  # lines coexist in the same stderr output.
  segfault_pattern = /Seg.*fault/i
  bare_segfault = shell_result[:stderr].each_line.any? do |line|
    line.match?(segfault_pattern) && !line.start_with?(test_filename)
  end

  if bare_segfault
    @loginator.lazy( Verbosity::DEBUG, LogLabels::CRASH ) do
      "#{runner} STDERR reports segmentation fault"
    end
    crash = true
  end

  return crash
end