rspec-expectations 2.13.0 → 3.0.0

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 (142) hide show
  1. checksums.yaml +15 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data/Changelog.md +413 -23
  4. data/License.txt +1 -0
  5. data/README.md +109 -80
  6. data/lib/rspec/{matchers → expectations}/configuration.rb +44 -14
  7. data/lib/rspec/expectations/expectation_target.rb +71 -38
  8. data/lib/rspec/expectations/fail_with.rb +12 -44
  9. data/lib/rspec/expectations/handler.rb +135 -35
  10. data/lib/rspec/expectations/minitest_integration.rb +16 -0
  11. data/lib/rspec/expectations/syntax.rb +60 -94
  12. data/lib/rspec/expectations/version.rb +1 -1
  13. data/lib/rspec/expectations.rb +46 -24
  14. data/lib/rspec/matchers/aliased_matcher.rb +41 -0
  15. data/lib/rspec/matchers/built_in/all.rb +78 -0
  16. data/lib/rspec/matchers/built_in/base_matcher.rb +63 -15
  17. data/lib/rspec/matchers/built_in/be.rb +170 -79
  18. data/lib/rspec/matchers/built_in/be_between.rb +77 -0
  19. data/lib/rspec/matchers/built_in/be_instance_of.rb +11 -3
  20. data/lib/rspec/matchers/built_in/be_kind_of.rb +5 -0
  21. data/lib/rspec/matchers/built_in/be_within.rb +40 -16
  22. data/lib/rspec/matchers/built_in/change.rb +275 -74
  23. data/lib/rspec/matchers/built_in/compound.rb +122 -0
  24. data/lib/rspec/matchers/built_in/contain_exactly.rb +256 -0
  25. data/lib/rspec/matchers/built_in/cover.rb +3 -0
  26. data/lib/rspec/matchers/built_in/eq.rb +60 -6
  27. data/lib/rspec/matchers/built_in/eql.rb +17 -5
  28. data/lib/rspec/matchers/built_in/equal.rb +55 -21
  29. data/lib/rspec/matchers/built_in/exist.rb +74 -10
  30. data/lib/rspec/matchers/built_in/has.rb +71 -15
  31. data/lib/rspec/matchers/built_in/include.rb +58 -29
  32. data/lib/rspec/matchers/built_in/match.rb +19 -1
  33. data/lib/rspec/matchers/built_in/operators.rb +119 -0
  34. data/lib/rspec/matchers/built_in/output.rb +150 -0
  35. data/lib/rspec/matchers/built_in/raise_error.rb +91 -33
  36. data/lib/rspec/matchers/built_in/respond_to.rb +42 -20
  37. data/lib/rspec/matchers/built_in/satisfy.rb +19 -3
  38. data/lib/rspec/matchers/built_in/start_and_end_with.rb +54 -18
  39. data/lib/rspec/matchers/built_in/throw_symbol.rb +38 -8
  40. data/lib/rspec/matchers/built_in/yield.rb +237 -55
  41. data/lib/rspec/matchers/built_in.rb +45 -33
  42. data/lib/rspec/matchers/composable.rb +179 -0
  43. data/lib/rspec/matchers/dsl.rb +362 -5
  44. data/lib/rspec/matchers/generated_descriptions.rb +13 -6
  45. data/lib/rspec/matchers/matcher_delegator.rb +37 -0
  46. data/lib/rspec/matchers/pretty.rb +33 -20
  47. data/lib/rspec/matchers.rb +450 -227
  48. data.tar.gz.sig +1 -0
  49. metadata +100 -215
  50. metadata.gz.sig +0 -0
  51. data/features/README.md +0 -49
  52. data/features/Upgrade.md +0 -53
  53. data/features/built_in_matchers/README.md +0 -90
  54. data/features/built_in_matchers/be.feature +0 -175
  55. data/features/built_in_matchers/be_within.feature +0 -48
  56. data/features/built_in_matchers/cover.feature +0 -47
  57. data/features/built_in_matchers/end_with.feature +0 -48
  58. data/features/built_in_matchers/equality.feature +0 -139
  59. data/features/built_in_matchers/exist.feature +0 -45
  60. data/features/built_in_matchers/expect_change.feature +0 -59
  61. data/features/built_in_matchers/expect_error.feature +0 -144
  62. data/features/built_in_matchers/have.feature +0 -109
  63. data/features/built_in_matchers/include.feature +0 -174
  64. data/features/built_in_matchers/match.feature +0 -52
  65. data/features/built_in_matchers/operators.feature +0 -227
  66. data/features/built_in_matchers/predicates.feature +0 -137
  67. data/features/built_in_matchers/respond_to.feature +0 -84
  68. data/features/built_in_matchers/satisfy.feature +0 -33
  69. data/features/built_in_matchers/start_with.feature +0 -48
  70. data/features/built_in_matchers/throw_symbol.feature +0 -91
  71. data/features/built_in_matchers/types.feature +0 -116
  72. data/features/built_in_matchers/yield.feature +0 -146
  73. data/features/custom_matchers/access_running_example.feature +0 -53
  74. data/features/custom_matchers/define_diffable_matcher.feature +0 -27
  75. data/features/custom_matchers/define_matcher.feature +0 -368
  76. data/features/custom_matchers/define_matcher_outside_rspec.feature +0 -38
  77. data/features/custom_matchers/define_matcher_with_fluent_interface.feature +0 -24
  78. data/features/customized_message.feature +0 -22
  79. data/features/diffing.feature +0 -85
  80. data/features/implicit_docstrings.feature +0 -52
  81. data/features/step_definitions/additional_cli_steps.rb +0 -22
  82. data/features/support/env.rb +0 -5
  83. data/features/syntax_configuration.feature +0 -68
  84. data/features/test_frameworks/test_unit.feature +0 -46
  85. data/lib/rspec/expectations/deprecation.rb +0 -38
  86. data/lib/rspec/expectations/differ.rb +0 -115
  87. data/lib/rspec/expectations/errors.rb +0 -9
  88. data/lib/rspec/expectations/extensions/array.rb +0 -9
  89. data/lib/rspec/expectations/extensions/object.rb +0 -39
  90. data/lib/rspec/expectations/extensions.rb +0 -2
  91. data/lib/rspec/matchers/be_close.rb +0 -9
  92. data/lib/rspec/matchers/built_in/have.rb +0 -108
  93. data/lib/rspec/matchers/built_in/match_array.rb +0 -51
  94. data/lib/rspec/matchers/compatibility.rb +0 -14
  95. data/lib/rspec/matchers/extensions/instance_eval_with_args.rb +0 -39
  96. data/lib/rspec/matchers/matcher.rb +0 -300
  97. data/lib/rspec/matchers/method_missing.rb +0 -12
  98. data/lib/rspec/matchers/operator_matcher.rb +0 -109
  99. data/lib/rspec-expectations.rb +0 -1
  100. data/spec/rspec/expectations/differ_spec.rb +0 -170
  101. data/spec/rspec/expectations/expectation_target_spec.rb +0 -75
  102. data/spec/rspec/expectations/extensions/kernel_spec.rb +0 -67
  103. data/spec/rspec/expectations/fail_with_spec.rb +0 -95
  104. data/spec/rspec/expectations/handler_spec.rb +0 -206
  105. data/spec/rspec/expectations/syntax_spec.rb +0 -97
  106. data/spec/rspec/matchers/base_matcher_spec.rb +0 -62
  107. data/spec/rspec/matchers/be_close_spec.rb +0 -22
  108. data/spec/rspec/matchers/be_instance_of_spec.rb +0 -63
  109. data/spec/rspec/matchers/be_kind_of_spec.rb +0 -41
  110. data/spec/rspec/matchers/be_spec.rb +0 -516
  111. data/spec/rspec/matchers/be_within_spec.rb +0 -129
  112. data/spec/rspec/matchers/change_spec.rb +0 -542
  113. data/spec/rspec/matchers/configuration_spec.rb +0 -238
  114. data/spec/rspec/matchers/cover_spec.rb +0 -69
  115. data/spec/rspec/matchers/description_generation_spec.rb +0 -176
  116. data/spec/rspec/matchers/dsl_spec.rb +0 -57
  117. data/spec/rspec/matchers/eq_spec.rb +0 -60
  118. data/spec/rspec/matchers/eql_spec.rb +0 -41
  119. data/spec/rspec/matchers/equal_spec.rb +0 -78
  120. data/spec/rspec/matchers/exist_spec.rb +0 -124
  121. data/spec/rspec/matchers/has_spec.rb +0 -122
  122. data/spec/rspec/matchers/have_spec.rb +0 -445
  123. data/spec/rspec/matchers/include_matcher_integration_spec.rb +0 -30
  124. data/spec/rspec/matchers/include_spec.rb +0 -531
  125. data/spec/rspec/matchers/match_array_spec.rb +0 -194
  126. data/spec/rspec/matchers/match_spec.rb +0 -61
  127. data/spec/rspec/matchers/matcher_spec.rb +0 -471
  128. data/spec/rspec/matchers/matchers_spec.rb +0 -37
  129. data/spec/rspec/matchers/method_missing_spec.rb +0 -28
  130. data/spec/rspec/matchers/operator_matcher_spec.rb +0 -223
  131. data/spec/rspec/matchers/raise_error_spec.rb +0 -395
  132. data/spec/rspec/matchers/respond_to_spec.rb +0 -292
  133. data/spec/rspec/matchers/satisfy_spec.rb +0 -44
  134. data/spec/rspec/matchers/start_with_end_with_spec.rb +0 -186
  135. data/spec/rspec/matchers/throw_symbol_spec.rb +0 -116
  136. data/spec/rspec/matchers/yield_spec.rb +0 -437
  137. data/spec/spec_helper.rb +0 -54
  138. data/spec/support/classes.rb +0 -56
  139. data/spec/support/in_sub_process.rb +0 -38
  140. data/spec/support/matchers.rb +0 -22
  141. data/spec/support/ruby_version.rb +0 -10
  142. data/spec/support/shared_examples.rb +0 -13
