rspec 0.5.3 → 0.5.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. data/CHANGES +57 -32
  2. data/EXAMPLES.rd +0 -0
  3. data/Rakefile +22 -21
  4. data/bin/spec +9 -11
  5. data/doc/README +1 -3
  6. data/doc/plugin/syntax.rb +27 -5
  7. data/doc/src/core_team.page +22 -0
  8. data/doc/src/default.css +11 -11
  9. data/doc/src/default.template +0 -1
  10. data/doc/src/documentation/index.page +183 -8
  11. data/doc/src/documentation/meta.info +7 -7
  12. data/doc/src/documentation/mocks.page +168 -109
  13. data/doc/src/documentation/underscores.page +20 -0
  14. data/doc/src/examples.page +2 -1
  15. data/doc/src/images/David_and_Aslak.jpg +0 -0
  16. data/doc/src/images/Whats_That_Dude.jpg +0 -0
  17. data/doc/src/index.page +70 -3
  18. data/doc/src/meta.info +18 -11
  19. data/doc/src/tools/index.page +40 -134
  20. data/doc/src/tools/meta.info +9 -3
  21. data/doc/src/tools/rails.page +3 -1
  22. data/doc/src/tools/rake.page +20 -3
  23. data/doc/src/tools/rcov.page +19 -0
  24. data/doc/src/tools/spec.page +99 -0
  25. data/doc/src/tools/test2rspec.page +2 -4
  26. data/doc/src/tutorials/index.page +52 -0
  27. data/doc/src/tutorials/meta.info +31 -0
  28. data/doc/src/tutorials/notes.txt +252 -0
  29. data/doc/src/tutorials/stack.rb +11 -0
  30. data/doc/src/tutorials/stack_01.page +224 -0
  31. data/doc/src/tutorials/stack_02.page +180 -0
  32. data/doc/src/tutorials/stack_03.page +291 -0
  33. data/doc/src/tutorials/stack_04.page +203 -0
  34. data/doc/src/tutorials/stack_04.page.orig +123 -0
  35. data/doc/src/tutorials/stack_05.page +90 -0
  36. data/doc/src/tutorials/stack_05.page.orig +124 -0
  37. data/doc/src/tutorials/stack_06.page +359 -0
  38. data/doc/src/tutorials/stack_06.page.orig +359 -0
  39. data/doc/src/tutorials/stack_spec.rb +41 -0
  40. data/examples/airport_spec.rb +4 -4
  41. data/examples/{spec_framework_spec.rb → bdd_framework_spec.rb} +6 -7
  42. data/examples/mocking_spec.rb +0 -5
  43. data/examples/stack_spec.rb +6 -7
  44. data/examples/sugar_spec.rb +14 -0
  45. data/lib/spec/api.rb +5 -2
  46. data/lib/spec/api/helper/should_base.rb +17 -22
  47. data/lib/spec/api/helper/should_helper.rb +4 -3
  48. data/lib/spec/api/helper/should_negator.rb +3 -2
  49. data/lib/spec/api/mocks/argument_expectation.rb +104 -0
  50. data/lib/spec/api/{mock.rb → mocks/message_expectation.rb} +47 -96
  51. data/lib/spec/api/mocks/mock.rb +63 -0
  52. data/lib/spec/api/mocks/order_group.rb +21 -0
  53. data/lib/spec/api/sugar.rb +47 -0
  54. data/lib/spec/rake/rcov_verify.rb +45 -0
  55. data/lib/spec/rake/spectask.rb +41 -56
  56. data/lib/spec/runner.rb +4 -1
  57. data/lib/spec/runner/backtrace_tweaker.rb +24 -3
  58. data/lib/spec/runner/base_text_formatter.rb +28 -0
  59. data/lib/spec/runner/context.rb +21 -18
  60. data/lib/spec/runner/context_runner.rb +20 -31
  61. data/lib/spec/runner/execution_context.rb +3 -3
  62. data/lib/spec/runner/kernel_ext.rb +10 -1
  63. data/lib/spec/runner/option_parser.rb +32 -14
  64. data/lib/spec/runner/progress_bar_formatter.rb +21 -0
  65. data/lib/spec/runner/rdoc_formatter.rb +15 -5
  66. data/lib/spec/runner/reporter.rb +100 -0
  67. data/lib/spec/runner/specdoc_formatter.rb +20 -0
  68. data/lib/spec/runner/specification.rb +42 -22
  69. data/lib/spec/version.rb +1 -1
  70. data/test/rcov/rcov_testtask.rb +1 -0
  71. data/test/spec/api/duck_type_test.rb +4 -4
  72. data/test/spec/api/helper/raising_test.rb +37 -17
  73. data/test/spec/api/{mock_arg_constraints_test.rb → mocks/mock_arg_constraints_test.rb} +10 -4
  74. data/test/spec/api/mocks/mock_ordering_test.rb +62 -0
  75. data/test/spec/api/{mock_test.rb → mocks/mock_test.rb} +30 -7
  76. data/test/spec/api/mocks/null_object_test.rb +31 -0
  77. data/test/spec/api/sugar_test.rb +71 -0
  78. data/test/spec/runner/backtrace_tweaker_test.rb +52 -4
  79. data/test/spec/runner/context_runner_test.rb +41 -21
  80. data/test/spec/runner/context_test.rb +60 -32
  81. data/test/spec/runner/execution_context_test.rb +4 -3
  82. data/test/spec/runner/failure_dump_test.rb +92 -0
  83. data/test/spec/runner/kernel_ext_test.rb +1 -2
  84. data/test/spec/runner/option_parser_test.rb +48 -28
  85. data/test/spec/runner/progress_bar_formatter_test.rb +48 -0
  86. data/test/spec/runner/rdoc_formatter_test.rb +31 -4
  87. data/test/spec/runner/reporter_test.rb +103 -0
  88. data/test/spec/runner/specdoc_formatter_test.rb +50 -0
  89. data/test/spec/runner/specification_test.rb +49 -11
  90. data/test/test_helper.rb +1 -4
  91. metadata +46 -15
  92. data/doc/src/community.page +0 -7
  93. data/doc/src/documentation/api.page +0 -185
  94. data/doc/src/why_rspec.page +0 -7
  95. data/examples/empty_stack_spec.rb +0 -22
  96. data/examples/team_spec.rb +0 -30
  97. data/lib/spec/api/duck_type.rb +0 -16
  98. data/lib/spec/runner/simple_text_reporter.rb +0 -88
  99. data/test/rcov/rcov_verify.rb +0 -28
  100. data/test/spec/runner/simple_text_reporter_test.rb +0 -123
