summaryrefslogtreecommitdiff
path: root/scripts/failed_tests.rb
blob: 786d3c24c74543196df7d3d7199b11082c5c44f2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
#!/usr/bin/env ruby
# frozen_string_literal: true

require 'optparse'
require 'fileutils'
require 'uri'
require 'json'
require 'set'

class FailedTests
  DEFAULT_OPTIONS = {
    previous_tests_report_path: 'test_results/previous/test_reports.json',
    output_directory: 'tmp/previous_failed_tests/',
    format: :oneline,
    rspec_pg_regex: /rspec .+ pg12( .+)?/,
    rspec_ee_pg_regex: /rspec-ee .+ pg12( .+)?/
  }.freeze

  def initialize(options)
    @filename = options.delete(:previous_tests_report_path)
    @output_directory = options.delete(:output_directory)
    @format = options.delete(:format).to_sym
    @rspec_pg_regex = options.delete(:rspec_pg_regex)
    @rspec_ee_pg_regex = options.delete(:rspec_ee_pg_regex)
  end

  def output_failed_tests
    create_output_dir

    failed_cases_for_suite_collection.each do |suite_name, suite_tests|
      puts "[FailedTests] Detected #{suite_tests.size} failed tests in suite #{suite_name}..."
      suite_tests =
        case format
        when :oneline
          suite_tests.map { |test| test['file'] }.join(' ') # rubocop:disable Rails/Pluck
        when :json
          JSON.pretty_generate(suite_tests.to_a)
        end

      output_file = File.join(output_directory, "#{suite_name}_failed_tests.#{output_file_format}")

      File.open(output_file, 'w') do |file|
        file.write(suite_tests)
      end
    end
  end

  def failed_cases_for_suite_collection
    suite_map.each_with_object(Hash.new { |h, k| h[k] = Set.new }) do |(suite_name, suite_collection_regex), hash|
      failed_suites.each do |suite|
        hash[suite_name].merge(failed_cases(suite)) if suite['name'] =~ suite_collection_regex
      end
    end
  end

  def suite_map
    @suite_map ||= {
      rspec: rspec_pg_regex,
      rspec_ee: rspec_ee_pg_regex,
      jest: /jest/
    }
  end

  private

  attr_reader :filename, :output_directory, :format, :rspec_pg_regex, :rspec_ee_pg_regex

  def file_contents
    @file_contents ||= begin
      File.read(filename)
    rescue Errno::ENOENT
      '{}'
    end
  end

  def file_contents_as_json
    @file_contents_as_json ||= begin
      JSON.parse(file_contents)
    rescue JSON::ParserError
      {}
    end
  end

  def output_file_format
    case format
    when :oneline
      'txt'
    when :json
      'json'
    else
      raise "[FailedTests] Unsupported format `#{format}` (allowed formats: `oneline` and `json`)!"
    end
  end

  def failed_suites
    return [] unless file_contents_as_json['suites']

    file_contents_as_json['suites'].select { |suite| suite['failed_count'] > 0 }
  end

  def failed_cases(suite)
    return [] unless suite

    suite['test_cases'].filter_map do |failure_hash|
      next if failure_hash['status'] != 'failed'

      failure_hash['job_url'] = suite['job_url']
      failure_hash['file'] = failure_hash['file'].delete_prefix('./')

      failure_hash
    end
  end

  def create_output_dir
    return if File.directory?(output_directory)

    puts '[FailedTests] Creating output directory...'
    FileUtils.mkdir_p(output_directory)
  end
end

if $PROGRAM_NAME == __FILE__
  options = FailedTests::DEFAULT_OPTIONS.dup

  OptionParser.new do |opts|
    opts.on("-p", "--previous-tests-report-path PREVIOUS_TESTS_REPORT_PATH", String,
      "Path of the file listing previous test failures (defaults to " \
      "`#{FailedTests::DEFAULT_OPTIONS[:previous_tests_report_path]}`)") do |value|
      options[:previous_tests_report_path] = value
    end

    opts.on("-o", "--output-directory OUTPUT_DIRECTORY", String,
      "Output directory for failed test files (defaults to " \
      "`#{FailedTests::DEFAULT_OPTIONS[:output_directory]}`)") do |value|
      options[:output_directory] = value
    end

    opts.on("-f", "--format [oneline|json]", String,
      "Format of the output files: oneline (with test filenames) or JSON (defaults to " \
      "`#{FailedTests::DEFAULT_OPTIONS[:format]}`)") do |value|
      options[:format] = value
    end

    opts.on("--rspec-pg-regex RSPEC_PG_REGEX", Regexp,
      "Regex to use when finding matching RSpec jobs (defaults to " \
      "`#{FailedTests::DEFAULT_OPTIONS[:rspec_pg_regex]}`)") do |value|
      options[:rspec_pg_regex] = value
    end

    opts.on("--rspec-ee-pg-regex RSPEC_EE_PG_REGEX", Regexp,
      "Regex to use when finding matching RSpec EE jobs (defaults to " \
      "`#{FailedTests::DEFAULT_OPTIONS[:rspec_ee_pg_regex]}`)") do |value|
      options[:rspec_ee_pg_regex] = value
    end

    opts.on("-h", "--help", "Prints this help") do
      puts opts
      exit
    end
  end.parse!

  FailedTests.new(options).output_failed_tests
end