raap 0.3.0 → 0.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 507ed156280ed3e0db1f2eb454bb03c57aa473d537d8c6ea5905ad852eba550d
4
- data.tar.gz: 3108fa50c664702f79141a3c0b5e83934d6742468d1d51bfb2febb5b64a00b12
3
+ metadata.gz: 94f0ec4ff924c5089f8e307002dd5f3cd778f04e686acd8b46466139c3c1aebd
4
+ data.tar.gz: e9a06fb3c71cfc6cd605031fe8cb899a123dc82a94ef00586d39e8329c45beac
5
5
  SHA512:
6
- metadata.gz: 269e220d72b9861881de860a71a2adff318105595880fe73e86492e255c9bb32fbeff7ac3685b83ea11d792a02b1509eb969d9956483a9415608530a3f8806bd
7
- data.tar.gz: df4e40973681eb292d7844aa5ed1427b7155f619c6ebea53792149f430ab28e42a0a0f52df9517a3af9af1e08494eaccdd8d4d84313c9d24184f6403a51e1dc5
6
+ metadata.gz: ed0d647fefa8591c0a03981232c84d64bf7c70bcf9207d2dd6471a9f3369747b5dd3adcc3cd60a8e1d65f0e1543ed7a5f5108eef6523c7d306cfc399d0bc63e4
7
+ data.tar.gz: 537c5dfbb65bb12064e46c47fb75a60321ee0f16296f1272ef27d68927f4f9f3d5875ea6f3ac2225c76a2d274fbe393bff20f573b8d138b52a6ced8027528e1f
data/.rubocop.yml ADDED
@@ -0,0 +1,31 @@
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
+ Style/FrozenStringLiteralComment:
18
+ Enabled: true
19
+
20
+ Metrics:
21
+ Enabled: false
22
+
23
+ Layout:
24
+ Enabled: true
25
+ Layout/FirstArrayElementIndentation:
26
+ EnforcedStyle: consistent
27
+ Layout/LineLength:
28
+ Max: 150
29
+
30
+ Naming:
31
+ Enabled: false
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # RaaP
2
2
 
3
+ <img src="https://raw.githubusercontent.com/ksss/raap/main/public/jacket.webp" width=400/>
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
 
@@ -68,6 +70,15 @@ $ raap 'MyClass' # Run only RBS of MyClass
68
70
  $ raap 'MyClass::*' # Run each class under MyClass
69
71
  $ raap 'MyClass.singleton_method' # Run only MyClass.singleton_method
70
72
  $ raap 'MyClass#instance_method' # Run only MyClass#instance_method
73
+ $ raap 'MyClass' '!MyClass#skip' # Run method MyClass without #skip
74
+ ```
75
+
76
+ ```
77
+ $ cat test/raap.txt
78
+ MyClass
79
+ !MyClass#skip
80
+
81
+ $ raap $(cat test/raap.txt) # You can manage the methods to be tested in a file
71
82
  ```
72
83
 
73
84
  ## Size
@@ -101,11 +112,15 @@ You can specify to load specify PATH as RBS.
101
112
 
102
113
  ### `--library lib`
103
114
 
104
- You can specify to load RBS library
115
+ You can specify to load RBS library.
105
116
 
106
117
  ### `--require lib`
107
118
 
108
- You can specify require Ruby library
119
+ You can specify require Ruby library.
120
+
121
+ ### `--log-level level`
122
+
123
+ You can specify log level (debug, info, warn or error).
109
124
 
110
125
  ### `--timeout sec`
111
126
 
@@ -123,6 +138,15 @@ You can specify size of end.
123
138
 
124
139
  You can specify size of step like `Integer#step: (to: Integer, by: Integer)`.
125
140
 
141
+ ### `--allow-private`
142
+
143
+ By default, raap only validates public methods.
144
+ However, by setting this option, it is possible to intentionally validate private methods in the RBS.
145
+
146
+ ## First support is CLI
147
+
148
+ 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.
149
+
126
150
  ## Achievements
