Updated to latest rspec
[gitorious:georgyos-clone.git] / vendor / plugins / rspec / lib / spec / runner / option_parser.rb
1 require 'optparse'
2 require 'stringio'
3
4 module Spec
5   module Runner
6     class OptionParser < ::OptionParser
7       class << self
8         def parse(args, err, out)
9           parser = new(err, out)
10           parser.parse(args)
11           parser.options
12         end
13       end
14
15       attr_reader :options
16
17       OPTIONS = {
18         :pattern => ["-p", "--pattern [PATTERN]","Limit files loaded to those matching this pattern. Defaults to '**/*_spec.rb'",
19                                                  "Separate multiple patterns with commas.",
20                                                  "Applies only to directories named on the command line (files",
21                                                  "named explicitly on the command line will be loaded regardless)."],
22         :diff =>    ["-D", "--diff [FORMAT]","Show diff of objects that are expected to be equal when they are not",
23                                              "Builtin formats: unified|u|context|c",
24                                              "You can also specify a custom differ class",
25                                              "(in which case you should also specify --require)"],
26         :colour =>  ["-c", "--colour", "--color", "Show coloured (red/green) output"],
27         :example => ["-e", "--example [NAME|FILE_NAME]",  "Execute example(s) with matching name(s). If the argument is",
28                                                           "the path to an existing file (typically generated by a previous",
29                                                           "run using --format failing_examples:file.txt), then the examples",
30                                                           "on each line of thatfile will be executed. If the file is empty,",
31                                                           "all examples will be run (as if --example was not specified).",
32                                                           " ",
33                                                           "If the argument is not an existing file, then it is treated as",
34                                                           "an example name directly, causing RSpec to run just the example",
35                                                           "matching that name"],
36         :specification => ["-s", "--specification [NAME]", "DEPRECATED - use -e instead", "(This will be removed when autotest works with -e)"],
37         :line => ["-l", "--line LINE_NUMBER", Integer, "Execute behaviout or specification at given line.",
38                                                        "(does not work for dynamically generated specs)"],
39         :format => ["-f", "--format FORMAT[:WHERE]","Specifies what format to use for output. Specify WHERE to tell",
40                                                     "the formatter where to write the output. All built-in formats",
41                                                     "expect WHERE to be a file name, and will write to STDOUT if it's",
42                                                     "not specified. The --format option may be specified several times",
43                                                     "if you want several outputs",
44                                                     " ",
45                                                     "Builtin formats for examples: ",
46                                                     "progress|p               : Text progress",
47                                                     "profile|o                : Text progress with profiling of 10 slowest examples",
48                                                     "specdoc|s                : Example doc as text",
49                                                     "indented|i               : Example doc as indented text",
50                                                     "html|h                   : A nice HTML report",
51                                                     "failing_examples|e       : Write all failing examples - input for --example",
52                                                     "failing_example_groups|g : Write all failing example groups - input for --example",
53                                                     " ",
54                                                     "Builtin formats for stories: ",
55                                                     "plain|p              : Plain Text",
56                                                     "html|h               : A nice HTML report",
57                                                     " ",
58                                                     "FORMAT can also be the name of a custom formatter class",
59                                                     "(in which case you should also specify --require to load it)"],
60         :require => ["-r", "--require FILE", "Require FILE before running specs",
61                                              "Useful for loading custom formatters or other extensions.",
62                                              "If this option is used it must come before the others"],
63         :backtrace => ["-b", "--backtrace", "Output full backtrace"],
64         :loadby => ["-L", "--loadby STRATEGY", "Specify the strategy by which spec files should be loaded.",
65                                                "STRATEGY can currently only be 'mtime' (File modification time)",
66                                                "By default, spec files are loaded in alphabetical order if --loadby",
67                                                "is not specified."],
68         :reverse => ["-R", "--reverse", "Run examples in reverse order"],
69         :timeout => ["-t", "--timeout FLOAT", "Interrupt and fail each example that doesn't complete in the",
70                                               "specified time"],
71         :heckle => ["-H", "--heckle CODE", "If all examples pass, this will mutate the classes and methods",
72                                            "identified by CODE little by little and run all the examples again",
73                                            "for each mutation. The intent is that for each mutation, at least",
74                                            "one example *should* fail, and RSpec will tell you if this is not the",
75                                            "case. CODE should be either Some::Module, Some::Class or",
76                                            "Some::Fabulous#method}"],
77         :dry_run => ["-d", "--dry-run", "Invokes formatters without executing the examples."],
78         :options_file => ["-O", "--options PATH", "Read options from a file"],
79         :generate_options => ["-G", "--generate-options PATH", "Generate an options file for --options"],
80         :runner => ["-U", "--runner RUNNER", "Use a custom Runner."],
81         :drb => ["-X", "--drb", "Run examples via DRb. (For example against script/spec_server)"],
82         :version => ["-v", "--version", "Show version"],
83         :help => ["-h", "--help", "You're looking at it"]
84       }
85
86       def initialize(err, out)
87         super()
88         @error_stream = err
89         @out_stream = out
90         @options = Options.new(@error_stream, @out_stream)
91
92         @file_factory = File
93
94         self.banner = "Usage: spec (FILE|DIRECTORY|GLOB)+ [options]"
95         self.separator ""
96         on(*OPTIONS[:pattern]) {|pattern| @options.filename_pattern = pattern}
97         on(*OPTIONS[:diff]) {|diff| @options.parse_diff(diff)}
98         on(*OPTIONS[:colour]) {@options.colour = true}
99         on(*OPTIONS[:example]) {|example| @options.parse_example(example)}
100         on(*OPTIONS[:specification]) {|example| @options.parse_example(example)}
101         on(*OPTIONS[:line]) {|line_number| @options.line_number = line_number.to_i}
102         on(*OPTIONS[:format]) {|format| @options.parse_format(format)}
103         on(*OPTIONS[:require]) {|requires| invoke_requires(requires)}
104         on(*OPTIONS[:backtrace]) {@options.backtrace_tweaker = NoisyBacktraceTweaker.new}
105         on(*OPTIONS[:loadby]) {|loadby| @options.loadby = loadby}
106         on(*OPTIONS[:reverse]) {@options.reverse = true}
107         on(*OPTIONS[:timeout]) {|timeout| @options.timeout = timeout.to_f}
108         on(*OPTIONS[:heckle]) {|heckle| @options.load_heckle_runner(heckle)}
109         on(*OPTIONS[:dry_run]) {@options.dry_run = true}
110         on(*OPTIONS[:options_file]) {|options_file| parse_options_file(options_file)}
111         on(*OPTIONS[:generate_options]) {|options_file|}
112         on(*OPTIONS[:runner]) {|runner|  @options.user_input_for_runner = runner}
113         on(*OPTIONS[:drb]) {}
114         on(*OPTIONS[:version]) {parse_version}
115         on_tail(*OPTIONS[:help]) {parse_help}
116       end
117
118       def order!(argv, &blk)
119         @argv = argv
120         @options.argv = @argv.dup
121         return if parse_generate_options
122         return if parse_drb
123         
124         super(@argv) do |file|
125           @options.files << file
126           blk.call(file) if blk
127         end
128
129         @options
130       end
131
132       protected
133       def invoke_requires(requires)
134         requires.split(",").each do |file|
135           require file
136         end
137       end
138       
139       def parse_options_file(options_file)
140         option_file_args = IO.readlines(options_file).map {|l| l.chomp.split " "}.flatten
141         @argv.push(*option_file_args)
142         # TODO - this is a brute force solution to http://rspec.lighthouseapp.com/projects/5645/tickets/293.
143         # Let's look for a cleaner way. Might not be one. But let's look. If not, perhaps
144         # this can be moved to a different method to indicate the special handling for drb?
145         parse_drb(@argv)
146       end
147
148       def parse_generate_options
149         # Remove the --generate-options option and the argument before writing to file
150         options_file = nil
151         ['-G', '--generate-options'].each do |option|
152           if index = @argv.index(option)
153             @argv.delete_at(index)
154             options_file = @argv.delete_at(index)
155           end
156         end
157         
158         if options_file
159           write_generated_options(options_file)
160           return true
161         else
162           return false
163         end
164       end
165       
166       def write_generated_options(options_file)
167         File.open(options_file, 'w') do |io|
168           io.puts @argv.join("\n")
169         end
170         @out_stream.puts "\nOptions written to #{options_file}. You can now use these options with:"
171         @out_stream.puts "spec --options #{options_file}"
172         @options.examples_should_not_be_run
173       end
174
175       def parse_drb(argv = nil)
176         argv ||= @options.argv # TODO - see note about about http://rspec.lighthouseapp.com/projects/5645/tickets/293
177         is_drb = false
178         is_drb ||= argv.delete(OPTIONS[:drb][0])
179         is_drb ||= argv.delete(OPTIONS[:drb][1])
180         return false unless is_drb
181         @options.examples_should_not_be_run
182         DrbCommandLine.run(
183           self.class.parse(argv, @error_stream, @out_stream)
184         )
185         true
186       end
187
188       def parse_version
189         @out_stream.puts ::Spec::VERSION::DESCRIPTION
190         exit if stdout?
191       end
192
193       def parse_help
194         @out_stream.puts self
195         exit if stdout?
196       end      
197
198       def stdout?
199         @out_stream == $stdout
200       end
201     end
202   end
203 end