Skip to content

Commit

Permalink
Wip display failures backtrace grouped by exception
Browse files Browse the repository at this point in the history
  • Loading branch information
safafa committed Dec 27, 2023
1 parent c8558b7 commit a0c178e
Show file tree
Hide file tree
Showing 9 changed files with 108 additions and 44 deletions.
34 changes: 34 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,37 @@ jobs:
run: bundle exec rake rubocop
- name: Run tests
run: bundle exec rake spec
- name: Upload Failures
uses: actions/upload-artifact@v3
with:
name: Failures
path: failures_log_*.yml
- name: Upload Exceptions
uses: actions/upload-artifact@v3
with:
name: Exceptions
path: exceptions_log_*.yml

print_log:
name: Test print logs task
needs: test
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: 2.7.2
bundler-cache: true
- name: Download Failures
uses: actions/download-artifact@v3
with:
name: Failures
- name: Download Exceptions
uses: actions/download-artifact@v3
with:
name: Exceptions
- name: Run print_log task
run: bundle exec rake failing_specs_detector:print_log

4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,7 @@

# rspec failure tracking
.rspec_status

# log files
exceptions_log_*.yml
failures_log_*.yml
21 changes: 8 additions & 13 deletions lib/failing_spec_detector/failing_spec_formatter.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# frozen_string_literal: true

require 'rspec/core/formatters/base_text_formatter'
require_relative 'failure'
require 'yaml'

module FailingSpecDetector
class FailingSpecFormatter < RSpec::Core::Formatters::BaseTextFormatter
Expand All @@ -10,32 +12,25 @@ def initialize(output)
super(output)
@failures = []
@exceptions = []
@filename = "failing_specs_detector_log_#{ENV.fetch('TEST_ENV_NUMBER', nil)}.txt"
@failures_filename = "failures_log_#{ENV.fetch('TEST_ENV_NUMBER', nil)}.yml"
@exceptions_filename = "exceptions_log_#{ENV.fetch('TEST_ENV_NUMBER', nil)}.yml"
end

def example_failed(failure)
exception = failure.exception.to_s.gsub(/\e\[(\d+)m/, '')

@exceptions << exception unless @exceptions.include?(exception)

failure = Failure.new(exception, failure.formatted_backtrace.join("\n"))

@failures << failure
end

def stop(_notification)
File.write(@filename, "Failing spec detector log_#{ENV.fetch('TEST_ENV_NUMBER', nil)}:\n")
return if @exceptions.empty?

@exceptions.each do |exception|
File.write(@filename, "#{exception}:\n\n", mode: 'a')
related_examples = @failures.select { |failure| failure.exception.to_s.gsub(/\e\[(\d+)m/, '') == exception }
next if related_examples.empty?

related_examples.each do |failure|
File.write(@filename, "#{failure.formatted_backtrace.join("\n")}:\n", mode: 'a')
end
File.write(@filename, "^^^^^^^^\n\n\n", mode: 'a')
end
File.write(@filename, '----------------------------------------------------------------', mode: 'a')
File.write(@exceptions_filename, YAML.dump(@exceptions), mode: 'w')
File.write(@failures_filename, YAML.dump(@failures), mode: 'w')
end
end
end
13 changes: 13 additions & 0 deletions lib/failing_spec_detector/failure.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# frozen_string_literal: true

module FailingSpecDetector
# a simplified Failure class allow us to store only the needed information
class Failure
attr_accessor :exception, :backtrace

def initialize(exception, backtrace)
@exception = exception
@backtrace = backtrace
end
end
end
31 changes: 25 additions & 6 deletions lib/tasks/failing_spec_detector/print_log.rake
Original file line number Diff line number Diff line change
@@ -1,15 +1,34 @@
# frozen_string_literal: true

require 'yaml'
require_relative '../../failing_spec_detector/failure'

namespace :failing_specs_detector do
desc 'Print all logs in console'
task :print_log do
puts "Failing specs detector:\n"
log_file_paths = Dir['failing_specs_detector_log_*.txt']
log_file_paths.each do |file_path|
file = File.open(file_path, 'r')
file_data = file.read
puts file_data
puts "Failing specs detector:\n\n\n"
failures = []
exceptions = []
failures_file_paths = Dir['failures_log_*.yml']
exceptions_file_paths = Dir['exceptions_log_*.yml']
failures_file_paths.each do |file_path|
failures.concat(YAML.load_file(file_path))
File.delete(file_path)
end
exceptions_file_paths.each do |file_path|
exceptions.concat(YAML.load_file(file_path))
File.delete(file_path)
end

exceptions.each do |exception|
puts "#{exception}:\n\n"
related_examples = failures.select { |failure| failure.exception == exception }
next if related_examples.empty?

related_examples.each do |failure|
puts "#{failure.backtrace}:\n"
end
puts "\n\n\n"
end
end
end
21 changes: 11 additions & 10 deletions spec/failing_spec_detector/failing_spec_formatter_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@
RSpec.describe FailingSpecDetector::FailingSpecFormatter do
let(:formatter) { described_class.new(output) }
let(:output) { Tempfile.new('./output_to_close') }
let(:expected_file_path) { './spec/support/expected_file.txt' }
let(:actual_file_path) { './failing_specs_detector_log_.txt' }
let(:expected_file) { File.new(expected_file_path, 'r') }
let(:actual_file) { File.new(actual_file_path, 'r') }
let(:expected_failures_file_path) { './spec/support/expected_failures_log_.yml' }
let(:expected_exceptions_file_path) { './spec/support/expected_exceptions_log_.yml' }
let(:actual_failures_file_path) { './failures_log_.yml' }
let(:actual_exceptions_file_path) { './exceptions_log_.yml' }
let(:expected_failures_file) { File.new(expected_failures_file_path, 'r') }
let(:expected_exceptions_file) { File.new(expected_exceptions_file_path, 'r') }
let(:actual_failures_file) { File.new(actual_failures_file_path, 'r') }
let(:actual_exceptions_file) { File.new(actual_exceptions_file_path, 'r') }

let(:examples) { [failed_notification1, failed_notification2, failed_notification3] }
let(:expected_exceptions) { [failed_notification1.exception.to_s, failed_notification3.exception.to_s] }
Expand Down Expand Up @@ -54,13 +58,10 @@
example
end

after do
File.delete(actual_file_path)
end

it 'prints the failing specs backtraces grouped by exception' do
it 'stores the failing specs failures and exceptions in yml files' do
mock_run_specs
expect(FileUtils.compare_file(actual_file, expected_file)).to be_truthy
expect(FileUtils.compare_file(actual_failures_file, expected_failures_file)).to be_truthy
expect(FileUtils.compare_file(actual_exceptions_file, expected_exceptions_file)).to be_truthy
end

def mock_run_specs
Expand Down
3 changes: 3 additions & 0 deletions spec/support/expected_exceptions_log_.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
- Test Error 1
- Test Error 2
10 changes: 10 additions & 0 deletions spec/support/expected_failures_log_.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
- !ruby/object:FailingSpecDetector::Failure
exception: Test Error 1
backtrace: "/spec/one_spec.rb:11:in `some_method'"
- !ruby/object:FailingSpecDetector::Failure
exception: Test Error 1
backtrace: "/spec/two_spec.rb:20:in `some_method'"
- !ruby/object:FailingSpecDetector::Failure
exception: Test Error 2
backtrace: "/spec/three_spec.rb:4:in `some_method'"
15 changes: 0 additions & 15 deletions spec/support/expected_file.txt

This file was deleted.

0 comments on commit a0c178e

Please sign in to comment.