raap 0.5.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -1
- data/README.md +63 -2
- data/Rakefile +3 -1
- data/lib/raap/cli.rb +30 -15
- data/lib/raap/coverage.rb +188 -0
- data/lib/raap/function_type.rb +36 -16
- data/lib/raap/method_property.rb +47 -16
- data/lib/raap/method_type.rb +17 -8
- data/lib/raap/minitest.rb +2 -2
- data/lib/raap/rbs.rb +42 -0
- data/lib/raap/symbolic_caller.rb +2 -2
- data/lib/raap/type.rb +53 -45
- data/lib/raap/type_substitution.rb +7 -3
- data/lib/raap/value/interface.rb +13 -24
- data/lib/raap/value/intersection.rb +7 -8
- data/lib/raap/value/module.rb +1 -1
- data/lib/raap/value/variable.rb +1 -1
- data/lib/raap/version.rb +1 -1
- data/lib/raap.rb +2 -0
- data/lib/shims.rb +38 -0
- data/public/example.webp +0 -0
- data/rbs_collection.lock.yaml +5 -5
- data/sig/raap.rbs +52 -11
- metadata +7 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 13f734d53db009621045979dfc7d705b14c2f378625ff36067784995e8889134
|
4
|
+
data.tar.gz: 472adf32ff8ea739d83a5b41c6a62e80149b29e34d44536df844010eb2e8cb90
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cc3782c7a65e098eef727163ccc069ca27b339eeaadc7be44abd2eac94c78fa74443c413364e579998dd49aa11c5be38bd27f6504f92759580ec893dd54a3e97
|
7
|
+
data.tar.gz: 68e133fa67a03d4c84ab264c1e0fd1c40204da8995a552e99912eb61c1cddb14d9c81bac26c4aa04615d9833dfa74ad64bd212071dd3195dacad6ae85bd23b54
|
data/.rubocop.yml
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
AllCops:
|
2
|
-
TargetRubyVersion: 3.
|
2
|
+
TargetRubyVersion: 3.0
|
3
3
|
NewCops: enable
|
4
4
|
Include:
|
5
5
|
- 'lib/**/*.rb'
|
@@ -8,6 +8,8 @@ Lint:
|
|
8
8
|
Enabled: true
|
9
9
|
Lint/EmptyBlock:
|
10
10
|
Enabled: false
|
11
|
+
Lint/SymbolConversion:
|
12
|
+
Enabled: false
|
11
13
|
|
12
14
|
Style:
|
13
15
|
Enabled: false
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# RaaP
|
2
2
|
|
3
|
-
<img src="https://raw.githubusercontent.com/ksss/raap/main/public/jacket.webp" width=
|
3
|
+
<img src="https://raw.githubusercontent.com/ksss/raap/main/public/jacket.webp" width=500/>
|
4
4
|
|
5
5
|
## RBS as a Property
|
6
6
|
|
@@ -14,6 +14,10 @@ The return value of the method is checked to see if it matches the type, if not,
|
|
14
14
|
|
15
15
|
If you write an RBS, it becomes a test case.
|
16
16
|
|
17
|
+
## Example
|
18
|
+
|
19
|
+
<img src="https://raw.githubusercontent.com/ksss/raap/main/public/example.webp" width=700/>
|
20
|
+
|
17
21
|
## Concept
|
18
22
|
|
19
23
|
If you has next signature.
|
@@ -70,7 +74,7 @@ $ raap 'MyClass' # Run only RBS of MyClass
|
|
70
74
|
$ raap 'MyClass::*' # Run each class under MyClass
|
71
75
|
$ raap 'MyClass.singleton_method' # Run only MyClass.singleton_method
|
72
76
|
$ raap 'MyClass#instance_method' # Run only MyClass#instance_method
|
73
|
-
$ raap 'MyClass' '!MyClass#skip' # Run
|
77
|
+
$ raap 'MyClass' '!MyClass#skip' # Run methods MyClass without #skip
|
74
78
|
```
|
75
79
|
|
76
80
|
```
|
@@ -81,6 +85,27 @@ MyClass
|
|
81
85
|
$ raap $(cat test/raap.txt) # You can manage the methods to be tested in a file
|
82
86
|
```
|
83
87
|
|
88
|
+
## Log level definition
|
89
|
+
|
90
|
+
- error: Information on status of inability to continue execution.
|
91
|
+
- warn: Information on partial corrections required.
|
92
|
+
- info: A somewhat visualized representation of the execution state.
|
93
|
+
- debug: All information including stack traces.
|
94
|
+
|
95
|
+
## Coverage
|
96
|
+
|
97
|
+
RaaP randomly selects a type and generates a value for optional and union types.
|
98
|
+
|
99
|
+
By default, displays the coverage of the type that actually produced the value.
|
100
|
+
|
101
|
+
The coverage display can help you determine if a correction is needed.
|
102
|
+
|
103
|
+
### Meaning of colored types
|
104
|
+
|
105
|
+
- 🔴 Red: A type that has never been used to generate or validate a value.
|
106
|
+
- 🟡 Yellow: It is systematically difficult to determine if the type was used or not. (FIXME)
|
107
|
+
- 🟢 Green: The type used to generate and validate the value.
|
108
|
+
|
84
109
|
## Size
|
85
110
|
|
86
111
|
Random values are determined based on size.
|
@@ -143,6 +168,42 @@ You can specify size of step like `Integer#step: (to: Integer, by: Integer)`.
|
|
143
168
|
By default, raap only validates public methods.
|
144
169
|
However, by setting this option, it is possible to intentionally validate private methods in the RBS.
|
145
170
|
|
171
|
+
### `--preload path`
|
172
|
+
|
173
|
+
Simply call `Kernel.load`.
|
174
|
+
However, this simplifies the preliminary preparation.
|
175
|
+
|
176
|
+
#### Preload examples
|
177
|
+
|
178
|
+
1) Check signature of aws-sdk-s3
|
179
|
+
|
180
|
+
```rb
|
181
|
+
$ cat preload.rb
|
182
|
+
require 'aws-sdk-s3'
|
183
|
+
|
184
|
+
# Register initialize of `Aws::S3::Client` class.
|
185
|
+
RaaP::Type.register("Aws::S3::Client") do
|
186
|
+
[:call, Aws::S3::Client, :new, [], { stub_responses: true }, nil]
|
187
|
+
end
|
188
|
+
|
189
|
+
$ raap --preload ./preload.rb 'Aws::S3::Client#get_object'
|
190
|
+
```
|
191
|
+
|
192
|
+
2) Output ruby coverage by simplecov
|
193
|
+
|
194
|
+
```rb
|
195
|
+
$ cat preload.rb
|
196
|
+
require 'simplecov'
|
197
|
+
|
198
|
+
SimpleCov.command_name 'raap'
|
199
|
+
SimpleCov.start do
|
200
|
+
enable_coverage :branch
|
201
|
+
add_filter "/test/"
|
202
|
+
end
|
203
|
+
|
204
|
+
$ bundle exec raap --preload ./preload.rb -r my_class 'MyClass'
|
205
|
+
```
|
206
|
+
|
146
207
|
## First support is CLI
|
147
208
|
|
148
209
|
In RaaP, usage through the CLI is the primary support focus, and efforts are made to maintain compatibility. The use of the library's code (such as `RaaP::Type`) does not guarantee compatibility.
|
data/Rakefile
CHANGED
data/lib/raap/cli.rb
CHANGED
@@ -12,6 +12,7 @@ module RaaP
|
|
12
12
|
:size_to,
|
13
13
|
:size_by,
|
14
14
|
:allow_private,
|
15
|
+
:coverage,
|
15
16
|
keyword_init: true
|
16
17
|
)
|
17
18
|
|
@@ -44,6 +45,7 @@ module RaaP
|
|
44
45
|
size_from: 0,
|
45
46
|
size_to: 99,
|
46
47
|
size_by: 1,
|
48
|
+
coverage: true,
|
47
49
|
allow_private: false,
|
48
50
|
)
|
49
51
|
@argv = argv
|
@@ -80,6 +82,12 @@ module RaaP
|
|
80
82
|
o.on('--allow-private', "default: #{@option.allow_private}") do
|
81
83
|
@option.allow_private = true
|
82
84
|
end
|
85
|
+
o.on('--preload path', 'Kernel.load path') do |path|
|
86
|
+
Kernel.load path
|
87
|
+
end
|
88
|
+
o.on('--[no-]coverage', "Show coverage for RBS (default: #{@option.coverage})") do |arg|
|
89
|
+
@option.coverage = arg
|
90
|
+
end
|
83
91
|
end.parse!(@argv)
|
84
92
|
|
85
93
|
@option.dirs.each do |dir|
|
@@ -104,7 +112,7 @@ module RaaP
|
|
104
112
|
|
105
113
|
# Search skip tag
|
106
114
|
@argv.each do |tag|
|
107
|
-
if tag.start_with?('!')
|
115
|
+
if tag.start_with?('!')
|
108
116
|
t = tag[1..] or raise
|
109
117
|
t = "::#{t}" unless t.start_with?('::')
|
110
118
|
t or raise
|
@@ -118,13 +126,13 @@ module RaaP
|
|
118
126
|
|
119
127
|
case
|
120
128
|
when tag.include?('#')
|
121
|
-
run_by(kind: :instance, tag:)
|
129
|
+
run_by(kind: :instance, tag: tag)
|
122
130
|
when tag.include?('.')
|
123
|
-
run_by(kind: :singleton, tag:)
|
131
|
+
run_by(kind: :singleton, tag: tag)
|
124
132
|
when tag.end_with?('*')
|
125
|
-
run_by_type_name_with_search(tag:)
|
133
|
+
run_by_type_name_with_search(tag: tag)
|
126
134
|
else
|
127
|
-
run_by_type_name(tag:)
|
135
|
+
run_by_type_name(tag: tag)
|
128
136
|
end
|
129
137
|
end
|
130
138
|
|
@@ -198,9 +206,10 @@ module RaaP
|
|
198
206
|
|
199
207
|
RaaP.logger.info("# #{type}")
|
200
208
|
@results << {
|
201
|
-
method
|
209
|
+
method: method,
|
202
210
|
properties: method.method_types.map do |method_type|
|
203
|
-
property(receiver_type
|
211
|
+
property(receiver_type: receiver_type, type_params_decl: type_params_decl, type_args: type_args, method_type: method_type,
|
212
|
+
method_name: method_name)
|
204
213
|
end
|
205
214
|
}
|
206
215
|
end
|
@@ -238,9 +247,10 @@ module RaaP
|
|
238
247
|
|
239
248
|
RaaP.logger.info("# #{type_name}.#{method_name}")
|
240
249
|
@results << {
|
241
|
-
method
|
250
|
+
method: method,
|
242
251
|
properties: method.method_types.map do |method_type|
|
243
|
-
property(receiver_type: Type.new("singleton(#{type.name})"), type_params_decl
|
252
|
+
property(receiver_type: Type.new("singleton(#{type.name})"), type_params_decl: type_params_decl, type_args: type_args,
|
253
|
+
method_type: method_type, method_name: method_name)
|
244
254
|
end
|
245
255
|
}
|
246
256
|
end
|
@@ -257,9 +267,10 @@ module RaaP
|
|
257
267
|
|
258
268
|
RaaP.logger.info("# #{type_name}##{method_name}")
|
259
269
|
@results << {
|
260
|
-
method
|
270
|
+
method: method,
|
261
271
|
properties: method.method_types.map do |method_type|
|
262
|
-
property(receiver_type: Type.new(type.name), type_params_decl
|
272
|
+
property(receiver_type: Type.new(type.name), type_params_decl: type_params_decl, type_args: type_args, method_type: method_type,
|
273
|
+
method_name: method_name)
|
263
274
|
end
|
264
275
|
}
|
265
276
|
end
|
@@ -283,12 +294,12 @@ module RaaP
|
|
283
294
|
status = 0
|
284
295
|
reason = nil
|
285
296
|
prop = MethodProperty.new(
|
286
|
-
receiver_type
|
287
|
-
method_name
|
297
|
+
receiver_type: receiver_type,
|
298
|
+
method_name: method_name,
|
288
299
|
method_type: MethodType.new(
|
289
300
|
method_type,
|
290
|
-
type_params_decl
|
291
|
-
type_args
|
301
|
+
type_params_decl: type_params_decl,
|
302
|
+
type_args: type_args,
|
292
303
|
self_type: rtype,
|
293
304
|
instance_type: ::RBS::Types::ClassInstance.new(name: rtype.name, args: type_args, location: nil),
|
294
305
|
class_type: ::RBS::Types::ClassSingleton.new(name: rtype.name, location: nil),
|
@@ -297,6 +308,7 @@ module RaaP
|
|
297
308
|
timeout: @option.timeout,
|
298
309
|
allow_private: @option.allow_private,
|
299
310
|
)
|
311
|
+
RaaP::Coverage.start(method_type) if @option.coverage
|
300
312
|
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
301
313
|
stats = prop.run do |called|
|
302
314
|
case called
|
@@ -334,6 +346,9 @@ module RaaP
|
|
334
346
|
end
|
335
347
|
end_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
336
348
|
puts
|
349
|
+
RaaP::Coverage.show($stdout) if @option.coverage
|
350
|
+
puts
|
351
|
+
|
337
352
|
time_diff = end_time - start_time
|
338
353
|
time = ", time: #{(time_diff * 1000).round}ms"
|
339
354
|
stats_log = "success: #{stats.success}, skip: #{stats.skip}, exception: #{stats.exception}#{time}"
|
@@ -0,0 +1,188 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RaaP
|
4
|
+
module Coverage
|
5
|
+
class Writer
|
6
|
+
def initialize(method_type, cov)
|
7
|
+
@method_type = method_type
|
8
|
+
@cov = cov
|
9
|
+
@cur = 0
|
10
|
+
end
|
11
|
+
|
12
|
+
def write(io)
|
13
|
+
RaaP.logger.debug { "Coverage: #{@cov}" }
|
14
|
+
ml = @method_type.location
|
15
|
+
unless ml
|
16
|
+
RaaP.logger.warn("No location information for `#{@method_type}`")
|
17
|
+
return
|
18
|
+
end
|
19
|
+
if ml.key?(:keyword)
|
20
|
+
# attr_{reader,writer,accessor}
|
21
|
+
phantom_member = RBS.parse_member(ml.source)
|
22
|
+
case phantom_member
|
23
|
+
when ::RBS::AST::Members::Attribute
|
24
|
+
unless phantom_member.location
|
25
|
+
RaaP.logger.warn("No location information for `#{phantom_member}`")
|
26
|
+
return
|
27
|
+
end
|
28
|
+
write_type(io, "return", phantom_member.type, :abs)
|
29
|
+
io.write(slice(@cur, @cur...phantom_member.location.end_pos))
|
30
|
+
else
|
31
|
+
RaaP.logger.error("#{phantom_member.class} is not supported")
|
32
|
+
return
|
33
|
+
end
|
34
|
+
else
|
35
|
+
# def name: () -> type
|
36
|
+
phantom_method_type = RBS.parse_method_type(ml.source)
|
37
|
+
phantom_method_type.type.yield_self do |fun|
|
38
|
+
case fun
|
39
|
+
when ::RBS::Types::Function
|
40
|
+
fun.required_positionals.each_with_index { |param, i| write_param(io, "req_#{i}", param, :abs) }
|
41
|
+
fun.optional_positionals.each_with_index { |param, i| write_param(io, "opt_#{i}", param, :opt) }
|
42
|
+
fun.rest_positionals&.yield_self { |param| write_param(io, "rest", param, :opt) }
|
43
|
+
fun.trailing_positionals.each_with_index { |param, i| write_param(io, "trail_#{i}", param, :abs) }
|
44
|
+
fun.required_keywords.each { |key, param| write_param(io, "keyreq_#{key}", param, :abs) }
|
45
|
+
fun.optional_keywords.each { |key, param| write_param(io, "key_#{key}", param, :opt) }
|
46
|
+
fun.rest_keywords&.yield_self { |param| write_param(io, "keyrest", param, :opt) }
|
47
|
+
# when ::RBS::Types::UntypedFunction
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
phantom_method_type.block&.yield_self do |b|
|
52
|
+
b.type.each_param.with_index { |param, i| write_param(io, "block_param_#{i}", param, :opt) }
|
53
|
+
write_type(io, "block_return", b.type.return_type, :abs)
|
54
|
+
end
|
55
|
+
write_type(io, "return", phantom_method_type.type.return_type, :abs)
|
56
|
+
raise unless phantom_method_type.location
|
57
|
+
|
58
|
+
io.write(slice(@cur, @cur...phantom_method_type.location.end_pos))
|
59
|
+
end
|
60
|
+
|
61
|
+
io.puts
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def slice(start, range)
|
67
|
+
ml = @method_type.location
|
68
|
+
raise unless ml
|
69
|
+
|
70
|
+
ml.source[start, range.end - range.begin] or raise
|
71
|
+
end
|
72
|
+
|
73
|
+
def write_param(io, position, param, accuracy)
|
74
|
+
write_type(io, position, param.type, accuracy)
|
75
|
+
end
|
76
|
+
|
77
|
+
def write_type(io, position, type, accuracy)
|
78
|
+
unless type.location
|
79
|
+
RaaP.logger.warn("No location information for `#{type}`")
|
80
|
+
return
|
81
|
+
end
|
82
|
+
io.write(slice(@cur, @cur...type.location.start_pos))
|
83
|
+
@cur = type.location.start_pos
|
84
|
+
case type
|
85
|
+
when ::RBS::Types::Tuple, ::RBS::Types::Union
|
86
|
+
name = type.class.name.split('::').last.downcase
|
87
|
+
type.types.each_with_index do |t, i|
|
88
|
+
t.location or raise
|
89
|
+
io.write(slice(@cur, @cur...t.location.start_pos)) # ( or [
|
90
|
+
@cur = t.location.start_pos
|
91
|
+
write_type(io, "#{position}_#{name}_#{i}", t, accuracy)
|
92
|
+
end
|
93
|
+
when ::RBS::Types::Optional
|
94
|
+
raise unless type.location
|
95
|
+
|
96
|
+
write_type(io, "#{position}_optional_left", type.type, accuracy)
|
97
|
+
io.write(slice(@cur, @cur...(type.location.end_pos - 1)))
|
98
|
+
@cur = type.location.end_pos - 1
|
99
|
+
if @cov.include?("#{position}_optional_right".to_sym)
|
100
|
+
io.write(green('?'))
|
101
|
+
else
|
102
|
+
io.write(red('?'))
|
103
|
+
end
|
104
|
+
raise unless type.location
|
105
|
+
|
106
|
+
@cur = type.location.end_pos
|
107
|
+
when ::RBS::Types::Variable
|
108
|
+
case accuracy
|
109
|
+
when :abs
|
110
|
+
io.write(green(type.name.to_s))
|
111
|
+
when :opt
|
112
|
+
# Variables are substed so raap don't know if they've been used.
|
113
|
+
io.write(yellow(type.name.to_s))
|
114
|
+
end
|
115
|
+
raise unless type.location
|
116
|
+
|
117
|
+
@cur = type.location.end_pos
|
118
|
+
else
|
119
|
+
raise unless type.location
|
120
|
+
|
121
|
+
if @cov.include?(position.to_sym)
|
122
|
+
io.write(green(type.location.source))
|
123
|
+
else
|
124
|
+
io.write(red(type.location.source))
|
125
|
+
end
|
126
|
+
@cur = type.location.end_pos
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def green(str) = "\e[32m#{str}\e[0m"
|
131
|
+
def red(str) = "\e[1;4;41m#{str}\e[0m"
|
132
|
+
def yellow(str) = "\e[93m#{str}\e[0m"
|
133
|
+
end
|
134
|
+
|
135
|
+
class << self
|
136
|
+
def start(method_type)
|
137
|
+
@cov = Set.new
|
138
|
+
@method_type = method_type
|
139
|
+
end
|
140
|
+
|
141
|
+
def running?
|
142
|
+
!!@cov
|
143
|
+
end
|
144
|
+
|
145
|
+
# position: req_0
|
146
|
+
# type: union_1
|
147
|
+
def log(position)
|
148
|
+
return unless running?
|
149
|
+
|
150
|
+
cov << position.to_sym
|
151
|
+
end
|
152
|
+
|
153
|
+
def cov
|
154
|
+
@cov or raise
|
155
|
+
end
|
156
|
+
|
157
|
+
def show(io)
|
158
|
+
return unless running?
|
159
|
+
|
160
|
+
writer = Writer.new(@method_type, cov)
|
161
|
+
writer.write(io)
|
162
|
+
end
|
163
|
+
|
164
|
+
def new_type_with_log(position, type)
|
165
|
+
case type
|
166
|
+
when ::RBS::Types::Tuple
|
167
|
+
# FIXME: Support Union in Tuple
|
168
|
+
type.types.each_with_index do |_t, i|
|
169
|
+
log("#{position}_tuple_#{i}")
|
170
|
+
end
|
171
|
+
Type.new(type)
|
172
|
+
when ::RBS::Types::Union
|
173
|
+
i = Random.rand(type.types.length)
|
174
|
+
new_type_with_log("#{position}_union_#{i}", type.types[i])
|
175
|
+
when ::RBS::Types::Optional
|
176
|
+
if Random.rand(2).zero?
|
177
|
+
new_type_with_log("#{position}_optional_left", type.type)
|
178
|
+
else
|
179
|
+
new_type_with_log("#{position}_optional_right", ::RBS::Types::Bases::Nil.new(location: nil))
|
180
|
+
end
|
181
|
+
else
|
182
|
+
log(position)
|
183
|
+
Type.new(type)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
data/lib/raap/function_type.rb
CHANGED
@@ -7,12 +7,12 @@ module RaaP
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def pick_arguments(size: 10)
|
10
|
-
SymbolicCaller.new(arguments_to_symbolic_call(size:)).eval
|
10
|
+
SymbolicCaller.new(arguments_to_symbolic_call(size: size)).eval
|
11
11
|
end
|
12
12
|
|
13
13
|
def arguments_to_symbolic_call(size: 10)
|
14
|
-
a = to_symbolic_call_recursive(build_args_type, size:)
|
15
|
-
k = to_symbolic_call_recursive(build_kwargs_type, size:)
|
14
|
+
a = to_symbolic_call_recursive(build_args_type, size: size)
|
15
|
+
k = to_symbolic_call_recursive(build_kwargs_type, size: size)
|
16
16
|
|
17
17
|
[a, k]
|
18
18
|
end
|
@@ -24,44 +24,64 @@ module RaaP
|
|
24
24
|
when type.nil?
|
25
25
|
nil
|
26
26
|
when type.respond_to?(:each_pair)
|
27
|
-
type.each_pair.to_h { |k, v| [k, to_symbolic_call_recursive(v, size:)] }
|
27
|
+
type.each_pair.to_h { |k, v| [k, to_symbolic_call_recursive(v, size: size)] }
|
28
28
|
when type.respond_to?(:each)
|
29
|
-
type.
|
29
|
+
type.map { |v| to_symbolic_call_recursive(v, size: size) }
|
30
30
|
else
|
31
|
-
type.to_symbolic_call(size:)
|
31
|
+
type.to_symbolic_call(size: size)
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
35
|
def build_args_type
|
36
|
-
reqs = @fun.required_positionals.map
|
37
|
-
|
38
|
-
|
39
|
-
|
36
|
+
reqs = @fun.required_positionals.map.with_index do |param, i|
|
37
|
+
build_type_with_coverage("req_#{i}", param)
|
38
|
+
end
|
39
|
+
|
40
|
+
take_num = Random.rand(@fun.optional_positionals.length + 1)
|
41
|
+
opts = @fun.optional_positionals.take(take_num).map.each_with_index do |param, i|
|
42
|
+
build_type_with_coverage("opt_#{i}", param)
|
43
|
+
end
|
44
|
+
|
40
45
|
rest = []
|
41
|
-
if (
|
42
|
-
rest = Array.new(Random.rand(
|
46
|
+
if (rest_param = @fun.rest_positionals)
|
47
|
+
rest = Array.new(Random.rand(4)) do
|
48
|
+
build_type_with_coverage("rest", rest_param)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
tras = @fun.trailing_positionals.map.with_index do |param, i|
|
53
|
+
build_type_with_coverage("trail_#{i}", param)
|
43
54
|
end
|
55
|
+
|
44
56
|
[reqs, opts, rest, tras].flatten
|
45
57
|
end
|
46
58
|
|
47
59
|
def build_kwargs_type
|
48
|
-
reqs = @fun.required_keywords.
|
60
|
+
reqs = @fun.required_keywords.keys.to_h do |key|
|
61
|
+
[key, build_type_with_coverage("keyreq_#{key}", @fun.required_keywords[key])]
|
62
|
+
end
|
49
63
|
rand = Random.rand(@fun.optional_keywords.length + 1)
|
50
|
-
opts = @fun.optional_keywords.to_a.sample(rand).to_h
|
64
|
+
opts = @fun.optional_keywords.to_a.sample(rand).to_h do |key, param|
|
65
|
+
[key, build_type_with_coverage("key_#{key}", param)]
|
66
|
+
end
|
51
67
|
kwargs = reqs.to_h.merge(opts)
|
52
68
|
if (param = @fun.rest_keywords)
|
53
|
-
keys = Array.new(Random.rand(
|
69
|
+
keys = Array.new(Random.rand(4)) do
|
54
70
|
random_key = nil
|
55
71
|
loop do
|
56
72
|
# @type var random_key: Symbol
|
57
73
|
random_key = Type.new("Symbol").pick(size: 6)
|
58
74
|
break unless kwargs.key?(random_key)
|
59
75
|
end
|
60
|
-
[random_key,
|
76
|
+
[random_key, build_type_with_coverage("keyrest", param)]
|
61
77
|
end
|
62
78
|
kwargs.merge!(keys.to_h)
|
63
79
|
end
|
64
80
|
kwargs
|
65
81
|
end
|
82
|
+
|
83
|
+
def build_type_with_coverage(position, param)
|
84
|
+
Coverage.new_type_with_log(position, param.type)
|
85
|
+
end
|
66
86
|
end
|
67
87
|
end
|
data/lib/raap/method_property.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module RaaP
|
4
4
|
class MethodProperty
|
5
|
-
class Stats < Struct.new(:success, :skip, :exception, :break)
|
5
|
+
class Stats < Struct.new(:success, :skip, :exception, :break, keyword_init: true)
|
6
6
|
def initialize(success: 0, skip: 0, exception: 0, break: false)
|
7
7
|
super
|
8
8
|
end
|
@@ -23,7 +23,7 @@ module RaaP
|
|
23
23
|
Timeout.timeout(@timeout) do
|
24
24
|
catch(:break) do
|
25
25
|
@size_step.each do |size|
|
26
|
-
call(size
|
26
|
+
call(size: size, stats: stats).tap do |ret|
|
27
27
|
case ret
|
28
28
|
when Result::Success
|
29
29
|
stats.success += 1
|
@@ -55,8 +55,8 @@ module RaaP
|
|
55
55
|
stats.break = true
|
56
56
|
throw :break
|
57
57
|
end
|
58
|
-
receiver_value = @receiver_type.to_symbolic_call(size:)
|
59
|
-
args, kwargs, block = @method_type.arguments_to_symbolic_call(size:)
|
58
|
+
receiver_value = @receiver_type.to_symbolic_call(size: size)
|
59
|
+
args, kwargs, block = @method_type.arguments_to_symbolic_call(size: size)
|
60
60
|
# @type var symbolic_call: symbolic_call
|
61
61
|
symbolic_call = [:call, receiver_value, @method_name, args, kwargs, block]
|
62
62
|
symbolic_caller = SymbolicCaller.new(symbolic_call, allow_private: @allow_private)
|
@@ -67,8 +67,9 @@ module RaaP
|
|
67
67
|
begin
|
68
68
|
return_value = symbolic_caller.eval
|
69
69
|
rescue StandardError, NotImplementedError
|
70
|
-
check = [:success]
|
71
70
|
return_value = Value::Bottom.new
|
71
|
+
coverage("return", return_value, return_type)
|
72
|
+
check = [:success]
|
72
73
|
rescue Timeout::ExitException
|
73
74
|
raise
|
74
75
|
rescue Exception => e # rubocop:disable Lint/RescueException
|
@@ -77,25 +78,25 @@ module RaaP
|
|
77
78
|
end
|
78
79
|
else
|
79
80
|
return_value = symbolic_caller.eval
|
80
|
-
check = check_return(receiver_value
|
81
|
+
check = check_return(receiver_value: receiver_value, return_value: return_value)
|
81
82
|
end
|
82
83
|
case check
|
83
84
|
in [:success]
|
84
|
-
Result::Success.new(symbolic_call
|
85
|
+
Result::Success.new(symbolic_call: symbolic_call, return_value: return_value)
|
85
86
|
in [:failure]
|
86
|
-
Result::Failure.new(symbolic_call
|
87
|
+
Result::Failure.new(symbolic_call: symbolic_call, return_value: return_value)
|
87
88
|
in [:exception, exception]
|
88
|
-
Result::Exception.new(symbolic_call
|
89
|
+
Result::Exception.new(symbolic_call: symbolic_call, exception: exception)
|
89
90
|
end
|
90
91
|
rescue TypeError => exception
|
91
|
-
Result::Failure.new(symbolic_call
|
92
|
+
Result::Failure.new(symbolic_call: symbolic_call, return_value: return_value, exception: exception)
|
92
93
|
end
|
93
94
|
|
94
95
|
# not ensure symbolic_call
|
95
96
|
rescue NoMethodError, NotImplementedError => exception
|
96
|
-
Result::Skip.new(symbolic_call
|
97
|
+
Result::Skip.new(symbolic_call: symbolic_call, exception: exception)
|
97
98
|
rescue NameError => e
|
98
|
-
RaaP.logger.
|
99
|
+
RaaP.logger.warn("[#{e.class}] #{e.detailed_message}")
|
99
100
|
msg = e.name.nil? ? '' : "for `#{BindCall.to_s(e.receiver)}::#{e.name}`"
|
100
101
|
RaaP.logger.warn("Implementation is not found #{msg} maybe.")
|
101
102
|
RaaP.logger.debug(e.backtrace&.join("\n"))
|
@@ -103,9 +104,9 @@ module RaaP
|
|
103
104
|
throw :break
|
104
105
|
rescue SystemStackError => exception
|
105
106
|
RaaP.logger.info "Found recursive type definition."
|
106
|
-
Result::Skip.new(symbolic_call
|
107
|
+
Result::Skip.new(symbolic_call: symbolic_call, exception: exception)
|
107
108
|
rescue => exception
|
108
|
-
Result::Exception.new(symbolic_call
|
109
|
+
Result::Exception.new(symbolic_call: symbolic_call, exception: exception)
|
109
110
|
end
|
110
111
|
|
111
112
|
def check_return(receiver_value:, return_value:)
|
@@ -113,6 +114,7 @@ module RaaP
|
|
113
114
|
if BindCall.is_a?(return_type, ::RBS::Types::ClassSingleton)
|
114
115
|
# ::RBS::Test::TypeCheck cannot support to check singleton class
|
115
116
|
if receiver_value == return_value
|
117
|
+
coverage("return", return_value, return_type)
|
116
118
|
[:success]
|
117
119
|
else
|
118
120
|
[:failure]
|
@@ -126,8 +128,8 @@ module RaaP
|
|
126
128
|
instance_class = BindCall.class(receiver_value)
|
127
129
|
end
|
128
130
|
type_check = ::RBS::Test::TypeCheck.new(
|
129
|
-
self_class
|
130
|
-
instance_class
|
131
|
+
self_class: self_class,
|
132
|
+
instance_class: instance_class,
|
131
133
|
class_class: Module,
|
132
134
|
builder: RBS.builder,
|
133
135
|
sample_size: 100,
|
@@ -135,6 +137,7 @@ module RaaP
|
|
135
137
|
)
|
136
138
|
begin
|
137
139
|
if type_check.value(return_value, return_type)
|
140
|
+
coverage("return", return_value, return_type, type_check)
|
138
141
|
[:success]
|
139
142
|
else
|
140
143
|
[:failure]
|
@@ -148,5 +151,33 @@ module RaaP
|
|
148
151
|
def return_type
|
149
152
|
@method_type.rbs.type.return_type
|
150
153
|
end
|
154
|
+
|
155
|
+
def coverage(position, return_value, return_type, type_check = nil)
|
156
|
+
return unless Coverage.running?
|
157
|
+
|
158
|
+
case return_type
|
159
|
+
when ::RBS::Types::Tuple
|
160
|
+
return_type.types.zip(return_value).each_with_index do |(type, value), i|
|
161
|
+
if type_check&.value(value, type)
|
162
|
+
coverage("#{position}_tuple_#{i}", value, type, type_check)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
when ::RBS::Types::Union
|
166
|
+
return_type.types.each_with_index do |type, i|
|
167
|
+
if type_check&.value(return_value, type)
|
168
|
+
coverage("#{position}_union_#{i}", return_value, type, type_check)
|
169
|
+
break
|
170
|
+
end
|
171
|
+
end
|
172
|
+
when ::RBS::Types::Optional
|
173
|
+
if return_value.nil?
|
174
|
+
Coverage.log("#{position}_optional_right")
|
175
|
+
else
|
176
|
+
coverage("#{position}_optional_left", return_value, return_type.type, type_check)
|
177
|
+
end
|
178
|
+
else
|
179
|
+
Coverage.log(position)
|
180
|
+
end
|
181
|
+
end
|
151
182
|
end
|
152
183
|
end
|