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
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
|
|
2
|
+
module Given
|
|
3
|
+
module Fuzzy
|
|
4
|
+
class FuzzyNumber
|
|
5
|
+
|
|
6
|
+
DEFAULT_EPSILON = 10 * Float::EPSILON
|
|
7
|
+
|
|
8
|
+
attr_reader :exact_value, :delta_amount
|
|
9
|
+
|
|
10
|
+
def initialize(exact_value)
|
|
11
|
+
@exact_value = exact_value
|
|
12
|
+
@delta_amount = exact_value * DEFAULT_EPSILON
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def exactly_equals?(other)
|
|
16
|
+
other.is_a?(self.class) &&
|
|
17
|
+
exact_value == other.exact_value &&
|
|
18
|
+
delta_amount == other.delta_amount
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Low limit of the fuzzy number.
|
|
22
|
+
def low_limit
|
|
23
|
+
exact_value - delta_amount
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# High limit of the fuzzy number.
|
|
27
|
+
def high_limit
|
|
28
|
+
exact_value + delta_amount
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# True if the other number is in range of the fuzzy number.
|
|
32
|
+
def ==(other)
|
|
33
|
+
(other - exact_value).abs <= delta_amount
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def to_s
|
|
37
|
+
"<Approximately #{exact_value} +/- #{delta_amount}>"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Set the delta for a fuzzy number.
|
|
41
|
+
def delta(delta)
|
|
42
|
+
@delta_amount = delta.abs
|
|
43
|
+
self
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Specifying a percentage of the exact number to be used in
|
|
47
|
+
# setting the delta.
|
|
48
|
+
def percent(percentage)
|
|
49
|
+
delta(exact_value * (percentage / 100.0))
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Specifying the number of epsilons to be used in setting the
|
|
53
|
+
# delta.
|
|
54
|
+
def epsilon(neps)
|
|
55
|
+
delta(exact_value * (neps * Float::EPSILON))
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Create an approximate number that is approximately equal to
|
|
60
|
+
# the given number, plus or minus the delta value. If no
|
|
61
|
+
# explicit delta is given, then the default delta that is about
|
|
62
|
+
# 10X the size of the smallest possible change in the given
|
|
63
|
+
# number will be used.
|
|
64
|
+
def about(*args)
|
|
65
|
+
FuzzyNumber.new(*args)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require 'given/ext/numeric'
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
require 'ripper'
|
|
2
|
+
require 'sorcerer'
|
|
3
|
+
require 'given/file_cache'
|
|
4
|
+
|
|
5
|
+
module Given
|
|
6
|
+
class LineExtractor
|
|
7
|
+
def initialize(file_cache=nil)
|
|
8
|
+
@files = file_cache || FileCache.new
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def line(file_name, line)
|
|
12
|
+
lines = @files.get(file_name)
|
|
13
|
+
extract_lines_from(lines, line-1)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def to_s
|
|
17
|
+
"<LineExtractor>"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def extract_lines_from(lines, line_index)
|
|
23
|
+
result = lines[line_index]
|
|
24
|
+
while result && incomplete?(result)
|
|
25
|
+
line_index += 1
|
|
26
|
+
result << lines[line_index]
|
|
27
|
+
end
|
|
28
|
+
result
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def incomplete?(string)
|
|
32
|
+
builder = Ripper::SexpBuilder.new(string)
|
|
33
|
+
sexp = builder.parse
|
|
34
|
+
if builder.error?
|
|
35
|
+
# Modern Ripper recovers from syntax errors by dropping
|
|
36
|
+
# statements. A truncated source line parses to a program with
|
|
37
|
+
# no sourceable content; anything else is left alone.
|
|
38
|
+
extractable_source(sexp).empty?
|
|
39
|
+
else
|
|
40
|
+
! complete_sexp?(sexp)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def extractable_source(sexp)
|
|
45
|
+
Sorcerer.source(sexp)
|
|
46
|
+
rescue Sorcerer::Resource::NotSexpError
|
|
47
|
+
""
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def complete_sexp?(sexp)
|
|
51
|
+
Sorcerer.source(sexp)
|
|
52
|
+
true
|
|
53
|
+
rescue Sorcerer::Resource::NotSexpError => ex
|
|
54
|
+
false
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
require 'minitest/spec'
|
|
2
|
+
require 'given'
|
|
3
|
+
|
|
4
|
+
require 'given/minitest/before_extension'
|
|
5
|
+
require 'given/minitest/context_extension'
|
|
6
|
+
require 'given/minitest/failure_must_raise'
|
|
7
|
+
require 'given/minitest/framework'
|
|
8
|
+
require 'given/minitest/configure'
|
|
9
|
+
|
|
10
|
+
unless Minitest::Spec.instance_methods.include?(:assertions)
|
|
11
|
+
require 'given/minitest/new_assertions'
|
|
12
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
|
|
2
|
+
# The before blocks defined in Minitest are inadequate for our use.
|
|
3
|
+
# This before_extension file allows us to use real before blocks.
|
|
4
|
+
|
|
5
|
+
module Minitest
|
|
6
|
+
class Spec
|
|
7
|
+
|
|
8
|
+
# Redefine setup to trigger before chains
|
|
9
|
+
alias original_setup_without_given setup
|
|
10
|
+
def setup
|
|
11
|
+
original_setup_without_given
|
|
12
|
+
_gvn_establish_befores
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Establish the before blocks
|
|
16
|
+
def _gvn_establish_befores
|
|
17
|
+
return if defined?(@_gvn_ran_befores) && @_gvn_ran_befores
|
|
18
|
+
@_gvn_ran_befores = true
|
|
19
|
+
_gvn_contexts.each do |context|
|
|
20
|
+
context._Gvn_before_blocks.each do |before_block|
|
|
21
|
+
instance_eval(&before_block)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Lazy accessor for Given's before blocks
|
|
27
|
+
def self._Gvn_before_blocks
|
|
28
|
+
@_Gvn_before_blocks ||= []
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Define a Given style before block
|
|
32
|
+
def self._Gvn_before(&block)
|
|
33
|
+
_Gvn_before_blocks << block
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
|
|
2
|
+
# Configure Minitest to use the Given extensions.
|
|
3
|
+
|
|
4
|
+
Minitest::Spec.send(:extend, Given::ClassExtensions)
|
|
5
|
+
Minitest::Spec.send(:include, Given::FailureMethod)
|
|
6
|
+
Minitest::Spec.send(:include, Given::InstanceExtensions)
|
|
7
|
+
Given.use_natural_assertions if Given::NATURAL_ASSERTIONS_SUPPORTED
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module Given
|
|
2
|
+
|
|
3
|
+
class Failure
|
|
4
|
+
# Minitest expectation method. Since Failure inherits from
|
|
5
|
+
# BasicObject, we need to add this method explicitly.
|
|
6
|
+
def must_raise(*args)
|
|
7
|
+
::Minitest::Spec.current.assert_raises(*args) do
|
|
8
|
+
die
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
|
|
2
|
+
module Minitest
|
|
3
|
+
module Given
|
|
4
|
+
|
|
5
|
+
# Framework adapter for Minitest/Given
|
|
6
|
+
#
|
|
7
|
+
class Framework
|
|
8
|
+
def start_evaluation
|
|
9
|
+
@starting_assertion_count = example.assertions
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def explicit_assertions?
|
|
13
|
+
example.assertions > @starting_assertion_count
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def count_assertion
|
|
17
|
+
example.assertions += 1
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def fail_with(*args)
|
|
21
|
+
raise Minitest::Assertion, args.join(" ")
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def pending_error
|
|
25
|
+
Minitest::Skip
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def example
|
|
31
|
+
Minitest::Spec.current
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
Given.framework = Minitest::Given::Framework.new
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
|
|
2
|
+
module Minitest
|
|
3
|
+
module Given
|
|
4
|
+
|
|
5
|
+
# If this version of Minitest does not support the #assertions and
|
|
6
|
+
# #assertions= methods, define a working version of them.
|
|
7
|
+
#
|
|
8
|
+
# This allows Minitest/Given to work with Minitest 4.x.
|
|
9
|
+
#
|
|
10
|
+
module NewAssertions
|
|
11
|
+
def assertions
|
|
12
|
+
_assertions
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def assertions=(new_value)
|
|
16
|
+
self._assertions = new_value
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
Minitest::Spec.__send__(:include, Minitest::Given::NewAssertions)
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
|
|
2
|
+
module Given
|
|
3
|
+
# Does this platform support natural assertions?
|
|
4
|
+
RBX_IN_USE = (defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx')
|
|
5
|
+
JRUBY_IN_USE = defined?(JRUBY_VERSION)
|
|
6
|
+
OLD_JRUBY_IN_USE = JRUBY_IN_USE && (JRUBY_VERSION < '1.7.5')
|
|
7
|
+
|
|
8
|
+
NATURAL_ASSERTIONS_SUPPORTED = ! (OLD_JRUBY_IN_USE || RBX_IN_USE)
|
|
9
|
+
|
|
10
|
+
def self.framework
|
|
11
|
+
@_gvn_framework
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.framework=(framework)
|
|
15
|
+
@_gvn_framework = framework
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.source_caching_disabled
|
|
19
|
+
@_gvn_source_caching_disabled
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.source_caching_disabled=(value)
|
|
23
|
+
@_gvn_source_caching_disabled = value
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Globally enable/disable natural assertions.
|
|
27
|
+
#
|
|
28
|
+
# There is a similar function in Extensions that works at a
|
|
29
|
+
# describe or context scope.
|
|
30
|
+
def self.use_natural_assertions(enabled=true)
|
|
31
|
+
ok_to_use_natural_assertions(enabled)
|
|
32
|
+
@natural_assertions_enabled = enabled
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# TRUE if natural assertions are globally enabled?
|
|
36
|
+
def self.natural_assertions_enabled?
|
|
37
|
+
@natural_assertions_enabled
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Is is OK to use natural assertions on this platform.
|
|
41
|
+
#
|
|
42
|
+
# An error is raised if the the platform does not support natural
|
|
43
|
+
# assertions and the flag is attempting to enable them.
|
|
44
|
+
def self.ok_to_use_natural_assertions(enabled)
|
|
45
|
+
if enabled && ! NATURAL_ASSERTIONS_SUPPORTED
|
|
46
|
+
fail ArgumentError, "Natural Assertions are disabled for JRuby"
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Return file and line number where the block is defined.
|
|
51
|
+
def self.location_of(block)
|
|
52
|
+
block.source_location
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Methods forwarded to the framework object.
|
|
56
|
+
|
|
57
|
+
# Fail an example with the given messages.
|
|
58
|
+
def self.fail_with(*args)
|
|
59
|
+
Given.framework.fail_with(*args)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Mark the start of a Then assertion evaluation.
|
|
63
|
+
def self.start_evaluation(*args)
|
|
64
|
+
Given.framework.start_evaluation(*args)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Were there any explicit framework assertions made during the
|
|
68
|
+
# execution of the Then block?
|
|
69
|
+
def self.explicit_assertions?(*args)
|
|
70
|
+
Given.framework.explicit_assertions?(*args)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Increment the number of assertions made in the framework.
|
|
74
|
+
def self.count_assertion(*args)
|
|
75
|
+
Given.framework.count_assertion(*args)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Error object used by the current framework to indicate a pending
|
|
79
|
+
# example.
|
|
80
|
+
def self.pending_error
|
|
81
|
+
Given.framework.pending_error
|
|
82
|
+
end
|
|
83
|
+
end
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
require 'given/module_methods'
|
|
2
|
+
require 'given/evaluator'
|
|
3
|
+
require 'given/binary_operation'
|
|
4
|
+
|
|
5
|
+
if Given::NATURAL_ASSERTIONS_SUPPORTED
|
|
6
|
+
require 'ripper'
|
|
7
|
+
require 'sorcerer'
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
module Given
|
|
11
|
+
|
|
12
|
+
InvalidThenError = Class.new(StandardError)
|
|
13
|
+
|
|
14
|
+
class NaturalAssertion
|
|
15
|
+
|
|
16
|
+
def initialize(clause_type, block, example, line_extractor)
|
|
17
|
+
@clause_type = clause_type
|
|
18
|
+
@evaluator = Evaluator.new(example, block)
|
|
19
|
+
@line_extractor = line_extractor
|
|
20
|
+
set_file_and_line(block)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
VOID_SEXP = [:void_stmt]
|
|
24
|
+
|
|
25
|
+
def has_content?
|
|
26
|
+
assertion_sexp != VOID_SEXP
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def message
|
|
30
|
+
@output = "#{@clause_type} expression failed at #{source_line}\n"
|
|
31
|
+
@output << "Failing expression: #{source.strip}\n" if @clause_type != "Then"
|
|
32
|
+
explain_failure
|
|
33
|
+
display_pairs(expression_value_pairs)
|
|
34
|
+
@output << "\n"
|
|
35
|
+
@output
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def explain_failure
|
|
41
|
+
binary = BinaryOperation.parse(assertion_sexp)
|
|
42
|
+
if binary && binary.explain
|
|
43
|
+
@output << explain_expected("expected", binary.left, binary.explain, binary.right)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def explain_expected(expect_msg, expect_sexp, got_msg, got_sexp)
|
|
48
|
+
width = [expect_msg.size, got_msg.size].max
|
|
49
|
+
sprintf("%#{width}s: %s\n%#{width}s: %s\n",
|
|
50
|
+
expect_msg, eval_sexp(expect_sexp),
|
|
51
|
+
got_msg, eval_sexp(got_sexp))
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def expression_value_pairs
|
|
55
|
+
assertion_subexpressions.map { |exp|
|
|
56
|
+
[exp, @evaluator.eval_string(exp)]
|
|
57
|
+
}
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def assertion_subexpressions
|
|
61
|
+
Sorcerer.subexpressions(assertion_sexp).reverse.uniq.reverse
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def assertion_sexp
|
|
65
|
+
@assertion_sexp ||= extract_test_expression(Ripper::SexpBuilder.new(source).parse)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def source
|
|
69
|
+
@source ||= @line_extractor.line(@code_file, @code_line)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def set_file_and_line(block)
|
|
73
|
+
@code_file, @code_line = @evaluator.location
|
|
74
|
+
@code_line = @code_line.to_i
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def extract_test_expression(sexp)
|
|
78
|
+
brace_block = extract_brace_block(sexp)
|
|
79
|
+
extract_first_statement(brace_block)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def extract_brace_block(sexp)
|
|
83
|
+
unless then_block?(sexp)
|
|
84
|
+
source = Sorcerer.source(sexp)
|
|
85
|
+
fail InvalidThenError, "Unexpected code at #{source_line}\n#{source}"
|
|
86
|
+
end
|
|
87
|
+
sexp[1][2][2]
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def then_block?(sexp)
|
|
91
|
+
program_sexp?(sexp) && method_with_block?(sexp) && has_block_sexp?(sexp)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def program_sexp?(sexp)
|
|
95
|
+
delve(sexp,0) == :program
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def method_with_block?(sexp)
|
|
99
|
+
delve(sexp,1,0) == :stmts_add &&
|
|
100
|
+
delve(sexp,1,2,0) == :method_add_block
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def has_block_sexp?(sexp)
|
|
104
|
+
delve(sexp,1,2,2,0) == :brace_block || delve(sexp,1,2,2,0) == :do_block
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def extract_first_statement(block_sexp)
|
|
108
|
+
if contains_multiple_statements?(block_sexp)
|
|
109
|
+
source = Sorcerer.source(block_sexp)
|
|
110
|
+
fail InvalidThenError, "Multiple statements in Then block at #{source_line}\n#{source}"
|
|
111
|
+
end
|
|
112
|
+
extract_statement_from_block(block_sexp)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def contains_multiple_statements?(block_sexp)
|
|
116
|
+
stmts = block_statements(block_sexp)
|
|
117
|
+
!(delve(stmts,0) == :stmts_add &&
|
|
118
|
+
delve(stmts,1,0) == :stmts_new)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def extract_statement_from_block(block_sexp)
|
|
122
|
+
delve(block_statements(block_sexp),2)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# The statements of a block body. Modern Ripper wraps do_block
|
|
126
|
+
# bodies in a :bodystmt node; brace_block bodies (and older
|
|
127
|
+
# Ripper) expose the :stmts_add node directly.
|
|
128
|
+
def block_statements(block_sexp)
|
|
129
|
+
body = delve(block_sexp,2)
|
|
130
|
+
if body.is_a?(Array) && body[0] == :bodystmt
|
|
131
|
+
body[1]
|
|
132
|
+
else
|
|
133
|
+
body
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Safely dive into an array with a list of indicies. Return nil
|
|
138
|
+
# if the element doesn't exist, or if the intermediate result is
|
|
139
|
+
# not indexable.
|
|
140
|
+
def delve(ary, *indicies)
|
|
141
|
+
result = ary
|
|
142
|
+
while !indicies.empty? && result
|
|
143
|
+
return nil unless result.respond_to?(:[])
|
|
144
|
+
i = indicies.shift
|
|
145
|
+
result = result[i]
|
|
146
|
+
end
|
|
147
|
+
result
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def eval_sexp(sexp)
|
|
151
|
+
expr_string = Sorcerer.source(sexp)
|
|
152
|
+
@evaluator.eval_string(expr_string)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
WRAP_WIDTH = 20
|
|
156
|
+
|
|
157
|
+
def display_pairs(pairs)
|
|
158
|
+
width = suggest_width(pairs) + 4
|
|
159
|
+
pairs.each do |x, v|
|
|
160
|
+
v = adjust_indentation(v)
|
|
161
|
+
fmt = multi_line?(v) ?
|
|
162
|
+
"%-#{width}s\n#{' '*width} <- %s\n" :
|
|
163
|
+
"%-#{width}s <- %s\n"
|
|
164
|
+
@output << sprintf(fmt, v, x)
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def adjust_indentation(string)
|
|
169
|
+
string.to_s.gsub(/^/, ' ')
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def multi_line?(string)
|
|
173
|
+
(string.size > WRAP_WIDTH) || (string =~ /\n/)
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def suggest_width(pairs)
|
|
177
|
+
pairs.map { |x,v|
|
|
178
|
+
max_line_length(v)
|
|
179
|
+
}.select { |n| n < WRAP_WIDTH }.max || 10
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def max_line_length(string)
|
|
183
|
+
string.to_s.split(/\n/).map { |s| s.size }.max
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def source_line
|
|
187
|
+
"#{@code_file}:#{@code_line}"
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require 'rspec'
|
|
2
|
+
require 'given'
|
|
3
|
+
|
|
4
|
+
module RSpec
|
|
5
|
+
module Given
|
|
6
|
+
end
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
if Given::NATURAL_ASSERTIONS_SUPPORTED
|
|
10
|
+
require 'given/rspec/monkey'
|
|
11
|
+
raise "Unsupported version of RSpec (#{RSpec::Version::STRING}), unable to detect assertions" unless RSpec::Given::MONKEY
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
require 'given/rspec/have_failed'
|
|
15
|
+
require 'given/rspec/before_extensions'
|
|
16
|
+
require 'given/rspec/framework'
|
|
17
|
+
require 'given/rspec/use_natural_assertions'
|
|
18
|
+
require 'given/rspec/configure'
|
|
19
|
+
|
|
20
|
+
module Given
|
|
21
|
+
def self.using_old_rspec?
|
|
22
|
+
defined?(Spec) &&
|
|
23
|
+
defined?(Spec::VERSION) &&
|
|
24
|
+
defined?(Spec::VERSION::SUMMARY) &&
|
|
25
|
+
Spec::VERSION::SUMMARY =~ /^rspec +1\./
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
raise "Unsupported version of RSpec" if Given.using_old_rspec?
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
|
|
2
|
+
module RSpec
|
|
3
|
+
module Given
|
|
4
|
+
module BeforeHack
|
|
5
|
+
|
|
6
|
+
# Some frameworks don't support a robust before block, so we
|
|
7
|
+
# always use this one. In RSpec, we just delegate to the real
|
|
8
|
+
# before block handler.
|
|
9
|
+
def _Gvn_before(*args, &block)
|
|
10
|
+
before(*args, &block)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require 'rspec'
|
|
2
|
+
require 'rspec/given' unless defined?(RSpec::Given)
|
|
3
|
+
|
|
4
|
+
RSpec.configure do |c|
|
|
5
|
+
c.extend(Given::ClassExtensions)
|
|
6
|
+
c.include(Given::InstanceExtensions)
|
|
7
|
+
c.include(Given::Fuzzy)
|
|
8
|
+
c.include(Given::FailureMethod)
|
|
9
|
+
c.extend(RSpec::Given::BeforeHack)
|
|
10
|
+
c.include(RSpec::Given::HaveFailed)
|
|
11
|
+
|
|
12
|
+
if c.respond_to?(:backtrace_exclusion_patterns)
|
|
13
|
+
c.backtrace_exclusion_patterns << /lib\/rspec\/given/
|
|
14
|
+
else
|
|
15
|
+
c.backtrace_clean_patterns << /lib\/rspec\/given/
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
Given.use_natural_assertions if Given::NATURAL_ASSERTIONS_SUPPORTED
|
|
19
|
+
Given.source_caching_disabled = false
|
|
20
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
|
|
2
|
+
module RSpec
|
|
3
|
+
module Given
|
|
4
|
+
|
|
5
|
+
# Framework interface for RSpec/Given.
|
|
6
|
+
#
|
|
7
|
+
class Framework
|
|
8
|
+
def start_evaluation
|
|
9
|
+
@matcher_called = false
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def explicit_assertions?
|
|
13
|
+
@matcher_called
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def count_assertion
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def explicitly_asserted
|
|
20
|
+
@matcher_called = true
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def fail_with(*args)
|
|
24
|
+
::RSpec::Expectations.fail_with(*args)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Use the RSpec pending error if we can find it.
|
|
28
|
+
if defined?(RSpec::Core::Pending::PendingDeclaredInExample)
|
|
29
|
+
def pending_error
|
|
30
|
+
RSpec::Core::Pending::PendingDeclaredInExample
|
|
31
|
+
end
|
|
32
|
+
elsif defined?(RSpec::Core::Pending::SkipDeclaredInExample)
|
|
33
|
+
def pending_error
|
|
34
|
+
RSpec::Core::Pending::SkipDeclaredInExample
|
|
35
|
+
end
|
|
36
|
+
else
|
|
37
|
+
PendingError = Class.new(StandardError)
|
|
38
|
+
def pending_error
|
|
39
|
+
PendingError
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
Given.framework = RSpec::Given::Framework.new
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
|
|
2
|
+
# The implementation of RaiseError changed between RSpec 2.11 and 2.12.
|
|
3
|
+
if RSpec::Matchers::BuiltIn::RaiseError.instance_methods.include?(:does_not_match?)
|
|
4
|
+
require 'given/rspec/have_failed_212.rb'
|
|
5
|
+
else
|
|
6
|
+
require 'given/rspec/have_failed_pre212.rb'
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
module RSpec
|
|
10
|
+
module Given
|
|
11
|
+
module HaveFailed
|
|
12
|
+
|
|
13
|
+
# Specializes the RaiseError matcher to handle
|
|
14
|
+
# Failure/non-failure objects.
|
|
15
|
+
|
|
16
|
+
# Simular to raise_error(...), but reads a bit better when using
|
|
17
|
+
# a failure result from a when clause.
|
|
18
|
+
#
|
|
19
|
+
# Typical Usage:
|
|
20
|
+
#
|
|
21
|
+
# When(:result) { fail "OUCH" }
|
|
22
|
+
# Then { expect(result).to have_failed(StandardError, /OUCH/) }
|
|
23
|
+
#
|
|
24
|
+
# When(:result) { good_code }
|
|
25
|
+
# Then { expect(result).to_not have_failed }
|
|
26
|
+
#
|
|
27
|
+
# :call-seq:
|
|
28
|
+
# have_failed([exception_class [, message_pattern]])
|
|
29
|
+
# have_failed([exception_class [, message_pattern]]) { |ex| ... }
|
|
30
|
+
#
|
|
31
|
+
def have_failed(error=Exception, message=nil, &block)
|
|
32
|
+
HaveFailedMatcher.new(error, message, &block)
|
|
33
|
+
end
|
|
34
|
+
alias have_raised have_failed
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|