127
151
 
128
152
  RaaP has already found many RBS mistakes and bug in CRuby during the development phase.
@@ -134,6 +158,11 @@ RaaP has already found many RBS mistakes and bug in CRuby during the development
134
158
  * https://github.com/ruby/rbs/pull/1769
135
159
  * https://github.com/ruby/rbs/pull/1770
136
160
  * https://github.com/ruby/rbs/pull/1771
161
+ * https://github.com/ruby/rbs/pull/1779
162
+ * https://github.com/ruby/rbs/pull/1783
163
+ * https://github.com/ruby/rbs/pull/1789
164
+ * https://github.com/ruby/rbs/pull/1790
165
+ * https://github.com/ruby/rbs/pull/1793
137
166
 
138
167
  ## Development
139
168
 
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,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
1
4
  require 'raap'
2
5
 
3
- RaaP::CLI.new(ARGV).load.run
6
+ exit RaaP::CLI.new(ARGV).load.run
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RaaP
2
4
  module BindCall
3
5
  class << self
4
- def define_singleton_method(...) = ::Object.instance_method(:define_singleton_method).bind_call(...)
6
+ def define_method(...) = ::Module.instance_method(:define_method).bind_call(...)
5
7
  def respond_to?(...) = ::Kernel.instance_method(:respond_to?).bind_call(...)
6
8
  def instance_of?(...) = ::Kernel.instance_method(:instance_of?).bind_call(...)
7
9
  def is_a?(...) = ::Kernel.instance_method(:is_a?).bind_call(...)
@@ -11,19 +13,31 @@ module RaaP
11
13
  def public_send(...) = ::Kernel.instance_method(:public_send).bind_call(...)
12
14
 
13
15
  def class(obj)
14
- if respond_to?(obj, :class)
15
- obj.class
16
- else
16
+ if instance_of?(obj, BasicObject)
17
17
  ::Kernel.instance_method(:class).bind_call(obj)
18
+ else
19
+ obj.class
18
20
  end
19
21
  end
20
22
 
21
23
  def inspect(obj)
22
- if respond_to?(obj, :inspect)
23
- obj.inspect
24
- else
24
+ if instance_of?(obj, BasicObject)
25
25
  ::Kernel.instance_method(:inspect).bind_call(obj)
26
+ else
27
+ case obj
28
+ when Hash
29
+ body = obj.map do |k, v|
30
+ "#{inspect(k)} => #{inspect(v)}"
31
+ end
32
+ "{#{body.join(', ')}}"
33
+ when Array
34
+ "[#{obj.map { |o| inspect(o) }.join(', ')}]"
35
+ else
36
+ obj.inspect
37
+ end
26
38
  end
39
+ rescue NoMethodError
40
+ "#<#{self.class(obj)}>"
27
41
  end
28
42
  end
29
43
  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,
@@ -20,60 +15,80 @@ module RaaP
20
15
  keyword_init: true
21
16
  )