@@ -1,36 +1,84 @@
1
1
  module RSpec
2
2
  module Matchers
3
3
  module BuiltIn
4
+ # @api private
5
+ # Provides the implementation for `has_<predicate>`.
6
+ # Not intended to be instantiated directly.
4
7
  class Has
5
- def initialize(expected, *args)
6
- @expected, @args = expected, args
8
+ include Composable
9
+
10
+ def initialize(method_name, *args, &block)
11
+ @method_name, @args, @block = method_name, args, block
12
+ end
13
+
14
+ # @private
15
+ def matches?(actual, &block)
16
+ @actual = actual
17
+ @block ||= block
18
+ predicate_accessible? && predicate_matches?
7
19
  end
8
20
 
9
- def matches?(actual)
10
- actual.__send__(predicate(@expected), *@args)
21
+ # @private
22
+ def does_not_match?(actual, &block)
23
+ @actual = actual
24
+ @block ||= block
25
+ predicate_accessible? && !predicate_matches?
11
26
  end
12
- alias == matches?
13
27
 
14
- def failure_message_for_should
15
- "expected ##{predicate(@expected)}#{failure_message_args_description} to return true, got false"
28
+ # @api private
29
+ # @return [String]
30
+ def failure_message
31
+ validity_message || "expected ##{predicate}#{failure_message_args_description} to return true, got false"
16
32
  end