@@ -0,0 +1,63 @@
1
+ module Spec
2
+ module Api
3
+ class Mock
4
+ # Remove all methods so they can be mocked too
5
+ (public_instance_methods - ['__id__', '__send__', 'nil?']).each do |m|
6
+ undef_method m
7
+ end
8
+
9
+ # Creates a new mock with a +name+ (that will be used in error messages only)
10
+ # Options:
11
+ # * <tt>:null_object</tt> - if true, the mock object acts as a forgiving null object allowing any message to be sent to it.
12
+ def initialize(name, options={})
13
+ @name = name
14
+ @options = DEFAULT_OPTIONS.dup.merge(options)
15
+ @expectations = []
16
+ @expectation_ordering = OrderGroup.new
17
+ end
18
+
19
+ def should
20
+ self
21
+ end
22
+
23
+ def receive(sym, &block)
24
+ expected_from = caller(1)[0]
25
+ expectation = MessageExpectation.new(@name, @expectation_ordering, expected_from, sym, block_given? ? block : nil)
26
+ @expectations << expectation
27
+ expectation
28
+ end
29
+
30
+ def __verify #:nodoc:
31
+ @expectations.each do |expectation|
32
+ expectation.verify_messages_received
33
+ end
34
+ end
35
+
36
+ def method_missing(sym, *args, &block)
37
+ if expectation = find_matching_expectation(sym, *args)
38
+ expectation.verify_message(args, block)
39
+ else
40
+ begin
41
+ # act as null object if method is missing and we ignore them. return value too!
42
+ @options[:null_object] ? self : super(sym, *args, &block)
43
+ rescue NoMethodError
44
+ arg_message = args.collect{|arg| "<#{arg}:#{arg.class.name}>"}.join(", ")
45
+ Kernel::raise Spec::Api::MockExpectationError, "Mock '#{@name}' received unexpected message '#{sym}' with [#{arg_message}]"
46
+ end
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ DEFAULT_OPTIONS = {
53
+ :null_object => false
54
+ }
55
+
56
+ def find_matching_expectation(sym, *args)
57
+ expectation = @expectations.find {|expectation| expectation.matches(sym, args)}
58
+ end
59
+
60
+ end
61
+
62
+ end
63
+ end
@@ -0,0 +1,21 @@
1
+ module Spec
2
+ module Api
3
+ class OrderGroup
4
+ def initialize
5
+ @ordering = Array.new
6
+ end
7
+
8
+ def register(expectation)
9
+ @ordering << expectation
10
+ end
11
+
12
+ def ready_for?(expectation)
13
+ return @ordering.first == expectation
14
+ end
15
+
16
+ def consume(expectation)
17
+ @ordering.shift
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,47 @@
1
+ module Spec
2
+ module Api
3
+ # This module adds syntactic sugar that allows usage of should_* instead of should.*
4
+ module Sugar
5
+ alias_method :__orig_method_missing, :method_missing
6
+ def method_missing(sym, *args, &block)
7
+ if __is_sweetened? sym
8
+ object = self
9
+ calls = sym.to_s.split("_")
10
+ while calls.length > 1
11
+ call = calls.shift
12
+ object = object.__send__(call)
13
+ break if call == "be"
14
+ end
15
+ return object.__send__(calls.join("_"), *args, &block)
16
+ end
17
+ __orig_method_missing(sym, *args, &block)
18
+ end
19
+
20
+ def __is_sweetened? sym
21
+ return true if sym.to_s =~ /^should_/
22
+ end
23
+ end
24
+
25
+ module MessageExpectationSugar
26
+ def __is_sweetened? sym
27
+ return true if sym.to_s =~ /^and_|^at_|^any_|^once_/
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ class Object #:nodoc:
34
+ include Spec::Api::Sugar
35
+ end
36
+
37
+ class Spec::Api::Mock #:nodoc:
38
+ #NOTE: this resolves a bug caused by a conflict between Sugar#method_missing and Mock#method_missing, specifically
39
+ # when the mock is set null_object=>true. It would be nice to get rid of this.
40
+ def should_receive(sym, &block)
41
+ return receive(sym, &block)
42
+ end
43
+ end
44
+
45
+ class Spec::Api::MessageExpectation #:nodoc:
46
+ include Spec::Api::MessageExpectationSugar
47
+ end
@@ -0,0 +1,45 @@
1
+ module RCov
2
+ # A task that can verify that the RCov coverage doesn't
3
+ # drop below a certain threshold.
4
+ class VerifyTask < Rake::TaskLib
5
+ # Name of the task. Defaults to :rcov_verify
6
+ attr_accessor :name
7
+
8
+ # Path to the index.html file generated by RCov, which
9
+ # is the file containing the total coverage.
10
+ # Defaults to 'coverage/index.html'
11
+ attr_accessor :index_html
12
+
13
+ # Whether or not to output details
14
+ attr_accessor :verbose
15
+
16
+ # The threshold value (in percent) for coverage. If the
17
+ # actual coverage is below this value, the task will raise an
18
+ # exception
19
+ attr_accessor :threshold
20
+
21
+ def initialize(name=:rcov_verify)
22
+ @name = name
23
+ @index_html = 'coverage/index.html'
24
+ @verbose = false
25
+ yield self if block_given?
26
+ raise "Threshold must be set" if @threshold.nil?
27
+ define
28
+ end
29
+
30
+ def define
31
+ desc "Verify that rcov coverage is at least #{threshold}%"
32
+ task @name do
33
+ total_coverage = nil
34
+ File.open(index_html).each_line do |line|
35
+ if line =~ /<tt>(\d+\.\d+)%<\/tt>&nbsp;<\/td>/
36
+ total_coverage = eval($1)
37
+ break
38
+ end
39
+ end
40
+ puts "Coverage: #{total_coverage}% (threshold: #{threshold}%)" if verbose
41
+ raise "Coverage must be at least #{threshold}% but was #{total_coverage}%" if total_coverage < threshold
42
+ end
43
+ end
44
+ end
45
+ end
@@ -9,33 +9,15 @@ require File.dirname(__FILE__) + '/../../spec'
9
9
  module Spec