22
17
 
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
- )
18
+ # Should skip methods has side effects
19
+ DEFAULT_SKIP = Set.new
20
+ %i[
21
+ fork system exec spawn `
22
+ abort exit exit! raise fail
23
+ load require require_relative
24
+ gem
25
+ ].each do |kernel_method|
26
+ DEFAULT_SKIP << "::Kernel##{kernel_method}"
27
+ DEFAULT_SKIP << "::Kernel.#{kernel_method}"
28
+ end
29
+ %i[
30
+ delete unlink chmod lchmod chown lchown
31
+ link mkfifo new open rename truncate
32
+ ].each { |m| DEFAULT_SKIP << "::File.#{m}" }
33
+ %i[flock truncate].each { |m| DEFAULT_SKIP << "::File##{m}" }
34
+
35
+ attr_accessor :option, :argv, :skip, :results
34
36
 
35
37
  def initialize(argv)
38
+ # defaults
39
+ @option = Option.new(
40
+ dirs: [],
41
+ requires: [],
42
+ libraries: [],
43
+ timeout: 3,
44
+ size_from: 0,
45
+ size_to: 99,
46
+ size_by: 1,
47
+ allow_private: false,
48
+ )
36
49
  @argv = argv
50
+ @skip = DEFAULT_SKIP.dup
51
+ @results = []
37
52
  end
38
53
 
39
54
  def load
40
55
  OptionParser.new do |o|
41
56
  o.on('-I', '--include PATH') do |path|
42
- CLI.option.dirs << path
57
+ @option.dirs << path
43
58
  end
44
59
  o.on('--library lib', 'load rbs library') do |lib|
45
- CLI.option.libraries << lib
60
+ @option.libraries << lib
46
61
  end
47
62
  o.on('--require lib', 'require ruby library') do |lib|
48
- CLI.option.requires << lib
63
+ @option.requires << lib
49
64
  end
50
- o.on('--log-level level', "default: warn") do |arg|
65
+ o.on('--log-level level', "default: info") do |arg|
51
66
  RaaP.logger.level = arg
52
67
  end
53
- o.on('--timeout sec', Integer, "default: #{CLI.option.timeout}") do |arg|
54
- CLI.option.timeout = arg
68
+ o.on('--timeout sec', Float, "default: #{@option.timeout}") do |arg|
69
+ @option.timeout = arg
55
70
  end
56
- o.on('--size-from int', Integer, "default: #{CLI.option.size_from}") do |arg|
57
- CLI.option.size_from = arg
71
+ o.on('--size-from int', Integer, "default: #{@option.size_from}") do |arg|
72
+ @option.size_from = arg
58
73
  end
59
- o.on('--size-to int', Integer, "default: #{CLI.option.size_to}") do |arg|
60
- CLI.option.size_to = arg
74
+ o.on('--size-to int', Integer, "default: #{@option.size_to}") do |arg|
75
+ @option.size_to = arg
61
76
  end
62
- o.on('--size-by int', Integer, "default: #{CLI.option.size_by}") do |arg|
63
- CLI.option.size_by = arg
77
+ o.on('--size-by int', Integer, "default: #{@option.size_by}") do |arg|
78
+ @option.size_by = arg
64
79
  end
65
- o.on('--allow-private', "default: #{CLI.option.allow_private}") do
66
- CLI.option.allow_private = true
80
+ o.on('--allow-private', "default: #{@option.allow_private}") do
81
+ @option.allow_private = true
67
82
  end
68
83
  end.parse!(@argv)
69
84
 
70
- CLI.option.dirs.each do |dir|
85
+ @option.dirs.each do |dir|
71
86
  RaaP::RBS.loader.add(path: Pathname(dir))
72
87
  end
73
- CLI.option.libraries.each do |lib|
88
+ @option.libraries.each do |lib|
74
89
  RaaP::RBS.loader.add(library: lib, version: nil)
75
90
  end
76
- CLI.option.requires.each do |lib|
91
+ @option.requires.each do |lib|
77
92
  require lib
78
93
  end
79
94
 
@@ -81,137 +96,195 @@ module RaaP
81
96
  end
82
97
 
83
98
  def run
84
- @argv.map do |tag|
99
+ Signal.trap(:INT) do
100
+ puts "Interrupted by SIGINT"
101
+ report
102
+ exit 1
103
+ end
104
+
105
+ # Search skip tag
106
+ @argv.each do |tag|
107
+ if tag.start_with?('!') && (tag.include?('#') || tag.include?('.'))
108
+ t = tag[1..] or raise
109
+ t = "::#{t}" unless t.start_with?('::')
110
+ t or raise
111
+ @skip << t
112
+ end
113
+ end
114
+ @skip.freeze
115
+
116
+ @argv.each do |tag|
117
+ next if tag.start_with?('!')
118
+
85
119
  case
86
120
  when tag.include?('#')
87
- run_by_instance(tag:)
121
+ run_by(kind: :instance, tag:)
88
122
  when tag.include?('.')
89
- run_by_singleton(tag:)
123
+ run_by(kind: :singleton, tag:)
90
124
  when tag.end_with?('*')
91
125
  run_by_type_name_with_search(tag:)
92
126
  else
93
127
  run_by_type_name(tag:)
94
128
  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
129
  end
105
130
 
106
- self
131
+ report
107
132
  end
108
133
 
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:)
134
+ private
135
+
136
+ def report
137
+ i = 0
138
+ exit_status = 0
139
+ @results.each do |ret|
140
+ ret => { method:, properties: }
141
+ properties.select { |status,| status == 1 }.each do |_, method_name, method_type, reason|
142
+ i += 1
143
+ location = if method.alias_of
144
+ alias_decl = RBS.find_alias_decl(method.defined_in, method_name) or raise "alias decl not found: #{method_name}"
145
+ alias_decl.location
146
+ else
147
+ method_type.location
148
+ end
149
+ prefix = method.defs.first.member.kind == :instance ? '' : 'self.'
150
+
151
+ puts "\e[41m\e[1m#\e[m\e[1m #{i}) Failure:\e[m"
152
+ puts
153
+ puts "def #{prefix}#{method_name}: #{method_type}"
154
+ puts " in #{location}"
155
+ puts
156
+ puts "## Reason"
157
+ puts
158
+ puts reason&.string
159
+ puts
160
+ exit_status = 1
127
161
  end
128
- ]
162
+ end
163
+ exit_status
129
164
  end
130
165
 
131
- def run_by_singleton(tag:)
132
- t, m = tag.split('.', 2)
166
+ def run_by(kind:, tag:)
167
+ split = kind == :instance ? '#' : '.'
168
+ t, m = tag.split(split, 2)
133
169
  t or raise
134
170
  m or raise
135
171
  type = RBS.parse_type(t)
136
172
  raise "cannot specified #{type.class}" unless type.respond_to?(:name)
173
+
137
174
  type = __skip__ = type
138
- receiver_type = Type.new("singleton(#{type.name})")
175
+ type_name = type.name.absolute!
176
+ type_to_s = type.to_s.start_with?('::') ? type.to_s : "::#{type}"
177
+ receiver_type = if kind == :instance
178
+ Type.new(type_to_s)
179
+ else
180
+ Type.new("singleton(#{type_name})")
181
+ end
139
182
  method_name = m.to_sym
140
- definition = RBS.builder.build_singleton(type.name)
183
+ definition = if kind == :instance
184
+ RBS.builder.build_instance(type_name)
185
+ else
186
+ RBS.builder.build_singleton(type_name)
187
+ end
188
+
141
189
  method = definition.methods[method_name]
142
- type_params_decl = definition.type_params_decl
143
190
  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:)
191
+
192
+ if @skip.include?("#{type_name}#{split}#{method_name}")
193
+ raise "`#{type_name}#{split}#{method_name}` is a method to be skipped"
194
+ end
195
+
196
+ type_params_decl = definition.type_params_decl
197
+ type_args = type.args
198
+
199
+ RaaP.logger.info("# #{type}")
200
+ @results << {
201
+ method:,
202
+ properties: method.method_types.map do |method_type|
203
+ property(receiver_type:, type_params_decl:, type_args:, method_type:, method_name:)
149
204
  end
150
- ]
205
+ }
151
206
  end