17
33
 
18
- def failure_message_for_should_not
19
- "expected ##{predicate(@expected)}#{failure_message_args_description} to return false, got true"
34
+ # @api private
35
+ # @return [String]
36
+ def failure_message_when_negated
37
+ validity_message || "expected ##{predicate}#{failure_message_args_description} to return false, got true"
20
38
  end
21
39
 
40
+ # @api private
41
+ # @return [String]
22
42
  def description
23
- [method_description(@expected), args_description].compact.join(' ')
43
+ [method_description, args_description].compact.join(' ')
44
+ end
45
+
46
+ # @private
47
+ def supports_block_expectations?
48
+ false
24
49
  end
25
50
 
26
- private
51
+ private
52
+
53
+ def predicate_accessible?
54
+ !private_predicate? && predicate_exists?
55
+ end
27
56
 
28
- def predicate(sym)
29
- "#{sym.to_s.sub("have_","has_")}?".to_sym
57
+ # support 1.8.7, evaluate once at load time for performance
58
+ if String === methods.first
59
+ def private_predicate?
60
+ @actual.private_methods.include? predicate.to_s
61
+ end
62
+ else
63
+ def private_predicate?
64
+ @actual.private_methods.include? predicate
65
+ end
30
66
  end
31
67
 
32
- def method_description(method)
33
- method.to_s.gsub('_', ' ')
68
+ def predicate_exists?
69
+ @actual.respond_to? predicate
70
+ end
71
+
72
+ def predicate_matches?
73
+ @actual.__send__(predicate, *@args, &@block)
74
+ end
75
+
76
+ def predicate
77
+ @predicate ||= :"has_#{@method_name.to_s.match(Matchers::HAS_REGEX).captures.first}?"
78
+ end
79
+
80
+ def method_description
81
+ @method_name.to_s.gsub('_', ' ')
34
82
  end
