--- /dev/null
+module Spec
+ module Runner
+ class Options
+ FILE_SORTERS = {
+ 'mtime' => lambda {|file_a, file_b| File.mtime(file_b) <=> File.mtime(file_a)}
+ }
+
+ EXAMPLE_FORMATTERS = { # Load these lazily for better speed
+ 'specdoc' => ['spec/runner/formatter/specdoc_formatter', 'Formatter::SpecdocFormatter'],
+ 's' => ['spec/runner/formatter/specdoc_formatter', 'Formatter::SpecdocFormatter'],
+ 'html' => ['spec/runner/formatter/html_formatter', 'Formatter::HtmlFormatter'],
+ 'h' => ['spec/runner/formatter/html_formatter', 'Formatter::HtmlFormatter'],
+ 'progress' => ['spec/runner/formatter/progress_bar_formatter', 'Formatter::ProgressBarFormatter'],
+ 'p' => ['spec/runner/formatter/progress_bar_formatter', 'Formatter::ProgressBarFormatter'],
+ 'failing_examples' => ['spec/runner/formatter/failing_examples_formatter', 'Formatter::FailingExamplesFormatter'],
+ 'e' => ['spec/runner/formatter/failing_examples_formatter', 'Formatter::FailingExamplesFormatter'],
+'failing_example_groups' => ['spec/runner/formatter/failing_example_groups_formatter', 'Formatter::FailingExampleGroupsFormatter'],
+ 'g' => ['spec/runner/formatter/failing_example_groups_formatter', 'Formatter::FailingExampleGroupsFormatter'],
+ 'profile' => ['spec/runner/formatter/profile_formatter', 'Formatter::ProfileFormatter'],
+ 'o' => ['spec/runner/formatter/profile_formatter', 'Formatter::ProfileFormatter'],
+ 'textmate' => ['spec/runner/formatter/text_mate_formatter', 'Formatter::TextMateFormatter']
+ }
+
+ STORY_FORMATTERS = {
+ 'plain' => ['spec/runner/formatter/story/plain_text_formatter', 'Formatter::Story::PlainTextFormatter'],
+ 'p' => ['spec/runner/formatter/story/plain_text_formatter', 'Formatter::Story::PlainTextFormatter'],
+ 'html' => ['spec/runner/formatter/story/html_formatter', 'Formatter::Story::HtmlFormatter'],
+ 'h' => ['spec/runner/formatter/story/html_formatter', 'Formatter::Story::HtmlFormatter']
+ }
+
+ attr_accessor(
+ :backtrace_tweaker,
+ :context_lines,
+ :diff_format,
+ :dry_run,
+ :profile,
+ :examples,
+ :heckle_runner,
+ :line_number,
+ :loadby,
+ :reporter,
+ :reverse,
+ :timeout,
+ :verbose,
+ :user_input_for_runner,
+ :error_stream,
+ :output_stream,
+ # TODO: BT - Figure out a better name
+ :argv
+ )
+ attr_reader :colour, :differ_class, :files, :example_groups
+
+ def initialize(error_stream, output_stream)
+ @error_stream = error_stream
+ @output_stream = output_stream
+ @backtrace_tweaker = QuietBacktraceTweaker.new
+ @examples = []
+ @colour = false
+ @profile = false
+ @dry_run = false
+ @reporter = Reporter.new(self)
+ @context_lines = 3
+ @diff_format = :unified
+ @files = []
+ @example_groups = []
+ @examples_run = false
+ @examples_should_be_run = nil
+ @user_input_for_runner = nil
+ end
+
+ def add_example_group(example_group)
+ @example_groups << example_group
+ end
+
+ def remove_example_group(example_group)
+ @example_groups.delete(example_group)
+ end
+
+ def run_examples
+ return true unless examples_should_be_run?
+ runner = custom_runner || ExampleGroupRunner.new(self)
+
+ runner.load_files(files_to_load)
+ if example_groups.empty?
+ true
+ else
+ success = runner.run
+ @examples_run = true
+ heckle if heckle_runner
+ success
+ end
+ end
+
+ def examples_run?
+ @examples_run
+ end
+
+ def examples_should_not_be_run
+ @examples_should_be_run = false
+ end
+
+ def colour=(colour)
+ @colour = colour
+ begin; \
+ require 'Win32/Console/ANSI' if @colour && PLATFORM =~ /win32/; \
+ rescue LoadError ; \
+ raise "You must gem install win32console to use colour on Windows" ; \
+ end
+ end
+
+ def parse_diff(format)
+ case format
+ when :context, 'context', 'c'
+ @diff_format = :context
+ default_differ
+ when :unified, 'unified', 'u', '', nil
+ @diff_format = :unified
+ default_differ
+ else
+ @diff_format = :custom
+ self.differ_class = load_class(format, 'differ', '--diff')
+ end
+ end
+
+ def parse_example(example)
+ if(File.file?(example))
+ @examples = File.open(example).read.split("\n")
+ else
+ @examples = [example]
+ end
+ end
+
+ def parse_format(format_arg)
+ format, where = ClassAndArgumentsParser.parse(format_arg)
+ unless where
+ raise "When using several --format options only one of them can be without a file" if @out_used
+ where = @output_stream
+ @out_used = true
+ end
+ @format_options ||= []
+ @format_options << [format, where]
+ end
+
+ def formatters
+ @format_options ||= [['progress', @output_stream]]
+ @formatters ||= load_formatters(@format_options, EXAMPLE_FORMATTERS)
+ end
+
+ def story_formatters
+ @format_options ||= [['plain', @output_stream]]
+ @formatters ||= load_formatters(@format_options, STORY_FORMATTERS)
+ end
+
+ def load_formatters(format_options, formatters)
+ format_options.map do |format, where|
+ formatter_type = if formatters[format]
+ require formatters[format][0]
+ eval(formatters[format][1], binding, __FILE__, __LINE__)
+ else
+ load_class(format, 'formatter', '--format')
+ end
+ formatter_type.new(self, where)
+ end
+ end
+
+ def load_heckle_runner(heckle)
+ suffix = [/mswin/, /java/].detect{|p| p =~ RUBY_PLATFORM} ? '_unsupported' : ''
+ require "spec/runner/heckle_runner#{suffix}"
+ @heckle_runner = HeckleRunner.new(heckle)
+ end
+
+ def number_of_examples
+ @example_groups.inject(0) do |sum, example_group|
+ sum + example_group.number_of_examples
+ end
+ end
+
+ protected
+ def examples_should_be_run?
+ return @examples_should_be_run unless @examples_should_be_run.nil?
+ @examples_should_be_run = true
+ end
+
+ def differ_class=(klass)
+ return unless klass
+ @differ_class = klass
+ Spec::Expectations.differ = self.differ_class.new(self)
+ end
+
+ def load_class(name, kind, option)
+ if name =~ /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/
+ arg = $2 == "" ? nil : $2
+ [$1, arg]
+ else
+ m = "#{name.inspect} is not a valid class name"
+ @error_stream.puts m
+ raise m
+ end
+ begin
+ eval(name, binding, __FILE__, __LINE__)
+ rescue NameError => e
+ @error_stream.puts "Couldn't find #{kind} class #{name}"
+ @error_stream.puts "Make sure the --require option is specified *before* #{option}"
+ if $_spec_spec ; raise e ; else exit(1) ; end
+ end
+ end
+
+ def files_to_load
+ result = []
+ sorted_files.each do |file|
+ if test ?d, file
+ result += Dir[File.expand_path("#{file}/**/*.rb")]
+ elsif test ?f, file
+ result << file
+ else
+ raise "File or directory not found: #{file}"
+ end
+ end
+ result
+ end
+
+ def custom_runner
+ return nil unless custom_runner?
+ klass_name, arg = ClassAndArgumentsParser.parse(user_input_for_runner)
+ runner_type = load_class(klass_name, 'behaviour runner', '--runner')
+ return runner_type.new(self, arg)
+ end
+
+ def custom_runner?
+ return user_input_for_runner ? true : false
+ end
+
+ def heckle
+ returns = self.heckle_runner.heckle_with
+ self.heckle_runner = nil
+ returns
+ end
+
+ def sorted_files
+ return sorter ? files.sort(&sorter) : files
+ end
+
+ def sorter
+ FILE_SORTERS[loadby]
+ end
+
+ def default_differ
+ require 'spec/expectations/differs/default'
+ self.differ_class = Spec::Expectations::Differs::Default
+ end
+ end
+ end
+end