152
207
 
153
208
  def run_by_type_name_with_search(tag:)
154
209
  first, _last = tag.split('::')
155
- ret = []
156
- RBS.env.class_decls.each do |name, entry|
157
- if ['', '::'].any? { |pre| name.to_s.match?(/\A#{pre}#{first}\b/) }
158
- ret << run_by_type_name(tag: name.to_s)
210
+ RBS.env.class_decls.each do |name, _entry|
211
+ if ['', '::'].any? { |pre| name.to_s.start_with?("#{pre}#{first}") }
212
+ if @skip.include?(name.to_s)
213
+ RaaP.logger.info("Skip #{name}")
214
+ next
215
+ end
216
+ run_by_type_name(tag: name.to_s)
159
217
  end
160
218
  end
161
- ret.flatten(1)
162
219
  end
163
220
 
164
221
  def run_by_type_name(tag:)
165
222
  type = RBS.parse_type(tag)
166
223
  type = __skip__ = type
167
224
  raise "cannot specified #{type.class}" unless type.respond_to?(:name)
168
- type_name = type.name.absolute!
169
225
 
170
- ret = []
226
+ type_name = type.name.absolute!
227
+ type_args = type.args
171
228
 
172
229
  definition = RBS.builder.build_singleton(type_name)
173
230
  type_params_decl = definition.type_params_decl
174
- definition.methods.filter_map do |method_name, method|
231
+ definition.methods.each do |method_name, method|
232
+ if @skip.include?("#{type_name.absolute!}.#{method_name}")
233
+ RaaP.logger.info("Skip #{"#{type_name.absolute!}.#{method_name}"}")
234
+ next
235
+ end
175
236
  next unless method.accessibility == :public
176
237
  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
238
+
239
+ RaaP.logger.info("# #{type_name}.#{method_name}")
240
+ @results << {
241
+ method:,
242
+ properties: method.method_types.map do |method_type|
243
+ property(receiver_type: Type.new("singleton(#{type.name})"), type_params_decl:, type_args:, method_type:, method_name:)
244
+ end
245
+ }
183
246
  end
184
247
 
185
248
  definition = RBS.builder.build_instance(type_name)
186
249
  type_params_decl = definition.type_params_decl
187
- definition.methods.filter_map do |method_name, method|
250
+ definition.methods.each do |method_name, method|
251
+ if @skip.include?("#{type_name.absolute!}##{method_name}")
252
+ RaaP.logger.info("Skip #{"#{type_name.absolute!}.#{method_name}"}")
253
+ next
254
+ end
188
255
  next unless method.accessibility == :public
189
256
  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
257
 
198
- ret
258
+ RaaP.logger.info("# #{type_name}##{method_name}")
259
+ @results << {
260
+ method:,
261
+ properties: method.method_types.map do |method_type|
262
+ property(receiver_type: Type.new(type.name), type_params_decl:, type_args:, method_type:, method_name:)
263
+ end
264
+ }
265
+ end
199
266
  end
200
267
 
201
- def property(receiver_type:, type_params_decl:, method_type:, method_name:)
268
+ def property(receiver_type:, type_params_decl:, type_args:, method_type:, method_name:)
202
269
  rtype = __skip__ = receiver_type.type
203
270
  if receiver_type.type.instance_of?(::RBS::Types::ClassSingleton)
204
271
  prefix = 'self.'
205
- type_args = []
206
272
  else
207
273
  prefix = ''
208
- type_args = rtype.args
209
274
  end
210
- puts "## def #{prefix}#{method_name}: #{method_type}"
275
+
276
+ # type_args delegate to self_type
277
+ type_params_decl.each_with_index do |param, i|
278
+ if rtype.instance_of?(::RBS::Types::ClassInstance)
279
+ rtype.args[i] = type_args[i] || param.upper_bound || ::RBS::Types::Bases::Any.new(location: nil)
280
+ end
281
+ end
282
+ RaaP.logger.info("## def #{prefix}#{method_name}: #{method_type}")
211
283
  status = 0
212
- stats = MethodProperty.new(
284
+ reason = nil
285
+ prop = MethodProperty.new(
213
286
  receiver_type:,
214
- method_name: method_name,
287
+ method_name:,
215
288
  method_type: MethodType.new(
216
289
  method_type,
217
290
  type_params_decl:,
@@ -220,46 +293,59 @@ module RaaP
220
293
  instance_type: ::RBS::Types::ClassInstance.new(name: rtype.name, args: type_args, location: nil),
221
294
  class_type: ::RBS::Types::ClassSingleton.new(name: rtype.name, location: nil),
222
295
  ),
223
- size_step: CLI.option.size_from.step(to: CLI.option.size_to, by: CLI.option.size_by),
224
- timeout: CLI.option.timeout,
225
- allow_private: true,
226
- ).run do |called|
296
+ size_step: @option.size_from.step(to: @option.size_to, by: @option.size_by),
297
+ timeout: @option.timeout,
298
+ allow_private: @option.allow_private,
299
+ )
300
+ start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
301
+ stats = prop.run do |called|
227
302
  case called
228
303
  in Result::Success => s
229
304
  print '.'
230
305
  RaaP.logger.debug { "Success: #{s.called_str}" }
231
306
  in Result::Failure => f
232
307
  puts 'F'
233
- puts "Failed in case of `#{f.called_str}`"
234
- if e = f.exception
235
- RaaP.logger.debug { "Failure: [#{e.class}] #{e.message}" }
308
+ if (e = f.exception)
309
+ RaaP.logger.info { "Failure: [#{e.class}] #{e.message}" }
310
+ RaaP.logger.debug { e.backtrace.join("\n") }
236
311
  end
237
- puts
238
312
  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 "```"
313
+ reason = StringIO.new
314
+ reason.puts "Failed in case of `#{f.called_str}`"
315
+ reason.puts
316
+ reason.puts "### Repro"
317
+ reason.puts
318
+ reason.puts "```rb"
319
+ reason.puts SymbolicCaller.new(f.symbolic_call).to_lines.join("\n")
320
+ reason.puts "```"
244
321
  status = 1
245
322
  throw :break
246
323
  in Result::Skip => s
247
324
  print 'S'
248
- RaaP.logger.debug { PP.pp(s.symbolic_call, ''.dup) }
249
- RaaP.logger.debug("Skip: [#{s.exception.class}] #{s.exception.message}")
325
+ RaaP.logger.debug { "\n```\n#{SymbolicCaller.new(s.symbolic_call).to_lines.join("\n")}\n```" }
326
+ RaaP.logger.debug("Skip: #{s.exception.detailed_message}")
250
327
  RaaP.logger.debug(s.exception.backtrace.join("\n"))
251
328
  in Result::Exception => e
252
329
  print 'E'
253
- RaaP.logger.debug { PP.pp(e.symbolic_call, ''.dup) }
254
- RaaP.logger.debug("Exception: [#{e.exception.class}] #{e.exception.message}")
330
+ RaaP.logger.debug { "\n```\n#{SymbolicCaller.new(e.symbolic_call).to_lines.join("\n")}\n```" }
331
+ RaaP.logger.debug("Exception: #{e.exception.detailed_message}")
255
332
  RaaP.logger.debug(e.exception.backtrace.join("\n"))
256
333
  end
257
334
  end
335
+ end_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
258
336
  puts
259
- puts "success: #{stats.success}, skip: #{stats.skip}, exception: #{stats.exception}"
260
- puts
337
+ time_diff = end_time - start_time
338
+ time = ", time: #{(time_diff * 1000).round}ms"
339
+ stats_log = "success: #{stats.success}, skip: #{stats.skip}, exception: #{stats.exception}#{time}"
340
+ RaaP.logger.info(stats_log)
341
+
342
+ if status == 0 && stats.success.zero? && !stats.break
343
+ status = 1
344
+ reason = StringIO.new
345
+ reason.puts "Never succeeded => #{stats_log}"
346
+ end
261
347
 
262
- [status, method_name, method_type]
348
+ [status, method_name, method_type, reason]
263
349
  end
264
350
  end
265
351
  end