5 'mtime' => lambda {|file_a, file_b| File.mtime(file_b) <=> File.mtime(file_a)}
8 EXAMPLE_FORMATTERS = { # Load these lazily for better speed
9 'specdoc' => ['spec/runner/formatter/specdoc_formatter', 'Formatter::SpecdocFormatter'],
10 's' => ['spec/runner/formatter/specdoc_formatter', 'Formatter::SpecdocFormatter'],
11 'html' => ['spec/runner/formatter/html_formatter', 'Formatter::HtmlFormatter'],
12 'h' => ['spec/runner/formatter/html_formatter', 'Formatter::HtmlFormatter'],
13 'progress' => ['spec/runner/formatter/progress_bar_formatter', 'Formatter::ProgressBarFormatter'],
14 'p' => ['spec/runner/formatter/progress_bar_formatter', 'Formatter::ProgressBarFormatter'],
15 'failing_examples' => ['spec/runner/formatter/failing_examples_formatter', 'Formatter::FailingExamplesFormatter'],
16 'e' => ['spec/runner/formatter/failing_examples_formatter', 'Formatter::FailingExamplesFormatter'],
17 'failing_example_groups' => ['spec/runner/formatter/failing_example_groups_formatter', 'Formatter::FailingExampleGroupsFormatter'],
18 'g' => ['spec/runner/formatter/failing_example_groups_formatter', 'Formatter::FailingExampleGroupsFormatter'],
19 'profile' => ['spec/runner/formatter/profile_formatter', 'Formatter::ProfileFormatter'],
20 'o' => ['spec/runner/formatter/profile_formatter', 'Formatter::ProfileFormatter'],
21 'textmate' => ['spec/runner/formatter/text_mate_formatter', 'Formatter::TextMateFormatter']
25 'plain' => ['spec/runner/formatter/story/plain_text_formatter', 'Formatter::Story::PlainTextFormatter'],
26 'p' => ['spec/runner/formatter/story/plain_text_formatter', 'Formatter::Story::PlainTextFormatter'],
27 'html' => ['spec/runner/formatter/story/html_formatter', 'Formatter::Story::HtmlFormatter'],
28 'h' => ['spec/runner/formatter/story/html_formatter', 'Formatter::Story::HtmlFormatter']
45 :user_input_for_runner,
48 # TODO: BT - Figure out a better name
51 attr_reader :colour, :differ_class, :files, :example_groups
53 def initialize(error_stream, output_stream)
54 @error_stream = error_stream
55 @output_stream = output_stream
56 @backtrace_tweaker = QuietBacktraceTweaker.new
61 @reporter = Reporter.new(self)
63 @diff_format = :unified
67 @examples_should_be_run = nil
68 @user_input_for_runner = nil
71 def add_example_group(example_group)
72 @example_groups << example_group
75 def remove_example_group(example_group)
76 @example_groups.delete(example_group)
80 return true unless examples_should_be_run?
81 runner = custom_runner || ExampleGroupRunner.new(self)
83 runner.load_files(files_to_load)
84 if example_groups.empty?
89 heckle if heckle_runner
98 def examples_should_not_be_run
99 @examples_should_be_run = false
105 require 'Win32/Console/ANSI' if @colour && PLATFORM =~ /win32/; \
107 raise "You must gem install win32console to use colour on Windows" ; \
111 def parse_diff(format)
113 when :context, 'context', 'c'
114 @diff_format = :context
116 when :unified, 'unified', 'u', '', nil
117 @diff_format = :unified
120 @diff_format = :custom
121 self.differ_class = load_class(format, 'differ', '--diff')
125 def parse_example(example)
126 if(File.file?(example))
127 @examples = File.open(example).read.split("\n")
129 @examples = [example]
133 def parse_format(format_arg)
134 format, where = ClassAndArgumentsParser.parse(format_arg)
136 raise "When using several --format options only one of them can be without a file" if @out_used
137 where = @output_stream
140 @format_options ||= []
141 @format_options << [format, where]
145 @format_options ||= [['progress', @output_stream]]
146 @formatters ||= load_formatters(@format_options, EXAMPLE_FORMATTERS)
150 @format_options ||= [['plain', @output_stream]]
151 @formatters ||= load_formatters(@format_options, STORY_FORMATTERS)
154 def load_formatters(format_options, formatters)
155 format_options.map do |format, where|
156 formatter_type = if formatters[format]
157 require formatters[format][0]
158 eval(formatters[format][1], binding, __FILE__, __LINE__)
160 load_class(format, 'formatter', '--format')
162 formatter_type.new(self, where)
166 def load_heckle_runner(heckle)
167 suffix = [/mswin/, /java/].detect{|p| p =~ RUBY_PLATFORM} ? '_unsupported' : ''
168 require "spec/runner/heckle_runner#{suffix}"
169 @heckle_runner = HeckleRunner.new(heckle)
172 def number_of_examples
173 @example_groups.inject(0) do |sum, example_group|
174 sum + example_group.number_of_examples
179 def examples_should_be_run?
180 return @examples_should_be_run unless @examples_should_be_run.nil?
181 @examples_should_be_run = true
184 def differ_class=(klass)
186 @differ_class = klass
187 Spec::Expectations.differ = self.differ_class.new(self)
190 def load_class(name, kind, option)
191 if name =~ /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/
192 arg = $2 == "" ? nil : $2
195 m = "#{name.inspect} is not a valid class name"
200 eval(name, binding, __FILE__, __LINE__)
201 rescue NameError => e
202 @error_stream.puts "Couldn't find #{kind} class #{name}"
203 @error_stream.puts "Make sure the --require option is specified *before* #{option}"
204 if $_spec_spec ; raise e ; else exit(1) ; end
210 sorted_files.each do |file|
212 result += Dir[File.expand_path("#{file}/**/*.rb")]
216 raise "File or directory not found: #{file}"
223 return nil unless custom_runner?
224 klass_name, arg = ClassAndArgumentsParser.parse(user_input_for_runner)
225 runner_type = load_class(klass_name, 'behaviour runner', '--runner')
226 return runner_type.new(self, arg)
230 return user_input_for_runner ? true : false
234 returns = self.heckle_runner.heckle_with
235 self.heckle_runner = nil
240 return sorter ? files.sort(&sorter) : files
248 require 'spec/expectations/differs/default'
249 self.differ_class = Spec::Expectations::Differs::Default