raap 0.2.0 → 0.4.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/.rubocop.yml +29 -0
- data/README.md +27 -1
- data/Rakefile +10 -1
- data/exe/raap +3 -1
- data/lib/raap/bind_call.rb +18 -6
- data/lib/raap/cli.rb +194 -112
- data/lib/raap/function_type.rb +14 -10
- data/lib/raap/method_property.rb +77 -49
- data/lib/raap/method_type.rb +13 -6
- data/lib/raap/minitest.rb +7 -3
- data/lib/raap/rbs.rb +19 -0
- data/lib/raap/result.rb +89 -8
- data/lib/raap/sized.rb +2 -0
- data/lib/raap/symbolic_caller.rb +74 -38
- data/lib/raap/type.rb +93 -77
- data/lib/raap/type_substitution.rb +2 -1
- data/lib/raap/value/interface.rb +15 -7
- data/lib/raap/value/intersection.rb +22 -10
- data/lib/raap/value/module.rb +46 -6
- data/lib/raap/value/variable.rb +10 -1
- data/lib/raap/version.rb +1 -1
- data/lib/raap.rb +3 -3
- data/public/jacket.webp +0 -0
- data/rbs_collection.lock.yaml +61 -1
- data/rbs_collection.yaml +0 -1
- data/sig/raap.rbs +89 -75
- data/sig/shims.rbs +4 -0
- metadata +10 -10
- data/lib/raap/method_value.rb +0 -38
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 312e27904176673d6962904901cb3e8d31c6b613b8efdf669c877d4670cf9d15
|
4
|
+
data.tar.gz: fe940b9d2928a54bfbba8a4842c097cb2a8efe6cf0b873baa383f757de37d017
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 55070cb0335ae161e3a1f6e62d9faa9d205e0b1c92186d2e81eaea2547d476d092fa90a9c6607d2d4ad11cf1f0a0bab723539e90cc4932d49bc297e069e07632
|
7
|
+
data.tar.gz: 771e2f640454bfcd8774ccc0d1ac3079df7e6f1edb7d209327881fea2febb6bbfe04af937481e3e6d81bf9b05046860fc7d861af38b523872d1af8d62a0717da
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
AllCops:
|
2
|
+
TargetRubyVersion: 3.3
|
3
|
+
NewCops: enable
|
4
|
+
Include:
|
5
|
+
- 'lib/**/*.rb'
|
6
|
+
|
7
|
+
Lint:
|
8
|
+
Enabled: true
|
9
|
+
Lint/EmptyBlock:
|
10
|
+
Enabled: false
|
11
|
+
|
12
|
+
Style:
|
13
|
+
Enabled: false
|
14
|
+
Style/HashSyntax:
|
15
|
+
EnforcedShorthandSyntax: always
|
16
|
+
Enabled: true
|
17
|
+
|
18
|
+
Metrics:
|
19
|
+
Enabled: false
|
20
|
+
|
21
|
+
Layout:
|
22
|
+
Enabled: true
|
23
|
+
Layout/FirstArrayElementIndentation:
|
24
|
+
EnforcedStyle: consistent
|
25
|
+
Layout/LineLength:
|
26
|
+
Max: 150
|
27
|
+
|
28
|
+
Naming:
|
29
|
+
Enabled: false
|
data/README.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# RaaP
|
2
2
|
|
3
|
+