35
83
 
36
84
  def args_description
@@ -42,6 +90,14 @@ module RSpec
42
90
  desc = args_description
43
91
  "(#{desc})" if desc
44
92
  end
93
+
94
+ def validity_message
95
+ if private_predicate?
96
+ "expected #{@actual} to respond to `#{predicate}` but `#{predicate}` is a private method"
97
+ elsif !predicate_exists?
98
+ "expected #{@actual} to respond to `#{predicate}`"
99
+ end
100
+ end
45
101
  end
46
102
  end
47
103
  end
@@ -1,67 +1,96 @@
1
1
  module RSpec
2
2
  module Matchers
3
3
  module BuiltIn
4
+ # @api private
5
+ # Provides the implementation for `include`.
6
+ # Not intended to be instantiated directly.
4
7
  class Include < BaseMatcher
5
8
  def initialize(*expected)
6
9
  @expected = expected
7
10
  end
8
11
 
12
+ # @api private
13
+ # @return [Boolean]
9
14
  def matches?(actual)
10
15
  @actual = actual
11
- perform_match(:all?, :all?, @actual, @expected)
16
+ perform_match(:all?, :all?)
12
17
  end
13
18
 
19
+ # @api private
20
+ # @return [Boolean]
14
21
  def does_not_match?(actual)
15
22
  @actual = actual
16
- perform_match(:none?, :any?, @actual, @expected)
23
+ perform_match(:none?, :any?)
17
24
  end
18
25
 
26
+ # @api private
27
+ # @return [String]
19
28
  def description
20
- "include#{expected_to_sentence}"
29
+ described_items = surface_descriptions_in(expected)
30
+ improve_hash_formatting "include#{to_sentence(described_items)}"
21
31
  end
22
32
 
33
+ # @api private
34
+ # @return [String]
35
+ def failure_message
36
+ improve_hash_formatting(super)
37
+ end
38
+
39
+ # @api private
40
+ # @return [String]
41
+ def failure_message_when_negated
42
+ improve_hash_formatting(super)
43
+ end
44
+
45
+ # @api private
46
+ # @return [Boolean]
23
47
  def diffable?
24
- # Matchers do not diff well, since diff uses their inspect
25
- # output, which includes their instance variables and such.
26
- @expected.none? { |e| is_a_matcher?(e) }
48
+ true
27
49
  end
28
50
 
29
- private
51
+ private
30
52
 
