power_assert-0.2.7/ 0000755 0000041 0000041 00000000000 12665355034 014276 5 ustar www-data www-data power_assert-0.2.7/Rakefile 0000644 0000041 0000041 00000000403 12665355034 015740 0 ustar www-data www-data require "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/Gemfile 0000644 0000041 0000041 00000000346 12665355034 015574 0 ustar www-data www-data source "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.gemspec 0000644 0000041 0000041 00000002107 12665355034 020360 0 ustar www-data www-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.rdoc 0000644 0000041 0000041 00000001411 12665355034 016101 0 ustar www-data www-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.yml 0000644 0000041 0000041 00000000233 12665355034 016405 0 ustar www-data www-data language: 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/ 0000755 0000041 0000041 00000000000 12665355034 015044 5 ustar www-data www-data power_assert-0.2.7/lib/power_assert/ 0000755 0000041 0000041 00000000000 12665355034 017561 5 ustar www-data www-data power_assert-0.2.7/lib/power_assert/version.rb 0000644 0000041 0000041 00000000053 12665355034 021571 0 ustar www-data www-data module PowerAssert
VERSION = "0.2.7"
end
power_assert-0.2.7/lib/power_assert/enable_tracepoint_events.rb 0000644 0000041 0000041 00000003322 12665355034 025150 0 ustar www-data www-data if 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.rb 0000644 0000041 0000041 00000023632 12665355034 020114 0 ustar www-data www-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.yml 0000644 0000041 0000041 00000004666 12665355034 016615 0 ustar www-data www-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/ 0000755 0000041 0000041 00000000000 12665355034 015255 5 ustar www-data www-data power_assert-0.2.7/test/helper.rb 0000644 0000041 0000041 00000000243 12665355034 017060 0 ustar www-data www-data begin
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.rb 0000644 0000041 0000041 00000024042 12665355034 021360 0 ustar www-data www-data require '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 <