clarion-given 1.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.
- checksums.yaml +7 -0
- data/lib/given/assertions.rb +71 -0
- data/lib/given/binary_operation.rb +34 -0
- data/lib/given/core.rb +11 -0
- data/lib/given/evaluator.rb +52 -0
- data/lib/given/ext/numeric.rb +31 -0
- data/lib/given/extensions.rb +277 -0
- data/lib/given/failure.rb +80 -0
- data/lib/given/failure_matcher.rb +100 -0
- data/lib/given/file_cache.rb +18 -0
- data/lib/given/fuzzy_number.rb +68 -0
- data/lib/given/fuzzy_shortcuts.rb +1 -0
- data/lib/given/line_extractor.rb +57 -0
- data/lib/given/minitest/all.rb +12 -0
- data/lib/given/minitest/before_extension.rb +36 -0
- data/lib/given/minitest/configure.rb +7 -0
- data/lib/given/minitest/context_extension.rb +3 -0
- data/lib/given/minitest/failure_must_raise.rb +13 -0
- data/lib/given/minitest/framework.rb +37 -0
- data/lib/given/minitest/new_assertions.rb +22 -0
- data/lib/given/module_methods.rb +83 -0
- data/lib/given/natural_assertion.rb +191 -0
- data/lib/given/rspec/all.rb +29 -0
- data/lib/given/rspec/before_extensions.rb +15 -0
- data/lib/given/rspec/configure.rb +20 -0
- data/lib/given/rspec/framework.rb +46 -0
- data/lib/given/rspec/have_failed.rb +37 -0
- data/lib/given/rspec/have_failed_212.rb +39 -0
- data/lib/given/rspec/have_failed_pre212.rb +22 -0
- data/lib/given/rspec/monkey.rb +42 -0
- data/lib/given/rspec/use_natural_assertions.rb +10 -0
- data/lib/given/version.rb +9 -0
- data/lib/given.rb +2 -0
- data/lib/minitest/given.rb +1 -0
- data/lib/minitest-given.rb +9 -0
- data/lib/rspec/given.rb +1 -0
- data/lib/rspec-given.rb +9 -0
- metadata +96 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 3840491d2082a82d4fdcf8c2006cb192b754f7264096259f2133defd6360ef0e
|
|
4
|
+
data.tar.gz: 0c487c45718dd66c3b68033d676174358a97d971d9b413bbcb30240d3d3a0be1
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: a1d23a9499b1856f62e7b9510b8eba10412dac9f4ebfa646ceee331d0a0852b925329a06e190a6361c8296f47e4d2f81c9273986e5cdec82909da6c8c89fbe73
|
|
7
|
+
data.tar.gz: 6cba9eba9739126417bd1513572399131525989b7b26d1b5ad8af1db73a07398ee93f5fdb1d56e8b092354f98f9f86fd4784e3a2a5d9a893b4c2e879f6ee3095
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
require 'given/natural_assertion'
|
|
2
|
+
require 'given/line_extractor'
|
|
3
|
+
|
|
4
|
+
module Given
|
|
5
|
+
module Assertions
|
|
6
|
+
|
|
7
|
+
def self.enable_asserts(enabled=true)
|
|
8
|
+
@enable_asserts = enabled
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.enable_preconditions(enabled=true)
|
|
12
|
+
@enable_preconditions = enabled
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.enable_postconditions(enabled=true)
|
|
16
|
+
@enable_postconditions = enabled
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.asserts?
|
|
20
|
+
@enable_asserts
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.preconditions?
|
|
24
|
+
@enable_preconditions
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.postconditions?
|
|
28
|
+
@enable_postconditions
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.enable_all(enabled=true)
|
|
32
|
+
enable_asserts enabled
|
|
33
|
+
enable_preconditions enabled
|
|
34
|
+
enable_postconditions enabled
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
enable_all
|
|
38
|
+
|
|
39
|
+
AssertError = Class.new(StandardError)
|
|
40
|
+
PreconditionError = Class.new(AssertError)
|
|
41
|
+
PostconditionError = Class.new(AssertError)
|
|
42
|
+
|
|
43
|
+
def Assert(&block)
|
|
44
|
+
return nil unless Given::Assertions.asserts?
|
|
45
|
+
unless block.call
|
|
46
|
+
na = Given::NaturalAssertion.new("Assert", block, self, Given::LineExtractor.new)
|
|
47
|
+
raise AssertError, na.message
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def Precondition(&block)
|
|
52
|
+
return nil unless Given::Assertions.preconditions?
|
|
53
|
+
if block.call
|
|
54
|
+
true
|
|
55
|
+
else
|
|
56
|
+
na = Given::NaturalAssertion.new("Precondition", block, self, Given::LineExtractor.new)
|
|
57
|
+
raise PreconditionError, na.message
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def Postcondition(&block)
|
|
62
|
+
return nil unless Given::Assertions.preconditions?
|
|
63
|
+
if block.call
|
|
64
|
+
true
|
|
65
|
+
else
|
|
66
|
+
na = Given::NaturalAssertion.new("Postcondition", block, self, Given::LineExtractor.new)
|
|
67
|
+
raise PostconditionError, na.message
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module Given
|
|
2
|
+
|
|
3
|
+
class BinaryOperation
|
|
4
|
+
BINARY_EXPLAINATIONS = {
|
|
5
|
+
:== => "to equal",
|
|
6
|
+
:!= => "to not equal",
|
|
7
|
+
:< => "to be less than",
|
|
8
|
+
:<= => "to be less or equal to",
|
|
9
|
+
:> => "to be greater than",
|
|
10
|
+
:>= => "to be greater or equal to",
|
|
11
|
+
:=~ => "to match",
|
|
12
|
+
:!~ => "to not match",
|
|
13
|
+
:=== => "to be matched by",
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
attr_reader :left, :operator, :right
|
|
17
|
+
|
|
18
|
+
def initialize(left, operator, right)
|
|
19
|
+
@left = left
|
|
20
|
+
@operator = operator
|
|
21
|
+
@right = right
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def explain
|
|
25
|
+
BINARY_EXPLAINATIONS[operator]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.parse(sexp)
|
|
29
|
+
return nil unless sexp.first == :binary
|
|
30
|
+
new(sexp[1], sexp[2], sexp[3])
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
end
|
data/lib/given/core.rb
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
|
|
2
|
+
# Require all the core features
|
|
3
|
+
|
|
4
|
+
require 'given/version'
|
|
5
|
+
require 'given/module_methods'
|
|
6
|
+
require 'given/file_cache'
|
|
7
|
+
require 'given/line_extractor'
|
|
8
|
+
require 'given/extensions'
|
|
9
|
+
require 'given/fuzzy_number'
|
|
10
|
+
require 'given/failure'
|
|
11
|
+
require 'given/failure_matcher'
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
|
|
2
|
+
module Given
|
|
3
|
+
class EvalErr
|
|
4
|
+
def initialize(str)
|
|
5
|
+
@string = str
|
|
6
|
+
end
|
|
7
|
+
def size
|
|
8
|
+
inspect.size
|
|
9
|
+
end
|
|
10
|
+
def to_s
|
|
11
|
+
@string
|
|
12
|
+
end
|
|
13
|
+
def inspect
|
|
14
|
+
@string
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class Evaluator
|
|
19
|
+
def initialize(example, block)
|
|
20
|
+
@example = example
|
|
21
|
+
@block = block
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def eval_string(exp_string)
|
|
25
|
+
limit_length(eval_in_context(exp_string).inspect)
|
|
26
|
+
rescue StandardError => ex
|
|
27
|
+
EvalErr.new("#{ex.class}: #{ex.message}")
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def location
|
|
31
|
+
Given.location_of(@block)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
MAX_INSPECT_SIZE = 2000
|
|
37
|
+
|
|
38
|
+
def limit_length(string)
|
|
39
|
+
if string.size > MAX_INSPECT_SIZE
|
|
40
|
+
string[0..MAX_INSPECT_SIZE] + " (...truncated...)"
|
|
41
|
+
else
|
|
42
|
+
string
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def eval_in_context(exp_string)
|
|
47
|
+
exp_proc = "proc { #{exp_string} }"
|
|
48
|
+
blk = eval(exp_proc, @block.binding)
|
|
49
|
+
@example.instance_eval(&blk)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
module Given
|
|
4
|
+
module Ext
|
|
5
|
+
module Numeric
|
|
6
|
+
|
|
7
|
+
def ±(del=nil)
|
|
8
|
+
result = Given::Fuzzy::FuzzyNumber.new(self)
|
|
9
|
+
result.delta(del) if del
|
|
10
|
+
result
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def ‰(percentage=nil)
|
|
14
|
+
result = Given::Fuzzy::FuzzyNumber.new(self)
|
|
15
|
+
result.percent(percentage) if percentage
|
|
16
|
+
result
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def €(neps=nil)
|
|
20
|
+
result = Given::Fuzzy::FuzzyNumber.new(self)
|
|
21
|
+
result.epsilon(neps) if neps
|
|
22
|
+
result
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
class Numeric
|
|
30
|
+
include Given::Ext::Numeric
|
|
31
|
+
end
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
require 'given/module_methods'
|
|
2
|
+
require 'given/natural_assertion'
|
|
3
|
+
require 'given/failure'
|
|
4
|
+
|
|
5
|
+
module Given
|
|
6
|
+
|
|
7
|
+
# Provide run-time instance methods to support Given infrastructure.
|
|
8
|
+
# All the methods in this module are considered private and
|
|
9
|
+
# implementation-specific, and should not be directly called by the
|
|
10
|
+
# application developer.
|
|
11
|
+
#
|
|
12
|
+
# By convention, these private instance specific methods are
|
|
13
|
+
# prefixed with _gvn_ to avoid name collisions with application
|
|
14
|
+
# methods defined in a spec.
|
|
15
|
+
#
|
|
16
|
+
# (Note that private class methods are prefixed with _Gvn_ and
|
|
17
|
+
# private instance methods are prefixed with _gvn_).
|
|
18
|
+
#
|
|
19
|
+
module InstanceExtensions # :nodoc:
|
|
20
|
+
|
|
21
|
+
# List of containing contexts in order from innermost to
|
|
22
|
+
# outermost.
|
|
23
|
+
def _gvn_inner_contexts # :nodoc:
|
|
24
|
+
self.class.ancestors.select { |context|
|
|
25
|
+
context.respond_to?(:_Gvn_givens)
|
|
26
|
+
}
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# List of containing contexts in order from outermost to
|
|
30
|
+
# innermost.
|
|
31
|
+
def _gvn_contexts # :nodoc:
|
|
32
|
+
_gvn_inner_contexts.reverse
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Return the context information for keyword from the innermost
|
|
36
|
+
# defining context.
|
|
37
|
+
def _gvn_info(keyword) # :nodoc:
|
|
38
|
+
_gvn_inner_contexts.each do |context|
|
|
39
|
+
h = context._Gvn_context_info
|
|
40
|
+
if h.has_key?(keyword)
|
|
41
|
+
return h[keyword]
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
nil
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Should a natural assertion failure message be generated?
|
|
48
|
+
#
|
|
49
|
+
# A natural assertion failure message is generated if the
|
|
50
|
+
# assertion has non-empty content. The configuration options for
|
|
51
|
+
# natural assertions are checked and applied accordingly.
|
|
52
|
+
#
|
|
53
|
+
def _gvn_need_na_message?(nassert) # :nodoc:
|
|
54
|
+
return false unless nassert.has_content?
|
|
55
|
+
_gvn_na_configured?
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Return the configuration value for natural assertions.
|
|
59
|
+
#
|
|
60
|
+
# If natural assertions are not configured in the contexts, use
|
|
61
|
+
# the global configuration value.
|
|
62
|
+
def _gvn_na_configured? # :nodoc:
|
|
63
|
+
info_value = _gvn_info(:natural_assertions_enabled)
|
|
64
|
+
info_value.nil? ? Given.natural_assertions_enabled? : info_value
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Establish all the Given preconditions the current and
|
|
68
|
+
# surrounding describe/context blocks, starting with the
|
|
69
|
+
# outermost context.
|
|
70
|
+
def _gvn_establish_givens # :nodoc:
|
|
71
|
+
return if defined?(@_gvn_ran) && @_gvn_ran
|
|
72
|
+
@_gvn_ran = true
|
|
73
|
+
_gvn_contexts.each do |context|
|
|
74
|
+
context._Gvn_givens.each do |block|
|
|
75
|
+
instance_eval(&block)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Check all the invariants in the current and surrounding
|
|
81
|
+
# describe/context blocks, starting with the outermost context.
|
|
82
|
+
def _gvn_check_invariants # :nodoc:
|
|
83
|
+
_gvn_contexts.each do |context|
|
|
84
|
+
context._Gvn_invariants.each do |block|
|
|
85
|
+
_gvn_evaluate("Invariant", block)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def _gvn_check_ands # :nodoc:
|
|
91
|
+
return if self.class._Gvn_context_info[:and_ran]
|
|
92
|
+
self.class._Gvn_and_blocks.each do |block|
|
|
93
|
+
_gvn_evaluate("And", block)
|
|
94
|
+
end
|
|
95
|
+
self.class._Gvn_context_info[:and_ran] = true
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Implement the run-time semantics of the Then clause.
|
|
99
|
+
def _gvn_then(&block) # :nodoc:
|
|
100
|
+
_gvn_establish_givens
|
|
101
|
+
_gvn_check_invariants
|
|
102
|
+
_gvn_evaluate("Then", block)
|
|
103
|
+
_gvn_check_ands
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Determine of the natural assertion pass/fail status of the block
|
|
107
|
+
def _gvn_block_passed?(block) # :nodoc:
|
|
108
|
+
passed = instance_eval(&block)
|
|
109
|
+
passed = passed.to_bool if passed.respond_to?(:to_bool)
|
|
110
|
+
passed
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Evaluate a Then, And, or Invariant assertion.
|
|
114
|
+
def _gvn_evaluate(clause_type, block) # :nodoc:
|
|
115
|
+
Given.start_evaluation
|
|
116
|
+
passed = _gvn_block_passed?(block)
|
|
117
|
+
if ! Given.explicit_assertions? && _gvn_na_configured?
|
|
118
|
+
_gvn_naturally_assert(clause_type, block, passed)
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Naturally assert the block (based on +passed+).
|
|
123
|
+
def _gvn_naturally_assert(clause_type, block, passed)
|
|
124
|
+
Given.count_assertion
|
|
125
|
+
unless passed
|
|
126
|
+
nassert = NaturalAssertion.new(clause_type, block, self, self.class._Gvn_lines)
|
|
127
|
+
Given.fail_with nassert.message if _gvn_need_na_message?(nassert)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Provide run-time class methods to support Given infrastructure.
|
|
133
|
+
# Methods that begin with _Gvn_ are considered private and
|
|
134
|
+
# implementation specific, and should not be directly called by
|
|
135
|
+
# appliation code. Other methods without the _Gvn_ prefix are public
|
|
136
|
+
# and intended for use by the application developer.
|
|
137
|
+
#
|
|
138
|
+
# (Note that private class methods are prefixed with _Gvn_ and
|
|
139
|
+
# private instance methods are prefixed with _gvn_).
|
|
140
|
+
#
|
|
141
|
+
module ClassExtensions
|
|
142
|
+
|
|
143
|
+
# List of all givens directly in the current describe/context
|
|
144
|
+
# block.
|
|
145
|
+
def _Gvn_givens # :nodoc:
|
|
146
|
+
@_Gvn_givens ||= []
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# List of all invariants directly in the current
|
|
150
|
+
# describe/context block.
|
|
151
|
+
def _Gvn_invariants # :nodoc:
|
|
152
|
+
@_Gvn_invariants ||= []
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# List of the and blocks directly in the current describe/context
|
|
156
|
+
# block.
|
|
157
|
+
def _Gvn_and_blocks # :nodoc:
|
|
158
|
+
@_Gvn_and_blocks ||= []
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Context information ofr the current describe/context block.
|
|
162
|
+
def _Gvn_context_info # :nodoc:
|
|
163
|
+
@_Gvn_context_info ||= {}
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Line extractor for the context.
|
|
167
|
+
def _Gvn_lines # :nodoc:
|
|
168
|
+
@_Gvn_lines ||= LineExtractor.new
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# Trigger the evaluation of a Given! block by referencing its
|
|
172
|
+
# name.
|
|
173
|
+
def _Gvn_trigger_given(name) # :nodoc:
|
|
174
|
+
Proc.new { send(name) }
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# Declare a "given" of the current specification. If the given
|
|
178
|
+
# is named, the block will be lazily evaluated the first time
|
|
179
|
+
# the given is mentioned by name in the specification. If the
|
|
180
|
+
# given is unnamed, the block is evaluated for side effects
|
|
181
|
+
# every time the specification is executed.
|
|
182
|
+
#
|
|
183
|
+
# :call-seq:
|
|
184
|
+
# Given(:name) { ... code ... }
|
|
185
|
+
# Given { ... code ... }
|
|
186
|
+
#
|
|
187
|
+
def Given(*args, &block)
|
|
188
|
+
if args.first.is_a?(Symbol)
|
|
189
|
+
let(args.first, &block)
|
|
190
|
+
else
|
|
191
|
+
_Gvn_givens << block
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# Declare a named given of the current specification. Similar
|
|
196
|
+
# to the named version of the "Given" command, except that the
|
|
197
|
+
# block is always evaluated.
|
|
198
|
+
#
|
|
199
|
+
# :call-seq:
|
|
200
|
+
# Given!(:name) { ... code ... }
|
|
201
|
+
#
|
|
202
|
+
def Given!(name, &block)
|
|
203
|
+
let(name, &block)
|
|
204
|
+
_Gvn_givens << _Gvn_trigger_given(name)
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# Declare the code that is under test.
|
|
208
|
+
#
|
|
209
|
+
# :call-seq:
|
|
210
|
+
# When(:named_result) { ... code_under_test ... }
|
|
211
|
+
# When { ... code_under_test ... }
|
|
212
|
+
#
|
|
213
|
+
def When(*args, &block)
|
|
214
|
+
if args.first.is_a?(Symbol)
|
|
215
|
+
_Gvn_when_actions_with_capture(args.first, block)
|
|
216
|
+
else
|
|
217
|
+
_Gvn_when_actions(block)
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# Normal When clause actions.
|
|
222
|
+
def _Gvn_when_actions(block) # :nodoc:
|
|
223
|
+
_Gvn_before do
|
|
224
|
+
_gvn_establish_givens
|
|
225
|
+
instance_eval(&block)
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
# Normal When clause actions except that exceptions are captured
|
|
230
|
+
# in a Failure object.
|
|
231
|
+
def _Gvn_when_actions_with_capture(name, block) # :nodoc:
|
|
232
|
+
let(name) do
|
|
233
|
+
Failure.capture(Given.pending_error) do
|
|
234
|
+
_gvn_establish_givens
|
|
235
|
+
instance_eval(&block)
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
_Gvn_before do __send__(name) end
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
# Provide an assertion about the specification.
|
|
242
|
+
#
|
|
243
|
+
# Then supplies an assertion that should be true after all the
|
|
244
|
+
# Given and When blocks have been run. All invariants in scope
|
|
245
|
+
# will be checked before the Then block is run.
|
|
246
|
+
#
|
|
247
|
+
# :call-seq:
|
|
248
|
+
# Then { ... assertion ... }
|
|
249
|
+
#
|
|
250
|
+
def Then(opts={}, &block)
|
|
251
|
+
on_eval = opts.fetch(:on_eval, "_gvn_then")
|
|
252
|
+
file, line = Given.location_of(block)
|
|
253
|
+
description = _Gvn_lines.line(file, line) unless Given.source_caching_disabled
|
|
254
|
+
cmd = description ? "it(description)" : "specify"
|
|
255
|
+
eval %{#{cmd} do #{on_eval}(&block) end}, binding, file, line
|
|
256
|
+
_Gvn_context_info[:then_defined] = true
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
# Establish an invariant that must be true for all Then blocks
|
|
260
|
+
# in the current (and nested) scopes.
|
|
261
|
+
def Invariant(&block)
|
|
262
|
+
_Gvn_invariants << block
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
# Provide an assertion that shares setup with a peer Then command.
|
|
266
|
+
def And(&block)
|
|
267
|
+
fail "And defined without a Then" unless _Gvn_context_info[:then_defined]
|
|
268
|
+
_Gvn_and_blocks << block
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
# Configure the use of natural assertions in this context.
|
|
272
|
+
def use_natural_assertions(enabled=true)
|
|
273
|
+
Given.ok_to_use_natural_assertions(enabled)
|
|
274
|
+
_Gvn_context_info[:natural_assertions_enabled] = enabled
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
|
|
2
|
+
module Given
|
|
3
|
+
|
|
4
|
+
# Failure objects will raise the given exception whenever you try
|
|
5
|
+
# to send it *any* message.
|
|
6
|
+
class Failure < BasicObject
|
|
7
|
+
undef_method :==, :!=, :!
|
|
8
|
+
|
|
9
|
+
# Evaluate a block. If an exception is raised, capture it in a
|
|
10
|
+
# Failure object. Explicitly listed exceptions are passed thru
|
|
11
|
+
# without capture.
|
|
12
|
+
def self.capture(*exceptions)
|
|
13
|
+
begin
|
|
14
|
+
yield
|
|
15
|
+
rescue *exceptions => ex
|
|
16
|
+
raise
|
|
17
|
+
rescue ::Exception => ex
|
|
18
|
+
new(ex)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Create a failure object that will rethrow the given exception
|
|
23
|
+
# whenever an undefined method is called.
|
|
24
|
+
def initialize(exception)
|
|
25
|
+
@exception = exception
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Failure objects will respond to #is_a?.
|
|
29
|
+
def is_a?(klass)
|
|
30
|
+
klass == Failure
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Failure objects may be compared for equality. If the comparison
|
|
34
|
+
# object is not a matcher, then the exception is re-raised.
|
|
35
|
+
def ==(other)
|
|
36
|
+
if failure_matcher?(other)
|
|
37
|
+
other.matches?(self)
|
|
38
|
+
else
|
|
39
|
+
die
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Failure objects may be compared for in-equality. If the comparison
|
|
44
|
+
# object is not a matcher, then the exception is re-raised.
|
|
45
|
+
def !=(other)
|
|
46
|
+
if failure_matcher?(other)
|
|
47
|
+
other.does_not_match?(self)
|
|
48
|
+
else
|
|
49
|
+
die
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Most methods will just re-raise the captured exception.
|
|
54
|
+
def method_missing(sym, *args, &block)
|
|
55
|
+
die
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Report that we respond to a limited number of methods.
|
|
59
|
+
def respond_to?(method_symbol)
|
|
60
|
+
method_symbol == :call ||
|
|
61
|
+
method_symbol == :== ||
|
|
62
|
+
method_symbol == :!= ||
|
|
63
|
+
method_symbol == :is_a? ||
|
|
64
|
+
method_symbol == :to_bool
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
# Re-raise the captured exception.
|
|
70
|
+
def die
|
|
71
|
+
::Kernel.raise @exception
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Is the comparison object a failure matcher?
|
|
75
|
+
def failure_matcher?(other)
|
|
76
|
+
other.respond_to?(:matches?) && other.respond_to?(:does_not_match?)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
end
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
module Given
|
|
2
|
+
|
|
3
|
+
class FailureMatcher
|
|
4
|
+
def initialize(exception_class, message_pattern)
|
|
5
|
+
@no_pattern = false
|
|
6
|
+
@expected_exception_class = exception_class
|
|
7
|
+
@expected_message_pattern = message_pattern
|
|
8
|
+
if @expected_message_pattern.nil?
|
|
9
|
+
@expected_message_pattern = //
|
|
10
|
+
@no_pattern = true
|
|
11
|
+
elsif @expected_message_pattern.is_a?(String)
|
|
12
|
+
@expected_message_pattern =
|
|
13
|
+
Regexp.new("\\A" + Regexp.quote(@expected_message_pattern) + "\\z")
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def ==(other)
|
|
18
|
+
if other.respond_to?(:call)
|
|
19
|
+
matches?(other)
|
|
20
|
+
else
|
|
21
|
+
super
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def !=(other)
|
|
26
|
+
if other.respond_to?(:call)
|
|
27
|
+
does_not_match?(other)
|
|
28
|
+
else
|
|
29
|
+
super
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def matches?(possible_failure)
|
|
34
|
+
if possible_failure.respond_to?(:call)
|
|
35
|
+
match_or_fail(possible_failure)
|
|
36
|
+
else
|
|
37
|
+
Given.fail_with("#{description}, but nothing failed")
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def does_not_match?(possible_failure)
|
|
42
|
+
if possible_failure.respond_to?(:call)
|
|
43
|
+
mismatch_or_fail(possible_failure)
|
|
44
|
+
else
|
|
45
|
+
true
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def inspect
|
|
50
|
+
result = "<Failure on #{@expected_exception_class}"
|
|
51
|
+
result << " matching #{@expected_message_pattern.inspect}" unless @no_pattern
|
|
52
|
+
result << ">"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def match_or_fail(possible_failure)
|
|
58
|
+
ex = extract_exception(possible_failure)
|
|
59
|
+
match_exception(ex) ||
|
|
60
|
+
Given.fail_with("#{description}, but got #{ex.inspect}")
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def mismatch_or_fail(possible_failure)
|
|
64
|
+
ex = extract_exception(possible_failure)
|
|
65
|
+
(! match_exception(ex)) ||
|
|
66
|
+
Given.fail_with("#{unexpected_description}, but got #{ex.inspect}")
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def match_exception(ex)
|
|
70
|
+
ex.is_a?(@expected_exception_class) && @expected_message_pattern =~ ex.message
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def extract_exception(possible_failure)
|
|
74
|
+
possible_failure.call
|
|
75
|
+
Given.fail_with("Expected an exception")
|
|
76
|
+
return nil
|
|
77
|
+
rescue Exception => ex
|
|
78
|
+
return ex
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def description
|
|
82
|
+
result = "Expected failure with #{@expected_exception_class}"
|
|
83
|
+
result << " matching #{@expected_message_pattern.inspect}" unless @no_pattern
|
|
84
|
+
result
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def unexpected_description
|
|
88
|
+
result = "Did not expect failure with #{@expected_exception_class}"
|
|
89
|
+
result << " matching #{@expected_message_pattern.inspect}" unless @no_pattern
|
|
90
|
+
result
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
module FailureMethod
|
|
95
|
+
def Failure(exception_class=Exception, message_pattern=nil)
|
|
96
|
+
FailureMatcher.new(exception_class, message_pattern)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
|
|
2
|
+
module Given
|
|
3
|
+
class FileCache
|
|
4
|
+
def initialize
|
|
5
|
+
@lines = {}
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def get(file_name)
|
|
9
|
+
@lines[file_name] ||= read_lines(file_name)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def read_lines(file_name)
|
|
15
|
+
open(file_name, "r:UTF-8") { |f| f.readlines }
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|