31
- def perform_match(predicate, hash_predicate, actuals, expecteds)
32
- expecteds.__send__(predicate) do |expected|
33
- if comparing_hash_values?(actuals, expected)
34
- expected.__send__(hash_predicate) { |k,v|
35
- actuals.has_key?(k) && actuals[k] == v
36
- }
37
- elsif comparing_hash_keys?(actuals, expected)
38
- actuals.has_key?(expected)
39
- elsif comparing_with_matcher?(actual, expected)
40
- actual.any? { |value| expected.matches?(value) }
53
+ def perform_match(predicate, hash_subset_predicate)
54
+ expected.__send__(predicate) do |expected_item|
55
+ if comparing_hash_to_a_subset?(expected_item)
56
+ expected_item.__send__(hash_subset_predicate) do |(key, value)|
57
+ actual_hash_includes?(key, value)
58
+ end
59
+ elsif comparing_hash_keys?(expected_item)
60
+ actual_hash_has_key?(expected_item)
41
61
  else
42
- actuals.include?(expected)
62
+ actual_collection_includes?(expected_item)
43
63
  end
44
64
  end
45
65
  end
46
66
 
47
- def comparing_hash_keys?(actual, expected)
48
- actual.is_a?(Hash) && !expected.is_a?(Hash)
67
+ def comparing_hash_to_a_subset?(expected_item)
68
+ actual.is_a?(Hash) && expected_item.is_a?(Hash)
49
69
  end
50
70
 
51
- def comparing_hash_values?(actual, expected)
52
- actual.is_a?(Hash) && expected.is_a?(Hash)
71
+ def actual_hash_includes?(expected_key, expected_value)
72
+ actual_value = actual.fetch(expected_key) { return false }
73
+ values_match?(expected_value, actual_value)
53
74
  end
54
75
 
55
- def comparing_with_matcher?(actual, expected)
56
- actual.is_a?(Array) && is_a_matcher?(expected)
76
+ def comparing_hash_keys?(expected_item)
77
+ actual.is_a?(Hash) && !expected_item.is_a?(Hash)
57
78
  end
58
79
 
59
- def is_a_matcher?(object)
60
- return false if object.respond_to?(:i_respond_to_everything_so_im_not_really_a_matcher)
80
+ def actual_hash_has_key?(expected_key)
81
+ # We check `has_key?` first for perf:
82
+ # has_key? is O(1), but `any?` is O(N).
83
+ actual.has_key?(expected_key) ||
84
+ actual.keys.any? { |key| values_match?(expected_key, key) }
85
+ end
86
+
87
+ def actual_collection_includes?(expected_item)
88
+ return true if actual.include?(expected_item)
89
+
90
+ # String lacks an `any?` method...
91
+ return false unless actual.respond_to?(:any?)
61
92
 
62
- [:failure_message_for_should, :failure_message].any? do |msg|
63
- object.respond_to?(msg)
64
- end && object.respond_to?(:matches?)
93
+ actual.any? { |value| values_match?(expected_item, value) }
65
94
  end
66
95
  end
67
96
  end
@@ -1,10 +1,28 @@
1
1
  module RSpec
2
2
  module Matchers
3
3
  module BuiltIn
4
+ # @api private
5
+ # Provides the implementation for `match`.
6
+ # Not intended to be instantiated directly.
4
7
  class Match < BaseMatcher
5
8
 
9
+ # @api private
10
+ # @return [String]
11
+ def description
12
+ "match #{surface_descriptions_in(expected).inspect}"
13
+ end
14
+
15
+ # @api private
16
+ # @return [Boolean]
17
+ def diffable?
18
+ true
19
+ end
20
+
21
+ private
22
+
6
23
  def match(expected, actual)
7
- actual.match expected
24
+ return true if values_match?(expected, actual)
25
+ actual.match(expected) if actual.respond_to?(:match)
8
26
  end
9
27
  end
10
28
  end