10
10
  module Rake
11
11
 
12
- # Create a task that runs a set of RSpec contexts.
12
+ # A task that runs a set of RSpec contexts.
13
13
  #
14
14
  # Example:
15
15
  #
16
16
  # Rake::SpecTask.new do |t|
17
17
  # t.libs << "spec"
18
18
  # t.spec_files = FileList['spec/**/*_spec.rb']
19
- # t.verbose = true
20
19
  # end
21
20
  #
22
- # If rake is invoked with a "SPEC=filename" command line option,
23
- # then the list of spec files will be overridden to include only the
24
- # filename specified on the command line. This provides an easy way
25
- # to run just one spec.
26
- #
27
- # If rake is invoked with a "SPECOPTS=options" command line option,
28
- # then the given options are passed to the spec process after a
29
- # '--'. This allows Test::Unit options to be passed to the spec
30
- # suite.
31
- #
32
- # Examples:
33
- #
34
- # rake spec # run specs normally
35
- # rake spec SPEC=just_one_file.rb # run just one spec file.
36
- # rake spec SPECOPTS="-v" # run in verbose mode
37
- # rake spec SPECOPTS="--runner=fox" # use the fox spec runner
38
- #
39
21
  class SpecTask < ::Rake::TaskLib
40
22
 
41
23
  # Name of spec task. (default is :spec)
