Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

bunch of refactorings, now tracks the implementation source file/line…

… for each context
  • Loading branch information...
commit b980f48d9acfcc10eee28d98588adc74b2a62f45 1 parent 81e0833
@svenfuchs authored
View
37 lib/with.rb
@@ -1,3 +1,4 @@
+require 'with/implementation'
require 'with/node'
require 'with/sharing'
require 'with/context'
@@ -18,9 +19,15 @@ def with_common(*names)
def describe(name, &block)
context = Context.build(name, *with_common, &block).first
- context.compile(self)
+ context.compile(self, With.options.slice(:file, :line))
context
end
+
+ def reset
+ # shared.clear
+ with_common.clear
+ instance_methods.select{|m| m =~ /^test/ }.each {|m| remove_method(m) }
+ end
end
class << self
@@ -28,22 +35,46 @@ def applies?(names, conditions)
conditions[:in].nil? || names.include?(conditions[:in]) and
conditions[:not_in].nil? || !names.include?(conditions[:not_in])
end
+
+ def options
+ @@options ||= {}
+ end
+
+ def options=(options)
+ @@options = options
+ end
def aspects
@@aspects ||= []
end
+
+ def aspects=(aspects)
+ @@aspects = aspects
+ end
def aspect?(aspect)
self.aspects.include?(aspect)
end
+
+ def reset_all
+ ObjectSpace.each_object(Test::Unit::TestCase) do |test_case|
+ test_case.class.reset if test_case.class.respond_to?(:reset)
+ end
+ end
end
# hmm, i can't see a way to solve nested it blocks rather than this because
# :it usually indicates an assertion and within assertions we want to be able
# to use instance vars as in:
# it "articles should be new" do @article.new_record?.should == true end
- def it(name, &block)
- yield
+ # def it(name, &block)
+ # yield
+ # end
+
+ def within(name)
+ result = @_with_contexts.include?(name)
+ yield if result and block_given?
+ return result
end
end
View
13 lib/with/call.rb
@@ -2,6 +2,8 @@ module With
extend Sharing
class Call
+ include Implementation
+
attr_reader :name, :block
def initialize(name, conditions = {}, &block)
@@ -9,10 +11,7 @@ def initialize(name, conditions = {}, &block)
@name = name
@conditions = conditions
- @block = Proc.new {
- @_with_current_context = name
- instance_eval &block
- }
+ @block = block
end
def applies?(context)
@@ -21,7 +20,11 @@ def applies?(context)
end
def to_proc
- @block
+ name, block = self.name, self.block
+ Proc.new {
+ @_with_current_context = name
+ instance_eval &block
+ }
end
def call
View
34 lib/with/context.rb
@@ -6,23 +6,18 @@ def build(*names, &block)
names.each do |names|
children = Array(names).map do |name|
- name.is_a?(Symbol) ? With.shared(name) : new(name)
+ name.is_a?(Symbol) ? With.shared(name) : new(name, &block)
end.flatten
context.append_children children
end
context.children.each do |child|
child.parent = nil
- child.leafs.each { |leaf| leaf.instance_eval(&block) } if block
+ child.leafs.each { |leaf| leaf.define(&block) } if block
end
end
end
- def initialize(name = nil, &block)
- super(name)
- instance_eval &block if block
- end
-
def with(*names, &block)
options = names.last.is_a?(Hash) ? names.pop : {}
Context.build(*names, &block).each do |child|
@@ -42,8 +37,9 @@ def #{name}(name = nil, options = {}, &block)
alias :expect :before
alias :it :assertion
- def compile(target)
- leafs.each { |leaf| define_test_method(target, leaf) }
+ def compile(target, options = {})
+ file, line = With.options[:file], With.options[:line]
+ leafs.each { |leaf| define_test_method(target, leaf) if leaf.implemented_at?(file, line) }
end
protected
@@ -54,12 +50,17 @@ def method_missing(method_name, *args, &block)
[:with, :in, :not_in].each { |key| options[key] = args.last.delete(key) }
args.pop if args.last.empty?
end
- assertion ([method_name] << args.map(&:inspect)).join('_'), options do
- send method_name, *args, &block
+
+ if Test::Unit::TestCase.method_defined?(method_name)
+ assertion ([method_name] << args.map(&:inspect)).join('_'), options do
+ send method_name, *args, &block
+ end
+ else
+ lambda { send method_name, *args, &block }
end
end
- def define_test_method(target, context)
+ def define_test_method(target, context, options = {})
method_name = generate_test_method_name(context)
target.send :define_method, method_name, &lambda {
@_with_contexts = (context.parents << context).map(&:name)
@@ -70,17 +71,14 @@ def define_test_method(target, context)
}
end
- # TODO urghs. super ugly and doesn't even really work well.
- # Need some better ideas for generating readable method names.
- # Maybe even play with method names containing \n characters?
def generate_test_method_name(context)
contexts = context.parents << context
assertions = context.calls(:assertion)
name = "test_##{context.object_id}\n#{contexts.shift.name}"
- name += contexts.map { |c| "\nwith #{c.name} " }.join("and")
- name += assertions.map { |a| "\nit #{a.name} " }.join("and")
- name.gsub('_', ' ').gsub(' ', ' ').gsub('it it', 'it') #.gsub('__', '_').gsub('__', '_').gsub(/"|(_$)/, '')
+ name += "\n with " + contexts.map(&:name).to_sentence
+ name += "\n it " + assertions.map(&:name).to_sentence
+ name.gsub('_', ' ').gsub(' ', ' ').gsub('it it', 'it')
end
end
end
View
21 lib/with/node.rb
@@ -1,9 +1,12 @@
module With
class Node
- attr_accessor :name, :parent, :children
+ include Implementation
- def initialize(name = nil)
+ attr_accessor :name, :parent, :children, :calls
+
+ def initialize(name = nil, &block)
@name = name
+ @block = block
@children = []
@calls = {}
end
@@ -15,6 +18,16 @@ def initialize_copy(orig)
orig.calls.each { |key, calls| @calls[key] = calls.map{ |call| call.clone } }
end
+ def define(&block)
+ @block = block
+ instance_eval &block
+ end
+
+ # def select(&block)
+ # result = yield(self) ? [self] : [] # || !@calls.select(&block).empty?
+ # result += @children.map { |child| child.select(&block) }.flatten
+ # end
+
def filter(conditions)
if With.applies?(parents.map(&:name), conditions)
@children.each {|child| child.filter(conditions) }
@@ -46,9 +59,7 @@ def add_child(child)
end
def append_children(children)
- leafs.each do |leaf|
- children.each {|child| leaf.add_child(child.dup) }
- end
+ leafs.each { |leaf| children.each {|child| leaf.add_child(child.dup) } }
end
end
end
View
5 lib/with/sharing.rb
@@ -1,8 +1,11 @@
module With
module Sharing
def share(group, name = nil, &block)
+ context = Context.new(name || group, &block)
+ context.instance_eval &block if block
+
self.shared[group] ||= []
- self.shared[group] << Context.new(name || group, &block)
+ self.shared[group] << context
end
def shared(name = nil)
View
4 test/context_calls_test.rb
@@ -35,13 +35,13 @@ def test_wraps_explicit_call_into_shared_context
contexts = With::Context.build('foo'){ assertion('assertion', :with => :bar){ }}
assert_equal [[['foo', :bar]]], context_names(contexts)
end
-
+
def test_wraps_missing_method_call_into_shared_context
With.share(:bar){ }
contexts = With::Context.build('foo'){ assert(true, :with => :bar){ }}
assert_equal [[['foo', :bar]]], context_names(contexts)
end
-
+
def test_registers_explicit_call_with_in_condition
context = With::Context.build('foo'){ before('before', :in => 'foo'){ }}.first
assert_equal 'foo', context.calls(:before).first.instance_variable_get(:@conditions)[:in]
View
18 test/context_compile_test.rb
@@ -3,13 +3,14 @@
class ContextCompileTest < Test::Unit::TestCase
def setup
Target.reset
+ With.shared.clear
end
def call(context)
context.compile(Target)
target = Target.new
-
- target.methods.select{|m| m =~ /^test_/ }.sort.reverse.inject([]) do |called, method|
+
+ target.methods.select{|m| m =~ /^test/ }.sort.reverse.inject([]) do |called, method|
target.send(method)
called << target.called.dup
target.called.clear
@@ -102,17 +103,4 @@ def test_compile_with_conditions
assert_equal expected, call(context)
end
-
- # def test_compile_shared_backtrace
- # collector = lambda { @trace = caller; break! }
- # With.share(:shared, 'shared') { before 'shared_before', &collector }
- # context = With::Context.build('foo', :shared){}.first
- # context.compile(Target)
- # target = Target.new
- #
- # target.methods.select{|m| m =~ /^test_/ }.sort.reverse.inject([]) do |called, method|
- # target.send(method)
- # target.instance_variable_get(:@trace).each { |line| puts line }
- # end
- # end
end
View
21 test/context_structure_test.rb
@@ -118,4 +118,25 @@ def test_different_nested_contexts_with_common_shared_parent
assert_equal 'renders :new', context.children[0].calls(:assertion)[0].name
assert_equal 'renders :edit', context.children[1].calls(:assertion)[0].name
end
+
+ def test_node_knows_its_filename_and_linenumber
+ context = With::Context.build('foo'){ }.first
+ assert_equal __FILE__, context.send(:file)
+ assert_equal __LINE__ - 2, context.send(:line)
+ end
+
+ def test_select_finds_nodes_with_a_block_matching
+ context = With::Context.build('foo'){
+ with('bar'){}
+ }.first.select { |block| block.send(:line) == __LINE__ - 1 }.first
+ assert_equal 'bar', context.name
+ end
+
+ def test_implemented_at_returns_true_when_context_or_call_is_implemented_at_given_file_and_line
+ context = With::Context.build('foo'){
+ with('bar'){}
+ }.first
+ assert context.implemented_at?(:file => __FILE__, :line => __LINE__ - 3)
+ assert context.children.first.implemented_at?(:file => __FILE__, :line => __LINE__ - 3)
+ end
end
View
4 test/demo_test.rb
@@ -48,8 +48,8 @@ def test_something
end
end
- methods = Target.instance_methods.grep(/^test_/)
- #puts; puts Target.instance_methods.grep(/^test_/).join(", \n ")
+ methods = Target.instance_methods.grep(/^test/)
+ #puts; puts Target.instance_methods.grep(/^test/).join(", \n ")
assert_equal 5, methods.count
expected = [[['POST to :create', :caching, :observers, :login_as_admin, :valid_article_params],
View
31 test/helper.rb
@@ -4,21 +4,9 @@
class Target
include With
- class << self
- def reset
- With.shared.clear
- with_common.clear
- instance_methods.select{|m| m =~ /^test_/ }.each {|m| Target.send(:remove_method, m) }
- end
- end
-
def called
@called ||= []
end
-
- def with?(name)
- @_with_contexts.include?(name)
- end
end
class Symbol
@@ -27,6 +15,25 @@ def to_proc
end
end
+class Array
+ def to_sentence
+ case length
+ when 0
+ ""
+ when 1
+ self[0].to_s
+ else
+ "#{self[0...-1].join(', ')} and #{self[-1]}"
+ end
+ end
+end
+
+class Hash
+ def slice(*keys)
+ Hash[*keys.map { |key| [key, self[key]] if has_key?(key) }.flatten.compact]
+ end
+end
+
class Test::Unit::TestCase
def context_names(contexts)
contexts.map do |context|
Please sign in to comment.
Something went wrong with that request. Please try again.