raap 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 507ed156280ed3e0db1f2eb454bb03c57aa473d537d8c6ea5905ad852eba550d
4
- data.tar.gz: 3108fa50c664702f79141a3c0b5e83934d6742468d1d51bfb2febb5b64a00b12
3
+ metadata.gz: 312e27904176673d6962904901cb3e8d31c6b613b8efdf669c877d4670cf9d15
4
+ data.tar.gz: fe940b9d2928a54bfbba8a4842c097cb2a8efe6cf0b873baa383f757de37d017
5
5
  SHA512:
6
- metadata.gz: 269e220d72b9861881de860a71a2adff318105595880fe73e86492e255c9bb32fbeff7ac3685b83ea11d792a02b1509eb969d9956483a9415608530a3f8806bd
7
- data.tar.gz: df4e40973681eb292d7844aa5ed1427b7155f619c6ebea53792149f430ab28e42a0a0f52df9517a3af9af1e08494eaccdd8d4d84313c9d24184f6403a51e1dc5
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
+ ![Jacket](https://raw.githubusercontent.com/ksss/raap/main/public/DALL%C2%B7E%202024-03-23%2000.02.29%20-%20Imagine%20a%20scene%20where%20the%20abstract%20concepts%20of%20Ruby%20programming%20and%20property-based%20testing%20blend%20harmoniously.%20Picture%20a%20large%2C%20glowing%20ruby%20crystal%2C%20.webp)
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
 
@@ -123,6 +125,10 @@ You can specify size of end.
123
125
 
124
126
  You can specify size of step like `Integer#step: (to: Integer, by: Integer)`.
125
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
+
126
132
  ## Achievements
127
133
 
128
134
  RaaP has already found many RBS mistakes and bug in CRuby during the development phase.
@@ -134,6 +140,11 @@ RaaP has already found many RBS mistakes and bug in CRuby during the development
134
140
  * https://github.com/ruby/rbs/pull/1769
135
141
  * https://github.com/ruby/rbs/pull/1770
136
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
137
148
 
138
149
  ## Development
139
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
- task default: :test
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
@@ -1,3 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
1
3
  require 'raap'
2
4
 
3
- RaaP::CLI.new(ARGV).load.run
5
+ exit RaaP::CLI.new(ARGV).load.run
@@ -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 respond_to?(obj, :class)
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 respond_to?(obj, :inspect)
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,
@@ -17,201 +12,263 @@ module RaaP
17
12
  :size_to,
18
13
  :size_by,
19
14
  :allow_private,
15
+ :skips,
20
16
  keyword_init: true
21
17
  )
22
18
 
23
- # defaults
24
- self.option = Option.new(
25
- dirs: [],
26
- requires: [],
27
- libraries: [],
28
- timeout: 3,
29
- size_from: 0,
30
- size_to: 99,
31
- size_by: 1,
32
- allow_private: false,
33
- )
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
34
37
 
35
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
+ )
36
51
  @argv = argv
52
+ @skip = DEFAULT_SKIP.dup
53
+ @results = []
37
54
  end
38
55
 
39
56
  def load
40
57
  OptionParser.new do |o|
41
58
  o.on('-I', '--include PATH') do |path|
42
- CLI.option.dirs << path
59
+ @option.dirs << path
43
60
  end
44
61
  o.on('--library lib', 'load rbs library') do |lib|
45
- CLI.option.libraries << lib
62
+ @option.libraries << lib
46
63
  end
47
64
  o.on('--require lib', 'require ruby library') do |lib|
48
- CLI.option.requires << lib
65
+ @option.requires << lib
49
66
  end
50
67
  o.on('--log-level level', "default: warn") do |arg|
51
68
  RaaP.logger.level = arg
52
69
  end
53
- o.on('--timeout sec', Integer, "default: #{CLI.option.timeout}") do |arg|
54
- CLI.option.timeout = arg
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
55
75
  end
56
- o.on('--size-from int', Integer, "default: #{CLI.option.size_from}") do |arg|
57
- CLI.option.size_from = arg
76
+ o.on('--size-to int', Integer, "default: #{@option.size_to}") do |arg|
77
+ @option.size_to = arg
58
78
  end
59
- o.on('--size-to int', Integer, "default: #{CLI.option.size_to}") do |arg|
60
- CLI.option.size_to = arg
79
+ o.on('--size-by int', Integer, "default: #{@option.size_by}") do |arg|
80
+ @option.size_by = arg
61
81
  end
62
- o.on('--size-by int', Integer, "default: #{CLI.option.size_by}") do |arg|
63
- CLI.option.size_by = arg
82
+ o.on('--allow-private', "default: #{@option.allow_private}") do
83
+ @option.allow_private = true
64
84
  end
65
- o.on('--allow-private', "default: #{CLI.option.allow_private}") do
66
- CLI.option.allow_private = true
85
+ o.on('--skip tag', "skip case (e.g. `Foo#meth`)") do |tag|
86
+ @option.skips << tag
67
87
  end
68
88
  end.parse!(@argv)
69
89
 