@@ -45,8 +27,8 @@ module Rake
45
27
  # specs. (default is 'lib')
46
28
  attr_accessor :libs
47
29
 
48
- # True if verbose spec output desired. (default is false)
49
- attr_accessor :verbose
30
+ # Options poassed to spec
31
+ attr_accessor :spec_opts
50
32
 
51
33
  # Test options passed to the spec suite. An explicit
52
34
  # SPECOPTS=opts on the command line will override this. (default
@@ -60,15 +42,14 @@ module Rake
60
42
  # Glob pattern to match spec files. (default is 'spec/spec*.rb')
61
43
  attr_accessor :pattern
62
44
 
63
- # Style of spec loader to use. Options are:
64
- #
65
- # * :rake -- Rake provided spec loading script (default).
66
- # * :specrb -- Ruby provided spec loading script.
67
- # * :direct -- Load specs using command line loader.
68
- #
69
- attr_accessor :loader
45
+ # Whether or not to use rcov (default is false)
46
+ # See http://eigenclass.org/hiki.rb?rcov
47
+ attr_accessor :rcov
70
48
 
71
- # Array of commandline options to pass to ruby when running spec loader.
49
+ # Where output is written. Default is STDOUT.
50
+ attr_accessor :out
51
+
52
+ # Array of commandline options to pass to ruby (or rcov) when running specs.
72
53
  attr_accessor :ruby_opts
73
54
 
74
55
  # Explicitly define the list of spec files to be included in a
@@ -86,10 +67,11 @@ module Rake
86
67
  @pattern = nil
87
68
  @options = nil
88
69
  @spec_files = nil
89
- @verbose = false
70
+ @spec_opts = []
90
71
  @warning = false
91
- @loader = :rake
72
+ @rcov = false
92
73
  @ruby_opts = []
74
+ @out = nil
93
75
  yield self if block_given?
94
76
  @pattern = 'spec/**/*_spec.rb' if @pattern.nil? && @spec_files.nil?
95
77
  define
@@ -100,30 +82,24 @@ module Rake
100
82
  lib_path = @libs.join(File::PATH_SEPARATOR)
101
83
  desc "Run specs" + (@name==:spec ? "" : " for #{@name}")
