Skip to content

Commit

Permalink
✨ First working version of :simple backtrace
Browse files Browse the repository at this point in the history
  • Loading branch information
mkarlesky committed Jun 11, 2024
1 parent f827ed7 commit d3e755a
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 26 deletions.
113 changes: 92 additions & 21 deletions lib/ceedling/backtrace.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ def setup
@new_line_tag = '$$$'
@colon_tag = '!!!'
@command_line = nil
@test_result_collector_struct = Struct.new(:passed, :failed, :ignored,
:output, keyword_init: true)
@test_result_collector_struct =
Struct.new(:passed, :failed, :ignored, :output, keyword_init: true)
end

# Copy original command line generated from @tool_executor.build_command_line
Expand All @@ -35,26 +35,75 @@ def configure_debugger(command)
end
end

# Execute test_runner file under gdb and return:
# - output -> stderr and stdout
# - time -> execution of single test
#
# @param [hash, #command] - Command line generated from @tool_executor.build_command_line
# @return [String, #output] - output from binary execution
# @return [Float, #time] - time execution of the binary file
def collect_cmd_output_with_gdb(command, cmd, test_case=nil)
gdb_file_name = @configurator.project_config_hash[:tools_backtrace_reporter][:executable]
gdb_extra_args = @configurator.project_config_hash[:tools_backtrace_reporter][:arguments]
gdb_extra_args = gdb_extra_args.join(' ')
def do_simple(filename, command, shell_result, test_cases)
test_case_results = @test_result_collector_struct.new(
passed: 0,
failed: 0,
ignored: 0,
output: []
)

gdb_exec_cmd = command.clone
gdb_exec_cmd[:line] = "#{gdb_file_name} #{gdb_extra_args} #{cmd}"
crash_result = @tool_executor.exec(gdb_exec_cmd)
if (crash_result[:exit_code] == 0) and (crash_result[:output] =~ /(?:PASS|FAIL|IGNORE)/)
[crash_result[:output], crash_result[:time].to_f]
else
["#{gdb_file_name.split(/\w+/)[0]}:1:#{test_case || 'test_Unknown'}:FAIL:#{crash_result[:output]}", 0.0]
# Reset time
shell_result[:time] = 0

filter_test_cases( test_cases ).each do |test_case|
test_run_cmd = command.clone
test_run_cmd[:line] += @unity_utils.additional_test_run_args( test_case[:test], :test_case )

exec_time = 0
test_output = ''


crash_result = @tool_executor.exec(test_run_cmd)
if (crash_result[:output] =~ /(?:PASS|FAIL|IGNORE)/)
test_output = crash_result[:output]
exec_time = crash_result[:time].to_f()
else
test_output = "#{filename}:1:#{test_case[:name]}:FAIL:#{crash_result[:output]}"
exec_time = 0.0
end

# Concatenate execution time between tests
# (Running tests serpatately increases total execution time)
shell_result[:time] += exec_time

# Concatenate successful single test runs
m = test_output.match /([\S]+):(\d+):([\S]+):(IGNORE|PASS|FAIL:)(.*)/
if m
test_output = "#{m[1]}:#{m[2]}:#{m[3]}:#{m[4]}#{m[5]}"
if test_output =~ /:PASS/
test_case_results[:passed] += 1
elsif test_output =~ /:IGNORE/
test_case_results[:ignored] += 1
elsif test_output =~ /:FAIL:/
test_case_results[:failed] += 1
end

# Process crashed test case details
else
test_output = "ERR:#{test_case[:line_number]}:#{test_case[:test]}:FAIL:Test Case Crashed"

# Mark test as failure
test_case_results[:failed] += 1
end
test_case_results[:output].append("#{test_output}\r\n")
end

template = "\n-----------------------\n" \
"\n#{(test_case_results[:passed] + \
test_case_results[:failed] + \
test_case_results[:ignored])} " \
"Tests #{test_case_results[:failed]} " \
"Failures #{test_case_results[:ignored]} Ignored\n\n"

template += if test_case_results[:failed] > 0
"FAIL\n"
else
"OK\n"
end
shell_result[:output] = test_case_results[:output].join('') + template

return shell_result
end

# Support function to collect backtrace from gdb.
Expand Down Expand Up @@ -138,7 +187,7 @@ def gdb_output_collector(shell_result, test_cases)
end
shell_result[:output] = test_case_result_collector[:output].join('') + template

shell_result
return shell_result
end

# Unflat segmentation fault log
Expand Down Expand Up @@ -188,6 +237,28 @@ def filter_test_cases(test_cases)
return _test_cases
end

# Execute test_runner file under gdb and return:
# - output -> stderr and stdout
# - time -> execution of single test
#
# @param [hash, #command] - Command line generated from @tool_executor.build_command_line
# @return [String, #output] - output from binary execution
# @return [Float, #time] - time execution of the binary file
def collect_cmd_output_with_gdb(command, cmd, test_case=nil)
gdb_file_name = @configurator.project_config_hash[:tools_backtrace_reporter][:executable]
gdb_extra_args = @configurator.project_config_hash[:tools_backtrace_reporter][:arguments]
gdb_extra_args = gdb_extra_args.join(' ')

gdb_exec_cmd = command.clone
gdb_exec_cmd[:line] = "#{gdb_file_name} #{gdb_extra_args} #{cmd}"
crash_result = @tool_executor.exec(gdb_exec_cmd)
if (crash_result[:exit_code] == 0) and (crash_result[:output] =~ /(?:PASS|FAIL|IGNORE)/)
[crash_result[:output], crash_result[:time].to_f]
else
["#{gdb_file_name.split(/\w+/)[0]}:1:#{test_case || 'test_Unknown'}:FAIL:#{crash_result[:output]}", 0.0]
end
end

# Restore colon character under flatten log
#
# @param(String, #text) - string containing flatten output log
Expand Down
4 changes: 1 addition & 3 deletions lib/ceedling/configurator_setup.rb
Original file line number Diff line number Diff line change
Expand Up @@ -175,9 +175,7 @@ def validate_backtrace(config)
when :none
# Do nothing
when :simple
# TODO: Remove when :simple support is completed
@loginator.log( ":project ↳ :use_backtrace => :simple is not yet supported", Verbosity::ERRORS )
valid = false
# Do nothing
when :gdb
# Do nothing
else
Expand Down
13 changes: 11 additions & 2 deletions lib/ceedling/generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -311,15 +311,24 @@ def generate_test_results(tool:, context:, test_name:, test_filepath:, executabl
@helper.log_test_results_crash( test_name, executable, shell_result )

case @configurator.project_config_hash[:project_use_backtrace]
# If we have the options and tools to learn more, dig into the details
when :gdb
# If we have the options and tools to learn more, dig into the details
shell_result =
@backtrace.gdb_output_collector(
shell_result,
@test_context_extractor.lookup_test_cases( test_filepath )
)

# Simple test-case-by-test-case exercise
when :simple
# TODO: Identify problematic test just from iterating with test case filters
shell_result =
@backtrace.do_simple(
File.basename( test_filepath ),
command,
shell_result,
@test_context_extractor.lookup_test_cases( test_filepath )
)

else # :none
# Otherwise, call a crash a single failure so it shows up in the report
shell_result = @generator_test_results.create_crash_failure( executable, shell_result )
Expand Down
2 changes: 2 additions & 0 deletions lib/ceedling/generator_test_results.rb
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ def create_crash_failure( executable, shell_result )
return shell_result
end

### Private ###

private

def get_results_structure
Expand Down

0 comments on commit d3e755a

Please sign in to comment.