aboutsummaryrefslogtreecommitdiffstats
path: root/railties/lib/rails/test_unit/runner.rb
blob: 084ae2f4788ffda42a85607e7e82692360b55917 (plain) (blame)
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
require "optparse"
require "rake/file_list"
require "method_source"

module Rails
  class TestRunner
    class Options
      def self.parse(args)
        options = { backtrace: !ENV["BACKTRACE"].nil?, name: nil, environment: "test" }

        opt_parser = ::OptionParser.new do |opts|
          opts.banner = "Usage: bin/rails test [options] [file or directory]"

          opts.separator ""
          opts.on("-e", "--environment [ENV]",
                  "run tests in the ENV environment") do |env|
            options[:environment] = env.strip
          end
          opts.separator ""
          opts.separator "Filter options:"
          opts.separator ""
          opts.separator <<-DESC
  You can run a single test by appending the line number to filename:

    bin/rails test test/models/user_test.rb:27

          DESC

          opts.on("-n", "--name [NAME]",
                  "Only run tests matching NAME") do |name|
            options[:name] = name
          end
          opts.on("-p", "--pattern [PATTERN]",
                  "Only run tests matching PATTERN") do |pattern|
            options[:name] = "/#{pattern}/"
          end

          opts.separator ""
          opts.separator "Output options:"

          opts.on("-b", "--backtrace",
                  "show the complte backtrace") do
            options[:backtrace] = true
          end

          opts.separator ""
          opts.separator "Common options:"

          opts.on_tail("-h", "--help", "Show this message") do
            puts opts
            exit
          end
        end

        opt_parser.order!(args)

        options[:patterns] = []
        while arg = args.shift
          if (file_and_line = arg.split(':')).size > 1
            options[:filename], options[:line] = file_and_line
            options[:filename] = File.expand_path options[:filename]
            options[:line] &&= options[:line].to_i
          else
            arg = arg.gsub(':', '')
            if Dir.exists?("#{arg}")
              options[:patterns] << File.expand_path("#{arg}/**/*_test.rb")
            elsif File.file?(arg)
              options[:patterns] << File.expand_path(arg)
            end
          end
        end
        options
      end
    end

    def initialize(options = {})
      @options = options
    end

    def self.run(arguments)
      options = Rails::TestRunner::Options.parse(arguments)
      Rails::TestRunner.new(options).run
    end

    def run
      $rails_test_runner = self
      ENV["RAILS_ENV"] = @options[:environment]
      run_tests
    end

    def find_method
      return @options[:name] if @options[:name]
      return unless @options[:line]
      method = test_methods.find do |location, test_method, start_line, end_line|
        location == @options[:filename] &&
          (start_line..end_line).include?(@options[:line].to_i)
      end
      method[1] if method
    end

    def show_backtrace?
      @options[:backtrace]
    end

    def test_files
      return [@options[:filename]] if @options[:filename]
      if @options[:patterns] && @options[:patterns].count > 0
        pattern = @options[:patterns]
      else
        pattern = "test/**/*_test.rb"
      end
      Rake::FileList[pattern]
    end

    private
    def run_tests
      test_files.to_a.each do |file|
        require File.expand_path file
      end
    end

    def test_methods
      methods_map = []
      suites = Minitest::Runnable.runnables.shuffle
      suites.each do |suite_class|
        suite_class.runnable_methods.each do |test_method|
          method = suite_class.instance_method(test_method)
          location = method.source_location
          start_line = location.last
          end_line = method.source.split("\n").size + start_line - 1
          methods_map << [File.expand_path(location.first), test_method, start_line, end_line]
        end
      end
      methods_map
    end
  end
end