From a0c178ed96005020ff89e67a673390d9f1b1e970 Mon Sep 17 00:00:00 2001 From: safa Date: Wed, 27 Dec 2023 21:21:26 +0100 Subject: [PATCH] Wip display failures backtrace grouped by exception --- .github/workflows/ci.yml | 34 +++++++++++++++++++ .gitignore | 4 +++ .../failing_spec_formatter.rb | 21 +++++------- lib/failing_spec_detector/failure.rb | 13 +++++++ .../failing_spec_detector/print_log.rake | 31 +++++++++++++---- .../failing_spec_formatter_spec.rb | 21 ++++++------ spec/support/expected_exceptions_log_.yml | 3 ++ spec/support/expected_failures_log_.yml | 10 ++++++ spec/support/expected_file.txt | 15 -------- 9 files changed, 108 insertions(+), 44 deletions(-) create mode 100644 lib/failing_spec_detector/failure.rb create mode 100644 spec/support/expected_exceptions_log_.yml create mode 100644 spec/support/expected_failures_log_.yml delete mode 100644 spec/support/expected_file.txt diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6678fcf..4b1575d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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 + diff --git a/.gitignore b/.gitignore index b04a8c8..7af1d38 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,7 @@ # rspec failure tracking .rspec_status + +# log files +exceptions_log_*.yml +failures_log_*.yml diff --git a/lib/failing_spec_detector/failing_spec_formatter.rb b/lib/failing_spec_detector/failing_spec_formatter.rb index 1bab1cd..4214bcd 100644 --- a/lib/failing_spec_detector/failing_spec_formatter.rb +++ b/lib/failing_spec_detector/failing_spec_formatter.rb @@ -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 @@ -10,7 +12,8 @@ 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) @@ -18,24 +21,16 @@ def example_failed(failure) @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 diff --git a/lib/failing_spec_detector/failure.rb b/lib/failing_spec_detector/failure.rb new file mode 100644 index 0000000..ed21b30 --- /dev/null +++ b/lib/failing_spec_detector/failure.rb @@ -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 diff --git a/lib/tasks/failing_spec_detector/print_log.rake b/lib/tasks/failing_spec_detector/print_log.rake index db9069d..1f585aa 100644 --- a/lib/tasks/failing_spec_detector/print_log.rake +++ b/lib/tasks/failing_spec_detector/print_log.rake @@ -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 diff --git a/spec/failing_spec_detector/failing_spec_formatter_spec.rb b/spec/failing_spec_detector/failing_spec_formatter_spec.rb index 21cf226..566bae8 100644 --- a/spec/failing_spec_detector/failing_spec_formatter_spec.rb +++ b/spec/failing_spec_detector/failing_spec_formatter_spec.rb @@ -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] } @@ -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 diff --git a/spec/support/expected_exceptions_log_.yml b/spec/support/expected_exceptions_log_.yml new file mode 100644 index 0000000..e63ee86 --- /dev/null +++ b/spec/support/expected_exceptions_log_.yml @@ -0,0 +1,3 @@ +--- +- Test Error 1 +- Test Error 2 diff --git a/spec/support/expected_failures_log_.yml b/spec/support/expected_failures_log_.yml new file mode 100644 index 0000000..ee9e536 --- /dev/null +++ b/spec/support/expected_failures_log_.yml @@ -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'" diff --git a/spec/support/expected_file.txt b/spec/support/expected_file.txt deleted file mode 100644 index 8eb57d0..0000000 --- a/spec/support/expected_file.txt +++ /dev/null @@ -1,15 +0,0 @@ -Failing spec detector log_: -Test Error 1: - -/spec/one_spec.rb:11:in `some_method': -/spec/two_spec.rb:20:in `some_method': -^^^^^^^^ - - -Test Error 2: - -/spec/three_spec.rb:4:in `some_method': -^^^^^^^^ - - ----------------------------------------------------------------- \ No newline at end of file