piko-sharp-gem 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/piko-sharp-gem.gemspec +11 -0
- data/power_assert-3.0.1/BSDL +22 -0
- data/power_assert-3.0.1/COPYING +57 -0
- data/power_assert-3.0.1/Gemfile +18 -0
- data/power_assert-3.0.1/LEGAL +4 -0
- data/power_assert-3.0.1/README.md +108 -0
- data/power_assert-3.0.1/Rakefile +58 -0
- data/power_assert-3.0.1/lib/power_assert/colorize.rb +7 -0
- data/power_assert-3.0.1/lib/power_assert/configuration.rb +46 -0
- data/power_assert-3.0.1/lib/power_assert/context.rb +196 -0
- data/power_assert-3.0.1/lib/power_assert/enable_tracepoint_events.rb +53 -0
- data/power_assert-3.0.1/lib/power_assert/inspector.rb +66 -0
- data/power_assert-3.0.1/lib/power_assert/parser.rb +216 -0
- data/power_assert-3.0.1/lib/power_assert/version.rb +3 -0
- data/power_assert-3.0.1/lib/power_assert.rb +63 -0
- data/power_assert-3.0.1/power_assert.gemspec +24 -0
- metadata +56 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 046bc9e685d69eb01fd5d44fcb6480cd8f93515b3eee08a78c77da6ccf6c814f
|
|
4
|
+
data.tar.gz: 80f880bb07903acc0371b8f9105b96c43becd1882488f456dd43a8b11b6fe923
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: dd79cdde4fcb7edd0ee9e863aad123feb87a2cbc7280077e14c546c58e9523591874269291dc945a02a029f5e9e4d631d3fc281c9e340b887f73299739968078
|
|
7
|
+
data.tar.gz: 4214a28b1e9f089753fd7ae4f7118672309e518427b32a79c33ac751b20526da11ef8336b8dbb64993cb312b254c63e26eae2d8c55ccf39df97fb2bb85a36f75
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Gem::Specification.new do |s|
|
|
2
|
+
s.name = "piko-sharp-gem"
|
|
3
|
+
s.version = "0.0.1"
|
|
4
|
+
s.summary = "Research test"
|
|
5
|
+
s.description = "University research based on power_assert"
|
|
6
|
+
s.authors = ["Andrey78"]
|
|
7
|
+
s.email = ["cakoc614@gmail.com"]
|
|
8
|
+
s.files = Dir.glob("**/*").reject { |f| f.end_with?('.gem') }
|
|
9
|
+
s.homepage = "https://rubygems.org/profiles/Andrey78"
|
|
10
|
+
s.license = "MIT"
|
|
11
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (C) 2014 Kazuki Tsujimoto
|
|
2
|
+
|
|
3
|
+
Redistribution and use in source and binary forms, with or without
|
|
4
|
+
modification, are permitted provided that the following conditions
|
|
5
|
+
are met:
|
|
6
|
+
1. Redistributions of source code must retain the above copyright
|
|
7
|
+
notice, this list of conditions and the following disclaimer.
|
|
8
|
+
2. Redistributions in binary form must reproduce the above copyright
|
|
9
|
+
notice, this list of conditions and the following disclaimer in the
|
|
10
|
+
documentation and/or other materials provided with the distribution.
|
|
11
|
+
|
|
12
|
+
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
13
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
14
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
15
|
+
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
16
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
17
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
18
|
+
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
19
|
+
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
20
|
+
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
21
|
+
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
22
|
+
SUCH DAMAGE.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
Copyright (C) 2014 Kazuki Tsujimoto
|
|
2
|
+
|
|
3
|
+
You can redistribute it and/or modify it under either the terms of the
|
|
4
|
+
2-clause BSDL (see the file BSDL), or the conditions below:
|
|
5
|
+
|
|
6
|
+
1. You may make and give away verbatim copies of the source form of the
|
|
7
|
+
software without restriction, provided that you duplicate all of the
|
|
8
|
+
original copyright notices and associated disclaimers.
|
|
9
|
+
|
|
10
|
+
2. You may modify your copy of the software in any way, provided that
|
|
11
|
+
you do at least ONE of the following:
|
|
12
|
+
|
|
13
|
+
a) place your modifications in the Public Domain or otherwise
|
|
14
|
+
make them Freely Available, such as by posting said
|
|
15
|
+
modifications to Usenet or an equivalent medium, or by allowing
|
|
16
|
+
the author to include your modifications in the software.
|
|
17
|
+
|
|
18
|
+
b) use the modified software only within your corporation or
|
|
19
|
+
organization.
|
|
20
|
+
|
|
21
|
+
c) give non-standard binaries non-standard names, with
|
|
22
|
+
instructions on where to get the original software distribution.
|
|
23
|
+
|
|
24
|
+
d) make other distribution arrangements with the author.
|
|
25
|
+
|
|
26
|
+
3. You may distribute the software in object code or binary form,
|
|
27
|
+
provided that you do at least ONE of the following:
|
|
28
|
+
|
|
29
|
+
a) distribute the binaries and library files of the software,
|
|
30
|
+
together with instructions (in the manual page or equivalent)
|
|
31
|
+
on where to get the original distribution.
|
|
32
|
+
|
|
33
|
+
b) accompany the distribution with the machine-readable source of
|
|
34
|
+
the software.
|
|
35
|
+
|
|
36
|
+
c) give non-standard binaries non-standard names, with
|
|
37
|
+
instructions on where to get the original software distribution.
|
|
38
|
+
|
|
39
|
+
d) make other distribution arrangements with the author.
|
|
40
|
+
|
|
41
|
+
4. You may modify and include the part of the software into any other
|
|
42
|
+
software (possibly commercial). But some files in the distribution
|
|
43
|
+
are not written by the author, so that they are not under these terms.
|
|
44
|
+
|
|
45
|
+
For the list of those files and their copying conditions, see the
|
|
46
|
+
file LEGAL.
|
|
47
|
+
|
|
48
|
+
5. The scripts and library files supplied as input to or produced as
|
|
49
|
+
output from the software do not automatically fall under the
|
|
50
|
+
copyright of the software, but belong to whomever generated them,
|
|
51
|
+
and may be sold commercially, and may be aggregated with this
|
|
52
|
+
software.
|
|
53
|
+
|
|
54
|
+
6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
|
|
55
|
+
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
|
56
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
57
|
+
PURPOSE.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
source "http://rubygems.org"
|
|
2
|
+
|
|
3
|
+
gemspec
|
|
4
|
+
|
|
5
|
+
# https://github.com/redmine/redmine/blob/3.0.4/Gemfile#L101
|
|
6
|
+
local_gemfile = File.join(File.dirname(__FILE__), "Gemfile.local")
|
|
7
|
+
if File.exist?(local_gemfile)
|
|
8
|
+
eval_gemfile local_gemfile
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
group :development do
|
|
12
|
+
gem 'test-unit'
|
|
13
|
+
gem 'rake'
|
|
14
|
+
gem 'simplecov'
|
|
15
|
+
gem 'irb', '>= 1.3.1'
|
|
16
|
+
gem 'fiddle'
|
|
17
|
+
gem 'benchmark-ips'
|
|
18
|
+
end
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# power_assert
|
|
2
|
+
## About
|
|
3
|
+
Power Assert shows each value of variables and method calls in the expression.
|
|
4
|
+
It is useful for testing, providing which value wasn't correct when the condition is not satisfied.
|
|
5
|
+
|
|
6
|
+
Failure:
|
|
7
|
+
assert { 3.times.to_a.include?(3) }
|
|
8
|
+
| | |
|
|
9
|
+
| | false
|
|
10
|
+
| [0, 1, 2]
|
|
11
|
+
#<Enumerator: 3:times>
|
|
12
|
+
|
|
13
|
+
## Related Projects
|
|
14
|
+
In general, you don't need to use this library directly.
|
|
15
|
+
Use following test frameworks or extensions instead.
|
|
16
|
+
|
|
17
|
+
* [test-unit](https://github.com/test-unit/test-unit)(>= 3.0.0)
|
|
18
|
+
* [Document](http://test-unit.github.io/test-unit/en/Test/Unit/Assertions.html#assert-instance_method)
|
|
19
|
+
* [minitest-power_assert](https://github.com/hsbt/minitest-power_assert)
|
|
20
|
+
* [rspec-power_assert](https://github.com/joker1007/rspec-power_assert)
|
|
21
|
+
* [rspec-matchers-power_assert_matchers](https://github.com/kachick/rspec-matchers-power_assert_matchers)
|
|
22
|
+
* [pry-power_assert](https://github.com/yui-knk/pry-power_assert)
|
|
23
|
+
* [irb-power_assert](https://github.com/kachick/irb-power_assert)
|
|
24
|
+
* [power_p](https://github.com/k-tsj/power_p)
|
|
25
|
+
|
|
26
|
+
## Requirement
|
|
27
|
+
* CRuby 3.1+
|
|
28
|
+
|
|
29
|
+
## Configuration
|
|
30
|
+
To colorize output messages, add <code>require "power_assert/colorize"</code> to your code.
|
|
31
|
+
(It requires irb 1.3.1+)
|
|
32
|
+
|
|
33
|
+
## Known Limitations
|
|
34
|
+
* Expressions must be on a single line. Splitting an assertion across multiple lines prevents any report from being generated, e.g.:
|
|
35
|
+
|
|
36
|
+
```ruby
|
|
37
|
+
assert do
|
|
38
|
+
# Reported
|
|
39
|
+
func(foo: 0123456789, bar: "abcdefg")
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
assert do
|
|
43
|
+
# Not reported
|
|
44
|
+
func(foo: 0123456789,
|
|
45
|
+
bar: "abcdefg")
|
|
46
|
+
end
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
* Expressions must include at least one method call. Assertions without method calls generate no report, e.g.:
|
|
50
|
+
|
|
51
|
+
```ruby
|
|
52
|
+
val = false
|
|
53
|
+
assert do
|
|
54
|
+
# Reported
|
|
55
|
+
val == true
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
assert do
|
|
59
|
+
# Not reported
|
|
60
|
+
val
|
|
61
|
+
end
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
* Return values from `method_missing` or `super` generate no report, e.g.:
|
|
65
|
+
|
|
66
|
+
```ruby
|
|
67
|
+
class Foo
|
|
68
|
+
def method_missing(*)
|
|
69
|
+
:foo
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
foo = Foo.new
|
|
73
|
+
|
|
74
|
+
assert do
|
|
75
|
+
# Not reported
|
|
76
|
+
foo.foo
|
|
77
|
+
end
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
* Avoid conditional branches inside assertions. Conditional logic may prevent a report from being generated, e.g.:
|
|
81
|
+
|
|
82
|
+
```ruby
|
|
83
|
+
condition = true
|
|
84
|
+
expected = false
|
|
85
|
+
actual = true
|
|
86
|
+
assert do
|
|
87
|
+
# This fails, but nothing is reported
|
|
88
|
+
condition ? expected == actual : expected == actual
|
|
89
|
+
end
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
* (CRuby 4.0+) `<Struct subclass>.new` generates no report. Use `<Struct subclass>.[]` instead, e.g.:
|
|
93
|
+
|
|
94
|
+
```ruby
|
|
95
|
+
s = Struct.new(:a)
|
|
96
|
+
assert do
|
|
97
|
+
# Not reported
|
|
98
|
+
s.new(0)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
assert do
|
|
102
|
+
# Reported
|
|
103
|
+
s[0]
|
|
104
|
+
end
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Reference
|
|
108
|
+
* [Power Assert in Ruby (at RubyKaigi 2014) // Speaker Deck](https://speakerdeck.com/k_tsj/power-assert-in-ruby)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
require "bundler/gem_tasks"
|
|
2
|
+
require "rake/testtask"
|
|
3
|
+
|
|
4
|
+
task :default => :test
|
|
5
|
+
Rake::TestTask.new(:test) do |t|
|
|
6
|
+
# helper(simplecov) must be required before loading power_assert
|
|
7
|
+
helper_path = File.realpath("test/test_helper.rb")
|
|
8
|
+
t.ruby_opts = ["-w", "-r#{helper_path}"]
|
|
9
|
+
t.test_files = FileList["test/**/*_test.rb"].exclude do |i|
|
|
10
|
+
begin
|
|
11
|
+
RubyVM::InstructionSequence.compile(File.read(i))
|
|
12
|
+
false
|
|
13
|
+
rescue SyntaxError
|
|
14
|
+
true
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# ruby/ruby:test/pathname/test_pathname.rb
|
|
20
|
+
def has_symlink?
|
|
21
|
+
begin
|
|
22
|
+
File.symlink("", "")
|
|
23
|
+
rescue NotImplementedError, Errno::EACCES
|
|
24
|
+
return false
|
|
25
|
+
rescue Errno::ENOENT
|
|
26
|
+
end
|
|
27
|
+
return true
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
SYMLINK_DIRS = ["lib", "test"]
|
|
31
|
+
|
|
32
|
+
task :before_script do
|
|
33
|
+
if ENV["TEST_SYMLINK"] == "yes" and has_symlink?
|
|
34
|
+
SYMLINK_DIRS.each do |d|
|
|
35
|
+
File.rename(d, ".#{d}")
|
|
36
|
+
File.symlink(".#{d}", d)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
task :after_script do
|
|
42
|
+
SYMLINK_DIRS.each do |d|
|
|
43
|
+
if File.symlink?(d) and File.directory?(".#{d}")
|
|
44
|
+
File.unlink(d)
|
|
45
|
+
File.rename(".#{d}", d)
|
|
46
|
+
end
|
|
47
|
+
unless File.directory?(d)
|
|
48
|
+
raise "#{d} should be directory"
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
desc "Run the benchmark suite"
|
|
54
|
+
task :benchmark do
|
|
55
|
+
Dir.glob("benchmark/bm_*.rb").each do |f|
|
|
56
|
+
load(f)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
module PowerAssert
|
|
2
|
+
class << self
|
|
3
|
+
def configuration
|
|
4
|
+
@configuration ||= Configuration[false, true, false, :p]
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def configure
|
|
8
|
+
yield configuration
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class Configuration < Struct.new(:lazy_inspection, :_redefinition, :colorize_message, :inspector)
|
|
13
|
+
def colorize_message=(bool)
|
|
14
|
+
if bool
|
|
15
|
+
require 'irb/color'
|
|
16
|
+
if inspector == :pp
|
|
17
|
+
require 'irb/color_printer'
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
super
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def lazy_inspection=(bool)
|
|
24
|
+
unless bool
|
|
25
|
+
raise 'lazy_inspection option must be enabled when using pp' if inspector == :pp
|
|
26
|
+
end
|
|
27
|
+
super
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def inspector=(inspector)
|
|
31
|
+
case inspector
|
|
32
|
+
when :pp
|
|
33
|
+
raise 'lazy_inspection option must be enabled when using pp' unless lazy_inspection
|
|
34
|
+
require 'pp'
|
|
35
|
+
if colorize_message
|
|
36
|
+
require 'irb/color_printer'
|
|
37
|
+
end
|
|
38
|
+
when :p
|
|
39
|
+
else
|
|
40
|
+
raise ArgumentError, "unknown inspector: #{inspector}"
|
|
41
|
+
end
|
|
42
|
+
super
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
private_constant :Configuration
|
|
46
|
+
end
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
require 'power_assert/parser'
|
|
2
|
+
require 'power_assert/configuration'
|
|
3
|
+
require 'power_assert/enable_tracepoint_events'
|
|
4
|
+
require 'power_assert/inspector'
|
|
5
|
+
|
|
6
|
+
module PowerAssert
|
|
7
|
+
class Context
|
|
8
|
+
Value = Struct.new(:name, :value, :lineno, :column, :display_offset)
|
|
9
|
+
|
|
10
|
+
def initialize(assertion_proc_or_source, assertion_method, source_binding)
|
|
11
|
+
@fired = false
|
|
12
|
+
@target_thread = Thread.current
|
|
13
|
+
|
|
14
|
+
if assertion_proc_or_source.respond_to?(:to_proc)
|
|
15
|
+
@assertion_proc = assertion_proc_or_source.to_proc
|
|
16
|
+
line = nil
|
|
17
|
+
else
|
|
18
|
+
@assertion_proc = source_binding.eval "Proc.new {#{assertion_proc_or_source}}"
|
|
19
|
+
line = assertion_proc_or_source
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
@parser = Parser::DUMMY
|
|
23
|
+
@trace_call = TracePoint.new(:call, :c_call) do
|
|
24
|
+
if PowerAssert.app_context? and Thread.current == @target_thread
|
|
25
|
+
@trace_call.disable
|
|
26
|
+
locs = PowerAssert.app_caller_locations
|
|
27
|
+
path = locs.last.path
|
|
28
|
+
lineno = locs.last.lineno
|
|
29
|
+
if File.exist?(path)
|
|
30
|
+
line ||= File.open(path) {|fp| fp.each_line.drop(lineno - 1).first }
|
|
31
|
+
end
|
|
32
|
+
if line
|
|
33
|
+
@parser = Parser.new(line, path, lineno, @assertion_proc.binding, assertion_method.to_s, @assertion_proc)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
method_id_set = nil
|
|
39
|
+
@return_values = []
|
|
40
|
+
@trace_return = TracePoint.new(:return, :c_return) do |tp|
|
|
41
|
+
unless method_id_set
|
|
42
|
+
next unless Thread.current == @target_thread
|
|
43
|
+
method_id_set = @parser.method_id_set
|
|
44
|
+
end
|
|
45
|
+
method_id = tp.callee_id
|
|
46
|
+
next if ! method_id_set[method_id]
|
|
47
|
+
next if tp.event == :c_return and
|
|
48
|
+
not (@parser.lineno == tp.lineno and @parser.path == tp.path)
|
|
49
|
+
locs = PowerAssert.app_caller_locations
|
|
50
|
+
if (tp.event == :c_return && locs.length == 1 || tp.event == :return && locs.length <= 2) and Thread.current == @target_thread
|
|
51
|
+
if @parser.path == locs.last.path and @parser.lineno == locs.last.lineno
|
|
52
|
+
val = PowerAssert.configuration.lazy_inspection ?
|
|
53
|
+
tp.return_value :
|
|
54
|
+
InspectedValue.new(SafeInspectable.new(tp.return_value).inspect)
|
|
55
|
+
@return_values << Value[method_id.to_s, val, locs.last.lineno, nil]
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
rescue Exception => e
|
|
59
|
+
warn "power_assert: [BUG] Failed to trace: #{e.class}: #{e.message}"
|
|
60
|
+
if e.respond_to?(:full_message)
|
|
61
|
+
warn e.full_message.gsub(/^/, 'power_assert: ')
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def message
|
|
67
|
+
raise 'call #yield at first' unless fired?
|
|
68
|
+
@message ||= build_assertion_message(@parser, @return_values).freeze
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def message_proc
|
|
72
|
+
-> { message }
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def yield
|
|
76
|
+
@fired = true
|
|
77
|
+
invoke_yield(&@assertion_proc)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
private
|
|
81
|
+
|
|
82
|
+
def invoke_yield
|
|
83
|
+
@trace_return.enable do
|
|
84
|
+
@trace_call.enable do
|
|
85
|
+
yield
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def fired?
|
|
91
|
+
@fired
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def build_assertion_message(parser, return_values)
|
|
95
|
+
if PowerAssert.configuration.colorize_message
|
|
96
|
+
line = IRB::Color.colorize_code(parser.line, ignore_error: true)
|
|
97
|
+
else
|
|
98
|
+
line = parser.line
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
path = detect_path(parser, return_values)
|
|
102
|
+
return line unless path
|
|
103
|
+
|
|
104
|
+
c2d = column2display_offset(parser.line)
|
|
105
|
+
return_values, methods_in_path = find_all_identified_calls(return_values, path)
|
|
106
|
+
return_values.zip(methods_in_path) do |i, j|
|
|
107
|
+
unless i.name == j.name
|
|
108
|
+
warn "power_assert: [BUG] Failed to get column: #{i.name}"
|
|
109
|
+
return line
|
|
110
|
+
end
|
|
111
|
+
i.display_offset = c2d[j.column]
|
|
112
|
+
end
|
|
113
|
+
refs_in_path = path.find_all {|i| i.type == :ref }
|
|
114
|
+
ref_values = refs_in_path.map {|i| Value[i.name, parser.binding.eval(i.name), parser.lineno, i.column, c2d[i.column]] }
|
|
115
|
+
vals = (return_values + ref_values).find_all(&:display_offset).sort_by(&:display_offset).reverse
|
|
116
|
+
return line if vals.empty?
|
|
117
|
+
|
|
118
|
+
fmt = (0..vals[0].display_offset).map do |i|
|
|
119
|
+
if vals.find {|v| v.display_offset == i }
|
|
120
|
+
"%<#{i}>s"
|
|
121
|
+
else
|
|
122
|
+
line[i] == "\t" ? "\t" : ' '
|
|
123
|
+
end
|
|
124
|
+
end.join
|
|
125
|
+
lines = []
|
|
126
|
+
lines << line.chomp
|
|
127
|
+
lines << sprintf(fmt, vals.each_with_object({}) {|v, h| h[:"#{v.display_offset}"] = '|' }).chomp
|
|
128
|
+
vals.each do |i|
|
|
129
|
+
inspected_val = SafeInspectable.new(Inspector.new(i.value, i.display_offset)).inspect
|
|
130
|
+
inspected_val.each_line do |l|
|
|
131
|
+
map_to = vals.each_with_object({}) do |j, h|
|
|
132
|
+
h[:"#{j.display_offset}"] = [l, '|', ' '][i.display_offset <=> j.display_offset]
|
|
133
|
+
end
|
|
134
|
+
lines << encoding_safe_rstrip(sprintf(fmt, map_to))
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
lines.join("\n")
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def detect_path(parser, return_values)
|
|
141
|
+
return parser.call_paths.flatten.uniq if parser.method_id_set.empty?
|
|
142
|
+
all_paths = parser.call_paths
|
|
143
|
+
return_value_names = return_values.map(&:name)
|
|
144
|
+
uniq_calls = uniq_calls(all_paths)
|
|
145
|
+
uniq_call = return_value_names.find {|i| uniq_calls.include?(i) }
|
|
146
|
+
detected_paths = all_paths.find_all do |path|
|
|
147
|
+
method_names = path.find_all {|ident| ident.type == :method }.map(&:name)
|
|
148
|
+
break [path] if uniq_call and method_names.include?(uniq_call)
|
|
149
|
+
return_value_names == method_names
|
|
150
|
+
end
|
|
151
|
+
return nil unless detected_paths.length == 1
|
|
152
|
+
detected_paths[0]
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def uniq_calls(paths)
|
|
156
|
+
all_calls = enum_count_by(paths.map {|path| path.find_all {|ident| ident.type == :method }.map(&:name).uniq }.flatten) {|i| i }
|
|
157
|
+
all_calls.find_all {|_, call_count| call_count == 1 }.map {|name, _| name }
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def find_all_identified_calls(return_values, path)
|
|
161
|
+
return_value_num_of_calls = enum_count_by(return_values, &:name)
|
|
162
|
+
path_num_of_calls = enum_count_by(path.find_all {|ident| ident.type == :method }, &:name)
|
|
163
|
+
identified_calls = return_value_num_of_calls.find_all {|name, num| path_num_of_calls[name] == num }.map(&:first)
|
|
164
|
+
[
|
|
165
|
+
return_values.find_all {|val| identified_calls.include?(val.name) },
|
|
166
|
+
path.find_all {|ident| ident.type == :method and identified_calls.include?(ident.name) }
|
|
167
|
+
]
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def enum_count_by(enum, &blk)
|
|
171
|
+
Hash[enum.group_by(&blk).map{|k, v| [k, v.length] }]
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def encoding_safe_rstrip(str)
|
|
175
|
+
str.rstrip
|
|
176
|
+
rescue ArgumentError, Encoding::CompatibilityError
|
|
177
|
+
enc = str.encoding
|
|
178
|
+
if enc.ascii_compatible?
|
|
179
|
+
str.b.rstrip.force_encoding(enc)
|
|
180
|
+
else
|
|
181
|
+
str
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def column2display_offset(str)
|
|
186
|
+
display_offset = 0
|
|
187
|
+
str.each_char.with_object([]) do |c, r|
|
|
188
|
+
c.bytesize.times do
|
|
189
|
+
r << display_offset
|
|
190
|
+
end
|
|
191
|
+
display_offset += c.ascii_only? ? 1 : 2 # FIXME
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
private_constant :Context
|
|
196
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
require 'power_assert/configuration'
|
|
2
|
+
|
|
3
|
+
if PowerAssert.configuration._redefinition
|
|
4
|
+
module PowerAssert
|
|
5
|
+
# set redefined flag
|
|
6
|
+
basic_classes = [
|
|
7
|
+
Integer, Float, String, Array, Hash, Symbol, Time, Regexp, NilClass, TrueClass, FalseClass
|
|
8
|
+
]
|
|
9
|
+
|
|
10
|
+
basic_operators = [
|
|
11
|
+
:+, :-, :*, :/, :%, :==, :===, :<, :<=, :<<, :[], :[]=, :length, :size,
|
|
12
|
+
:empty?, :nil?, :succ, :>, :>=, :!, :!=, :=~, :freeze, :-@, :max, :min,
|
|
13
|
+
# :call (it is just used for block call optimization)
|
|
14
|
+
:&, :|,
|
|
15
|
+
# :default (no specialized instruction for this)
|
|
16
|
+
:pack, :include?,
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
basic_classes.each do |klass|
|
|
20
|
+
basic_operators.each do |bop|
|
|
21
|
+
if klass.public_method_defined?(bop)
|
|
22
|
+
refine(klass) do
|
|
23
|
+
define_method(bop) {}
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# bypass check_cfunc
|
|
30
|
+
refine BasicObject do
|
|
31
|
+
def !
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def ==
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
refine Module do
|
|
39
|
+
def ==
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
refine Class do
|
|
44
|
+
def new
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# disable optimization
|
|
51
|
+
RubyVM::InstructionSequence.compile_option = {
|
|
52
|
+
specialized_instruction: false
|
|
53
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
require 'power_assert/configuration'
|
|
2
|
+
begin
|
|
3
|
+
require 'io/console/size'
|
|
4
|
+
rescue LoadError
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
module PowerAssert
|
|
8
|
+
class InspectedValue
|
|
9
|
+
def initialize(value)
|
|
10
|
+
@value = value
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def inspect
|
|
14
|
+
@value
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
private_constant :InspectedValue
|
|
18
|
+
|
|
19
|
+
class SafeInspectable
|
|
20
|
+
def initialize(value)
|
|
21
|
+
@value = value
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def inspect
|
|
25
|
+
inspected = @value.inspect
|
|
26
|
+
if Encoding.compatible?(Encoding.default_external, inspected)
|
|
27
|
+
inspected
|
|
28
|
+
else
|
|
29
|
+
begin
|
|
30
|
+
"#{inspected.encode(Encoding.default_external)}(#{inspected.encoding})"
|
|
31
|
+
rescue Encoding::UndefinedConversionError, Encoding::InvalidByteSequenceError
|
|
32
|
+
inspected.force_encoding(Encoding.default_external)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
rescue => e
|
|
36
|
+
"InspectionFailure: #{e.class}: #{e.message.each_line.first}"
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
private_constant :SafeInspectable
|
|
40
|
+
|
|
41
|
+
class Inspector
|
|
42
|
+
def initialize(value, indent)
|
|
43
|
+
@value = value
|
|
44
|
+
@indent = indent
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def inspect
|
|
48
|
+
if PowerAssert.configuration.colorize_message
|
|
49
|
+
if PowerAssert.configuration.inspector == :pp
|
|
50
|
+
console_width = IO.respond_to?(:console_size) ? IO.console_size[1] : 80
|
|
51
|
+
width = [console_width - 1 - @indent, 10].max
|
|
52
|
+
IRB::ColorPrinter.pp(@value, +'', width)
|
|
53
|
+
else
|
|
54
|
+
IRB::Color.colorize_code(@value.to_s, ignore_error: true)
|
|
55
|
+
end
|
|
56
|
+
else
|
|
57
|
+
if PowerAssert.configuration.inspector == :pp
|
|
58
|
+
PP.pp(@value, +'')
|
|
59
|
+
else
|
|
60
|
+
@value.inspect
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
private_constant :Inspector
|
|
66
|
+
end
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
require 'ripper'
|
|
2
|
+
|
|
3
|
+
module PowerAssert
|
|
4
|
+
class Parser
|
|
5
|
+
Ident = Struct.new(:type, :name, :column)
|
|
6
|
+
|
|
7
|
+
attr_reader :line, :path, :lineno, :binding
|
|
8
|
+
|
|
9
|
+
def initialize(line, path, lineno, binding, assertion_method_name = nil, assertion_proc = nil)
|
|
10
|
+
@line = line
|
|
11
|
+
@line_for_parsing = (valid_syntax?(line) ? line : slice_expression(line)).b
|
|
12
|
+
@path = path
|
|
13
|
+
@lineno = lineno
|
|
14
|
+
@binding = binding
|
|
15
|
+
@proc_local_variables = binding.eval('local_variables').map(&:to_s)
|
|
16
|
+
@assertion_method_name = assertion_method_name
|
|
17
|
+
@assertion_proc = assertion_proc
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def idents
|
|
21
|
+
@idents ||= extract_idents(Ripper.sexp(@line_for_parsing))
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def call_paths
|
|
25
|
+
collect_paths(idents).uniq
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def method_id_set
|
|
29
|
+
methods = idents.flatten.find_all {|i| i.type == :method }
|
|
30
|
+
@method_id_set ||= methods.map(&:name).map(&:to_sym).each_with_object({}) {|i, h| h[i] = true }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def valid_syntax?(str)
|
|
36
|
+
verbose, $VERBOSE = $VERBOSE, nil
|
|
37
|
+
RubyVM::InstructionSequence.compile(str)
|
|
38
|
+
true
|
|
39
|
+
rescue SyntaxError
|
|
40
|
+
false
|
|
41
|
+
ensure
|
|
42
|
+
$VERBOSE = verbose
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def slice_expression(str)
|
|
46
|
+
str = str.chomp
|
|
47
|
+
str.sub!(/\A\s*(?:if|unless|elsif|case|while|until) /) {|i| ' ' * i.length }
|
|
48
|
+
str.sub!(/\A\s*(?:\}|\]|end)?\./) {|i| ' ' * i.length }
|
|
49
|
+
str.sub!(/[\{\.\\]\z/, '')
|
|
50
|
+
str.sub!(/(?:&&|\|\|)\z/, '')
|
|
51
|
+
str.sub!(/ (?:do|and|or)\z/, '')
|
|
52
|
+
str
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
class Branch < Array
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
AND_OR_OPS = %i(and or && ||)
|
|
59
|
+
|
|
60
|
+
#
|
|
61
|
+
# Returns idents as graph structure.
|
|
62
|
+
#
|
|
63
|
+
# +--c--b--+
|
|
64
|
+
# extract_idents(Ripper.sexp('a&.b(c).d')) #=> a--+ +--d
|
|
65
|
+
# +--------+
|
|
66
|
+
#
|
|
67
|
+
def extract_idents(sexp)
|
|
68
|
+
case sexp
|
|
69
|
+
in [:arg_paren | :assoc_splat | :fcall | :hash | :method_add_block | :string_literal | :return, s, *]
|
|
70
|
+
extract_idents(s)
|
|
71
|
+
in [:assign | :massign, _, s]
|
|
72
|
+
extract_idents(s)
|
|
73
|
+
in [:opassign, _, [_, op_name, [_, op_column]], s]
|
|
74
|
+
extract_idents(s) + [Ident[:method, op_name.sub(/=\z/, ''), op_column]]
|
|
75
|
+
in [:dyna_symbol, [Symbol, *] => s]
|
|
76
|
+
# s can be [:string_content, [..]] while parsing an expression like { "a": 1 }
|
|
77
|
+
extract_idents(s)
|
|
78
|
+
in [:dyna_symbol, ss]
|
|
79
|
+
ss.flat_map {|s| extract_idents(s) }
|
|
80
|
+
in [:assoclist_from_args | :bare_assoc_hash | :paren | :string_embexpr | :regexp_literal | :xstring_literal, ss, *]
|
|
81
|
+
ss.flat_map {|s| extract_idents(s) }
|
|
82
|
+
in [:command, s0, s1]
|
|
83
|
+
[s1, s0].flat_map {|s| extract_idents(s) }
|
|
84
|
+
in [:assoc_new | :dot2 | :dot3 | :string_content, *ss]
|
|
85
|
+
ss.flat_map {|s| extract_idents(s) }
|
|
86
|
+
in [:unary, mid, s]
|
|
87
|
+
handle_columnless_ident([], mid, extract_idents(s))
|
|
88
|
+
in [:binary, s0, op, s1] if AND_OR_OPS.include?(op)
|
|
89
|
+
extract_idents(s0) + [Branch[extract_idents(s1), []]]
|
|
90
|
+
in [:binary, s0, op, s1]
|
|
91
|
+
handle_columnless_ident(extract_idents(s0), op, extract_idents(s1))
|
|
92
|
+
in [:call, recv, [op_sym, op_name, _], method]
|
|
93
|
+
with_safe_op = ((op_sym == :@op and op_name == '&.') or op_sym == :"&.")
|
|
94
|
+
if method == :call
|
|
95
|
+
handle_columnless_ident(extract_idents(recv), :call, [], with_safe_op)
|
|
96
|
+
else
|
|
97
|
+
extract_idents(recv) + (with_safe_op ? [Branch[extract_idents(method), []]] : extract_idents(method))
|
|
98
|
+
end
|
|
99
|
+
in [:array, ss]
|
|
100
|
+
ss ? ss.flat_map {|s| extract_idents(s) } : []
|
|
101
|
+
in [:command_call, s0, _, s1, s2]
|
|
102
|
+
[s0, s2, s1].flat_map {|s| extract_idents(s) }
|
|
103
|
+
in [:aref, s0, s1]
|
|
104
|
+
handle_columnless_ident(extract_idents(s0), :[], extract_idents(s1))
|
|
105
|
+
in [:method_add_arg, s0, s1]
|
|
106
|
+
case extract_idents(s0)
|
|
107
|
+
in []
|
|
108
|
+
# idents(s0) may be empty(e.g. ->{}.())
|
|
109
|
+
extract_idents(s1)
|
|
110
|
+
in [*is0, Branch[is1, []]]
|
|
111
|
+
# Safe navigation operator is used. See :call clause also.
|
|
112
|
+
is0 + [Branch[extract_idents(s1) + is1, []]]
|
|
113
|
+
in [*is, i]
|
|
114
|
+
is + extract_idents(s1) + [i]
|
|
115
|
+
end
|
|
116
|
+
in [:args_add_block, [:args_add_star, ss0, *ss1], _]
|
|
117
|
+
(ss0 + ss1).flat_map {|s| extract_idents(s) }
|
|
118
|
+
in [:args_add_block, ss, _]
|
|
119
|
+
ss.flat_map {|s| extract_idents(s) }
|
|
120
|
+
in [:vcall, [:@ident, name, [_, column]]]
|
|
121
|
+
[Ident[@proc_local_variables.include?(name) ? :ref : :method, name, column]]
|
|
122
|
+
in [:vcall, _]
|
|
123
|
+
[]
|
|
124
|
+
in [:program, [[:method_add_block, [:method_add_arg, [:fcall, [:@ident | :@const, ^@assertion_method_name, _]], _], [:brace_block | :do_block, _, ss]]]]
|
|
125
|
+
ss.flat_map {|s| extract_idents(s) }
|
|
126
|
+
in [:program, [s, *]]
|
|
127
|
+
extract_idents(s)
|
|
128
|
+
in [:ifop, s0, s1, s2]
|
|
129
|
+
[*extract_idents(s0), Branch[extract_idents(s1), extract_idents(s2)]]
|
|
130
|
+
in [:if | :unless, s0, ss0, [_, ss1]]
|
|
131
|
+
[*extract_idents(s0), Branch[ss0.flat_map {|s| extract_idents(s) }, ss1.flat_map {|s| extract_idents(s) }]]
|
|
132
|
+
in [:if | :unless, s0, ss0, _]
|
|
133
|
+
[*extract_idents(s0), Branch[ss0.flat_map {|s| extract_idents(s) }, []]]
|
|
134
|
+
in [:if_mod | :unless_mod, s0, s1]
|
|
135
|
+
[*extract_idents(s0), Branch[extract_idents(s1), []]]
|
|
136
|
+
in [:var_ref | :var_field, [:@kw, 'self', [_, column]]]
|
|
137
|
+
[Ident[:ref, 'self', column]]
|
|
138
|
+
in [:var_ref | :var_field, [:@ident | :@const | :@cvar | :@ivar | :@gvar, ref_name, [_, column]]]
|
|
139
|
+
[Ident[:ref, ref_name, column]]
|
|
140
|
+
in [:var_ref | :var_field, _]
|
|
141
|
+
[]
|
|
142
|
+
in [:@ident | :@const | :@op, method_name, [_, column]]
|
|
143
|
+
[Ident[:method, method_name, column]]
|
|
144
|
+
else
|
|
145
|
+
[]
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def str_indices(str, re, offset, limit)
|
|
150
|
+
idx = str.index(re, offset)
|
|
151
|
+
if idx and idx <= limit
|
|
152
|
+
[idx, *str_indices(str, re, idx + 1, limit)]
|
|
153
|
+
else
|
|
154
|
+
[]
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
MID2SRCTXT = {
|
|
159
|
+
:[] => '[',
|
|
160
|
+
:+@ => '+',
|
|
161
|
+
:-@ => '-',
|
|
162
|
+
:call => '('
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
def handle_columnless_ident(left_idents, mid, right_idents, with_safe_op = false)
|
|
166
|
+
left_max = left_idents.flatten.max_by(&:column)
|
|
167
|
+
right_min = right_idents.flatten.min_by(&:column)
|
|
168
|
+
bg = left_max ? left_max.column + left_max.name.length : 0
|
|
169
|
+
ed = right_min ? right_min.column - 1 : @line_for_parsing.length - 1
|
|
170
|
+
mname = mid.to_s
|
|
171
|
+
srctxt = MID2SRCTXT[mid] || mname
|
|
172
|
+
re = /
|
|
173
|
+
#{'\b' if /\A\w/ =~ srctxt}
|
|
174
|
+
#{Regexp.escape(srctxt)}
|
|
175
|
+
#{'\b' if /\w\z/ =~ srctxt}
|
|
176
|
+
/x
|
|
177
|
+
indices = str_indices(@line_for_parsing, re, bg, ed)
|
|
178
|
+
if indices.length == 1 or !(right_idents.empty? and left_idents.empty?)
|
|
179
|
+
ident = Ident[:method, mname, right_idents.empty? ? indices.first : indices.last]
|
|
180
|
+
left_idents + right_idents + (with_safe_op ? [Branch[[ident], []]] : [ident])
|
|
181
|
+
else
|
|
182
|
+
left_idents + right_idents
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def collect_paths(idents, prefixes = [[]], index = 0)
|
|
187
|
+
if index < idents.length
|
|
188
|
+
node = idents[index]
|
|
189
|
+
if node.kind_of?(Branch)
|
|
190
|
+
prefixes = node.flat_map {|n| collect_paths(n, prefixes, 0) }
|
|
191
|
+
else
|
|
192
|
+
prefixes = prefixes.map {|prefix| prefix + [node] }
|
|
193
|
+
end
|
|
194
|
+
collect_paths(idents, prefixes, index + 1)
|
|
195
|
+
else
|
|
196
|
+
prefixes
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
class DummyParser < Parser
|
|
201
|
+
def initialize
|
|
202
|
+
super('', nil, nil, TOPLEVEL_BINDING)
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def idents
|
|
206
|
+
[]
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def call_paths
|
|
210
|
+
[]
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
DUMMY = DummyParser.new
|
|
214
|
+
end
|
|
215
|
+
private_constant :Parser
|
|
216
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# power_assert.rb
|
|
2
|
+
#
|
|
3
|
+
# Copyright (C) 2014 Kazuki Tsujimoto
|
|
4
|
+
|
|
5
|
+
begin
|
|
6
|
+
captured = false
|
|
7
|
+
target_thread = Thread.current
|
|
8
|
+
TracePoint.new(:return, :c_return) do |tp|
|
|
9
|
+
next unless Thread.current == target_thread
|
|
10
|
+
captured = true
|
|
11
|
+
unless tp.return_value and tp.callee_id
|
|
12
|
+
raise ''
|
|
13
|
+
end
|
|
14
|
+
end.enable { __id__ }
|
|
15
|
+
raise '' unless captured
|
|
16
|
+
rescue
|
|
17
|
+
raise LoadError, 'Fully compatible TracePoint API required'
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
require 'power_assert/context'
|
|
21
|
+
require 'power_assert/configuration'
|
|
22
|
+
require 'power_assert/version'
|
|
23
|
+
|
|
24
|
+
module PowerAssert
|
|
25
|
+
POWER_ASSERT_LIB_DIR = File.dirname(caller_locations(1, 1).first.path)
|
|
26
|
+
INTERNAL_LIB_DIRS = {PowerAssert => POWER_ASSERT_LIB_DIR}
|
|
27
|
+
private_constant :POWER_ASSERT_LIB_DIR, :INTERNAL_LIB_DIRS
|
|
28
|
+
|
|
29
|
+
class << self
|
|
30
|
+
def start(assertion_proc_or_source, assertion_method: nil, source_binding: TOPLEVEL_BINDING)
|
|
31
|
+
clear_global_method_cache
|
|
32
|
+
yield Context.new(assertion_proc_or_source, assertion_method, source_binding)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def app_caller_locations
|
|
36
|
+
caller_locations.drop_while {|i| internal_file?(i.path) }.take_while {|i| ! internal_file?(i.path) }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def app_context?
|
|
40
|
+
top_frame = caller_locations.drop_while {|i| i.path.start_with?(POWER_ASSERT_LIB_DIR) }.first
|
|
41
|
+
top_frame and ! internal_file?(top_frame.path)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def internal_file?(file)
|
|
47
|
+
INTERNAL_LIB_DIRS.find do |_, dir|
|
|
48
|
+
file.start_with?(dir)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
CLEAR_CACHE_ISEQ = RubyVM::InstructionSequence.compile('using PowerAssert.const_get(:Empty)')
|
|
53
|
+
private_constant :CLEAR_CACHE_ISEQ
|
|
54
|
+
|
|
55
|
+
def clear_global_method_cache
|
|
56
|
+
CLEAR_CACHE_ISEQ.eval
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
module Empty
|
|
61
|
+
end
|
|
62
|
+
private_constant :Empty
|
|
63
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require 'power_assert/version'
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |s|
|
|
7
|
+
s.name = 'power_assert'
|
|
8
|
+
s.version = PowerAssert::VERSION
|
|
9
|
+
s.authors = ['Kazuki Tsujimoto']
|
|
10
|
+
s.email = ['kazuki@callcc.net']
|
|
11
|
+
s.homepage = 'https://github.com/ruby/power_assert'
|
|
12
|
+
s.summary = "Power Assert for Ruby"
|
|
13
|
+
s.description = "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."
|
|
14
|
+
|
|
15
|
+
s.files = `git ls-files -z`.split("\x0").reject do |f|
|
|
16
|
+
f.match(%r{\A(?:test|spec|features|benchmark|bin)/})
|
|
17
|
+
end
|
|
18
|
+
s.bindir = 'exe'
|
|
19
|
+
s.executables = s.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
20
|
+
s.require_paths = ['lib']
|
|
21
|
+
s.extra_rdoc_files = ['README.md']
|
|
22
|
+
s.rdoc_options = ['--main', 'README.md']
|
|
23
|
+
s.licenses = ['BSD-2-Clause', "Ruby"]
|
|
24
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: piko-sharp-gem
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Andrey78
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 2026-07-06 00:00:00.000000000 Z
|
|
11
|
+
dependencies: []
|
|
12
|
+
description: University research based on power_assert
|
|
13
|
+
email:
|
|
14
|
+
- cakoc614@gmail.com
|
|
15
|
+
executables: []
|
|
16
|
+
extensions: []
|
|
17
|
+
extra_rdoc_files: []
|
|
18
|
+
files:
|
|
19
|
+
- piko-sharp-gem.gemspec
|
|
20
|
+
- power_assert-3.0.1/BSDL
|
|
21
|
+
- power_assert-3.0.1/COPYING
|
|
22
|
+
- power_assert-3.0.1/Gemfile
|
|
23
|
+
- power_assert-3.0.1/LEGAL
|
|
24
|
+
- power_assert-3.0.1/README.md
|
|
25
|
+
- power_assert-3.0.1/Rakefile
|
|
26
|
+
- power_assert-3.0.1/lib/power_assert.rb
|
|
27
|
+
- power_assert-3.0.1/lib/power_assert/colorize.rb
|
|
28
|
+
- power_assert-3.0.1/lib/power_assert/configuration.rb
|
|
29
|
+
- power_assert-3.0.1/lib/power_assert/context.rb
|
|
30
|
+
- power_assert-3.0.1/lib/power_assert/enable_tracepoint_events.rb
|
|
31
|
+
- power_assert-3.0.1/lib/power_assert/inspector.rb
|
|
32
|
+
- power_assert-3.0.1/lib/power_assert/parser.rb
|
|
33
|
+
- power_assert-3.0.1/lib/power_assert/version.rb
|
|
34
|
+
- power_assert-3.0.1/power_assert.gemspec
|
|
35
|
+
homepage: https://rubygems.org/profiles/Andrey78
|
|
36
|
+
licenses:
|
|
37
|
+
- MIT
|
|
38
|
+
metadata: {}
|
|
39
|
+
rdoc_options: []
|
|
40
|
+
require_paths:
|
|
41
|
+
- lib
|
|
42
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '0'
|
|
47
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
48
|
+
requirements:
|
|
49
|
+
- - ">="
|
|
50
|
+
- !ruby/object:Gem::Version
|
|
51
|
+
version: '0'
|
|
52
|
+
requirements: []
|
|
53
|
+
rubygems_version: 3.6.2
|
|
54
|
+
specification_version: 4
|
|
55
|
+
summary: Research test
|
|
56
|
+
test_files: []
|