102
84
  task @name do
103
- run_code =
104
- case @loader
105
- when :direct
106
- "-e 'ARGV.each{|f| load f}'"
107
- when :rake
108
- rake_loader
109
- end
110
-
111
- RakeFileUtils.verbose(@verbose) do
112
- @ruby_opts.unshift( "-I#{lib_path}" )
113
- @ruby_opts.unshift( "-w" ) if @warning
114
- ruby @ruby_opts.join(" ") +
115
- " \"#{run_code}\" " +
116
- file_list.collect { |fn| "\"#{fn}\"" }.join(' ') +
117
- " #{option_list}"
118
- end
85
+ spec = File.dirname(__FILE__) + '/../../../bin/spec'
86
+ file_prefix = @rcov ? " -- " : ""
87
+ interpreter = @rcov ? "rcov" : "ruby"
88
+ redirect = @out.nil? ? "" : " > #{@out}"
89
+
90
+ @ruby_opts.unshift( "-I#{lib_path}" )
91
+ @ruby_opts.unshift( "-w" ) if @warning
92
+ @ruby_opts.unshift( '--exclude "lib\/spec\/.*"' ) if @rcov
93
+ run interpreter, @ruby_opts.join(" ") +
94
+ " \"#{spec}\" " +
95
+ " #{@spec_opts.join(' ')} " +
96
+ file_prefix +
97
+ file_list.collect { |fn| "\"#{fn}\"" }.join(' ') +
98
+ redirect
119
99
  end
120
100
  self
121
101
  end
122
102
 
123
- def option_list # :nodoc:
124
- ENV['SPECOPTS'] || @options || ""
125
- end
126
-
127
103
  def file_list # :nodoc:
128
104
  if ENV['SPEC']
129
105
  FileList[ ENV['SPEC'] ]
@@ -135,11 +111,6 @@ module Rake
135
111
  end
136
112
  end
137
113
 
138
- def rake_loader # :nodoc:
139
- find_file('rake/rake_test_loader') or
140
- fail "unable to find rake test loader"
141
- end
142
-
143
114
  def find_file(fn) # :nodoc:
144
115
  $LOAD_PATH.each do |path|
145
116
  file_path = File.join(path, "#{fn}.rb")
@@ -147,6 +118,20 @@ module Rake
147
118
  end
148
119
  nil
149
120
  end
121
+
122
+ def run(interpreter, *args, &block)
123
+ if Hash === args.last
124
+ options = args.pop
125
+ else
126
+ options = {}
127
+ end
128
+ if args.length > 1 then
129
+ sh(*([interpreter] + args + [options]), &block)
130
+ else
131
+ sh("#{interpreter} #{args}", options, &block)
132
+ end
133
+ end
134
+
150
135
  end
151
136
  end
152
137
  end
@@ -5,5 +5,8 @@ require 'spec/runner/execution_context'
5
5
  require 'spec/runner/context_runner'
6
6
  require 'spec/runner/option_parser'
7
7
  require 'spec/runner/backtrace_tweaker'
8
+ require 'spec/runner/reporter'
9
+ require 'spec/runner/base_text_formatter'
10
+ require 'spec/runner/progress_bar_formatter'
8
11
  require 'spec/runner/rdoc_formatter'
9
- require 'spec/runner/simple_text_reporter'
12
+ require 'spec/runner/specdoc_formatter'
@@ -1,11 +1,32 @@
1
1
  module Spec
2
2
  module Runner
3
3
  class BacktraceTweaker
4
- def tweak_backtrace error, spec_name
4
+ def tweak_instance_exec_line line, spec_name
5
+ line = line.split(':in')[0] + ":in `#{spec_name}'" if line.include?('__instance_exec')
6
+ line
7
+ end
8
+ end
9
+
10
+ # Tweaks raised Exceptions to mask noisy (unneeded) parts of the backtrace
11
+ class NoisyBacktraceTweaker < BacktraceTweaker
12
+ def tweak_backtrace(error, spec_name)
13
+ return if error.backtrace.nil?
14
+ error.backtrace.collect! do |line|
15
+ tweak_instance_exec_line line, spec_name
16
+ end
17
+ error.backtrace.compact!
18
+ end
19
+ end
20
+
21
+ # Tweaks raised Exceptions to mask noisy (unneeded) parts of the backtrace
22
+ class QuietBacktraceTweaker < BacktraceTweaker
23
+ def tweak_backtrace(error, spec_name)
5
24
  return if error.backtrace.nil?
