rspec 0.5.3 → 0.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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