power_assert 2.0.5 → 3.0.0
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 +4 -4
- data/.github/workflows/ci.yml +2 -5
- data/Gemfile +1 -2
- data/README.md +31 -21
- data/Rakefile +0 -1
- data/lib/power_assert/context.rb +43 -80
- data/lib/power_assert/enable_tracepoint_events.rb +38 -47
- data/lib/power_assert/parser.rb +73 -108
- data/lib/power_assert/version.rb +1 -1
- data/lib/power_assert.rb +16 -46
- metadata +3 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 705f51cc47d3a6d949df579d782c02ed246b1dc782794a1855a7bdd412ccbb63
|
|
4
|
+
data.tar.gz: 38ca48e3a3efccafce2c68715c592a62dd98d388925e5bb60088258deaa52273
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a8e8d18cca0c77c0bd0d9092ba167a3c3e99628516b053e37f4f218c9e512eef4c9d348f2ac00276841d0c9ee7470556f99382fd551e2231084f4cebdf052926
|
|
7
|
+
data.tar.gz: f665351eb5861e18676a17e7247c939cf59815df6692b64af1c5aabe4f1d303403ffe60341c940c7f304ffcec9c9596377b91f1c46007c9a51a04a0b24a78e84
|
data/.github/workflows/ci.yml
CHANGED
|
@@ -5,7 +5,7 @@ jobs:
|
|
|
5
5
|
uses: ruby/actions/.github/workflows/ruby_versions.yml@master
|
|
6
6
|
with:
|
|
7
7
|
engine: cruby
|
|
8
|
-
min_version:
|
|
8
|
+
min_version: 3.1
|
|
9
9
|
test:
|
|
10
10
|
needs: ruby-versions
|
|
11
11
|
name: >-
|
|
@@ -21,15 +21,12 @@ jobs:
|
|
|
21
21
|
os: "ubuntu-latest"
|
|
22
22
|
TEST_SYMLINK: yes
|
|
23
23
|
rubyopt: "--enable-frozen-string-literal"
|
|
24
|
-
exclude:
|
|
25
|
-
- ruby-version: "2.5"
|
|
26
|
-
os: "macos-latest"
|
|
27
24
|
runs-on: ${{ matrix.os }}
|
|
28
25
|
env:
|
|
29
26
|
TEST_SYMLINK: ${{ matrix.TEST_SYMLINK }}
|
|
30
27
|
continue-on-error: ${{ matrix.ruby-version == 'head' }}
|
|
31
28
|
steps:
|
|
32
|
-
- uses: actions/checkout@
|
|
29
|
+
- uses: actions/checkout@v5
|
|
33
30
|
- name: Set up Ruby
|
|
34
31
|
uses: ruby/setup-ruby@v1
|
|
35
32
|
with:
|
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -20,79 +20,89 @@ Use following test frameworks or extensions instead.
|
|
|
20
20
|
* [rspec-power_assert](https://github.com/joker1007/rspec-power_assert)
|
|
21
21
|
* [rspec-matchers-power_assert_matchers](https://github.com/kachick/rspec-matchers-power_assert_matchers)
|
|
22
22
|
* [pry-power_assert](https://github.com/yui-knk/pry-power_assert)
|
|
23
|
-
* [pry-byebug-power_assert](https://github.com/k-tsj/pry-byebug-power_assert)
|
|
24
23
|
* [irb-power_assert](https://github.com/kachick/irb-power_assert)
|
|
25
24
|
* [power_p](https://github.com/k-tsj/power_p)
|
|
26
25
|
|
|
27
26
|
## Requirement
|
|
28
|
-
* CRuby
|
|
27
|
+
* CRuby 3.1+
|
|
29
28
|
|
|
30
29
|
## Configuration
|
|
31
30
|
To colorize output messages, add <code>require "power_assert/colorize"</code> to your code.
|
|
32
|
-
(It requires
|
|
31
|
+
(It requires irb 1.3.1+)
|
|
33
32
|
|
|
34
33
|
## Known Limitations
|
|
35
|
-
* Expressions must be
|
|
34
|
+
* Expressions must be on a single line. Splitting an assertion across multiple lines prevents any report from being generated, e.g.:
|
|
36
35
|
|
|
37
36
|
```ruby
|
|
38
37
|
assert do
|
|
39
|
-
#
|
|
38
|
+
# Reported
|
|
40
39
|
func(foo: 0123456789, bar: "abcdefg")
|
|
41
40
|
end
|
|
42
41
|
|
|
43
42
|
assert do
|
|
44
|
-
#
|
|
43
|
+
# Not reported
|
|
45
44
|
func(foo: 0123456789,
|
|
46
45
|
bar: "abcdefg")
|
|
47
46
|
end
|
|
48
47
|
```
|
|
49
48
|
|
|
50
|
-
* Expressions must
|
|
49
|
+
* Expressions must include at least one method call. Assertions without method calls generate no report, e.g.:
|
|
51
50
|
|
|
52
51
|
```ruby
|
|
53
52
|
val = false
|
|
54
53
|
assert do
|
|
55
|
-
#
|
|
54
|
+
# Reported
|
|
56
55
|
val == true
|
|
57
56
|
end
|
|
58
57
|
|
|
59
58
|
assert do
|
|
60
|
-
#
|
|
59
|
+
# Not reported
|
|
61
60
|
val
|
|
62
61
|
end
|
|
63
62
|
```
|
|
64
63
|
|
|
65
|
-
*
|
|
64
|
+
* Return values from `method_missing` or `super` generate no report, e.g.:
|
|
66
65
|
|
|
67
66
|
```ruby
|
|
68
67
|
class Foo
|
|
69
|
-
|
|
68
|
+
def method_missing(*)
|
|
69
|
+
:foo
|
|
70
|
+
end
|
|
70
71
|
end
|
|
71
72
|
foo = Foo.new
|
|
72
|
-
foo.val = false
|
|
73
73
|
|
|
74
74
|
assert do
|
|
75
|
-
# reported
|
|
76
|
-
foo.
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
assert do
|
|
80
|
-
# won't be reported
|
|
81
|
-
foo.val
|
|
75
|
+
# Not reported
|
|
76
|
+
foo.foo
|
|
82
77
|
end
|
|
83
78
|
```
|
|
84
79
|
|
|
85
|
-
*
|
|
80
|
+
* Avoid conditional branches inside assertions. Conditional logic may prevent a report from being generated, e.g.:
|
|
86
81
|
|
|
87
82
|
```ruby
|
|
88
83
|
condition = true
|
|
89
84
|
expected = false
|
|
90
85
|
actual = true
|
|
91
86
|
assert do
|
|
92
|
-
#
|
|
87
|
+
# This fails, but nothing is reported
|
|
93
88
|
condition ? expected == actual : expected == actual
|
|
94
89
|
end
|
|
95
90
|
```
|
|
96
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
|
+
|
|
97
107
|
## Reference
|
|
98
108
|
* [Power Assert in Ruby (at RubyKaigi 2014) // Speaker Deck](https://speakerdeck.com/k_tsj/power-assert-in-ruby)
|
data/Rakefile
CHANGED
|
@@ -8,7 +8,6 @@ Rake::TestTask.new(:test) do |t|
|
|
|
8
8
|
t.ruby_opts = ["-w", "-r#{helper_path}"]
|
|
9
9
|
t.test_files = FileList["test/**/*_test.rb"].exclude do |i|
|
|
10
10
|
begin
|
|
11
|
-
next false unless defined?(RubyVM)
|
|
12
11
|
RubyVM::InstructionSequence.compile(File.read(i))
|
|
13
12
|
false
|
|
14
13
|
rescue SyntaxError
|
data/lib/power_assert/context.rb
CHANGED
|
@@ -7,9 +7,34 @@ module PowerAssert
|
|
|
7
7
|
class Context
|
|
8
8
|
Value = Struct.new(:name, :value, :lineno, :column, :display_offset)
|
|
9
9
|
|
|
10
|
-
def initialize(
|
|
10
|
+
def initialize(assertion_proc_or_source, assertion_method, source_binding)
|
|
11
11
|
@fired = false
|
|
12
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
|
+
|
|
13
38
|
method_id_set = nil
|
|
14
39
|
@return_values = []
|
|
15
40
|
@trace_return = TracePoint.new(:return, :c_return) do |tp|
|
|
@@ -22,14 +47,12 @@ module PowerAssert
|
|
|
22
47
|
next if tp.event == :c_return and
|
|
23
48
|
not (@parser.lineno == tp.lineno and @parser.path == tp.path)
|
|
24
49
|
locs = PowerAssert.app_caller_locations
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
idx = -(base_caller_length + 1)
|
|
28
|
-
if @parser.path == locs[idx].path and @parser.lineno == locs[idx].lineno
|
|
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
|
|
29
52
|
val = PowerAssert.configuration.lazy_inspection ?
|
|
30
53
|
tp.return_value :
|
|
31
54
|
InspectedValue.new(SafeInspectable.new(tp.return_value).inspect)
|
|
32
|
-
@return_values << Value[method_id.to_s, val, locs
|
|
55
|
+
@return_values << Value[method_id.to_s, val, locs.last.lineno, nil]
|
|
33
56
|
end
|
|
34
57
|
end
|
|
35
58
|
rescue Exception => e
|
|
@@ -41,7 +64,7 @@ module PowerAssert
|
|
|
41
64
|
end
|
|
42
65
|
|
|
43
66
|
def message
|
|
44
|
-
raise 'call #yield
|
|
67
|
+
raise 'call #yield at first' unless fired?
|
|
45
68
|
@message ||= build_assertion_message(@parser, @return_values).freeze
|
|
46
69
|
end
|
|
47
70
|
|
|
@@ -49,8 +72,21 @@ module PowerAssert
|
|
|
49
72
|
-> { message }
|
|
50
73
|
end
|
|
51
74
|
|
|
75
|
+
def yield
|
|
76
|
+
@fired = true
|
|
77
|
+
invoke_yield(&@assertion_proc)
|
|
78
|
+
end
|
|
79
|
+
|
|
52
80
|
private
|
|
53
81
|
|
|
82
|
+
def invoke_yield
|
|
83
|
+
@trace_return.enable do
|
|
84
|
+
@trace_call.enable do
|
|
85
|
+
yield
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
54
90
|
def fired?
|
|
55
91
|
@fired
|
|
56
92
|
end
|
|
@@ -157,77 +193,4 @@ module PowerAssert
|
|
|
157
193
|
end
|
|
158
194
|
end
|
|
159
195
|
private_constant :Context
|
|
160
|
-
|
|
161
|
-
class BlockContext < Context
|
|
162
|
-
def initialize(assertion_proc_or_source, assertion_method, source_binding)
|
|
163
|
-
super(0)
|
|
164
|
-
if assertion_proc_or_source.respond_to?(:to_proc)
|
|
165
|
-
@assertion_proc = assertion_proc_or_source.to_proc
|
|
166
|
-
line = nil
|
|
167
|
-
else
|
|
168
|
-
@assertion_proc = source_binding.eval "Proc.new {#{assertion_proc_or_source}}"
|
|
169
|
-
line = assertion_proc_or_source
|
|
170
|
-
end
|
|
171
|
-
@parser = Parser::DUMMY
|
|
172
|
-
@trace_call = TracePoint.new(:call, :c_call) do
|
|
173
|
-
if PowerAssert.app_context? and Thread.current == @target_thread
|
|
174
|
-
@trace_call.disable
|
|
175
|
-
locs = PowerAssert.app_caller_locations
|
|
176
|
-
path = locs.last.path
|
|
177
|
-
lineno = locs.last.lineno
|
|
178
|
-
if File.exist?(path)
|
|
179
|
-
line ||= File.open(path) {|fp| fp.each_line.drop(lineno - 1).first }
|
|
180
|
-
end
|
|
181
|
-
if line
|
|
182
|
-
@parser = Parser.new(line, path, lineno, @assertion_proc.binding, assertion_method.to_s, @assertion_proc)
|
|
183
|
-
end
|
|
184
|
-
end
|
|
185
|
-
end
|
|
186
|
-
end
|
|
187
|
-
|
|
188
|
-
def yield
|
|
189
|
-
@fired = true
|
|
190
|
-
invoke_yield(&@assertion_proc)
|
|
191
|
-
end
|
|
192
|
-
|
|
193
|
-
private
|
|
194
|
-
|
|
195
|
-
def invoke_yield
|
|
196
|
-
@trace_return.enable do
|
|
197
|
-
@trace_call.enable do
|
|
198
|
-
yield
|
|
199
|
-
end
|
|
200
|
-
end
|
|
201
|
-
end
|
|
202
|
-
end
|
|
203
|
-
private_constant :BlockContext
|
|
204
|
-
|
|
205
|
-
class TraceContext < Context
|
|
206
|
-
def initialize(binding)
|
|
207
|
-
target_frame, *base = PowerAssert.app_caller_locations
|
|
208
|
-
super(base.length)
|
|
209
|
-
path = target_frame.path
|
|
210
|
-
lineno = target_frame.lineno
|
|
211
|
-
if File.exist?(path)
|
|
212
|
-
line = File.open(path) {|fp| fp.each_line.drop(lineno - 1).first }
|
|
213
|
-
@parser = Parser.new(line, path, lineno, binding)
|
|
214
|
-
else
|
|
215
|
-
@parser = Parser::DUMMY
|
|
216
|
-
end
|
|
217
|
-
end
|
|
218
|
-
|
|
219
|
-
def enable
|
|
220
|
-
@fired = true
|
|
221
|
-
@trace_return.enable
|
|
222
|
-
end
|
|
223
|
-
|
|
224
|
-
def disable
|
|
225
|
-
@trace_return.disable
|
|
226
|
-
end
|
|
227
|
-
|
|
228
|
-
def enabled?
|
|
229
|
-
@trace_return.enabled?
|
|
230
|
-
end
|
|
231
|
-
end
|
|
232
|
-
private_constant :TraceContext
|
|
233
196
|
end
|
|
@@ -1,62 +1,53 @@
|
|
|
1
1
|
require 'power_assert/configuration'
|
|
2
2
|
|
|
3
|
-
if
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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) {}
|
|
17
24
|
end
|
|
18
25
|
end
|
|
19
|
-
ensure
|
|
20
|
-
$VERBOSE = verbose
|
|
21
26
|
end
|
|
27
|
+
end
|
|
22
28
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
# :call (it is just used for block call optimization)
|
|
27
|
-
:&, :|,
|
|
28
|
-
# :default (no specialized instruction for this)
|
|
29
|
-
:pack, :include?,
|
|
30
|
-
]
|
|
31
|
-
|
|
32
|
-
basic_classes.each do |klass|
|
|
33
|
-
basic_operators.each do |bop|
|
|
34
|
-
if klass.public_method_defined?(bop)
|
|
35
|
-
refine(klass) do
|
|
36
|
-
define_method(bop) {}
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
end
|
|
29
|
+
# bypass check_cfunc
|
|
30
|
+
refine BasicObject do
|
|
31
|
+
def !
|
|
40
32
|
end
|
|
41
33
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
end
|
|
34
|
+
def ==
|
|
35
|
+
end
|
|
36
|
+
end
|
|
46
37
|
|
|
47
|
-
|
|
48
|
-
|
|
38
|
+
refine Module do
|
|
39
|
+
def ==
|
|
49
40
|
end
|
|
41
|
+
end
|
|
50
42
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
end
|
|
43
|
+
refine Class do
|
|
44
|
+
def new
|
|
54
45
|
end
|
|
55
46
|
end
|
|
56
47
|
end
|
|
57
|
-
|
|
58
|
-
# disable optimization
|
|
59
|
-
RubyVM::InstructionSequence.compile_option = {
|
|
60
|
-
specialized_instruction: false
|
|
61
|
-
}
|
|
62
48
|
end
|
|
49
|
+
|
|
50
|
+
# disable optimization
|
|
51
|
+
RubyVM::InstructionSequence.compile_option = {
|
|
52
|
+
specialized_instruction: false
|
|
53
|
+
}
|
data/lib/power_assert/parser.rb
CHANGED
|
@@ -33,16 +33,13 @@ module PowerAssert
|
|
|
33
33
|
private
|
|
34
34
|
|
|
35
35
|
def valid_syntax?(str)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
ensure
|
|
44
|
-
$VERBOSE = verbose
|
|
45
|
-
end
|
|
36
|
+
verbose, $VERBOSE = $VERBOSE, nil
|
|
37
|
+
RubyVM::InstructionSequence.compile(str)
|
|
38
|
+
true
|
|
39
|
+
rescue SyntaxError
|
|
40
|
+
false
|
|
41
|
+
ensure
|
|
42
|
+
$VERBOSE = verbose
|
|
46
43
|
end
|
|
47
44
|
|
|
48
45
|
def slice_expression(str)
|
|
@@ -68,113 +65,81 @@ module PowerAssert
|
|
|
68
65
|
# +--------+
|
|
69
66
|
#
|
|
70
67
|
def extract_idents(sexp)
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
when :binary
|
|
97
|
-
op = sexp[2]
|
|
98
|
-
if AND_OR_OPS.include?(op)
|
|
99
|
-
extract_idents(sexp[1]) + [Branch[extract_idents(sexp[3]), []]]
|
|
100
|
-
else
|
|
101
|
-
handle_columnless_ident(extract_idents(sexp[1]), op, extract_idents(sexp[3]))
|
|
102
|
-
end
|
|
103
|
-
when :call
|
|
104
|
-
_, recv, (op_sym, op_name, _), method = 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]
|
|
105
93
|
with_safe_op = ((op_sym == :@op and op_name == '&.') or op_sym == :"&.")
|
|
106
94
|
if method == :call
|
|
107
95
|
handle_columnless_ident(extract_idents(recv), :call, [], with_safe_op)
|
|
108
96
|
else
|
|
109
97
|
extract_idents(recv) + (with_safe_op ? [Branch[extract_idents(method), []]] : extract_idents(method))
|
|
110
98
|
end
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
[
|
|
115
|
-
|
|
116
|
-
handle_columnless_ident(extract_idents(
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
# idents may be empty(e.g. ->{}.())
|
|
121
|
-
extract_idents(
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
idents[0..-2] + extract_idents(sexp[2]) + [idents[-1]]
|
|
128
|
-
end
|
|
129
|
-
end
|
|
130
|
-
when :args_add_block
|
|
131
|
-
_, (tag, ss0, *ss1), _ = sexp
|
|
132
|
-
if tag == :args_add_star
|
|
133
|
-
(ss0 + ss1).flat_map {|s| extract_idents(s) }
|
|
134
|
-
else
|
|
135
|
-
sexp[1].flat_map {|s| extract_idents(s) }
|
|
136
|
-
end
|
|
137
|
-
when :vcall
|
|
138
|
-
_, (tag, name, (_, column)) = sexp
|
|
139
|
-
if tag == :@ident
|
|
140
|
-
[Ident[@proc_local_variables.include?(name) ? :ref : :method, name, column]]
|
|
141
|
-
else
|
|
142
|
-
[]
|
|
143
|
-
end
|
|
144
|
-
when :program
|
|
145
|
-
_, ((tag0, (tag1, (tag2, (tag3, mname, _)), _), (tag4, _, ss))) = sexp
|
|
146
|
-
if tag0 == :method_add_block and tag1 == :method_add_arg and tag2 == :fcall and
|
|
147
|
-
(tag3 == :@ident or tag3 == :@const) and mname == @assertion_method_name and (tag4 == :brace_block or tag4 == :do_block)
|
|
148
|
-
ss.flat_map {|s| extract_idents(s) }
|
|
149
|
-
else
|
|
150
|
-
_, (s0, *) = sexp
|
|
151
|
-
extract_idents(s0)
|
|
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]
|
|
152
115
|
end
|
|
153
|
-
|
|
154
|
-
|
|
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]
|
|
155
129
|
[*extract_idents(s0), Branch[extract_idents(s1), extract_idents(s2)]]
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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]
|
|
161
135
|
[*extract_idents(s0), Branch[extract_idents(s1), []]]
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
[]
|
|
170
|
-
end
|
|
171
|
-
when :@ident, :@const, :@cvar, :@ivar, :@gvar
|
|
172
|
-
[Ident[:ref, ref_name, column]]
|
|
173
|
-
else
|
|
174
|
-
[]
|
|
175
|
-
end
|
|
176
|
-
when :@ident, :@const, :@op
|
|
177
|
-
_, method_name, (_, column) = sexp
|
|
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]]
|
|
178
143
|
[Ident[:method, method_name, column]]
|
|
179
144
|
else
|
|
180
145
|
[]
|
data/lib/power_assert/version.rb
CHANGED
data/lib/power_assert.rb
CHANGED
|
@@ -3,18 +3,16 @@
|
|
|
3
3
|
# Copyright (C) 2014 Kazuki Tsujimoto
|
|
4
4
|
|
|
5
5
|
begin
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
raise '' unless captured
|
|
17
|
-
end
|
|
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
|
|
18
16
|
rescue
|
|
19
17
|
raise LoadError, 'Fully compatible TracePoint API required'
|
|
20
18
|
end
|
|
@@ -30,21 +28,8 @@ module PowerAssert
|
|
|
30
28
|
|
|
31
29
|
class << self
|
|
32
30
|
def start(assertion_proc_or_source, assertion_method: nil, source_binding: TOPLEVEL_BINDING)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
end
|
|
36
|
-
yield BlockContext.new(assertion_proc_or_source, assertion_method, source_binding)
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def trace(frame)
|
|
40
|
-
begin
|
|
41
|
-
raise 'Byebug is not started yet' unless Byebug.started?
|
|
42
|
-
rescue NameError
|
|
43
|
-
raise "PowerAssert.#{__method__} requires Byebug"
|
|
44
|
-
end
|
|
45
|
-
ctx = TraceContext.new(frame._binding)
|
|
46
|
-
ctx.enable
|
|
47
|
-
ctx
|
|
31
|
+
clear_global_method_cache
|
|
32
|
+
yield Context.new(assertion_proc_or_source, assertion_method, source_binding)
|
|
48
33
|
end
|
|
49
34
|
|
|
50
35
|
def app_caller_locations
|
|
@@ -59,31 +44,16 @@ module PowerAssert
|
|
|
59
44
|
private
|
|
60
45
|
|
|
61
46
|
def internal_file?(file)
|
|
62
|
-
setup_internal_lib_dir(Byebug, :attach, 2) if defined?(Byebug)
|
|
63
|
-
setup_internal_lib_dir(PryByebug, :start_with_pry_byebug, 2, Pry) if defined?(PryByebug)
|
|
64
47
|
INTERNAL_LIB_DIRS.find do |_, dir|
|
|
65
48
|
file.start_with?(dir)
|
|
66
49
|
end
|
|
67
50
|
end
|
|
68
51
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
INTERNAL_LIB_DIRS[lib] = lib_dir(lib_obj, mid, depth)
|
|
72
|
-
end
|
|
73
|
-
rescue NameError
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
def lib_dir(obj, mid, depth)
|
|
77
|
-
File.expand_path('../' * depth, obj.method(mid).source_location[0])
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
if defined?(RubyVM)
|
|
81
|
-
CLEAR_CACHE_ISEQ = RubyVM::InstructionSequence.compile('using PowerAssert.const_get(:Empty)')
|
|
82
|
-
private_constant :CLEAR_CACHE_ISEQ
|
|
52
|
+
CLEAR_CACHE_ISEQ = RubyVM::InstructionSequence.compile('using PowerAssert.const_get(:Empty)')
|
|
53
|
+
private_constant :CLEAR_CACHE_ISEQ
|
|
83
54
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
end
|
|
55
|
+
def clear_global_method_cache
|
|
56
|
+
CLEAR_CACHE_ISEQ.eval
|
|
87
57
|
end
|
|
88
58
|
end
|
|
89
59
|
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: power_assert
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 3.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kazuki Tsujimoto
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: exe
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies: []
|
|
13
12
|
description: Power Assert shows each value of variables and method calls in the expression.
|
|
14
13
|
It is useful for testing, providing which value wasn't correct when the condition
|
|
@@ -43,7 +42,6 @@ licenses:
|
|
|
43
42
|
- BSD-2-Clause
|
|
44
43
|
- Ruby
|
|
45
44
|
metadata: {}
|
|
46
|
-
post_install_message:
|
|
47
45
|
rdoc_options:
|
|
48
46
|
- "--main"
|
|
49
47
|
- README.md
|
|
@@ -60,8 +58,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
60
58
|
- !ruby/object:Gem::Version
|
|
61
59
|
version: '0'
|
|
62
60
|
requirements: []
|
|
63
|
-
rubygems_version: 3.
|
|
64
|
-
signing_key:
|
|
61
|
+
rubygems_version: 3.6.9
|
|
65
62
|
specification_version: 4
|
|
66
63
|
summary: Power Assert for Ruby
|
|
67
64
|
test_files: []
|