summaryrefslogtreecommitdiff
path: root/qa/qa/specs/runner.rb
blob: c30e5d822c4db01f4351459084e263c50f11840f (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
# frozen_string_literal: true

require 'rspec/core'
require 'rspec/expectations'

module QA
  module Specs
    class Runner < Scenario::Template
      attr_accessor :tty, :tags, :options

      RegexMismatchError = Class.new(StandardError)

      DEFAULT_TEST_PATH_ARGS = ['--', File.expand_path('./features', __dir__)].freeze
      DEFAULT_STD_ARGS = [$stderr, $stdout].freeze

      def initialize
        @tty = false
        @tags = []
        @options = []
      end

      def paths_from_knapsack
        allocator = Knapsack::AllocatorBuilder.new(Knapsack::Adapters::RSpecAdapter).allocator

        QA::Runtime::Logger.info '==== Knapsack specs to execute ====='
        QA::Runtime::Logger.info 'Report specs:'
        QA::Runtime::Logger.info allocator.report_node_tests.join(', ')
        QA::Runtime::Logger.info 'Leftover specs:'
        QA::Runtime::Logger.info allocator.leftover_node_tests.join(', ')

        ['--', allocator.node_tests]
      end

      def rspec_tags
        tags_for_rspec = []

        if tags.any?
          tags.each { |tag| tags_for_rspec.push(['--tag', tag.to_s]) }
        else
          tags_for_rspec.push(%w[--tag ~orchestrated --tag ~transient]) unless (%w[-t --tag] & options).any?
        end

        tags_for_rspec.push(%w[--tag ~geo]) unless QA::Runtime::Env.geo_environment?
        tags_for_rspec.push(%w[--tag ~skip_signup_disabled]) if QA::Runtime::Env.signup_disabled?
        tags_for_rspec.push(%w[--tag ~smoke --tag ~reliable]) if QA::Runtime::Env.skip_smoke_reliable?
        tags_for_rspec.push(%w[--tag ~skip_live_env]) if QA::Specs::Helpers::ContextSelector.dot_com?

        QA::Runtime::Env.supported_features.each_key do |key|
          tags_for_rspec.push(%W[--tag ~requires_#{key}]) unless QA::Runtime::Env.can_test? key
        end

        tags_for_rspec
      end

      # rubocop:disable Metrics/AbcSize
      # rubocop:disable Metrics/CyclomaticComplexity
      def perform
        args = []
        args.push('--tty') if tty
        args.push(rspec_tags)
        args.push(options)

        if Runtime::Env.knapsack?
          args.push(paths_from_knapsack)
        else
          args.push(DEFAULT_TEST_PATH_ARGS) unless options.any? { |opt| opt =~ %r{/features/} }
        end

        Runtime::Scenario.define(:large_setup?, args.flatten.include?('can_use_large_setup'))

        if Runtime::Scenario.attributes[:parallel]
          ParallelRunner.run(args.flatten)
        elsif Runtime::Scenario.attributes[:loop]
          LoopRunner.run(args.flatten)
        elsif Runtime::Scenario.attributes[:count_examples_only]
          args.unshift('--dry-run')
          out = StringIO.new

          RSpec::Core::Runner.run(args.flatten, $stderr, out).tap do |status|
            abort if status.nonzero?
          end

          begin
            total_examples = out.string.match(/(\d+) examples?,/)[1]
          rescue StandardError
            raise RegexMismatchError, 'Rspec output did not match regex'
          end

          filename = build_filename

          File.open(filename, 'w') { |f| f.write(total_examples) } if total_examples.to_i > 0

          $stdout.puts "Total examples in #{Runtime::Scenario.klass}: #{total_examples}#{total_examples.to_i > 0 ? ". Saved to file: #{filename}" : ''}"
        elsif Runtime::Scenario.attributes[:test_metadata_only]
          args.unshift('--dry-run')

          output_file = Pathname.new(File.join(Runtime::Path.qa_root, 'tmp', 'test-metadata.json'))

          RSpec.configure do |config|
            config.add_formatter(QA::Support::JsonFormatter, output_file)
            config.fail_if_no_examples = true
          end

          RSpec::Core::Runner.run(args.flatten, $stderr, $stdout) do |status|
            abort if status.nonzero?
          end

          $stdout.puts "Saved to file: #{output_file}"
        else
          RSpec::Core::Runner.run(args.flatten, *DEFAULT_STD_ARGS).tap do |status|
            abort if status.nonzero?
          end
        end
      end
      # rubocop:enable Metrics/AbcSize
      # rubocop:enable Metrics/CyclomaticComplexity

      private

      def build_filename
        filename = Runtime::Scenario.klass.split('::').last(3).join('_').downcase

        tags = []
        options.reduce do |before, after|
          tags << after if %w[--tag -t].include?(before)
          after
        end
        tags = tags.compact.join('_')

        filename.concat("_#{tags}") unless tags.empty?

        filename.concat('.txt')

        FileUtils.mkdir_p('no_of_examples')
        File.join('no_of_examples', filename)
      end
    end
  end
end