Class: GeneratorTestResultsBacktrace
- Defined in:
- lib/ceedling/generators/generator_test_results_backtrace.rb
Overview
=========================================================================
Ceedling - Test-Centered Build System for C ThrowTheSwitch.org Copyright (c) 2010-26 Mike Karlesky, Mark VanderVoord, & Greg Williams SPDX-License-Identifier: MIT
Instance Method Summary collapse
-
#do_gdb(filename, executable, shell_result, test_cases, context:) ⇒ Object
Re-runs each test case under gdb to identify which ones crashed and why.
-
#do_simple(filename, executable, shell_result, test_cases, context:) ⇒ Object
Re-runs each test case individually to determine which ones crashed.
- #setup ⇒ Object
Instance Method Details
#do_gdb(filename, executable, shell_result, test_cases, context:) ⇒ Object
Re-runs each test case under gdb to identify which ones crashed and why. Writes the full gdb transcript to a per-test-case log file and assembles a terse crash label (signal + description, optional source line in backticks) for each failing test case. Returns a modified shell_result with regenerated output.
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 73 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 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
# File 'lib/ceedling/generators/generator_test_results_backtrace.rb', line 20 def do_gdb(filename, executable, shell_result, test_cases, context:) gdb_script_filepath = File.join( @configurator.project_build_tests_root, BACKTRACE_GDB_SCRIPT_FILE ) # Clean stats tracker test_case_results = @RESULTS_COLLECTOR.new( passed:0, failed:0, ignored:0, output:[] ) # Reset time shell_result[:time] = 0 test_name = File.basename( filename, '.*' ) # Iterate on test cases test_cases.each do |test_case| # Per-test-case log file: <log_path>/<context>/<test_name>/<test_case>.gdb.log log_path = @file_path_utils.form_test_gdb_log( test_name, context: context, name: test_case[:test] ) @file_wrapper.mkdir( File.dirname( log_path ) ) # Build the test fixture to run with our test case of interest command = @tool_executor.build_command_line( @configurator.tools_test_backtrace_gdb, [], gdb_script_filepath, executable, test_case[:test] ) # Things are gonna go boom, so ignore booms to get output command[:options][:boom] = false crash_result = @tool_executor.exec( command ) # Sum execution time for each test case # Note: Running tests separately increases total execution time shell_result[:time] += crash_result[:time].to_f() test_output = '' # Process single test case stats case crash_result[:output] # Success test case when /(^#{filename}.+:PASS\s*$)/ test_case_results[:passed] += 1 test_output = $1 # Grab regex match # Ignored test case when /(^#{filename}.+:IGNORE\s*$)/ test_case_results[:ignored] += 1 test_output = $1 # Grab regex match when /(^#{filename}.+:FAIL(:.+)?\s*$)/ test_case_results[:failed] += 1 test_output = $1 # Grab regex match else # Crash failure case test_case_results[:failed] += 1 # Append full gdb output for this test case to the log @file_wrapper.write( log_path, "=== #{test_case[:test]} ===\n#{crash_result[:output]}\n", 'a' ) # Collect file_name and line in which crash occurred matched = crash_result[:output].match( /#{test_case[:test]}\s*\(\)\sat.+#{filename}:(\d+)\n/ ) # If we found an error report line containing `test_case() at filename.c:###` in `gdb` output if matched # Line number line_number = matched[1] # Build terse signal label: "[SIGNAL] Description" signal_label = format_signal_label( crash_result[:output] ) # Extract the offending source line (nil for assertion crashes or when unavailable) source_line = extract_source_line( crash_result[:output], test_case[:test], filename ) # Unity's test executable output is line oriented. # Multi-line output is not possible (it looks like random `printf()` statements to the results parser). # "Encode" newlines in multiline string to be handled by the test results parser. crash_detail = source_line ? "#{NEWLINE_TOKEN}`#{source_line}`" : '' # Log path appears on its own encoded line so the results parser treats it separately test_output = "#{filename}:#{line_number}:#{test_case[:test]}:FAIL: Test case crashed" \ " >> #{signal_label}" \ "#{crash_detail}" \ "#{NEWLINE_TOKEN}(#{log_path})" # Try to extract a useful label even when no crash location frame was found. # A brief Windows assertion failure may report only the assertion text without frames. else label = format_signal_label( crash_result[:output] ) if !label.empty? test_output = "#{filename}:#{test_case[:line_number]}:#{test_case[:test]}:FAIL: Test case crashed" \ " >> #{label}" \ "#{NEWLINE_TOKEN}(#{log_path})" else test_output = "#{filename}:#{test_case[:line_number]}:#{test_case[:test]}:FAIL: " \ "Test case crashed (failed to extract `gdb` report)" \ "#{NEWLINE_TOKEN}(#{log_path})" end end end test_case_results[:output] << test_output end # Reset shell result exit code and output shell_result[:exit_code] = test_case_results[:failed] shell_result[:output] = @generator_test_results.regenerate_test_executable_stdout( total: test_cases.size(), ignored: test_case_results[:ignored], failed: test_case_results[:failed], output: test_case_results[:output] ) return shell_result end |
#do_simple(filename, executable, shell_result, test_cases, context:) ⇒ Object
Re-runs each test case individually to determine which ones crashed. For crash cases, captures any extra output from the test binary (e.g. assertion messages on stderr) and includes it in the failure report. Returns a modified shell_result with regenerated output.
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
# File 'lib/ceedling/generators/generator_test_results_backtrace.rb', line 142 def do_simple(filename, executable, shell_result, test_cases, context:) # Clean stats tracker test_case_results = @RESULTS_COLLECTOR.new( passed:0, failed:0, ignored:0, output:[] ) # Reset time shell_result[:time] = 0 # Iterate on test cases test_cases.each do |test_case| # Build the test fixture to run with our test case of interest command = @tool_executor.build_command_line( @configurator.tools_test_fixture_simple_backtrace, [], executable, test_case[:test] ) # Things are gonna go boom, so ignore booms to get output command[:options][:boom] = false crash_result = @tool_executor.exec( command ) # Sum execution time for each test case # Note: Running tests separately increases total execution time shell_result[:time] += crash_result[:time].to_f() # Process single test case stats case crash_result[:output] # Success test case when /(^#{filename}.+:PASS\s*$)/ test_case_results[:passed] += 1 test_output = $1 # Grab regex match # Ignored test case when /(^#{filename}.+:IGNORE\s*$)/ test_case_results[:ignored] += 1 test_output = $1 # Grab regex match when /(^#{filename}.+:FAIL(:.+)?\s*$)/ test_case_results[:failed] += 1 test_output = $1 # Grab regex match else # Crash failure case test_case_results[:failed] += 1 # Collect any non-result, non-blank lines (e.g. assertion messages on stderr) extra = extract_simple_crash_output( crash_result[:output], filename ) test_output = "#{filename}:#{test_case[:line_number]}:#{test_case[:test]}:FAIL: Test case crashed" test_output += " >> #{extra.join(NEWLINE_TOKEN)}" unless extra.empty? end # Collect up real and stand-in test results output test_case_results[:output] << test_output end # Reset shell result exit code and output shell_result[:exit_code] = test_case_results[:failed] shell_result[:output] = @generator_test_results.regenerate_test_executable_stdout( total: test_cases.size(), ignored: test_case_results[:ignored], failed: test_case_results[:failed], output: test_case_results[:output] ) return shell_result end |
#setup ⇒ Object
12 13 14 |
# File 'lib/ceedling/generators/generator_test_results_backtrace.rb', line 12 def setup() @RESULTS_COLLECTOR = Struct.new( :passed, :failed, :ignored, :output, keyword_init:true ) end |