Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Baseline natural assertions.

  • Loading branch information...
commit 4408e294f9607b334a196848a9b5beaefde22653 1 parent 5531301
@jimweirich authored
View
11 examples/natural_assertion_spec.rb
@@ -0,0 +1,11 @@
+require 'rspec/given'
+require 'rspec/given/natural_assertions'
+
+describe "Natural Assertions" do
+ Given(:foo) { 1 }
+ Given(:expected) { 2 }
+ Then { foo+foo+2*foo == expected }
+ Then { nil == "HI" && true && :symbol && 1}
+ Then { foo.should == 2 }
+ Then { foo == 1 }
+end
View
101 lib/rspec/given/extensions.rb
@@ -1,6 +1,94 @@
require 'rspec/given/failure'
require 'rspec/given/module_methods'
+require 'ripper'
+require 'sorcerer'
+module RSpec
+ module Given
+
+ class EvalErr
+ def initialize(str)
+ @string = str
+ end
+ def size
+ inspect.size
+ end
+ def to_s
+ @string
+ end
+ def inspect
+ @string
+ end
+ end
+
+ class NaturalExpression
+
+ def initialize(block, env)
+ @block = block
+ @env = env
+ @output = ""
+ end
+
+ def message
+ file, line = caller_line(@block)
+ @output << "Then expression failed at #{file}:#{line}\n"
+ lines = open(file).readlines
+ code = lines[line.to_i-1]
+ sexp = Ripper::SexpBuilder.new(code).parse
+ sexp = extract_test_expression(sexp)
+ puts "DBG: sexp=#{sexp.inspect}"
+ subs = Sorcerer.subexpressions(sexp).reverse.uniq.reverse
+ pairs = subs.map { |exp|
+ [exp, eval_in(exp, @env)]
+ }
+ if (sexp[2][0] == :binary && sexp[2][2] == :==)
+ expect_expr = Sorcerer.source(sexp[2][3])
+ @output << "expected: " << eval_in(expect_expr, @env) << "\n"
+ got_expr = Sorcerer.source(sexp[2][1])
+ @output << "got: " << eval_in(got_expr, @env)<< "\n"
+ end
+ display_pairs(pairs)
+ @output << "\n"
+ @output
+ end
+
+ private
+
+ def caller_line(block)
+ eval "[__FILE__, __LINE__]", block.binding
+ end
+
+ def extract_test_expression(sexp)
+ sexp[1][2][2][2]
+ end
+
+ def eval_in(exp, binding)
+ eval(exp, binding).inspect
+ rescue StandardError => ex
+ EvalErr.new("#{ex.class}: #{ex.message}")
+ end
+
+ def suggest_width(pairs)
+ pairs.map { |x,v| v.size }.select { |n| n < 20 }.max || 10
+ end
+
+ def display_pairs(pairs)
+ width = suggest_width(pairs)
+ pairs.each do |x, v|
+ if v.size > 20
+ @output << sprintf(" %-#{width+2}s\n #{' '*(width+2)} <- %s\n", v, x)
+ else
+ @output << sprintf(" %-#{width+2}s <- %s\n", v, x)
+ end
+ end
+ end
+
+ end
+
+ end
+end
+
+
module RSpec
module Given
@@ -35,7 +123,7 @@ def _rg_establish_givens # :nodoc:
def _rg_check_invariants # :nodoc:
_rg_contexts.each do |context|
context._rg_invariants.each do |block|
- instance_eval(&block)
+ _rg_evaluate(block)
end
end
end
@@ -43,7 +131,7 @@ def _rg_check_invariants # :nodoc:
def _rg_check_ands # :nodoc:
return if self.class._rg_context_info[:and_ran]
self.class._rg_and_blocks.each do |block|
- instance_eval(&block)
+ _rg_evaluate(block)
end
self.class._rg_context_info[:and_ran] = true
end
@@ -52,9 +140,16 @@ def _rg_check_ands # :nodoc:
def _rg_then(&block) # :nodoc:
_rg_establish_givens
_rg_check_invariants
- instance_eval(&block)
+ _rg_evaluate(block)
_rg_check_ands
end
+
+ def _rg_evaluate(block)
+ unless instance_eval(&block)
+ nexp = NaturalExpression.new(block, binding)
+ ::RSpec::Expectations.fail_with nexp.message
+ end
+ end
end
module ClassExtensions
View
1  lib/rspec/given/natural_assertions.rb
@@ -0,0 +1 @@
+# HI
View
100 support/experiments/demo.rb
@@ -0,0 +1,100 @@
+#!/usr/bin/env ruby
+
+require 'rubygems'
+require 'ripper'
+require 'sorcerer'
+
+class EvalErr
+ def initialize(str)
+ @string = str
+ end
+ def size
+ inspect.size
+ end
+ def to_s
+ @string
+ end
+ def inspect
+ @string
+ end
+end
+
+def eval_in(exp, binding)
+ eval(exp, binding).inspect
+rescue StandardError => ex
+ EvalErr.new("#{ex.class}: #{ex.message}")
+end
+
+def suggest_width(pairs)
+ pairs.map { |x,v| v.size }.select { |n| n < 20 }.max || 10
+end
+
+def display_pairs(pairs)
+ width = suggest_width(pairs)
+ pairs.each do |x, v|
+ if v.size > 20
+ printf " %-#{width+2}s\n #{' '*(width+2)} <- %s\n", v, x
+ else
+ printf " %-#{width+2}s <- %s\n", v, x
+ end
+ end
+end
+
+def caller_line
+ file, line = caller[1].split(/:/)
+ [file, line]
+end
+
+require 'pp'
+
+def evaluate(&block)
+ file, line = caller_line
+ puts "Evaluating all expressions and subexpressions on #{file}:#{line}"
+ lines = open(file).readlines
+ code = lines[line.to_i-1]
+ sexp = Ripper::SexpBuilder.new(code).parse
+ sexp = sexp[1][2][2][2]
+ subs = Sorcerer.subexpressions(sexp).reverse.uniq.reverse
+ pairs = subs.map { |exp|
+ [exp, eval_in(exp, block.binding)]
+ }
+ display_pairs(pairs)
+ puts
+end
+
+def f(n)
+ n*n
+end
+
+# Define a bunch of variables
+
+a = 10
+b = 2
+c = 11
+n = nil
+p = ->(n) { n*n }
+x = 'xyzzy'
+hi = "hello"
+there = "world"
+
+
+evaluate { 132 == (a + b) * c and x =~ /z+/ }
+
+evaluate { f(f(f(f(a)))) * f(f(b)) }
+
+evaluate { Math.sin(0.7) > 0.6 && Math.sin(0.7) < 0.8 }
+
+evaluate { [4, 2, 6, 3, 7].sort.select { |n| n % 2 == 0 }.collect { |n| n*n } }
+
+evaluate { hi.upcase + ', ' + there.capitalize }
+
+evaluate { hi.upcase + ', ' + there.capitalize + n.downcase }
+
+evaluate { a == b }
+
+evaluate { p.(b) } if RUBY_VERSION >= "1.9.2"
+
+evaluate { a && (a+=2) && a }
+
+p = lambda { a == b }
+evaluate(&p)
Please sign in to comment.
Something went wrong with that request. Please try again.