70
- CLI.option.dirs.each do |dir|
90
+ @option.dirs.each do |dir|
71
91
  RaaP::RBS.loader.add(path: Pathname(dir))
72
92
  end
73
- CLI.option.libraries.each do |lib|
93
+ @option.libraries.each do |lib|
74
94
  RaaP::RBS.loader.add(library: lib, version: nil)
75
95
  end
76
- CLI.option.requires.each do |lib|
96
+ @option.requires.each do |lib|
77
97
  require lib
78
98
  end
99
+ @option.skips.each do |skip|
100
+ @skip << skip
101
+ end
102
+ @skip.freeze
79
103
 
80
104
  self
81
105
  end
82
106
 
83
107
  def run
108
+ Signal.trap(:INT) do
109
+ puts "Interrupted by SIGINT"
110
+ report
111
+ exit 1
112
+ end
113
+
84
114
  @argv.map do |tag|
85
115
  case
86
116
  when tag.include?('#')
87
- run_by_instance(tag:)
117
+ run_by(kind: :instance, tag:)
88
118
  when tag.include?('.')
89
- run_by_singleton(tag:)
119
+ run_by(kind: :singleton, tag:)
90
120
  when tag.end_with?('*')
91
121
  run_by_type_name_with_search(tag:)
92
122
  else
93
123
  run_by_type_name(tag:)
94
124
  end
95
- end.each do |ret|
96
- ret.each do |methods|
97
- methods.each do |status, method_name, method_type|
98
- if status == 1
99
- puts "Fail:"
100
- puts "def #{method_name}: #{method_type}"
101
- end
102
- end
103
- end
104
125
  end
105
126
 
106
- self
127
+ report
107
128
  end
108
129
 
109
- def run_by_instance(tag:)
110
- t, m = tag.split('#', 2)
111
- t or raise
112
- m or raise
113
- type = RBS.parse_type(t)
114
- type = __skip__ = type
115
- raise "cannot specified #{type}" unless type.respond_to?(:name)
116
- receiver_type = Type.new(type.to_s)
117
- method_name = m.to_sym
118
- definition = RBS.builder.build_instance(type.name)
119
- type_params_decl = definition.type_params_decl
120
- method = definition.methods[method_name]
121
- raise "`#{tag}` is not found" unless method
122
- puts "# #{type.to_s}"
123
- puts
124
- [
125
- method.method_types.map do |method_type|
126
- property(receiver_type:, type_params_decl:, method_type:, method_name:)
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
127
157
  end
128
- ]
158
+ end
159
+ exit_status
129
160
  end
130
161
 
131
- def run_by_singleton(tag:)
132
- t, m = tag.split('.', 2)
162
+ def run_by(kind:, tag:)
163
+ split = kind == :instance ? '#' : '.'
164
+ t, m = tag.split(split, 2)
133
165
  t or raise
134
166
  m or raise
135
167
  type = RBS.parse_type(t)
136
168
  raise "cannot specified #{type.class}" unless type.respond_to?(:name)
169
+
137
170
  type = __skip__ = type
138
- receiver_type = Type.new("singleton(#{type.name})")
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
139
178
  method_name = m.to_sym
140
- definition = RBS.builder.build_singleton(type.name)
179
+ definition = if kind == :instance
180
+ RBS.builder.build_instance(type_name)
181
+ else
182
+ RBS.builder.build_singleton(type_name)
183
+ end
184
+
141
185
  method = definition.methods[method_name]
142
- type_params_decl = definition.type_params_decl
143
186
  raise "`#{tag}` not found" unless method
144
- puts "# #{type}"
145
- puts
146
- [
147
- method.method_types.map do |method_type|
148
- property(receiver_type:, type_params_decl:, method_type:, method_name:)
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:)
149
200
  end
150
- ]
201
+ }
151
202
  end
152
203
 
153
204
  def run_by_type_name_with_search(tag:)
154
205
  first, _last = tag.split('::')
155
- ret = []
156
- RBS.env.class_decls.each do |name, entry|
206
+ RBS.env.class_decls.each do |name, _entry|
157
207
  if ['', '::'].any? { |pre| name.to_s.match?(/\A#{pre}#{first}\b/) }
158
- ret << run_by_type_name(tag: name.to_s)
208
+ run_by_type_name(tag: name.to_s)
159
209
  end
160
210
  end
161
- ret.flatten(1)
162
211
  end
163
212
 
164
213
  def run_by_type_name(tag:)
165
214
  type = RBS.parse_type(tag)
166
215
  type = __skip__ = type
167
216
  raise "cannot specified #{type.class}" unless type.respond_to?(:name)
168
- type_name = type.name.absolute!
169
217
 
170
- ret = []
218
+ type_name = type.name.absolute!
219
+ type_args = type.args
171
220
 
172
221
  definition = RBS.builder.build_singleton(type_name)
173
222
  type_params_decl = definition.type_params_decl
174
223
  definition.methods.filter_map do |method_name, method|
224
+ next if @skip.include?("#{type_name.absolute!}.#{method_name}")
175
225
  next unless method.accessibility == :public
176
226
  next if method.defined_in != type_name
177
- next if method_name == :fork || method_name == :spawn # TODO: skip solution
178
- puts "# #{type_name}.#{method_name}"
179
- puts
180
- ret << method.method_types.map do |method_type|
181
- property(receiver_type: Type.new("singleton(#{type})"), type_params_decl:, method_type:, method_name:)
182
- end
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
+ }
183
235
  end
184
236
 
185
237
  definition = RBS.builder.build_instance(type_name)
186
238
  type_params_decl = definition.type_params_decl
187
239
  definition.methods.filter_map do |method_name, method|
240
+ next if @skip.include?("#{type_name.absolute!}##{method_name}")
188
241
  next unless method.accessibility == :public
189
242
  next if method.defined_in != type_name
190
- next if method_name == :fork || method_name == :spawn # TODO: skip solution
191
- puts "# #{type_name}##{method_name}"
192
- puts
193
- ret << method.method_types.map do |method_type|
194
- property(receiver_type: Type.new(type.to_s), type_params_decl:, method_type:, method_name:)
195
- end
196
- end
197
243
 
198
- ret
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
199
252
  end
200
253
 
201
- 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:)
202
255
  rtype = __skip__ = receiver_type.type