|
4
|
+
|
3
5
|
## RBS as a Property
|
4
6
|
|
5
7
|
RaaP is a property based testing tool.
|
@@ -55,7 +57,7 @@ Finally, you get the perfect RBS!
|
|
55
57
|
|
56
58
|
Install the gem and add to the application's Gemfile by executing:
|
57
59
|
|
58
|
-
$ bundle add raap
|
60
|
+
$ bundle add raap --require false
|
59
61
|
|
60
62
|
If bundler is not being used to manage dependencies, install the gem by executing:
|
61
63
|
|
@@ -78,6 +80,21 @@ For example, an Integer with size zero is `0` and an Array is `[]`.
|
|
78
80
|
|
79
81
|
RaaP, like other property-based tests, changes the size 100 times from 0 to 99 by default to generate test data.
|
80
82
|
|
83
|
+
## Symbolic call
|
84
|
+
|
85
|
+
You may possibly see the following data structure in the debug logs.
|
86
|
+
|
87
|
+
```rb
|
88
|
+
[:call, Object, :method, [], {}, nil]
|
89
|
+
```
|
90
|
+
|
91
|
+
This is a data structure of the state of the method call.
|
92
|
+
|
93
|
+
Symbolic call is a tuple beginning with `:call`, followed by the receiver, method name, positional arguments, keyword arguments, and block arguments, in that order.
|
94
|
+
And the receiver and arguments are nestable.
|
95
|
+
|
96
|
+
It is used in QuickCheck and Proper to keep as much history of method calls as possible.
|
97
|
+
|
81
98
|
## Options
|
82
99
|
|
83
100
|
### `-I PATH` or `--include PATH`
|
@@ -108,6 +125,10 @@ You can specify size of end.
|
|
108
125
|
|
109
126
|
You can specify size of step like `Integer#step: (to: Integer, by: Integer)`.
|
110
127
|
|
128
|
+
## First support is CLI
|
129
|
+
|
130
|
+
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.
|
131
|
+
|
111
132
|
## Achievements
|
112
133
|
|
113
134
|
RaaP has already found many RBS mistakes and bug in CRuby during the development phase.
|
@@ -119,6 +140,11 @@ RaaP has already found many RBS mistakes and bug in CRuby during the development
|
|
119
140
|
* https://github.com/ruby/rbs/pull/1769
|
120
141
|
* https://github.com/ruby/rbs/pull/1770
|
121
142
|
* https://github.com/ruby/rbs/pull/1771
|
143
|
+
* https://github.com/ruby/rbs/pull/1779
|
144
|
+
* https://github.com/ruby/rbs/pull/1783
|
145
|
+
* https://github.com/ruby/rbs/pull/1789
|
146
|
+
* https://github.com/ruby/rbs/pull/1790
|
147
|
+
* https://github.com/ruby/rbs/pull/1793
|
122
148
|
|
123
149
|
## Development
|
124
150
|
|
data/Rakefile
CHANGED
@@ -2,7 +2,16 @@
|
|
2
2
|
|
3
3
|
require "bundler/gem_tasks"
|
4
4
|
require "minitest/test_task"
|
5
|
+
require "rubocop/rake_task"
|
5
6
|
|
6
7
|
Minitest::TestTask.create
|
7
8
|
|
8
|
-
|
9
|
+
RuboCop::RakeTask.new
|
10
|
+
|
11
|
+
namespace :steep do
|
12
|
+
task :check do
|
13
|
+
sh "bundle exec steep check"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
task default: [:test, :rubocop, 'steep:check']
|
data/exe/raap
CHANGED
data/lib/raap/bind_call.rb
CHANGED
@@ -11,19 +11,31 @@ module RaaP
|
|
11
11
|
def public_send(...) = ::Kernel.instance_method(:public_send).bind_call(...)
|
12
12
|
|
13
13
|
def class(obj)
|
14
|
-
if
|
15
|
-
obj.class
|
16
|
-
else
|
14
|
+
if instance_of?(obj, BasicObject)
|
17
15
|
::Kernel.instance_method(:class).bind_call(obj)
|
16
|
+
else
|
17
|
+
obj.class
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
21
|
def inspect(obj)
|
22
|
-
if
|
23
|
-
obj.inspect
|
24
|
-
else
|
22
|
+
if instance_of?(obj, BasicObject)
|
25
23
|
::Kernel.instance_method(:inspect).bind_call(obj)
|
24
|
+
else
|
25
|
+
case obj
|
26
|
+
when Hash
|
27
|
+
body = obj.map do |k, v|
|
28
|
+
"#{inspect(k)} => #{inspect(v)}"
|
29
|
+
end
|
30
|
+
"{#{body.join(', ')}}"
|
31
|
+
when Array
|
32
|
+
"[#{obj.map { |o| inspect(o) }.join(', ')}]"
|
33
|
+
else
|
34
|
+
obj.inspect
|
35
|
+
end
|
26
36
|
end
|
37
|
+
rescue NoMethodError
|
38
|
+
"#<#{self.class(obj)}>"
|
27
39
|
end
|
28
40
|
end
|
29
41
|
end
|
data/lib/raap/cli.rb
CHANGED
@@ -2,12 +2,7 @@
|
|
2
2
|
|
3
3
|
module RaaP
|
4
4
|
# $ raap Integer#pow
|
5
|
-
# $ raap -I sig RaaP::Type
|
6
5
|
class CLI
|
7
|
-
class << self
|
8
|
-
attr_accessor :option
|
9
|
-
end
|
10
|
-
|
11
6
|
Option = Struct.new(
|
12
7
|
:dirs,
|
13
8
|
:requires,
|
@@ -16,197 +11,264 @@ module RaaP
|
|
16
11
|
:size_from,
|
17
12
|
:size_to,
|
18
13
|
:size_by,
|
14
|
+
:allow_private,
|
15
|
+
:skips,
|
19
16
|
keyword_init: true
|
20
17
|
)
|
21
18
|
|
22
|
-
#
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
19
|
+
# Should skip methods has side effects
|
20
|
+
DEFAULT_SKIP = Set.new
|
21
|
+
%i[
|
22
|
+
fork system exec spawn `
|
23
|
+
abort exit exit! raise fail
|
24
|
+
load require require_relative
|
25
|
+
gem
|
26
|
+
].each do |kernel_method|
|
27
|
+
DEFAULT_SKIP << "::Kernel##{kernel_method}"
|
28
|
+
DEFAULT_SKIP << "::Kernel.#{kernel_method}"
|
29
|
+
end
|
30
|
+
%i[
|
31
|
+
delete unlink chmod lchmod chown lchown
|
32
|
+
link mkfifo new open rename truncate
|
33
|
+
].each { |m| DEFAULT_SKIP << "::File.#{m}" }
|
34
|
+
%i[flock truncate].each { |m| DEFAULT_SKIP << "::File##{m}" }
|
35
|
+
|
36
|
+
attr_accessor :option, :argv, :skip, :results
|
32
37
|
|
33
38
|
def initialize(argv)
|
39
|
+
# defaults
|
40
|
+
@option = Option.new(
|
41
|
+
dirs: [],
|
42
|
+
requires: [],
|
43
|
+
libraries: [],
|
44
|
+
timeout: 3,
|
45
|
+
size_from: 0,
|
46
|
+
size_to: 99,
|
47
|
+
size_by: 1,
|
48
|
+
skips: [],
|
49
|
+
allow_private: false,
|
50
|
+
)
|
34
51
|
@argv = argv
|
52
|
+
@skip = DEFAULT_SKIP.dup
|
53
|
+
@results = []
|
35
54
|
end
|
36
55
|
|
37
56
|
def load
|
38
57
|
OptionParser.new do |o|
|
39
58
|
o.on('-I', '--include PATH') do |path|
|
40
|
-
|
59
|
+
@option.dirs << path
|
41
60
|
end
|
42
61
|
o.on('--library lib', 'load rbs library') do |lib|
|
43
|
-
|
62
|
+
@option.libraries << lib
|
44
63
|
end
|
45
64
|
o.on('--require lib', 'require ruby library') do |lib|
|
46
|
-
|
65
|
+
@option.requires << lib
|
47
66
|
end
|
48
67
|
o.on('--log-level level', "default: warn") do |arg|
|
49
68
|
RaaP.logger.level = arg
|
50
69
|
end
|
51
|
-
o.on('--timeout sec',
|
52
|
-
|
70
|
+
o.on('--timeout sec', Float, "default: #{@option.timeout}") do |arg|
|
71
|
+
@option.timeout = arg
|
72
|
+
end
|
73
|
+
o.on('--size-from int', Integer, "default: #{@option.size_from}") do |arg|
|
74
|
+
@option.size_from = arg
|
75
|
+
end
|
76
|
+
o.on('--size-to int', Integer, "default: #{@option.size_to}") do |arg|
|
77
|
+
@option.size_to = arg
|
53
78
|
end
|
54
|
-
o.on('--size-
|
55
|
-
|
79
|
+
o.on('--size-by int', Integer, "default: #{@option.size_by}") do |arg|
|
80
|
+
@option.size_by = arg
|
56
81
|
end
|
57
|
-
o.on('--
|
58
|
-
|
82
|
+
o.on('--allow-private', "default: #{@option.allow_private}") do
|
83
|
+
@option.allow_private = true
|
59
84
|
end
|
60
|
-
o.on('--
|
61
|
-
|
85
|
+
o.on('--skip tag', "skip case (e.g. `Foo#meth`)") do |tag|
|
86
|
+
@option.skips << tag
|
62
87
|
end
|
63
88
|
end.parse!(@argv)
|
64
89
|
|
65
|
-
|
90
|
+
@option.dirs.each do |dir|
|
66
91
|
RaaP::RBS.loader.add(path: Pathname(dir))
|
67
92
|
end
|
68
|
-
|
93
|
+
@option.libraries.each do |lib|
|
69
94
|
RaaP::RBS.loader.add(library: lib, version: nil)
|
70
95
|
end
|
71
|
-
|
96
|
+
@option.requires.each do |lib|
|
72
97
|
require lib
|
73
98
|
end
|
99
|
+
@option.skips.each do |skip|
|
100
|
+
@skip << skip
|
101
|
+
end
|
102
|
+
@skip.freeze
|
74
103
|
|
75
104
|
self
|
76
105
|
end
|
77
106
|
|
78
107
|
def run
|
108
|
+
Signal.trap(:INT) do
|
109
|
+
puts "Interrupted by SIGINT"
|
110
|
+
report
|
111
|
+
exit 1
|
112
|
+
end
|
113
|
+
|
79
114
|
@argv.map do |tag|
|
80
115
|
case
|
81
116
|
when tag.include?('#')
|
82
|
-
|
117
|
+
run_by(kind: :instance, tag:)
|
83
118
|
when tag.include?('.')
|
84
|
-
|
119
|
+
run_by(kind: :singleton, tag:)
|
85
120
|
when tag.end_with?('*')
|
86
121
|
run_by_type_name_with_search(tag:)
|
87
122
|
else
|
88
123
|
run_by_type_name(tag:)
|
89
124
|
end
|
90
|
-
end.each do |ret|
|
91
|
-
ret.each do |methods|
|
92
|
-
methods.each do |status, method_name, method_type|
|
93
|
-
if status == 1
|
94
|
-
puts "Fail:"
|
95
|
-
puts "def #{method_name}: #{method_type}"
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
125
|
end
|
100
126
|
|
101
|
-
|
127
|
+
report
|
102
128
|
end
|
103
129
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
130
|
+
private
|
131
|
+
|
132
|
+
def report
|
133
|
+
i = 0
|
134
|
+
exit_status = 0
|
135
|
+
@results.each do |ret|
|
136
|
+
ret => { method:, properties: }
|
137
|
+
properties.select { |status,| status == 1 }.each do |_, method_name, method_type, reason|
|
138
|
+
i += 1
|
139
|
+
location = if method.alias_of
|
140
|
+
alias_decl = RBS.find_alias_decl(method.defined_in, method_name) or raise "alias decl not found: #{method_name}"
|
141
|
+
alias_decl.location
|
142
|
+
else
|
143
|
+
method_type.location
|
144
|
+
end
|
145
|
+
prefix = method.defs.first.member.kind == :instance ? '' : 'self.'
|
146
|
+
|
147
|
+
puts "\e[41m\e[1m#\e[m\e[1m #{i}) Failure:\e[m"
|
148
|
+
puts
|
149
|
+
puts "def #{prefix}#{method_name}: #{method_type}"
|
150
|
+
puts " in #{location}"
|
151
|
+
puts
|
152
|
+
puts "## Reason"
|
153
|
+
puts
|
154
|
+
puts reason&.string
|
155
|
+
puts
|
156
|
+
exit_status = 1
|
122
157
|
end
|
123
|
-
|
158
|
+
end
|
159
|
+
exit_status
|
124
160
|
end
|
125
161
|
|
126
|
-
def
|
127
|
-
|
162
|
+
def run_by(kind:, tag:)
|
163
|
+
split = kind == :instance ? '#' : '.'
|
164
|
+
t, m = tag.split(split, 2)
|
128
165
|
t or raise
|
129
166
|
m or raise
|
130
167
|
type = RBS.parse_type(t)
|
131
168
|
raise "cannot specified #{type.class}" unless type.respond_to?(:name)
|
169
|
+
|
132
170
|
type = __skip__ = type
|
133
|
-
|
171
|
+
type_name = type.name.absolute!
|
172
|
+
type_to_s = type.to_s.start_with?('::') ? type.to_s : "::#{type}"
|
173
|
+
receiver_type = if kind == :instance
|
174
|
+
Type.new(type_to_s)
|
175
|
+
else
|
176
|
+
Type.new("singleton(#{type_name})")
|
177
|
+
end
|
134
178
|
method_name = m.to_sym
|
135
|
-
definition =
|
179
|
+
definition = if kind == :instance
|
180
|
+
RBS.builder.build_instance(type_name)
|
181
|
+
else
|
182
|
+
RBS.builder.build_singleton(type_name)
|
183
|
+
end
|
184
|
+
|
136
185
|
method = definition.methods[method_name]
|
137
|
-
type_params_decl = definition.type_params_decl
|
138
186
|
raise "`#{tag}` not found" unless method
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
187
|
+
|
188
|
+
if @skip.include?("#{type_name}#{split}#{method_name}")
|
189
|
+
raise "`#{type_name}#{split}#{method_name}` is a method to be skipped"
|
190
|
+
end
|
191
|
+
|
192
|
+
type_params_decl = definition.type_params_decl
|
193
|
+
type_args = type.args
|
194
|
+
|
195
|
+
RaaP.logger.info("# #{type}")
|
196
|
+
@results << {
|
197
|
+
method:,
|
198
|
+
properties: method.method_types.map do |method_type|
|
199
|
+
property(receiver_type:, type_params_decl:, type_args:, method_type:, method_name:)
|
144
200
|
end
|
145
|
-
|
201
|
+
}
|
146
202
|
end
|
147
203
|
|
148
204
|
def run_by_type_name_with_search(tag:)
|
149
205
|
first, _last = tag.split('::')
|
150
|
-
|
151
|
-
RBS.env.class_decls.each do |name, entry|
|
206
|
+
RBS.env.class_decls.each do |name, _entry|
|
152
207
|
if ['', '::'].any? { |pre| name.to_s.match?(/\A#{pre}#{first}\b/) }
|
153
|
-
|
208
|
+
run_by_type_name(tag: name.to_s)
|
154
209
|
end
|
155
210
|
end
|
156
|
-
ret.flatten(1)
|
157
211
|
end
|
158
212
|
|
159
213
|
def run_by_type_name(tag:)
|
160
214
|
type = RBS.parse_type(tag)
|
161
215
|
type = __skip__ = type
|
162
216
|
raise "cannot specified #{type.class}" unless type.respond_to?(:name)
|
163
|
-
type_name = type.name.absolute!
|
164
217
|
|
165
|
-
|
218
|
+
type_name = type.name.absolute!
|
219
|
+
type_args = type.args
|
166
220
|
|
167
221
|
definition = RBS.builder.build_singleton(type_name)
|
168
222
|
type_params_decl = definition.type_params_decl
|
169
223
|
definition.methods.filter_map do |method_name, method|
|
224
|
+
next if @skip.include?("#{type_name.absolute!}.#{method_name}")
|
170
225
|
next unless method.accessibility == :public
|
171
226
|
next if method.defined_in != type_name
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
227
|
+
|
228
|
+
RaaP.logger.info("# #{type_name}.#{method_name}")
|
229
|
+
@results << {
|
230
|
+
method:,
|
231
|
+
properties: method.method_types.map do |method_type|
|
232
|
+
property(receiver_type: Type.new("singleton(#{type.name})"), type_params_decl:, type_args:, method_type:, method_name:)
|
233
|
+
end
|
234
|
+
}
|
178
235
|
end
|
179
236
|
|
180
237
|
definition = RBS.builder.build_instance(type_name)
|
181
238
|
type_params_decl = definition.type_params_decl
|
182
239
|
definition.methods.filter_map do |method_name, method|
|
240
|
+
next if @skip.include?("#{type_name.absolute!}##{method_name}")
|
183
241
|
next unless method.accessibility == :public
|
184
242
|
next if method.defined_in != type_name
|
185
|
-
next if method_name == :fork || method_name == :spawn # TODO: skip solution
|
186
|
-
puts "# #{type_name}##{method_name}"
|
187
|
-
puts
|
188
|
-
ret << method.method_types.map do |method_type|
|
189
|
-
property(receiver_type: Type.new(type.to_s), type_params_decl:, method_type:, method_name:)
|
190
|
-
end
|
191
|
-
end
|
192
243
|
|
193
|
-
|
244
|
+
RaaP.logger.info("# #{type_name}##{method_name}")
|
245
|
+
@results << {
|
246
|
+
method:,
|
247
|
+
properties: method.method_types.map do |method_type|
|
248
|
+
property(receiver_type: Type.new(type.name), type_params_decl:, type_args:, method_type:, method_name:)
|
249
|
+
end
|
250
|
+
}
|
251
|
+
end
|
194
252
|
end
|
195
253
|
|
196
|
-
def property(receiver_type:, type_params_decl:, method_type:, method_name:)
|
254
|
+
def property(receiver_type:, type_params_decl:, type_args:, method_type:, method_name:)
|
197
255
|
rtype = __skip__ = receiver_type.type
|
198
256
|
if receiver_type.type.instance_of?(::RBS::Types::ClassSingleton)
|
199
257
|
prefix = 'self.'
|
200
|
-
type_args = []
|
201
258
|
else
|
202
259
|
prefix = ''
|
203
|
-
type_args = rtype.args
|
204
260
|
end
|
205
|
-
|
261
|
+
type_params_decl.each_with_index do |_, i|
|
262
|
+
if rtype.instance_of?(::RBS::Types::ClassInstance)
|
263
|
+
rtype.args[i] = type_args[i] || ::RBS::Types::Bases::Any.new(location: nil)
|
264
|
+
end
|
265
|
+
end
|
266
|
+
RaaP.logger.info("## def #{prefix}#{method_name}: #{method_type}")
|
206
267
|
status = 0
|
207
|
-
|
268
|
+
reason = nil
|
269
|
+
prop = MethodProperty.new(
|
208
270
|
receiver_type:,
|
209
|
-
method_name
|
271
|
+
method_name:,
|
210
272
|
method_type: MethodType.new(
|
211
273
|
method_type,
|
212
274
|
type_params_decl:,
|
@@ -215,39 +277,59 @@ module RaaP
|
|
215
277
|
instance_type: ::RBS::Types::ClassInstance.new(name: rtype.name, args: type_args, location: nil),
|
216
278
|
class_type: ::RBS::Types::ClassSingleton.new(name: rtype.name, location: nil),
|
217
279
|
),
|
218
|
-
size_step:
|
219
|
-
timeout:
|
220
|
-
|
280
|
+
size_step: @option.size_from.step(to: @option.size_to, by: @option.size_by),
|
281
|
+
timeout: @option.timeout,
|
282
|
+
allow_private: true,
|
283
|
+
)
|
284
|
+
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
285
|
+
stats = prop.run do |called|
|
221
286
|
case called
|
222
287
|
in Result::Success => s
|
223
288
|
print '.'
|
224
289
|
RaaP.logger.debug { "Success: #{s.called_str}" }
|
225
290
|
in Result::Failure => f
|
226
291
|
puts 'F'
|
227
|
-
|
228
|
-
|
292
|
+
if (e = f.exception)
|
293
|
+
RaaP.logger.debug { "Failure: [#{e.class}] #{e.message}" }
|
294
|
+
RaaP.logger.debug { e.backtrace.join("\n") }
|
295
|
+
end
|
229
296
|
RaaP.logger.debug { PP.pp(f.symbolic_call, ''.dup) }
|
230
|
-
|
231
|
-
puts
|
232
|
-
puts
|
233
|
-
puts
|
234
|
-
puts
|
297
|
+
reason = StringIO.new
|
298
|
+
reason.puts "Failed in case of `#{f.called_str}`"
|
299
|
+
reason.puts
|
300
|
+
reason.puts "### Repro"
|
301
|
+
reason.puts
|
302
|
+
reason.puts "```rb"
|
303
|
+
reason.puts SymbolicCaller.new(f.symbolic_call).to_lines.join("\n")
|
304
|
+
reason.puts "```"
|
235
305
|
status = 1
|
236
306
|
throw :break
|
237
307
|
in Result::Skip => s
|
238
308
|
print 'S'
|
239
|
-
RaaP
|
309
|
+
RaaP.logger.debug { "\n```\n#{SymbolicCaller.new(s.symbolic_call).to_lines.join("\n")}\n```" }
|
310
|
+
RaaP.logger.debug("Skip: #{s.exception.detailed_message}")
|
311
|
+
RaaP.logger.debug(s.exception.backtrace.join("\n"))
|
240
312
|
in Result::Exception => e
|
241
313
|
print 'E'
|
242
|
-
RaaP.logger.
|
314
|
+
RaaP.logger.debug { "\n```\n#{SymbolicCaller.new(e.symbolic_call).to_lines.join("\n")}\n```" }
|
315
|
+
RaaP.logger.debug("Exception: #{e.exception.detailed_message}")
|
243
316
|
RaaP.logger.debug(e.exception.backtrace.join("\n"))
|
244
317
|
end
|
245
318
|
end
|
319
|
+
end_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
246
320
|
puts
|
247
|
-
|
248
|
-
|
321
|
+
time_diff = end_time - start_time
|
322
|
+
time = ", time: #{(time_diff * 1000).round}ms"
|
323
|
+
stats_log = "success: #{stats.success}, skip: #{stats.skip}, exception: #{stats.exception}#{time}"
|
324
|
+
RaaP.logger.info(stats_log)
|
325
|
+
|
326
|
+
if status == 0 && stats.success.zero? && !stats.break
|
327
|
+
status = 1
|
328
|
+
reason = StringIO.new
|
329
|
+
reason.puts "Never succeeded => #{stats_log}"
|
330
|
+
end
|
249
331
|
|
250
|
-
[status, method_name, method_type]
|
332
|
+
[status, method_name, method_type, reason]
|
251
333
|
end
|
252
334
|
end
|
253
335
|
end
|
data/lib/raap/function_type.rb
CHANGED
@@ -6,35 +6,39 @@ module RaaP
|
|
6
6
|
@fun = fun
|
7
7
|
end
|
8
8
|
|
9
|
-
def pick_arguments(size: 10
|
10
|
-
|
11
|
-
|
9
|
+
def pick_arguments(size: 10)
|
10
|
+
SymbolicCaller.new(arguments_to_symbolic_call(size:)).eval
|
11
|
+
end
|
12
|
+
|
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:)
|
12
16
|
|
13
17
|
[a, k]
|
14
18
|
end
|
15
19
|
|
16
20
|
private
|
17
21
|
|
18
|
-
def
|
22
|
+
def to_symbolic_call_recursive(type, size:)
|
19
23
|
case
|
20
24
|
when type.nil?
|
21
25
|
nil
|
22
26
|
when type.respond_to?(:each_pair)
|
23
|
-
type.each_pair.to_h { |k, v| [k,
|
27
|
+
type.each_pair.to_h { |k, v| [k, to_symbolic_call_recursive(v, size:)] }
|
24
28
|
when type.respond_to?(:each)
|
25
|
-
type.each.map { |v|
|
29
|
+
type.each.map { |v| to_symbolic_call_recursive(v, size:) }
|
26
30
|
else
|
27
|
-
type.
|
31
|
+
type.to_symbolic_call(size:)
|
28
32
|
end
|
29
33
|
end
|
30
34
|
|
31
35
|
def build_args_type
|
32
36
|
reqs = @fun.required_positionals.map { |param| Type.new(param.type) }
|
33
37
|
tras = @fun.trailing_positionals.map { |param| Type.new(param.type) }
|
34
|
-
sampled_optional_positionals = @fun.optional_positionals.
|
38
|
+
sampled_optional_positionals = @fun.optional_positionals.take(Random.rand(@fun.optional_positionals.length + 1))
|
35
39
|
opts = sampled_optional_positionals.map { |param| Type.new(param.type) }
|
36
40
|
rest = []
|
37
|
-
if param = @fun.rest_positionals
|
41
|
+
if (param = @fun.rest_positionals)
|
38
42
|
rest = Array.new(Random.rand(0..3)) { Type.new(param.type) }
|
39
43
|
end
|
40
44
|
[reqs, opts, rest, tras].flatten
|
@@ -45,7 +49,7 @@ module RaaP
|
|
45
49
|
rand = Random.rand(@fun.optional_keywords.length + 1)
|
46
50
|
opts = @fun.optional_keywords.to_a.sample(rand).to_h { |name, param| [name, Type.new(param.type)] }
|
47
51
|
kwargs = reqs.to_h.merge(opts)
|
48
|
-
if param = @fun.rest_keywords
|
52
|
+
if (param = @fun.rest_keywords)
|
49
53
|
keys = Array.new(Random.rand(0..3)) do
|
50
54
|
random_key = nil
|
51
55
|
loop do
|