lib/pork.rb in pork-0.9.0 vs lib/pork.rb in pork-0.9.1

- old
+ new

@@ -1,11 +1,11 @@ require 'thread' module Kernel - def should message=nil, &checker - Pork::Should.new(self, message, &checker) + def should message=nil, message_lazy=nil, &checker + Pork::Should.new(self, message, message_lazy, &checker) end end module Pork Error = Class.new(Exception) @@ -17,14 +17,62 @@ def self.report; stats.report; reset ; end def self.report_at_exit Pork.stats.start @report_at_exit ||= at_exit do stats.report - exit stats.failures.size + stats.errors.size + exit stats.failures.size + stats.errors.size + ($! && 1).to_i end end + # default to :auto while eliminating warnings for uninitialized ivar + def self.inspect_failure_mode mode=nil; @mode = mode || @mode ||= :auto; end + def self.inspect_failure *args + lambda{ public_send("inspect_failure_#{inspect_failure_mode}", *args) } + end + def self.inspect_failure_auto object, msg, args, negate + if args.size > 1 + inspect_failure_inline(object, msg, args, negate) + elsif object.kind_of?(Hash) && args.first.kind_of?(Hash) + inspect_failure_inline(Hash[object.sort], msg, + [Hash[args.first.sort]], negate) + elsif object.kind_of?(String) && object.size > 400 && + object.count("\n") > 4 && !`which diff`.empty? + inspect_failure_diff(object, msg, args, negate) + else + ins = object.inspect + if ins.size > 78 + inspect_failure_newline(object, msg, args, negate) + else + inspect_failure_inline( object, msg, args, negate) + end + end + end + + def self.inspect_failure_inline object, msg, args, negate + a = args.map(&:inspect).join(', ') + "#{object.inspect}.#{msg}(#{a}) to return #{!negate}" + end + + def self.inspect_failure_newline object, msg, args, negate + a = args.map(&:inspect).join(",\n") + "\n#{object.inspect}.#{msg}(\n#{a}) to return #{!negate}" + end + + def self.inspect_failure_diff object, msg, args, negate + require 'tempfile' + Tempfile.open('pork-expect') do |expect| + Tempfile.open('pork-was') do |was| + expect.puts(object.to_s) + expect.close + was.puts(args.map(&:to_s).join(",\n")) + was.close + name = "#{object.class}##{msg}(\n" + "#{name}#{`diff #{expect.path} #{was.path}`}) to return #{!negate}" + end + end + end + module API module_function def before &block; Executor.before(&block); end def after &block; Executor.after( &block); end def describe desc=:default, &suite; Executor.describe(desc, &suite); end @@ -44,12 +92,11 @@ def describe desc=:default, &suite Class.new(self){ init("#{desc}: ") }.module_eval(&suite) end def copy desc=:default, &suite; stash[desc] = suite; end def paste desc=:default, *args - stashes = [self, super_executor].compact.map(&:stash) - module_exec(*args, &stashes.find{ |s| s[desc] }[desc]) + module_exec(*args, &search_stash(desc)) end def would desc=:default, &test assertions = Pork.stats.assertions context = new(desc) run_before(context) @@ -72,25 +119,26 @@ Pork.stats.incr_tests run_after(context) end protected - def init desc='' - @desc, @before, @after, @stash = desc, [], [], {} - end + def init desc=''; @desc, @before, @after, @stash = desc, [], [], {}; end def super_executor @super_executor ||= ancestors[1..-1].find{ |a| a <= Executor } end + def search_stash desc + stash[desc] or super_executor && super_executor.search_stash(desc) + end def description_for name='' "#{desc}#{super_executor && super_executor.description_for}#{name}" end def run_before context - super_executor.run_before(context) if super_executor + super_executor && super_executor.run_before(context) before.each{ |b| context.instance_eval(&b) } end def run_after context - super_executor.run_after(context) if super_executor + super_executor && super_executor.run_after(context) after.each{ |b| context.instance_eval(&b) } end end class Executor < Struct.new(:desc) @@ -99,62 +147,30 @@ def skip ; raise Skip.new("Skipping #{desc}"); end def flunk reason='Flunked'; raise Error.new(reason) ; end def ok ; Pork.stats.incr_assertions ; end end - module InspectInlineError - def inspect_error object, msg, args, negate - a = args.map(&:inspect).join(', ') - "#{object.inspect}.#{msg}(#{a}) to return #{!negate}" - end - end - - module InspectNewlineError - def inspect_error object, msg, args, negate - a = args.map(&:inspect).join(', ') - "\n#{object.inspect}.#{msg}(\n#{a}) to return #{!negate}" - end - end - - module InspectDiffError - def inspect_error object, msg, args, negate - ::Kernel.require 'tempfile' - ::Tempfile.open('pork-expect') do |expect| - ::Tempfile.open('pork-was') do |was| - expect.puts(object.to_s) - expect.close - was.puts(args.map(&:to_s).join(",\n")) - was.close - name = "#{object.class}##{msg}(\n" - diff = ::Kernel.__send__(:`, "diff #{expect.path} #{was.path}") - "#{name}#{diff}) to return #{!negate}" - end - end - end - end - class Should < BasicObject instance_methods.each{ |m| undef_method(m) unless m =~ /^__|^object_id$/ } - include ::Pork::InspectInlineError - - def initialize object, message, &checker - @object = object - @negate = false - @message = message + def initialize object, message=nil, message_lazy=nil, &checker + @object, @negate = object, false + @message, @message_lazy = message, message_lazy satisfy(&checker) if checker end def method_missing msg, *args, &block - satisfy(inspect_error(@object, msg, args, @negate)) do + satisfy(nil, ::Pork.inspect_failure(@object, msg, args, @negate)) do @object.public_send(msg, *args, &block) end end - def satisfy desc=@object + def satisfy desc=@object, desc_lazy=nil result = yield(@object) if !!result == @negate - ::Kernel.raise Failure.new("Expect #{desc}\n#{@message}".chomp) + d = desc_lazy && desc_lazy.call || desc + m = @message_lazy && @message_lazy.call || @message + ::Kernel.raise Failure.new("Expect #{d}\n#{m}".chomp) else ::Pork.stats.incr_assertions end result end @@ -193,17 +209,11 @@ flag && [msg, data] end end private - def __not__ - if @negate == true - 'not ' - else - '' - end - end + def __not__; if @negate == true then 'not ' else '' end; end end class Stats < Struct.new(:tests, :assertions, :skips, :failures, :errors) def initialize @mutex = Mutex.new @@ -212,15 +222,11 @@ def incr_assertions; @mutex.synchronize{ self.assertions += 1 }; end def incr_tests ; @mutex.synchronize{ self.tests += 1 }; end def incr_skips ; @mutex.synchronize{ self.skips += 1; print('s')}; end def add_failure *e ; @mutex.synchronize{ failures << e; print('F')}; end def add_error *e ; @mutex.synchronize{ errors << e; print('E')}; end - def numbers - [tests, assertions, failures.size, errors.size, skips] - end - def start - @start ||= Time.now - end + def numbers; [tests, assertions, failures.size, errors.size, skips]; end + def start ; @start ||= Time.now ; end def report puts puts (failures + errors).map{ |(e, m)| "\n#{m}\n#{e.class}: #{e.message}\n #{backtrace(e)}" }