appmap 0.34.4 → 0.36.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/CHANGELOG.md +24 -0
- data/README.md +11 -0
- data/Rakefile +1 -1
- data/appmap.gemspec +1 -1
- data/appmap.yml +1 -7
- data/ext/appmap/appmap.c +63 -4
- data/lib/appmap.rb +1 -1
- data/lib/appmap/class_map.rb +6 -7
- data/lib/appmap/config.rb +55 -25
- data/lib/appmap/event.rb +28 -10
- data/lib/appmap/hook.rb +12 -8
- data/lib/appmap/hook/method.rb +35 -28
- data/lib/appmap/rails/request_handler.rb +88 -0
- data/lib/appmap/rails/sql_handler.rb +10 -2
- data/lib/appmap/railtie.rb +4 -6
- data/lib/appmap/rspec.rb +10 -0
- data/lib/appmap/trace.rb +9 -7
- data/lib/appmap/util.rb +1 -1
- data/lib/appmap/version.rb +1 -1
- data/spec/abstract_controller4_base_spec.rb +1 -1
- data/spec/abstract_controller_base_spec.rb +6 -0
- data/spec/fixtures/hook/exception_method.rb +44 -0
- data/spec/hook_spec.rb +135 -2
- data/test/cli_test.rb +0 -10
- data/test/fixtures/gem_test/Gemfile +6 -0
- data/test/fixtures/gem_test/appmap.yml +3 -0
- data/test/fixtures/gem_test/test/to_param_test.rb +14 -0
- data/test/gem_test.rb +34 -0
- data/test/minitest_test.rb +2 -2
- data/test/openssl_test.rb +1 -49
- metadata +13 -10
- data/.ruby-version +0 -1
- data/lib/appmap/rails/action_handler.rb +0 -91
data/lib/appmap/hook/method.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module AppMap
|
2
4
|
class Hook
|
3
5
|
class Method
|
4
|
-
attr_reader :hook_class, :hook_method
|
6
|
+
attr_reader :hook_package, :hook_class, :hook_method
|
5
7
|
|
6
8
|
# +method_display_name+ may be nil if name resolution gets
|
7
9
|
# deferred until runtime (e.g. for a singleton method on an
|
@@ -15,8 +17,9 @@ module AppMap
|
|
15
17
|
# with the method we're hooking.
|
16
18
|
TIME_NOW = Time.method(:now)
|
17
19
|
private_constant :TIME_NOW
|
18
|
-
|
19
|
-
def initialize(hook_class, hook_method)
|
20
|
+
|
21
|
+
def initialize(hook_package, hook_class, hook_method)
|
22
|
+
@hook_package = hook_package
|
20
23
|
@hook_class = hook_class
|
21
24
|
@hook_method = hook_method
|
22
25
|
|
@@ -30,7 +33,7 @@ module AppMap
|
|
30
33
|
msg = if method_display_name
|
31
34
|
"#{method_display_name}"
|
32
35
|
else
|
33
|
-
"#{hook_method.name} (class resolution
|
36
|
+
"#{hook_method.name} (class resolution deferred)"
|
34
37
|
end
|
35
38
|
warn "AppMap: Hooking " + msg
|
36
39
|
end
|
@@ -41,34 +44,38 @@ module AppMap
|
|
41
44
|
after_hook = self.method(:after_hook)
|
42
45
|
with_disabled_hook = self.method(:with_disabled_hook)
|
43
46
|
|
44
|
-
|
45
|
-
|
47
|
+
hook_method_def = nil
|
48
|
+
hook_class.instance_eval do
|
49
|
+
hook_method_def = Proc.new do |*args, &block|
|
50
|
+
instance_method = hook_method.bind(self).to_proc
|
46
51
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
52
|
+
# We may not have gotten the class for the method during
|
53
|
+
# initialization (e.g. for a singleton method on an embedded
|
54
|
+
# struct), so make sure we have it now.
|
55
|
+
defined_class,_ = Hook.qualify_method_name(hook_method) unless defined_class
|
51
56
|
|
52
|
-
|
53
|
-
|
54
|
-
|
57
|
+
hook_disabled = Thread.current[HOOK_DISABLE_KEY]
|
58
|
+
enabled = true if !hook_disabled && AppMap.tracing.enabled?
|
59
|
+
return instance_method.call(*args, &block) unless enabled
|
55
60
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
61
|
+
call_event, start_time = with_disabled_hook.() do
|
62
|
+
before_hook.(self, defined_class, args)
|
63
|
+
end
|
64
|
+
return_value = nil
|
65
|
+
exception = nil
|
66
|
+
begin
|
67
|
+
return_value = instance_method.(*args, &block)
|
68
|
+
rescue
|
69
|
+
exception = $ERROR_INFO
|
70
|
+
raise
|
71
|
+
ensure
|
72
|
+
with_disabled_hook.() do
|
73
|
+
after_hook.(self, call_event, start_time, return_value, exception)
|
74
|
+
end
|
69
75
|
end
|
70
76
|
end
|
71
77
|
end
|
78
|
+
hook_class.define_method_with_arity(hook_method.name, hook_method.arity, hook_method_def)
|
72
79
|
end
|
73
80
|
|
74
81
|
protected
|
@@ -76,11 +83,11 @@ module AppMap
|
|
76
83
|
def before_hook(receiver, defined_class, args)
|
77
84
|
require 'appmap/event'
|
78
85
|
call_event = AppMap::Event::MethodCall.build_from_invocation(defined_class, hook_method, receiver, args)
|
79
|
-
AppMap.tracing.record_event call_event, defined_class: defined_class, method: hook_method
|
86
|
+
AppMap.tracing.record_event call_event, package: hook_package, defined_class: defined_class, method: hook_method
|
80
87
|
[ call_event, TIME_NOW.call ]
|
81
88
|
end
|
82
89
|
|
83
|
-
def after_hook(call_event, start_time, return_value, exception)
|
90
|
+
def after_hook(receiver, call_event, start_time, return_value, exception)
|
84
91
|
require 'appmap/event'
|
85
92
|
elapsed = TIME_NOW.call - start_time
|
86
93
|
return_event = \
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'appmap/event'
|
4
|
+
require 'appmap/hook'
|
5
|
+
|
6
|
+
module AppMap
|
7
|
+
module Rails
|
8
|
+
module RequestHandler
|
9
|
+
class HTTPServerRequest < AppMap::Event::MethodEvent
|
10
|
+
attr_accessor :request_method, :path_info, :params
|
11
|
+
|
12
|
+
def initialize(request)
|
13
|
+
super AppMap::Event.next_id_counter, :call, Thread.current.object_id
|
14
|
+
|
15
|
+
@request_method = request.request_method
|
16
|
+
@path_info = request.path_info.split('?')[0]
|
17
|
+
@params = ActionDispatch::Http::ParameterFilter.new(::Rails.application.config.filter_parameters).filter(request.params)
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_h
|
21
|
+
super.tap do |h|
|
22
|
+
h[:http_server_request] = {
|
23
|
+
request_method: request_method,
|
24
|
+
path_info: path_info
|
25
|
+
}
|
26
|
+
|
27
|
+
h[:message] = params.keys.map do |key|
|
28
|
+
val = params[key]
|
29
|
+
{
|
30
|
+
name: key,
|
31
|
+
class: val.class.name,
|
32
|
+
value: self.class.display_string(val),
|
33
|
+
object_id: val.__id__
|
34
|
+
}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class HTTPServerResponse < AppMap::Event::MethodReturnIgnoreValue
|
41
|
+
attr_accessor :status, :mime_type
|
42
|
+
|
43
|
+
def initialize(response, parent_id, elapsed)
|
44
|
+
super AppMap::Event.next_id_counter, :return, Thread.current.object_id
|
45
|
+
|
46
|
+
self.status = response.status
|
47
|
+
self.mime_type = response.headers['Content-Type']
|
48
|
+
self.parent_id = parent_id
|
49
|
+
self.elapsed = elapsed
|
50
|
+
end
|
51
|
+
|
52
|
+
def to_h
|
53
|
+
super.tap do |h|
|
54
|
+
h[:http_server_response] = {
|
55
|
+
status: status,
|
56
|
+
mime_type: mime_type
|
57
|
+
}.compact
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class HookMethod < AppMap::Hook::Method
|
63
|
+
def initialize
|
64
|
+
# ActionController::Instrumentation has issued start_processing.action_controller and
|
65
|
+
# process_action.action_controller since Rails 3. Therefore it's a stable place to hook
|
66
|
+
# the request. Rails controller notifications can't be used directly because they don't
|
67
|
+
# provide response headers, and we want the Content-Type.
|
68
|
+
super(nil, ActionController::Instrumentation, ActionController::Instrumentation.instance_method(:process_action))
|
69
|
+
end
|
70
|
+
|
71
|
+
protected
|
72
|
+
|
73
|
+
def before_hook(receiver, defined_class, _) # args
|
74
|
+
call_event = HTTPServerRequest.new(receiver.request)
|
75
|
+
# http_server_request events are i/o and do not require a package name.
|
76
|
+
AppMap.tracing.record_event call_event, defined_class: defined_class, method: hook_method
|
77
|
+
[ call_event, TIME_NOW.call ]
|
78
|
+
end
|
79
|
+
|
80
|
+
def after_hook(receiver, call_event, start_time, _, _) # return_value, exception
|
81
|
+
elapsed = TIME_NOW.call - start_time
|
82
|
+
return_event = HTTPServerResponse.new receiver.response, call_event.id, elapsed
|
83
|
+
AppMap.tracing.record_event return_event
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -72,9 +72,17 @@ module AppMap
|
|
72
72
|
end
|
73
73
|
|
74
74
|
class ActiveRecordExaminer
|
75
|
+
@@db_version_warning_issued = {}
|
76
|
+
|
77
|
+
def issue_warning
|
78
|
+
db_type = database_type
|
79
|
+
return if @@db_version_warning_issued[db_type]
|
80
|
+
warn("AppMap: Unable to determine database version for #{db_type.inspect}")
|
81
|
+
@@db_version_warning_issued[db_type] = true
|
82
|
+
end
|
83
|
+
|
75
84
|
def server_version
|
76
|
-
ActiveRecord::Base.connection.try(:database_version)
|
77
|
-
warn("Unable to determine database version for #{database_type.inspect}")
|
85
|
+
ActiveRecord::Base.connection.try(:database_version) || issue_warning
|
78
86
|
end
|
79
87
|
|
80
88
|
def database_type
|
data/lib/appmap/railtie.rb
CHANGED
@@ -13,13 +13,11 @@ module AppMap
|
|
13
13
|
# AppMap events.
|
14
14
|
initializer 'appmap.subscribe', after: 'appmap.init' do |_| # params: app
|
15
15
|
require 'appmap/rails/sql_handler'
|
16
|
-
require 'appmap/rails/
|
16
|
+
require 'appmap/rails/request_handler'
|
17
17
|
ActiveSupport::Notifications.subscribe 'sql.sequel', AppMap::Rails::SQLHandler.new
|
18
18
|
ActiveSupport::Notifications.subscribe 'sql.active_record', AppMap::Rails::SQLHandler.new
|
19
|
-
|
20
|
-
|
21
|
-
ActiveSupport::Notifications.subscribe \
|
22
|
-
'process_action.action_controller', AppMap::Rails::ActionHandler::HTTPServerResponse.new
|
19
|
+
|
20
|
+
AppMap::Rails::RequestHandler::HookMethod.new.activate
|
23
21
|
end
|
24
22
|
|
25
23
|
# appmap.trace begins recording an AppMap trace and writes it to appmap.json.
|
@@ -42,4 +40,4 @@ module AppMap
|
|
42
40
|
end.call
|
43
41
|
end
|
44
42
|
end
|
45
|
-
end
|
43
|
+
end unless ENV['APPMAP_INITIALIZE'] == 'false'
|
data/lib/appmap/rspec.rb
CHANGED
@@ -124,8 +124,18 @@ module AppMap
|
|
124
124
|
def initialize(example)
|
125
125
|
super
|
126
126
|
|
127
|
+
webdriver_port = lambda do
|
128
|
+
return unless defined?(page) && page&.driver
|
129
|
+
|
130
|
+
# This is the ugliest thing ever but I don't want to lose it.
|
131
|
+
# All the WebDriver calls are getting app-mapped and it's really unclear
|
132
|
+
# what they are.
|
133
|
+
page.driver.options[:http_client].instance_variable_get('@server_url').port
|
134
|
+
end
|
135
|
+
|
127
136
|
warn "Starting recording of example #{example}" if AppMap::RSpec::LOG
|
128
137
|
@trace = AppMap.tracing.trace
|
138
|
+
@webdriver_port = webdriver_port.()
|
129
139
|
end
|
130
140
|
|
131
141
|
def finish
|
data/lib/appmap/trace.rb
CHANGED
@@ -3,9 +3,10 @@
|
|
3
3
|
module AppMap
|
4
4
|
module Trace
|
5
5
|
class ScopedMethod < SimpleDelegator
|
6
|
-
attr_reader :defined_class, :static
|
7
|
-
|
8
|
-
def initialize(defined_class, method, static)
|
6
|
+
attr_reader :package, :defined_class, :static
|
7
|
+
|
8
|
+
def initialize(package, defined_class, method, static)
|
9
|
+
@package = package
|
9
10
|
@defined_class = defined_class
|
10
11
|
@static = static
|
11
12
|
super(method)
|
@@ -32,9 +33,9 @@ module AppMap
|
|
32
33
|
@tracing.any?(&:enabled?)
|
33
34
|
end
|
34
35
|
|
35
|
-
def record_event(event, defined_class: nil, method: nil)
|
36
|
+
def record_event(event, package: nil, defined_class: nil, method: nil)
|
36
37
|
@tracing.each do |tracer|
|
37
|
-
tracer.record_event(event, defined_class: defined_class, method: method)
|
38
|
+
tracer.record_event(event, package: package, defined_class: defined_class, method: method)
|
38
39
|
end
|
39
40
|
end
|
40
41
|
|
@@ -71,11 +72,12 @@ module AppMap
|
|
71
72
|
# Record a program execution event.
|
72
73
|
#
|
73
74
|
# The event should be one of the MethodEvent subclasses.
|
74
|
-
def record_event(event, defined_class: nil, method: nil)
|
75
|
+
def record_event(event, package: nil, defined_class: nil, method: nil)
|
75
76
|
return unless @enabled
|
76
77
|
|
77
78
|
@events << event
|
78
|
-
@methods << Trace::ScopedMethod.new(defined_class, method, event.static)
|
79
|
+
@methods << Trace::ScopedMethod.new(package, defined_class, method, event.static) \
|
80
|
+
if package && defined_class && method && (event.event == :call)
|
79
81
|
end
|
80
82
|
|
81
83
|
# Gets a unique list of the methods that were invoked by the program.
|
data/lib/appmap/util.rb
CHANGED
@@ -43,7 +43,7 @@ module AppMap
|
|
43
43
|
require 'hashie'
|
44
44
|
h.extend(Hashie::Extensions::DeepLocate)
|
45
45
|
keys = %i(path location)
|
46
|
-
|
46
|
+
h.deep_locate ->(k,v,o) {
|
47
47
|
next unless keys.include?(k)
|
48
48
|
|
49
49
|
fix = ->(v) {v.gsub(%r{#{Gem.dir}/gems/.*(?=lib)}, '')}
|
data/lib/appmap/version.rb
CHANGED
@@ -39,7 +39,7 @@ describe 'AbstractControllerBase' do
|
|
39
39
|
expect(appmap).to include(<<-SERVER_REQUEST.strip)
|
40
40
|
http_server_request:
|
41
41
|
request_method: POST
|
42
|
-
path_info: "/api/users
|
42
|
+
path_info: "/api/users"
|
43
43
|
SERVER_REQUEST
|
44
44
|
end
|
45
45
|
it 'Properly captures method parameters in the appmap' do
|
@@ -45,6 +45,12 @@ describe 'AbstractControllerBase' do
|
|
45
45
|
request_method: POST
|
46
46
|
path_info: "/api/users"
|
47
47
|
SERVER_REQUEST
|
48
|
+
|
49
|
+
expect(appmap).to include(<<-SERVER_RESPONSE.strip)
|
50
|
+
http_server_response:
|
51
|
+
status: 201
|
52
|
+
mime_type: application/json; charset=utf-8
|
53
|
+
SERVER_RESPONSE
|
48
54
|
end
|
49
55
|
|
50
56
|
it 'properly captures method parameters in the appmap' do
|
@@ -9,3 +9,47 @@ class ExceptionMethod
|
|
9
9
|
raise 'Exception occurred in raise_exception'
|
10
10
|
end
|
11
11
|
end
|
12
|
+
|
13
|
+
# subclass from BasicObject so we don't get #to_s. Requires some
|
14
|
+
# hackery to implement the other methods normally provided by Object.
|
15
|
+
class NoToSMethod < BasicObject
|
16
|
+
def is_a?(*args)
|
17
|
+
return false
|
18
|
+
end
|
19
|
+
|
20
|
+
def class
|
21
|
+
return ::Class
|
22
|
+
end
|
23
|
+
|
24
|
+
def respond_to?(*args)
|
25
|
+
return false
|
26
|
+
end
|
27
|
+
|
28
|
+
def inspect
|
29
|
+
"NoToSMethod"
|
30
|
+
end
|
31
|
+
|
32
|
+
def say_hello
|
33
|
+
"hello"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class InspectRaises < NoToSMethod
|
38
|
+
def inspect
|
39
|
+
::Kernel.raise "#to_s missing, #inspect raises"
|
40
|
+
end
|
41
|
+
|
42
|
+
def say_hello
|
43
|
+
"hello"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class ToSRaises
|
48
|
+
def to_s
|
49
|
+
raise "#to_s raises"
|
50
|
+
end
|
51
|
+
|
52
|
+
def say_hello
|
53
|
+
"hello"
|
54
|
+
end
|
55
|
+
end
|
data/spec/hook_spec.rb
CHANGED
@@ -27,7 +27,7 @@ describe 'AppMap class Hooking', docker: false do
|
|
27
27
|
|
28
28
|
def invoke_test_file(file, setup: nil, &block)
|
29
29
|
AppMap.configuration = nil
|
30
|
-
package = AppMap::Config::Package.
|
30
|
+
package = AppMap::Config::Package.build_from_path(file)
|
31
31
|
config = AppMap::Config.new('hook_spec', [ package ])
|
32
32
|
AppMap.configuration = config
|
33
33
|
tracer = nil
|
@@ -465,6 +465,132 @@ describe 'AppMap class Hooking', docker: false do
|
|
465
465
|
end
|
466
466
|
end
|
467
467
|
|
468
|
+
context 'string conversions works for the receiver when' do
|
469
|
+
|
470
|
+
it 'is missing #to_s' do
|
471
|
+
events_yaml = <<~YAML
|
472
|
+
---
|
473
|
+
- :id: 1
|
474
|
+
:event: :call
|
475
|
+
:defined_class: NoToSMethod
|
476
|
+
:method_id: respond_to?
|
477
|
+
:path: spec/fixtures/hook/exception_method.rb
|
478
|
+
:lineno: 24
|
479
|
+
:static: false
|
480
|
+
:parameters:
|
481
|
+
- :name: :args
|
482
|
+
:class: Symbol
|
483
|
+
:value: to_s
|
484
|
+
:kind: :rest
|
485
|
+
:receiver:
|
486
|
+
:class: Class
|
487
|
+
:value: NoToSMethod
|
488
|
+
- :id: 2
|
489
|
+
:event: :return
|
490
|
+
:parent_id: 1
|
491
|
+
- :id: 3
|
492
|
+
:event: :call
|
493
|
+
:defined_class: NoToSMethod
|
494
|
+
:method_id: say_hello
|
495
|
+
:path: spec/fixtures/hook/exception_method.rb
|
496
|
+
:lineno: 32
|
497
|
+
:static: false
|
498
|
+
:parameters: []
|
499
|
+
:receiver:
|
500
|
+
:class: Class
|
501
|
+
:value: NoToSMethod
|
502
|
+
- :id: 4
|
503
|
+
:event: :return
|
504
|
+
:parent_id: 3
|
505
|
+
:return_value:
|
506
|
+
:class: String
|
507
|
+
:value: hello
|
508
|
+
YAML
|
509
|
+
|
510
|
+
test_hook_behavior 'spec/fixtures/hook/exception_method.rb', events_yaml do
|
511
|
+
inst = NoToSMethod.new
|
512
|
+
# sanity check
|
513
|
+
expect(inst).not_to respond_to(:to_s)
|
514
|
+
inst.say_hello
|
515
|
+
end
|
516
|
+
end
|
517
|
+
|
518
|
+
it 'it is missing #to_s and it raises an exception in #inspect' do
|
519
|
+
events_yaml = <<~YAML
|
520
|
+
---
|
521
|
+
- :id: 1
|
522
|
+
:event: :call
|
523
|
+
:defined_class: NoToSMethod
|
524
|
+
:method_id: respond_to?
|
525
|
+
:path: spec/fixtures/hook/exception_method.rb
|
526
|
+
:lineno: 24
|
527
|
+
:static: false
|
528
|
+
:parameters:
|
529
|
+
- :name: :args
|
530
|
+
:class: Symbol
|
531
|
+
:value: to_s
|
532
|
+
:kind: :rest
|
533
|
+
:receiver:
|
534
|
+
:class: Class
|
535
|
+
:value: "*Error inspecting variable*"
|
536
|
+
- :id: 2
|
537
|
+
:event: :return
|
538
|
+
:parent_id: 1
|
539
|
+
- :id: 3
|
540
|
+
:event: :call
|
541
|
+
:defined_class: InspectRaises
|
542
|
+
:method_id: say_hello
|
543
|
+
:path: spec/fixtures/hook/exception_method.rb
|
544
|
+
:lineno: 42
|
545
|
+
:static: false
|
546
|
+
:parameters: []
|
547
|
+
:receiver:
|
548
|
+
:class: Class
|
549
|
+
:value: "*Error inspecting variable*"
|
550
|
+
- :id: 4
|
551
|
+
:event: :return
|
552
|
+
:parent_id: 3
|
553
|
+
:return_value:
|
554
|
+
:class: String
|
555
|
+
:value: hello
|
556
|
+
YAML
|
557
|
+
|
558
|
+
test_hook_behavior 'spec/fixtures/hook/exception_method.rb', events_yaml do
|
559
|
+
inst = InspectRaises.new
|
560
|
+
# sanity check
|
561
|
+
expect(inst).not_to respond_to(:to_s)
|
562
|
+
inst.say_hello
|
563
|
+
end
|
564
|
+
end
|
565
|
+
|
566
|
+
it 'it raises an exception in #to_s' do
|
567
|
+
events_yaml = <<~YAML
|
568
|
+
---
|
569
|
+
- :id: 1
|
570
|
+
:event: :call
|
571
|
+
:defined_class: ToSRaises
|
572
|
+
:method_id: say_hello
|
573
|
+
:path: spec/fixtures/hook/exception_method.rb
|
574
|
+
:lineno: 52
|
575
|
+
:static: false
|
576
|
+
:parameters: []
|
577
|
+
:receiver:
|
578
|
+
:class: ToSRaises
|
579
|
+
:value: "*Error inspecting variable*"
|
580
|
+
- :id: 2
|
581
|
+
:event: :return
|
582
|
+
:parent_id: 1
|
583
|
+
:return_value:
|
584
|
+
:class: String
|
585
|
+
:value: hello
|
586
|
+
YAML
|
587
|
+
|
588
|
+
test_hook_behavior 'spec/fixtures/hook/exception_method.rb', events_yaml do
|
589
|
+
ToSRaises.new.say_hello
|
590
|
+
end
|
591
|
+
end
|
592
|
+
end
|
593
|
+
|
468
594
|
it 're-raises exceptions' do
|
469
595
|
RSpec::Expectations.configuration.on_potential_false_positives = :nothing
|
470
596
|
|
@@ -624,7 +750,7 @@ describe 'AppMap class Hooking', docker: false do
|
|
624
750
|
config, tracer = invoke_test_file 'spec/fixtures/hook/compare.rb' do
|
625
751
|
expect(Compare.compare('string', 'string')).to be_truthy
|
626
752
|
end
|
627
|
-
cm = AppMap::Util.sanitize_paths(AppMap::ClassMap.build_from_methods(
|
753
|
+
cm = AppMap::Util.sanitize_paths(AppMap::ClassMap.build_from_methods(tracer.event_methods))
|
628
754
|
entry = cm[1][:children][0][:children][0][:children][0]
|
629
755
|
# Sanity check, make sure we got the right one
|
630
756
|
expect(entry[:name]).to eq('secure_compare')
|
@@ -669,4 +795,11 @@ describe 'AppMap class Hooking', docker: false do
|
|
669
795
|
end
|
670
796
|
end
|
671
797
|
end
|
798
|
+
|
799
|
+
it "preserves the arity of hooked methods" do
|
800
|
+
invoke_test_file 'spec/fixtures/hook/instance_method.rb' do
|
801
|
+
expect(InstanceMethod.instance_method(:say_echo).arity).to be(1)
|
802
|
+
expect(InstanceMethod.new.method(:say_echo).arity).to be(1)
|
803
|
+
end
|
804
|
+
end
|
672
805
|
end
|