6
25
  error.backtrace.collect! do |line|
7
- line = line.split(':in')[0] + ":in `#{spec_name}'" if line.include?('__instance_exec')
8
- line = nil if line =~ /\/lib\/spec\/api\/helper\/.+[helper|base|negator].rb/
26
+ line = tweak_instance_exec_line line, spec_name
27
+ line = nil if line =~ /\/lib\/spec\/api\//
28
+ line = nil if line =~ /\/lib\/spec\/runner\//
29
+ line = nil if line =~ /bin\/spec:/
9
30
  line
10
31
  end
11
32
  error.backtrace.compact!
@@ -0,0 +1,28 @@
1
+ module Spec
2
+ module Runner
3
+ class BaseTextFormatter
4
+ def initialize(output, dry_run=false)
5
+ @dry_run = dry_run
6
+ @output = output
7
+ end
8
+
9
+ def dump_failure(counter, failure)
10
+ @output << "\n"
11
+ @output << counter.to_s << ")\n"
12
+ @output << "#{failure.header}\n"
13
+ @output << "#{failure.message}\n"
14
+ @output << "#{failure.backtrace}\n"
15
+ end
16
+
17
+ def dump_summary(duration, context_count, spec_count, failure_count)
18
+ return if @dry_run
19
+ @output << "\n"
20
+ @output << "Finished in " << (duration).to_s << " seconds\n\n"
21
+ @output << "#{context_count} context#{'s' unless context_count == 1}, "
22
+ @output << "#{spec_count} specification#{'s' unless spec_count == 1}, "
23
+ @output << "#{failure_count} failure#{'s' unless failure_count == 1}"
24
+ @output << "\n"
25
+ end
26
+ end
27
+ end
28
+ end
@@ -1,33 +1,18 @@
1
1
  module Spec
2
2
  module Runner
3
3
  class Context
4
- @@context_runner = nil
5
-
6
- def self.context_runner= runner
7
- @@context_runner = runner
8
- end
9
-
10
4
  def initialize(name, &context_block)
11
5
  @setup_block = nil
12
6
  @teardown_block = nil
13
7
  @specifications = []
14
8
  @name = name
15
9
  instance_exec(&context_block)
16
- ContextRunner.standalone(self) if @@context_runner.nil?
17
- @@context_runner.add_context(self) unless @@context_runner.nil?
18
10
  end
19
11
 
20
- def run(reporter)
21
- reporter.add_context(@name)
22
- @specifications.each do |specification|
23
- specification.run(reporter, @setup_block, @teardown_block)
24
- end
25
- end
26
-
27
- def run_docs(reporter)
12
+ def run(reporter, dry_run=false)
28
13
  reporter.add_context(@name)
29
14
  @specifications.each do |specification|
30
- specification.run_docs(reporter)
15
+ specification.run(reporter, @setup_block, @teardown_block, dry_run)
31
16
  end
32
17
  end
33
18
 
@@ -42,6 +27,24 @@ module Spec
42
27
  def specify(spec_name, &block)
43
28
  @specifications << Specification.new(spec_name, &block)
44
29
  end
30
+
31
+ def number_of_specs
32
+ @specifications.length
33
+ end
34
+
35
+ def matches? name
36
+ return false unless name =~ /^#{@name} /
37
+ @specifications.each do |spec|
38
+ return true if spec.matches? name[(@name.length + 1)..-1]
39
+ end
40
+ return false
41
+ end
42
+
43
+ def isolate name
44
+ @specifications.reject! do |spec|
45
+ !spec.matches? name[(@name.length + 1)..-1]
46
+ end
47
+ end
45
48
  end
46
49
  end
47
- end
50
+ end