203
256
  if receiver_type.type.instance_of?(::RBS::Types::ClassSingleton)
204
257
  prefix = 'self.'
205
- type_args = []
206
258
  else
207
259
  prefix = ''
208
- type_args = rtype.args
209
260
  end
210
- puts "## def #{prefix}#{method_name}: #{method_type}"
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}")
211
267
  status = 0
212
- stats = MethodProperty.new(
268
+ reason = nil
269
+ prop = MethodProperty.new(
213
270
  receiver_type:,
214
- method_name: method_name,
271
+ method_name:,
215
272
  method_type: MethodType.new(
216
273
  method_type,
217
274
  type_params_decl:,
@@ -220,46 +277,59 @@ module RaaP
220
277
  instance_type: ::RBS::Types::ClassInstance.new(name: rtype.name, args: type_args, location: nil),
221
278
  class_type: ::RBS::Types::ClassSingleton.new(name: rtype.name, location: nil),
222
279
  ),
223
- size_step: CLI.option.size_from.step(to: CLI.option.size_to, by: CLI.option.size_by),
224
- timeout: CLI.option.timeout,
280
+ size_step: @option.size_from.step(to: @option.size_to, by: @option.size_by),
281
+ timeout: @option.timeout,
225
282
  allow_private: true,
226
- ).run do |called|
283
+ )
284
+ start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
285
+ stats = prop.run do |called|
227
286
  case called
228
287
  in Result::Success => s
229
288
  print '.'
230
289
  RaaP.logger.debug { "Success: #{s.called_str}" }
231
290
  in Result::Failure => f
232
291
  puts 'F'
233
- puts "Failed in case of `#{f.called_str}`"
234
- if e = f.exception
292
+ if (e = f.exception)
235
293
  RaaP.logger.debug { "Failure: [#{e.class}] #{e.message}" }
294
+ RaaP.logger.debug { e.backtrace.join("\n") }
236
295
  end
237
- puts
238
296
  RaaP.logger.debug { PP.pp(f.symbolic_call, ''.dup) }
239
- puts "### call stack:"
240
- puts
241
- puts "```"
242
- puts SymbolicCaller.new(f.symbolic_call).to_lines.join("\n")
243
- 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 "```"
244
305
  status = 1
245
306
  throw :break
246
307
  in Result::Skip => s
247
308
  print 'S'
248
- RaaP.logger.debug { PP.pp(s.symbolic_call, ''.dup) }
249
- RaaP.logger.debug("Skip: [#{s.exception.class}] #{s.exception.message}")
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}")
250
311
  RaaP.logger.debug(s.exception.backtrace.join("\n"))
251
312
  in Result::Exception => e
252
313
  print 'E'
253
- RaaP.logger.debug { PP.pp(e.symbolic_call, ''.dup) }
254
- RaaP.logger.debug("Exception: [#{e.exception.class}] #{e.exception.message}")
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}")
255
316
  RaaP.logger.debug(e.exception.backtrace.join("\n"))
256
317
  end
257
318
  end
319
+ end_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
258
320
  puts
259
- puts "success: #{stats.success}, skip: #{stats.skip}, exception: #{stats.exception}"
260
- puts
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
261
331
 
262
- [status, method_name, method_type]
332
+ [status, method_name, method_type, reason]
263
333
  end
264
334
  end
265
335
  end
@@ -6,35 +6,39 @@ module RaaP
6
6
  @fun = fun
7
7
  end
8
8
 
9
- def pick_arguments(size: 10, eval: true)
10
- a = recursive_pick(build_args_type, size:, eval:)
11
- k = recursive_pick(build_kwargs_type, size:, eval:)
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 recursive_pick(type, size:, eval:)
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, recursive_pick(v, size:, eval:)] }
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| recursive_pick(v, size:, eval:) }
29
+ type.each.map { |v| to_symbolic_call_recursive(v, size:) }
26
30
  else
27
- type.pick(size:, eval:)
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.sample(Random.rand(@fun.optional_positionals.length + 1))
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