@@ -0,0 +1,119 @@
1
+ require 'rspec/support'
2
+
3
+ module RSpec
4
+ module Matchers
5
+ module BuiltIn
6
+ # @api private
7
+ # Provides the implementation for operator matchers.
8
+ # Not intended to be instantiated directly.
9
+ # Only available for use with `should`.
10
+ class OperatorMatcher
11
+ class << self
12
+ # @private
13
+ def registry
14
+ @registry ||= {}
15
+ end
16
+
17
+ # @private
18
+ def register(klass, operator, matcher)
19
+ registry[klass] ||= {}
20
+ registry[klass][operator] = matcher
21
+ end
22
+
23
+ # @private
24
+ def unregister(klass, operator)
25
+ registry[klass] && registry[klass].delete(operator)
26
+ end
27
+
28
+ # @private
29
+ def get(klass, operator)
30
+ klass.ancestors.each { |ancestor|
31
+ matcher = registry[ancestor] && registry[ancestor][operator]
32
+ return matcher if matcher
33
+ }
34
+
35
+ nil
36
+ end
37
+ end
38
+
39
+ register Enumerable, '=~', BuiltIn::ContainExactly
40
+
41
+ def initialize(actual)
42
+ @actual = actual
43
+ end
44
+
45
+ # @private
46
+ def self.use_custom_matcher_or_delegate(operator)
47
+ define_method(operator) do |expected|
48
+ if uses_generic_implementation_of?(operator) && matcher = OperatorMatcher.get(@actual.class, operator)
49
+ @actual.__send__(::RSpec::Matchers.last_expectation_handler.should_method, matcher.new(expected))
50
+ else
51
+ eval_match(@actual, operator, expected)
52
+ end
53
+ end
54
+
55
+ negative_operator = operator.sub(/^=/, '!')
56
+ if negative_operator != operator && respond_to?(negative_operator)
57
+ define_method(negative_operator) do |expected|
58
+ opposite_should = ::RSpec::Matchers.last_expectation_handler.opposite_should_method
59
+ raise "RSpec does not support `#{::RSpec::Matchers.last_expectation_handler.should_method} #{negative_operator} expected`. " +
60
+ "Use `#{opposite_should} #{operator} expected` instead."
61
+ end
62
+ end
63
+ end
64
+
65
+ ['==', '===', '=~', '>', '>=', '<', '<='].each do |operator|
66
+ use_custom_matcher_or_delegate operator
67
+ end
68
+
69
+ # @private
70
+ def fail_with_message(message)
71
+ RSpec::Expectations.fail_with(message, @expected, @actual)
72
+ end
73
+
74
+ # @api private
75
+ # @return [String]
76
+ def description
77
+ "#{@operator} #{@expected.inspect}"
78
+ end
79
+
80
+ private
81
+
82
+ def uses_generic_implementation_of?(op)
83
+ Support.method_handle_for(@actual, op).owner == ::Kernel
84
+ rescue NameError
85
+ false
86
+ end
87
+
88
+ def eval_match(actual, operator, expected)
89
+ ::RSpec::Matchers.last_matcher = self
90
+ @operator, @expected = operator, expected
91
+ __delegate_operator(actual, operator, expected)
92
+ end
93
+ end
94
+
95
+ # @private
96
+ # Handles operator matcher for `should`.
97
+ class PositiveOperatorMatcher < OperatorMatcher
98
+ def __delegate_operator(actual, operator, expected)
99
+ if actual.__send__(operator, expected)
100
+ true
101
+ elsif ['==','===', '=~'].include?(operator)
102
+ fail_with_message("expected: #{expected.inspect}\n got: #{actual.inspect} (using #{operator})")
103
+ else
104
+ fail_with_message("expected: #{operator} #{expected.inspect}\n got: #{operator.gsub(/./, ' ')} #{actual.inspect}")
105
+ end
106
+ end
107
+ end
108
+
109
+ # @private
110
+ # Handles operator matcher for `should_not`.
111
+ class NegativeOperatorMatcher < OperatorMatcher
112
+ def __delegate_operator(actual, operator, expected)
113
+ return false unless actual.__send__(operator, expected)
114
+ return fail_with_message("expected not: #{operator} #{expected.inspect}\n got: #{operator.gsub(/./, ' ')} #{actual.inspect}")
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,150 @@
1
+ require 'stringio'
2
+
3
+ module RSpec
4
+ module Matchers
5
+ module BuiltIn
6
+ # @api private
7
+ # Provides the implementation for `output`.
8
+ # Not intended to be instantiated directly.
9
+ class Output < BaseMatcher
10
+ def initialize(expected)
11
+ @expected = expected
12
+ @stream_capturer = NullCapture
13
+ end
14
+
15
+ def matches?(block)
16
+ @block = block
17
+ return false unless Proc === block
18
+ @actual = @stream_capturer.capture(block)
19
+ @expected ? values_match?(@expected, @actual) : captured?
20
+ end
21
+
22
+ def does_not_match?(block)
23
+ !matches?(block) && Proc === block
24
+ end
25
+
26
+ # @api public
27
+ # Tells the matcher to match against stdout.
28
+ def to_stdout
29
+ @stream_capturer = CaptureStdout
30
+ self
31
+ end
32
+
33
+ # @api public
34
+ # Tells the matcher to match against stderr.
35
+ def to_stderr
36
+ @stream_capturer = CaptureStderr
37
+ self
38
+ end
39
+
40
+ # @api private
41
+ # @return [String]
42
+ def failure_message
43
+ "expected block to #{description}, but #{positive_failure_reason}"
44
+ end
45
+
46
+ # @api private
47
+ # @return [String]
48
+ def failure_message_when_negated
49
+ "expected block to not #{description}, but #{negative_failure_reason}"
50
+ end
51
+
52
+ # @api private
53
+ # @return [String]
54
+ def description
55
+ if @expected
56
+ "output #{description_of @expected} to #{@stream_capturer.name}"
57
+ else
58
+ "output to #{@stream_capturer.name}"
59
+ end
60
+ end
61
+
62
+ # @api private
63
+ # @return [Boolean]
64
+ def diffable?
65
+ true
66
+ end
67
+
68
+ # @api private
69
+ # Indicates this matcher matches against a block.
70
+ # @return [True]
71
+ def supports_block_expectations?
72
+ true
73
+ end
74
+
75
+ private
76
+
77
+ def captured?
78
+ @actual.length > 0
79
+ end
80
+
81
+ def positive_failure_reason
82
+ return "was not a block" unless Proc === @block
83
+ return "output #{actual_output_description}" if @expected
84
+ "did not"
85
+ end
86
+
87
+ def negative_failure_reason
88
+ return "was not a block" unless Proc === @block
89
+ "output #{actual_output_description}"
90
+ end
91
+
92
+ def actual_output_description
93
+ return "nothing" unless captured?
94
+ @actual.inspect
95
+ end
96
+ end
97
+
98
+ # @private
99
+ module NullCapture
100
+ def self.name
101
+ "some stream"
102
+ end
103
+
104
+ def self.capture(block)
105
+ raise "You must chain `to_stdout` or `to_stderr` off of the `output(...)` matcher."
106
+ end
107
+ end
108
+
109
+ # @private
110
+ module CaptureStdout
111
+ def self.name
112
+ 'stdout'
113
+ end
114
+
115
+ def self.capture(block)
116
+ captured_stream = StringIO.new
117
+
118
+ original_stream = $stdout
119
+ $stdout = captured_stream
120
+
121
+ block.call
122
+
123
+ captured_stream.string
124
+ ensure
125
+ $stdout = original_stream
126
+ end
127
+ end
128
+
129
+ # @private
130
+ module CaptureStderr
131
+ def self.name
132
+ 'stderr'
133
+ end
134
+
135
+ def self.capture(block)
136
+ captured_stream = StringIO.new
137
+
138
+ original_stream = $stderr
139
+ $stderr = captured_stream
140
+
141
+ block.call
142
+
143
+ captured_stream.string
144
+ ensure
145
+ $stderr = original_stream
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end