lib/power_assert/context.rb in power_assert-1.0.2 vs lib/power_assert/context.rb in power_assert-1.1.0

- old
+ new

@@ -3,22 +3,23 @@ require 'power_assert/inspector' require 'power_assert/parser' module PowerAssert class Context - Value = Struct.new(:name, :value, :column) + Value = Struct.new(:name, :value, :lineno, :column) - attr_reader :message_proc - def initialize(base_caller_length) @fired = false @target_thread = Thread.current method_id_set = nil - return_values = [] + @return_values = [] trace_alias_method = PowerAssert.configuration._trace_alias_method @trace_return = TracePoint.new(:return, :c_return) do |tp| - method_id_set ||= @parser.method_id_set + unless method_id_set + next unless Thread.current == @target_thread + method_id_set = @parser.method_id_set + end method_id = SUPPORT_ALIAS_METHOD ? tp.callee_id : trace_alias_method && tp.event == :return ? tp.binding.eval('::Kernel.__callee__') : tp.method_id next if ! method_id_set[method_id] next if tp.event == :c_return and @@ -29,48 +30,51 @@ idx = -(base_caller_length + 1) if @parser.path == locs[idx].path and @parser.lineno == locs[idx].lineno val = PowerAssert.configuration.lazy_inspection ? tp.return_value : InspectedValue.new(SafeInspectable.new(tp.return_value).inspect) - return_values << Value[method_id.to_s, val, nil] + @return_values << Value[method_id.to_s, val, locs[idx].lineno, nil] end end end - @message_proc = -> { - raise RuntimeError, 'call #yield or #enable at first' unless fired? - @message ||= build_assertion_message(@parser.line, @parser.idents, @parser.binding, return_values).freeze - } end def message - @message_proc.() + raise 'call #yield or #enable at first' unless fired? + @message ||= build_assertion_message(@parser, @return_values).freeze end + def message_proc + -> { message } + end + private def fired? @fired end - def build_assertion_message(line, idents, proc_binding, return_values) + def build_assertion_message(parser, return_values) if PowerAssert.configuration._colorize_message - line = Pry::Code.new(line).highlighted + line = Pry::Code.new(parser.line).highlighted + else + line = parser.line end - path = detect_path(idents, return_values) + path = detect_path(parser, return_values) return line unless path - delete_unidentified_calls(return_values, path) - methods, refs = path.partition {|i| i.type == :method } - return_values.zip(methods) do |i, j| + return_values, methods_in_path = find_all_identified_calls(return_values, path) + return_values.zip(methods_in_path) do |i, j| unless i.name == j.name warn "power_assert: [BUG] Failed to get column: #{i.name}" return line end i.column = j.column end - ref_values = refs.map {|i| Value[i.name, proc_binding.eval(i.name), i.column] } + refs_in_path = path.find_all {|i| i.type == :ref } + ref_values = refs_in_path.map {|i| Value[i.name, parser.binding.eval(i.name), parser.lineno, i.column] } vals = (return_values + ref_values).find_all(&:column).sort_by(&:column).reverse return line if vals.empty? fmt = (0..vals[0].column).map {|i| vals.find {|v| v.column == i } ? "%<#{i}>s" : ' ' }.join lines = [] @@ -86,13 +90,13 @@ end end lines.join("\n") end - def detect_path(idents, return_values) - return @parser.call_paths.flatten.uniq if @parser.method_id_set.empty? - all_paths = @parser.call_paths + def detect_path(parser, return_values) + return parser.call_paths.flatten.uniq if parser.method_id_set.empty? + all_paths = parser.call_paths return_value_names = return_values.map(&:name) uniq_calls = uniq_calls(all_paths) uniq_call = return_value_names.find {|i| uniq_calls.include?(i) } detected_paths = all_paths.find_all do |path| method_names = path.find_all {|ident| ident.type == :method }.map(&:name) @@ -106,16 +110,18 @@ def uniq_calls(paths) all_calls = enum_count_by(paths.map {|path| path.find_all {|ident| ident.type == :method }.map(&:name).uniq }.flatten) {|i| i } all_calls.find_all {|_, call_count| call_count == 1 }.map {|name, _| name } end - def delete_unidentified_calls(return_values, path) + def find_all_identified_calls(return_values, path) return_value_num_of_calls = enum_count_by(return_values, &:name) path_num_of_calls = enum_count_by(path.find_all {|ident| ident.type == :method }, &:name) identified_calls = return_value_num_of_calls.find_all {|name, num| path_num_of_calls[name] == num }.map(&:first) - return_values.delete_if {|val| ! identified_calls.include?(val.name) } - path.delete_if {|ident| ident.type == :method and ! identified_calls.include?(ident.name) } + [ + return_values.find_all {|val| identified_calls.include?(val.name) }, + path.find_all {|ident| ident.type == :method and identified_calls.include?(ident.name) } + ] end def enum_count_by(enum, &blk) Hash[enum.group_by(&blk).map{|k, v| [k, v.length] }] end @@ -150,10 +156,10 @@ locs = PowerAssert.app_caller_locations path = locs.last.path lineno = locs.last.lineno if File.exist?(path) line ||= open(path).each_line.drop(lineno - 1).first - @parser = Parser.new(line, path, lineno, @assertion_proc.binding, assertion_method.to_s) + @parser = Parser.new(line, path, lineno, @assertion_proc.binding, assertion_method.to_s, @assertion_proc) end end end end