power_assert-0.2.7/0000755000004100000410000000000012665355034014276 5ustar www-datawww-datapower_assert-0.2.7/Rakefile0000644000004100000410000000040312665355034015740 0ustar www-datawww-datarequire "bundler/gem_tasks" require "rake/testtask" task :default => :test Rake::TestTask.new do |t| # helper(simplecov) must be required before loading power_assert t.ruby_opts = ["-w", "-r./test/helper"] t.test_files = FileList["test/test_*.rb"] end power_assert-0.2.7/Gemfile0000644000004100000410000000034612665355034015574 0ustar www-datawww-datasource "http://rubygems.org" gemspec # https://github.com/redmine/redmine/blob/3.0.4/Gemfile#L101 local_gemfile = File.join(File.dirname(__FILE__), "Gemfile.local") if File.exist?(local_gemfile) eval_gemfile local_gemfile end power_assert-0.2.7/power_assert.gemspec0000644000004100000410000000210712665355034020360 0ustar www-datawww-data$:.push File.expand_path('../lib', __FILE__) require 'power_assert/version' Gem::Specification.new do |s| s.name = 'power_assert' s.version = PowerAssert::VERSION s.authors = ['Kazuki Tsujimoto'] s.email = ['kazuki@callcc.net'] s.homepage = 'https://github.com/k-tsj/power_assert' s.summary = %q{Power Assert for Ruby} s.description = %q{Power Assert for Ruby. Power Assert shows each value of variables and method calls in the expression. It is useful for testing, providing which value wasn't correct when the condition is not satisfied.} s.files = `git ls-files`.split("\n") s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") s.executables = `git ls-files -- bin/*`.split("\n").map{|f| File.basename(f) } s.require_paths = ['lib'] s.add_development_dependency 'test-unit' s.add_development_dependency 'rake' s.add_development_dependency 'simplecov' s.extra_rdoc_files = ['README.rdoc'] s.rdoc_options = ['--main', 'README.rdoc'] s.licenses = ['2-clause BSDL', "Ruby's"] end power_assert-0.2.7/README.rdoc0000644000004100000410000000141112665355034016101 0ustar www-datawww-data= power_assert == About Power Assert for Ruby. == Related Projects * {test-unit}[https://github.com/test-unit/test-unit](>= 3.0.0) * {test-unit-power_assert}[https://github.com/k-tsj/test-unit-power_assert] * {minitest-power_assert}[https://github.com/hsbt/minitest-power_assert] * {pry-power_assert}[https://github.com/yui-knk/pry-power_assert] * {rspec-power_assert}[https://github.com/joker1007/rspec-power_assert] * {power_p}[https://github.com/k-tsj/power_p] == Requirement * CRuby 2.0.0 or later == Reference * {Power Assert in Ruby (at RubyKaigi 2014) // Speaker Deck}[https://speakerdeck.com/k_tsj/power-assert-in-ruby] == Travis Build Status {}[http://travis-ci.org/k-tsj/power_assert] power_assert-0.2.7/.travis.yml0000644000004100000410000000023312665355034016405 0ustar www-datawww-datalanguage: ruby rvm: - 2.0.0 - 2.1 - 2.2 - 2.3.0 - ruby-head matrix: allow_failures: - rvm: ruby-head before_install: - gem update bundler power_assert-0.2.7/lib/0000755000004100000410000000000012665355034015044 5ustar www-datawww-datapower_assert-0.2.7/lib/power_assert/0000755000004100000410000000000012665355034017561 5ustar www-datawww-datapower_assert-0.2.7/lib/power_assert/version.rb0000644000004100000410000000005312665355034021571 0ustar www-datawww-datamodule PowerAssert VERSION = "0.2.7" end power_assert-0.2.7/lib/power_assert/enable_tracepoint_events.rb0000644000004100000410000000332212665355034025150 0ustar www-datawww-dataif defined? RubyVM verbose = $VERBOSE begin $VERBOSE = nil module PowerAssert # set redefined flag basic_classes = [ Fixnum, Float, String, Array, Hash, Bignum, Symbol, Time, Regexp ] basic_operators = [ :+, :-, :*, :/, :%, :==, :===, :<, :<=, :<<, :[], :[]=, :length, :size, :empty?, :succ, :>, :>=, :!, :!=, :=~, :freeze ] class Bug11182 def fixed? true end end private_constant :Bug11182 refine Bug11182 do def fixed? end end class Bug11182Sub < Bug11182 alias _fixed? fixed? protected :_fixed? end private_constant :Bug11182Sub if (Bug11182.new.fixed? rescue false) basic_classes.each do |klass| basic_operators.each do |bop| refine(klass) do define_method(bop) {} end end end else # workaround for https://bugs.ruby-lang.org/issues/11182 basic_classes.each do |klass| basic_operators.each do |bop| if klass.public_method_defined?(bop) klass.ancestors.find {|i| i.instance_methods(false).index(bop) }.module_eval do public bop end end end end refine Symbol do def == end end end # bypass check_cfunc refine BasicObject do def ! end def == end end refine Module do def == end end end ensure $VERBOSE = verbose end # disable optimization RubyVM::InstructionSequence.compile_option = { specialized_instruction: false } end power_assert-0.2.7/lib/power_assert.rb0000644000004100000410000002363212665355034020114 0ustar www-datawww-data# power_assert.rb # # Copyright (C) 2014-2015 Kazuki Tsujimoto, All rights reserved. begin captured = false TracePoint.new(:return, :c_return) do |tp| captured = true unless tp.binding and tp.return_value raise end end.enable { __id__ } raise unless captured rescue raise LoadError, 'Fully compatible TracePoint API required' end require 'power_assert/version' require 'power_assert/enable_tracepoint_events' require 'ripper' module PowerAssert class << self def configuration @configuration ||= Configuration[false] end def configure yield configuration end def start(assertion_proc_or_source, assertion_method: nil, source_binding: TOPLEVEL_BINDING) if respond_to?(:clear_global_method_cache, true) clear_global_method_cache end yield Context.new(assertion_proc_or_source, assertion_method, source_binding) end private if defined?(RubyVM) def clear_global_method_cache eval('using PowerAssert.const_get(:Empty)', TOPLEVEL_BINDING) end end end Configuration = Struct.new(:lazy_inspection) private_constant :Configuration module Empty end private_constant :Empty class InspectedValue def initialize(value) @value = value end def inspect @value end end private_constant :InspectedValue class SafeInspectable def initialize(value) @value = value end def inspect inspected = @value.inspect if Encoding.compatible?(Encoding.default_external, inspected) inspected else begin "#{inspected.encode(Encoding.default_external)}(#{inspected.encoding})" rescue Encoding::UndefinedConversionError, Encoding::InvalidByteSequenceError inspected.force_encoding(Encoding.default_external) end end rescue => e "InspectionFailure: #{e.class}: #{e.message.each_line.first}" end end private_constant :SafeInspectable class Context Value = Struct.new(:name, :value, :column) Ident = Struct.new(:type, :name, :column) TARGET_CALLER_DIFF = {return: 5, c_return: 4} TARGET_INDEX_OFFSET = {bmethod: 3, method: 2} attr_reader :message_proc def initialize(assertion_proc_or_source, assertion_method, source_binding) if assertion_proc_or_source.kind_of?(Proc) @assertion_proc = assertion_proc_or_source @line = nil else @assertion_proc = source_binding.eval "Proc.new {#{assertion_proc_or_source}}" @line = assertion_proc_or_source end path = nil lineno = nil methods = nil refs = nil method_ids = nil return_values = [] @base_caller_length = -1 @assertion_method_name = assertion_method.to_s @message_proc = -> { return nil if @base_caller_length < 0 @message ||= build_assertion_message(@line || '', methods || [], return_values, refs || [], @assertion_proc.binding).freeze } @proc_local_variables = @assertion_proc.binding.eval('local_variables').map(&:to_s) target_thread = Thread.current @trace = TracePoint.new(:return, :c_return) do |tp| next if method_ids and ! method_ids.include?(tp.method_id) next unless tp.binding # workaround for ruby 2.2 locs = tp.binding.eval('::Kernel.caller_locations') current_diff = locs.length - @base_caller_length target_diff = TARGET_CALLER_DIFF[tp.event] is_target_bmethod = current_diff < target_diff if (is_target_bmethod or current_diff == target_diff) and Thread.current == target_thread idx = target_diff - TARGET_INDEX_OFFSET[is_target_bmethod ? :bmethod : :method] unless path path = locs[idx].path lineno = locs[idx].lineno @line ||= open(path).each_line.drop(lineno - 1).first idents = extract_idents(Ripper.sexp(@line)) methods, refs = idents.partition {|i| i.type == :method } method_ids = methods.map(&:name).map(&:to_sym).uniq end if path == locs[idx].path and lineno == locs[idx].lineno val = PowerAssert.configuration.lazy_inspection ? tp.return_value : InspectedValue.new(SafeInspectable.new(tp.return_value).inspect) return_values << Value[tp.method_id.to_s, val, nil] end end end end def yield do_yield(&@assertion_proc) end def message @message_proc.() end private def do_yield @trace.enable do @base_caller_length = caller_locations.length yield end end def build_assertion_message(line, methods, return_values, refs, proc_binding) set_column(methods, return_values) ref_values = refs.map {|i| Value[i.name, proc_binding.eval(i.name), i.column] } vals = (return_values + ref_values).find_all(&:column).sort_by(&:column).reverse if vals.empty? return line end fmt = (0..vals[0].column).map {|i| vals.find {|v| v.column == i } ? "%<#{i}>s" : ' ' }.join lines = [] lines << line.chomp lines << sprintf(fmt, vals.each_with_object({}) {|v, h| h[v.column.to_s.to_sym] = '|' }).chomp vals.each do |i| inspected_vals = vals.each_with_object({}) do |j, h| h[j.column.to_s.to_sym] = [SafeInspectable.new(i.value).inspect, '|', ' '][i.column <=> j.column] end lines << encoding_safe_rstrip(sprintf(fmt, inspected_vals)) end lines.join("\n") end def set_column(methods, return_values) methods = methods.dup return_values.each do |val| idx = methods.index {|method| method.name == val.name } if idx val.column = methods.delete_at(idx).column end end end def encoding_safe_rstrip(str) str.rstrip rescue ArgumentError, Encoding::CompatibilityError enc = str.encoding if enc.ascii_compatible? str.b.rstrip.force_encoding(enc) else str end end def extract_idents(sexp) tag, * = sexp case tag when :arg_paren, :assoc_splat, :fcall, :hash, :method_add_block, :string_literal extract_idents(sexp[1]) when :assign, :massign extract_idents(sexp[2]) when :assoclist_from_args, :bare_assoc_hash, :dyna_symbol, :paren, :string_embexpr, :regexp_literal, :xstring_literal sexp[1].flat_map {|s| extract_idents(s) } when :assoc_new, :command, :dot2, :dot3, :string_content sexp[1..-1].flat_map {|s| extract_idents(s) } when :unary handle_columnless_ident([], sexp[1], extract_idents(sexp[2])) when :binary handle_columnless_ident(extract_idents(sexp[1]), sexp[2], extract_idents(sexp[3])) when :call if sexp[3] == :call handle_columnless_ident(extract_idents(sexp[1]), :call, []) else [sexp[1], sexp[3]].flat_map {|s| extract_idents(s) } end when :array sexp[1] ? sexp[1].flat_map {|s| extract_idents(s) } : [] when :command_call [sexp[1], sexp[4], sexp[3]].flat_map {|s| extract_idents(s) } when :aref handle_columnless_ident(extract_idents(sexp[1]), :[], extract_idents(sexp[2])) when :method_add_arg idents = extract_idents(sexp[1]) if idents.empty? # idents may be empty(e.g. ->{}.()) extract_idents(sexp[2]) else idents[0..-2] + extract_idents(sexp[2]) + [idents[-1]] end when :args_add_block _, (tag, ss0, *ss1), _ = sexp if tag == :args_add_star (ss0 + ss1).flat_map {|s| extract_idents(s) } else sexp[1].flat_map {|s| extract_idents(s) } end when :vcall _, (tag, name, (_, column)) = sexp if tag == :@ident [Ident[@proc_local_variables.include?(name) ? :ref : :method, name, column]] else [] end when :program _, ((tag0, (tag1, (tag2, (tag3, mname, _)), _), (tag4, _, ss))) = sexp if tag0 == :method_add_block and tag1 == :method_add_arg and tag2 == :fcall and (tag3 == :@ident or tag3 == :@const) and mname == @assertion_method_name and (tag4 == :brace_block or tag4 == :do_block) ss.flat_map {|s| extract_idents(s) } else _, (s, *) = sexp extract_idents(s) end when :var_ref _, (tag, ref_name, (_, column)) = sexp case tag when :@kw if ref_name == 'self' [Ident[:ref, 'self', column]] else [] end when :@const, :@cvar, :@ivar, :@gvar [Ident[:ref, ref_name, column]] else [] end when :@ident, :@const _, method_name, (_, column) = sexp [Ident[:method, method_name, column]] else [] end end def str_indices(str, re, offset, limit) idx = str.index(re, offset) if idx and idx <= limit [idx, *str_indices(str, re, idx + 1, limit)] else [] end end MID2SRCTXT = { :[] => '[', :+@ => '+', :-@ => '-', :call => '(' } def handle_columnless_ident(left_idents, mid, right_idents) left_max = left_idents.max_by(&:column) right_min = right_idents.min_by(&:column) bg = left_max ? left_max.column + left_max.name.length : 0 ed = right_min ? right_min.column - 1 : @line.length - 1 mname = mid.to_s srctxt = MID2SRCTXT[mid] || mname re = / #{'\b' if /\A\w/ =~ srctxt} #{Regexp.escape(srctxt)} #{'\b' if /\w\z/ =~ srctxt} /x indices = str_indices(@line, re, bg, ed) if left_idents.empty? and right_idents.empty? left_idents + right_idents elsif left_idents.empty? left_idents + right_idents + [Ident[:method, mname, indices.last]] else left_idents + right_idents + [Ident[:method, mname, indices.first]] end end end private_constant :Context end power_assert-0.2.7/metadata.yml0000644000004100000410000000466612665355034016615 0ustar www-datawww-data--- !ruby/object:Gem::Specification name: power_assert version: !ruby/object:Gem::Version version: 0.2.7 platform: ruby authors: - Kazuki Tsujimoto autorequire: bindir: bin cert_chain: [] date: 2016-01-09 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: test-unit requirement: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' - !ruby/object:Gem::Dependency name: rake requirement: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' - !ruby/object:Gem::Dependency name: simplecov requirement: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' description: Power Assert for Ruby. Power Assert shows each value of variables and method calls in the expression. It is useful for testing, providing which value wasn't correct when the condition is not satisfied. email: - kazuki@callcc.net executables: [] extensions: [] extra_rdoc_files: - README.rdoc files: - ".gitignore" - ".travis.yml" - BSDL - COPYING - Gemfile - LEGAL - README.rdoc - Rakefile - lib/power_assert.rb - lib/power_assert/enable_tracepoint_events.rb - lib/power_assert/version.rb - power_assert.gemspec - test/helper.rb - test/test_power_assert.rb homepage: https://github.com/k-tsj/power_assert licenses: - 2-clause BSDL - Ruby's metadata: {} post_install_message: rdoc_options: - "--main" - README.rdoc require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' required_rubygems_version: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' requirements: [] rubyforge_project: rubygems_version: 2.5.1 signing_key: specification_version: 4 summary: Power Assert for Ruby test_files: [] power_assert-0.2.7/test/0000755000004100000410000000000012665355034015255 5ustar www-datawww-datapower_assert-0.2.7/test/helper.rb0000644000004100000410000000024312665355034017060 0ustar www-datawww-databegin if ENV['COVERAGE'] require 'simplecov' SimpleCov.start do add_filter '/test/' add_filter '/vendor/' end end rescue LoadError end power_assert-0.2.7/test/test_power_assert.rb0000644000004100000410000002404212665355034021360 0ustar www-datawww-datarequire 'test/unit' require 'power_assert' require 'ripper' require 'set' class TestPowerAssert < Test::Unit::TestCase EXTRACT_METHODS_TEST = [ [[[:method, "c", 4], [:method, "b", 2], [:method, "d", 8], [:method, "a", 0]], 'a(b(c), d)'], [[[:method, "a", 0], [:method, "b", 2], [:method, "d", 6], [:method, "c", 4]], 'a.b.c(d)'], [[[:method, "b", 2], [:method, "a", 0], [:method, "c", 5], [:method, "e", 9], [:method, "d", 7]], 'a(b).c.d(e)'], [[[:method, "b", 4], [:method, "a", 2], [:method, "c", 7], [:method, "e", 13], [:method, "g", 11], [:method, "d", 9], [:method, "f", 0]], 'f(a(b).c.d(g(e)))'], [[[:method, "c", 5], [:method, "e", 11], [:method, "a", 0]], 'a(b: c, d: e)'], [[[:method, "b", 2], [:method, "c", 7], [:method, "d", 10], [:method, "e", 15], [:method, "a", 0]], 'a(b => c, d => e)'], [[[:method, "b", 4], [:method, "d", 10]], '{a: b, c: d}'], [[[:method, "a", 1], [:method, "b", 6], [:method, "c", 9], [:method, "d", 14]], '{a => b, c => d}'], [[[:method, "a", 2], [:method, "b", 5], [:method, "c", 10], [:method, "d", 13]], '[[a, b], [c, d]]'], [[[:method, "a", 0], [:method, "b", 2], [:method, "c", 5]], 'a b, c { d }'], [[[:method, "a", 20]], 'assertion_message { a }'], [[[:method, "a", 0]], 'a { b }'], [[[:method, "c", 4], [:method, "B", 2], [:method, "d", 8], [:method, "A", 0]], 'A(B(c), d)'], [[[:method, "c", 6], [:method, "f", 17], [:method, "h", 25], [:method, "a", 0]], 'a(b = c, (d, e = f), G = h)'], [[[:method, "b", 2], [:method, "c", 6], [:method, "d", 9], [:method, "e", 12], [:method, "g", 18], [:method, "i", 24], [:method, "j", 29], [:method, "a", 0]], 'a(b, *c, d, e, f: g, h: i, **j)'], [[[:method, "a", 0], [:method, "b", 5], [:method, "c", 9], [:method, "+", 7], [:method, "==", 2]], 'a == b + c'], [[[:ref, "var", 0], [:ref, "var", 8], [:method, "var", 4]], 'var.var(var)'], [[[:ref, "B", 2], [:ref, "@c", 5], [:ref, "@@d", 9], [:ref, "$e", 14], [:method, "f", 18], [:method, "self", 20], [:ref, "self", 26], [:method, "a", 0]], 'a(B, @c, @@d, $e, f.self, self)'], [[[:method, "a", 0], [:method, "c", 4], [:method, "b", 2]], 'a.b c'], [[[:method, "b", 4]], '"a#{b}c"'], [[[:method, "b", 4]], '/a#{b}c/'], [[], '[]'], [[[:method, "a", 0], [:method, "[]", 1]], 'a[0]'], # not supported [[], '[][]'], # not supported [[], '{}[]'], [[[:method, "a", 1], [:method, "!", 0]], '!a'], [[[:method, "a", 1], [:method, "+@", 0]], '+a'], [[[:method, "a", 1], [:method, "-@", 0]], '-a'], [[[:method, "a", 2], [:method, "!", 0], [:method, "b", 9], [:method, "+@", 8], [:method, "c", 15], [:method, "-@", 14], [:method, "==", 11], [:method, "==", 4]], '! a == (+b == -c)'], [[[:method, "b", 6]], '%x{a#{b}c}'], [[[:method, "a", 0], [:method, "b", 3]], "a..b"], [[[:method, "a", 0], [:method, "b", 4]], "a...b"], [[[:method, "b", 5]], ':"a#{b}c"'], # not supported [[], '->{}.()'], [[[:method, "a", 0], [:method, "b", 3], [:method, "call", 2]], 'a.(b)'], ] EXTRACT_METHODS_TEST.each_with_index do |(expect, source), idx| define_method("test_extract_methods_#{'%03d' % idx}") do pa = PowerAssert.const_get(:Context).new(-> { var = nil; -> { var } }.(), nil, TOPLEVEL_BINDING) pa.instance_variable_set(:@line, source) pa.instance_variable_set(:@assertion_method_name, 'assertion_message') assert_equal expect, pa.send(:extract_idents, Ripper.sexp(source)).map(&:to_a), source end end class BasicObjectSubclass < BasicObject def foo "foo" end end def assertion_message(source = nil, source_binding = TOPLEVEL_BINDING, &blk) ::PowerAssert.start(source || blk, assertion_method: __callee__, source_binding: source_binding) do |pa| pa.yield pa.message end end def Assertion(&blk) ::PowerAssert.start(blk, assertion_method: __callee__) do |pa| pa.yield pa.message end end define_method(:bmethod) do false end def test_lazy_inspection PowerAssert.configure do |c| assert !c.lazy_inspection end assert_equal < | | 3 | false String END "0".class == "3".to_i.times.map {|i| i + 1 }.class } assert_equal '', assertion_message { false } assert_equal < | | | Set | | false | # Set END Set.new == Set.new([0]) } var = [10,20] assert_equal <