appmap 0.43.0 → 0.47.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.releaserc.yml +11 -0
- data/.travis.yml +33 -2
- data/CHANGELOG.md +44 -0
- data/README.md +66 -11
- data/README_CI.md +29 -0
- data/Rakefile +4 -2
- data/appmap.gemspec +5 -3
- data/lib/appmap.rb +3 -7
- data/lib/appmap/class_map.rb +11 -22
- data/lib/appmap/command/record.rb +1 -1
- data/lib/appmap/config.rb +180 -67
- data/lib/appmap/cucumber.rb +1 -1
- data/lib/appmap/event.rb +29 -28
- data/lib/appmap/handler/function.rb +19 -0
- data/lib/appmap/handler/net_http.rb +107 -0
- data/lib/appmap/handler/rails/request_handler.rb +124 -0
- data/lib/appmap/handler/rails/sql_handler.rb +152 -0
- data/lib/appmap/handler/rails/template.rb +149 -0
- data/lib/appmap/hook.rb +111 -70
- data/lib/appmap/hook/method.rb +6 -8
- data/lib/appmap/middleware/remote_recording.rb +1 -1
- data/lib/appmap/minitest.rb +22 -20
- data/lib/appmap/railtie.rb +5 -5
- data/lib/appmap/record.rb +1 -1
- data/lib/appmap/rspec.rb +22 -21
- data/lib/appmap/trace.rb +47 -6
- data/lib/appmap/util.rb +57 -2
- data/lib/appmap/version.rb +2 -2
- data/package-lock.json +3 -3
- data/release.sh +17 -0
- data/spec/abstract_controller_base_spec.rb +76 -15
- data/spec/class_map_spec.rb +5 -13
- data/spec/config_spec.rb +33 -1
- data/spec/fixtures/hook/custom_instance_method.rb +11 -0
- data/spec/fixtures/hook/method_named_call.rb +11 -0
- data/spec/hook_spec.rb +143 -22
- data/spec/record_net_http_spec.rb +160 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/util_spec.rb +18 -1
- data/test/expectations/openssl_test_key_sign1.json +2 -4
- data/test/gem_test.rb +1 -1
- data/test/rspec_test.rb +0 -13
- metadata +20 -14
- data/exe/appmap +0 -154
- data/lib/appmap/rails/request_handler.rb +0 -140
- data/lib/appmap/rails/sql_handler.rb +0 -150
- data/test/cli_test.rb +0 -116
data/lib/appmap/trace.rb
CHANGED
@@ -2,14 +2,36 @@
|
|
2
2
|
|
3
3
|
module AppMap
|
4
4
|
module Trace
|
5
|
-
class
|
6
|
-
attr_reader :
|
5
|
+
class RubyMethod
|
6
|
+
attr_reader :class_name, :static
|
7
7
|
|
8
|
-
def initialize(package,
|
8
|
+
def initialize(package, class_name, method, static)
|
9
9
|
@package = package
|
10
|
-
@
|
10
|
+
@class_name = class_name
|
11
|
+
@method = method
|
11
12
|
@static = static
|
12
|
-
|
13
|
+
end
|
14
|
+
|
15
|
+
def source_location
|
16
|
+
@method.source_location
|
17
|
+
end
|
18
|
+
|
19
|
+
def comment
|
20
|
+
@method.comment
|
21
|
+
rescue MethodSource::SourceNotFoundError
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
|
25
|
+
def package
|
26
|
+
@package.name
|
27
|
+
end
|
28
|
+
|
29
|
+
def name
|
30
|
+
@method.name
|
31
|
+
end
|
32
|
+
|
33
|
+
def labels
|
34
|
+
@package.labels
|
13
35
|
end
|
14
36
|
end
|
15
37
|
|
@@ -43,6 +65,12 @@ module AppMap
|
|
43
65
|
end
|
44
66
|
end
|
45
67
|
|
68
|
+
def record_method(method)
|
69
|
+
@tracers.each do |tracer|
|
70
|
+
tracer.record_method(method)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
46
74
|
def delete(tracer)
|
47
75
|
return unless @tracers.member?(tracer)
|
48
76
|
|
@@ -82,10 +110,23 @@ module AppMap
|
|
82
110
|
|
83
111
|
@last_package_for_thread[Thread.current.object_id] = package if package
|
84
112
|
@events << event
|
85
|
-
|
113
|
+
static = event.static if event.respond_to?(:static)
|
114
|
+
@methods << Trace::RubyMethod.new(package, defined_class, method, static) \
|
86
115
|
if package && defined_class && method && (event.event == :call)
|
87
116
|
end
|
88
117
|
|
118
|
+
# +method+ should be duck-typed to respond to the following:
|
119
|
+
# * package
|
120
|
+
# * defined_class
|
121
|
+
# * name
|
122
|
+
# * static
|
123
|
+
# * comment
|
124
|
+
# * labels
|
125
|
+
# * source_location
|
126
|
+
def record_method(method)
|
127
|
+
@methods << method
|
128
|
+
end
|
129
|
+
|
89
130
|
# Gets the last package which was observed on the current thread.
|
90
131
|
def last_package_for_current_thread
|
91
132
|
@last_package_for_thread[Thread.current.object_id]
|
data/lib/appmap/util.rb
CHANGED
@@ -61,8 +61,16 @@ module AppMap
|
|
61
61
|
delete_object_id = ->(obj) { (obj || {}).delete(:object_id) }
|
62
62
|
delete_object_id.call(event[:receiver])
|
63
63
|
delete_object_id.call(event[:return_value])
|
64
|
-
|
65
|
-
|
64
|
+
%i[parameters exceptions message].each do |field|
|
65
|
+
(event[field] || []).each(&delete_object_id)
|
66
|
+
end
|
67
|
+
%i[http_client_request http_client_response http_server_request http_server_response].each do |field|
|
68
|
+
headers = event.dig(field, :headers)
|
69
|
+
next unless headers
|
70
|
+
|
71
|
+
headers['Date'] = '<instanceof date>' if headers['Date']
|
72
|
+
headers['Server'] = headers['Server'].match(/^(\w+)/)[0] if headers['Server']
|
73
|
+
end
|
66
74
|
|
67
75
|
case event[:event]
|
68
76
|
when :call
|
@@ -71,6 +79,53 @@ module AppMap
|
|
71
79
|
|
72
80
|
event
|
73
81
|
end
|
82
|
+
|
83
|
+
def select_headers(env)
|
84
|
+
# Rack prepends HTTP_ to all client-sent headers.
|
85
|
+
matching_headers = env
|
86
|
+
.select { |k,v| k.start_with? 'HTTP_'}
|
87
|
+
.reject { |k,v| v.blank? }
|
88
|
+
.each_with_object({}) do |kv, memo|
|
89
|
+
key = kv[0].sub(/^HTTP_/, '').split('_').map(&:capitalize).join('-')
|
90
|
+
value = kv[1]
|
91
|
+
memo[key] = value
|
92
|
+
end
|
93
|
+
matching_headers.blank? ? nil : matching_headers
|
94
|
+
end
|
95
|
+
|
96
|
+
def normalize_path(path)
|
97
|
+
if path.index(Dir.pwd) == 0
|
98
|
+
path[Dir.pwd.length + 1..-1]
|
99
|
+
else
|
100
|
+
path
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Convert a Rails-style path from /org/:org_id(.:format)
|
105
|
+
# to Swagger-style paths like /org/{org_id}
|
106
|
+
def swaggerize_path(path)
|
107
|
+
path = path.split('(.')[0]
|
108
|
+
tokens = path.split('/')
|
109
|
+
tokens.map do |token|
|
110
|
+
token.gsub /^:(.*)/, '{\1}'
|
111
|
+
end.join('/')
|
112
|
+
end
|
113
|
+
|
114
|
+
# Atomically writes AppMap data to +filename+.
|
115
|
+
def write_appmap(filename, appmap)
|
116
|
+
require 'fileutils'
|
117
|
+
require 'tmpdir'
|
118
|
+
|
119
|
+
# This is what Ruby Tempfile does; but we don't want the file to be unlinked.
|
120
|
+
mode = File::RDWR | File::CREAT | File::EXCL
|
121
|
+
::Dir::Tmpname.create([ 'appmap_', '.json' ]) do |tmpname|
|
122
|
+
tempfile = File.open(tmpname, mode)
|
123
|
+
tempfile.write(appmap)
|
124
|
+
tempfile.close
|
125
|
+
# Atomically move the tempfile into place.
|
126
|
+
FileUtils.mv tempfile.path, filename
|
127
|
+
end
|
128
|
+
end
|
74
129
|
end
|
75
130
|
end
|
76
131
|
end
|
data/lib/appmap/version.rb
CHANGED
data/package-lock.json
CHANGED
@@ -551,9 +551,9 @@
|
|
551
551
|
}
|
552
552
|
},
|
553
553
|
"lodash": {
|
554
|
-
"version": "4.17.
|
555
|
-
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.
|
556
|
-
"integrity": "sha512-
|
554
|
+
"version": "4.17.21",
|
555
|
+
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
556
|
+
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
557
557
|
},
|
558
558
|
"longest": {
|
559
559
|
"version": "1.0.1",
|
data/release.sh
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
# using bash wrapper as Rake blows up in `require/extentiontask` (line 10)
|
3
|
+
|
4
|
+
RELEASE_FLAGS=""
|
5
|
+
if [ ! -z "$TRAVIS_REPO_SLUG" ]; then
|
6
|
+
RELEASE_FLAGS="-r git+https://github.com/${TRAVIS_REPO_SLUG}.git"
|
7
|
+
fi
|
8
|
+
|
9
|
+
if [ ! -z "$GEM_ALTERNATIVE_NAME" ]; then
|
10
|
+
echo "Release: GEM_ALTERNATIVE_NAME=$GEM_ALTERNATIVE_NAME"
|
11
|
+
else
|
12
|
+
echo "No GEM_ALTERNATIVE_NAME is provided, releasing gem with default name ('appmap')"
|
13
|
+
fi
|
14
|
+
|
15
|
+
set -x
|
16
|
+
exec semantic-release $RELEASE_FLAGS
|
17
|
+
|
@@ -27,6 +27,8 @@ describe 'Rails' do
|
|
27
27
|
end
|
28
28
|
|
29
29
|
let(:appmap) { JSON.parse File.read File.join tmpdir, 'appmap/rspec', appmap_json_file }
|
30
|
+
let(:appmap_json_path) { File.join(tmpdir, 'appmap/rspec', appmap_json_file) }
|
31
|
+
let(:appmap) { JSON.parse File.read(appmap_json_path) }
|
30
32
|
let(:events) { appmap['events'] }
|
31
33
|
|
32
34
|
describe 'an API route' do
|
@@ -35,16 +37,12 @@ describe 'Rails' do
|
|
35
37
|
'Api_UsersController_POST_api_users_with_required_parameters_creates_a_user.appmap.json'
|
36
38
|
end
|
37
39
|
|
38
|
-
it 'inventory file is printed' do
|
39
|
-
expect(File).to exist(File.join(tmpdir, 'appmap/rspec/Inventory.appmap.json'))
|
40
|
-
end
|
41
|
-
|
42
40
|
it 'http_server_request is recorded in the appmap' do
|
43
41
|
expect(events).to include(
|
44
42
|
hash_including(
|
45
43
|
'http_server_request' => hash_including(
|
46
44
|
'request_method' => 'POST',
|
47
|
-
'normalized_path_info' => '/api/users
|
45
|
+
'normalized_path_info' => '/api/users',
|
48
46
|
'path_info' => '/api/users'
|
49
47
|
),
|
50
48
|
'message' => include(
|
@@ -69,8 +67,8 @@ describe 'Rails' do
|
|
69
67
|
expect(events).to include(
|
70
68
|
hash_including(
|
71
69
|
'http_server_response' => hash_including(
|
72
|
-
'
|
73
|
-
'mime_type' => 'application/json; charset=utf-8'
|
70
|
+
'status_code' => 201,
|
71
|
+
'mime_type' => 'application/json; charset=utf-8',
|
74
72
|
)
|
75
73
|
)
|
76
74
|
)
|
@@ -146,7 +144,49 @@ describe 'Rails' do
|
|
146
144
|
end
|
147
145
|
|
148
146
|
describe 'a UI route' do
|
149
|
-
describe 'rendering a page' do
|
147
|
+
describe 'rendering a page using a template file' do
|
148
|
+
let(:appmap_json_file) do
|
149
|
+
'UsersController_GET_users_lists_the_users.appmap.json'
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'records the template file' do
|
153
|
+
expect(events).to include hash_including(
|
154
|
+
'event' => 'call',
|
155
|
+
'defined_class' => 'app_views_users_index_html_haml',
|
156
|
+
'method_id' => 'render',
|
157
|
+
'path' => 'app/views/users/index.html.haml'
|
158
|
+
)
|
159
|
+
|
160
|
+
expect(appmap['classMap']).to include hash_including(
|
161
|
+
'name' => 'app/views',
|
162
|
+
'children' => include(hash_including(
|
163
|
+
'name' => 'app_views_users_index_html_haml',
|
164
|
+
'children' => include(hash_including(
|
165
|
+
'name' => 'render',
|
166
|
+
'type' => 'function',
|
167
|
+
'location' => 'app/views/users/index.html.haml',
|
168
|
+
'static' => true,
|
169
|
+
'labels' => [ 'mvc.template' ]
|
170
|
+
))
|
171
|
+
))
|
172
|
+
)
|
173
|
+
expect(appmap['classMap']).to include hash_including(
|
174
|
+
'name' => 'app/views',
|
175
|
+
'children' => include(hash_including(
|
176
|
+
'name' => 'app_views_layouts_application_html_haml',
|
177
|
+
'children' => include(hash_including(
|
178
|
+
'name' => 'render',
|
179
|
+
'type' => 'function',
|
180
|
+
'location' => 'app/views/layouts/application.html.haml',
|
181
|
+
'static' => true,
|
182
|
+
'labels' => [ 'mvc.template' ]
|
183
|
+
))
|
184
|
+
))
|
185
|
+
)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
describe 'rendering a page using a text template' do
|
150
190
|
let(:appmap_json_file) do
|
151
191
|
'UsersController_GET_users_login_shows_the_user.appmap.json'
|
152
192
|
end
|
@@ -157,25 +197,46 @@ describe 'Rails' do
|
|
157
197
|
'http_server_request' => {
|
158
198
|
'request_method' => 'GET',
|
159
199
|
'path_info' => '/users/alice',
|
160
|
-
'normalized_path_info' => '/users
|
200
|
+
'normalized_path_info' => '/users/{id}',
|
201
|
+
'headers' => {
|
202
|
+
'Host' => 'test.host',
|
203
|
+
'User-Agent' => 'Rails Testing'
|
204
|
+
}
|
161
205
|
}
|
162
206
|
)
|
163
207
|
)
|
164
208
|
end
|
165
209
|
|
210
|
+
it 'ignores the text template' do
|
211
|
+
expect(events).to_not include hash_including(
|
212
|
+
'event' => 'call',
|
213
|
+
'method_id' => 'render',
|
214
|
+
'render_template' => anything
|
215
|
+
)
|
216
|
+
|
217
|
+
expect(appmap['classMap']).to_not include hash_including(
|
218
|
+
'name' => 'views',
|
219
|
+
'children' => include(hash_including(
|
220
|
+
'name' => 'ViewTemplate',
|
221
|
+
'children' => include(hash_including(
|
222
|
+
'name' => 'render',
|
223
|
+
'type' => 'function',
|
224
|
+
'location' => 'text template'
|
225
|
+
))
|
226
|
+
))
|
227
|
+
)
|
228
|
+
end
|
229
|
+
|
166
230
|
it 'records and labels view rendering' do
|
167
231
|
expect(events).to include hash_including(
|
168
232
|
'event' => 'call',
|
169
233
|
'thread_id' => Numeric,
|
170
|
-
'defined_class' => '
|
171
|
-
'method_id' => 'render'
|
172
|
-
'path' => String,
|
173
|
-
'lineno' => Integer,
|
174
|
-
'static' => false
|
234
|
+
'defined_class' => 'inline_template',
|
235
|
+
'method_id' => 'render'
|
175
236
|
)
|
176
237
|
|
177
238
|
expect(appmap['classMap']).to include hash_including(
|
178
|
-
'name' => '
|
239
|
+
'name' => 'actionview',
|
179
240
|
'children' => include(hash_including(
|
180
241
|
'name' => 'ActionView',
|
181
242
|
'children' => include(hash_including(
|
data/spec/class_map_spec.rb
CHANGED
@@ -4,18 +4,10 @@ require 'spec_helper'
|
|
4
4
|
|
5
5
|
describe 'AppMap::ClassMap' do
|
6
6
|
describe '.build_from_methods' do
|
7
|
-
it 'includes
|
8
|
-
map = AppMap.class_map([
|
7
|
+
it 'includes method comment' do
|
8
|
+
map = AppMap.class_map([ruby_method((method :test_method))])
|
9
9
|
function = dig_map(map, 5)[0]
|
10
|
-
expect(function
|
11
|
-
expect(function[:comment]).to include 'test method comment'
|
12
|
-
end
|
13
|
-
|
14
|
-
it 'can omit source code even if available' do
|
15
|
-
map = AppMap.class_map([scoped_method((method :test_method))], include_source: false)
|
16
|
-
function = dig_map(map, 5)[0]
|
17
|
-
expect(function).to_not include(:source)
|
18
|
-
expect(function).to_not include(:comment)
|
10
|
+
expect(function).to include(:comment)
|
19
11
|
end
|
20
12
|
|
21
13
|
# test method comment
|
@@ -23,8 +15,8 @@ describe 'AppMap::ClassMap' do
|
|
23
15
|
'test method body'
|
24
16
|
end
|
25
17
|
|
26
|
-
def
|
27
|
-
AppMap::Trace::
|
18
|
+
def ruby_method(method)
|
19
|
+
AppMap::Trace::RubyMethod.new AppMap::Config::Package.new, method.receiver.class.name, method, false
|
28
20
|
end
|
29
21
|
|
30
22
|
def dig_map(map, depth)
|
data/spec/config_spec.rb
CHANGED
@@ -17,10 +17,42 @@ describe AppMap::Config, docker: false do
|
|
17
17
|
path: 'path-2',
|
18
18
|
exclude: [ 'exclude-1' ]
|
19
19
|
}
|
20
|
+
],
|
21
|
+
functions: [
|
22
|
+
{
|
23
|
+
package: 'pkg',
|
24
|
+
class: 'cls',
|
25
|
+
function: 'fn',
|
26
|
+
label: 'lbl'
|
27
|
+
}
|
20
28
|
]
|
21
29
|
}.deep_stringify_keys!
|
22
30
|
config = AppMap::Config.load(config_data)
|
23
31
|
|
24
|
-
|
32
|
+
config_expectation = {
|
33
|
+
exclude: [],
|
34
|
+
name: 'test',
|
35
|
+
packages: [
|
36
|
+
{
|
37
|
+
path: 'path-1',
|
38
|
+
handler_class: 'AppMap::Handler::Function'
|
39
|
+
},
|
40
|
+
{
|
41
|
+
path: 'path-2',
|
42
|
+
handler_class: 'AppMap::Handler::Function',
|
43
|
+
exclude: [ 'exclude-1' ]
|
44
|
+
}
|
45
|
+
],
|
46
|
+
functions: [
|
47
|
+
{
|
48
|
+
package: 'pkg',
|
49
|
+
class: 'cls',
|
50
|
+
functions: [ :fn ],
|
51
|
+
labels: ['lbl']
|
52
|
+
}
|
53
|
+
]
|
54
|
+
}.deep_stringify_keys!
|
55
|
+
|
56
|
+
expect(config.to_h.deep_stringify_keys!).to eq(config_expectation)
|
25
57
|
end
|
26
58
|
end
|
data/spec/hook_spec.rb
CHANGED
@@ -16,14 +16,7 @@ end
|
|
16
16
|
Psych::Visitors::YAMLTree.prepend(ShowYamlNulls)
|
17
17
|
|
18
18
|
describe 'AppMap class Hooking', docker: false do
|
19
|
-
|
20
|
-
def collect_events(tracer)
|
21
|
-
[].tap do |events|
|
22
|
-
while tracer.event?
|
23
|
-
events << tracer.next_event.to_h
|
24
|
-
end
|
25
|
-
end.map(&AppMap::Util.method(:sanitize_event))
|
26
|
-
end
|
19
|
+
include_context 'collect events'
|
27
20
|
|
28
21
|
def invoke_test_file(file, setup: nil, &block)
|
29
22
|
AppMap.configuration = nil
|
@@ -64,11 +57,144 @@ describe 'AppMap class Hooking', docker: false do
|
|
64
57
|
it 'excludes named classes and methods' do
|
65
58
|
load 'spec/fixtures/hook/exclude.rb'
|
66
59
|
package = AppMap::Config::Package.build_from_path('spec/fixtures/hook/exclude.rb')
|
67
|
-
config = AppMap::Config.new('hook_spec', [ package ], %w[ExcludeTest])
|
60
|
+
config = AppMap::Config.new('hook_spec', [ package ], exclude: %w[ExcludeTest])
|
68
61
|
AppMap.configuration = config
|
69
62
|
|
70
|
-
expect(config.never_hook?(ExcludeTest.new.method(:instance_method))).to be_truthy
|
71
|
-
expect(config.never_hook?(ExcludeTest.method(:cls_method))).to be_truthy
|
63
|
+
expect(config.never_hook?(ExcludeTest, ExcludeTest.new.method(:instance_method))).to be_truthy
|
64
|
+
expect(config.never_hook?(ExcludeTest, ExcludeTest.method(:cls_method))).to be_truthy
|
65
|
+
end
|
66
|
+
|
67
|
+
it "handles an instance method named 'call' without issues" do
|
68
|
+
events_yaml = <<~YAML
|
69
|
+
---
|
70
|
+
- :id: 1
|
71
|
+
:event: :call
|
72
|
+
:defined_class: MethodNamedCall
|
73
|
+
:method_id: call
|
74
|
+
:path: spec/fixtures/hook/method_named_call.rb
|
75
|
+
:lineno: 8
|
76
|
+
:static: false
|
77
|
+
:parameters:
|
78
|
+
- :name: :a
|
79
|
+
:class: Integer
|
80
|
+
:value: '1'
|
81
|
+
:kind: :req
|
82
|
+
- :name: :b
|
83
|
+
:class: Integer
|
84
|
+
:value: '2'
|
85
|
+
:kind: :req
|
86
|
+
- :name: :c
|
87
|
+
:class: Integer
|
88
|
+
:value: '3'
|
89
|
+
:kind: :req
|
90
|
+
- :name: :d
|
91
|
+
:class: Integer
|
92
|
+
:value: '4'
|
93
|
+
:kind: :req
|
94
|
+
- :name: :e
|
95
|
+
:class: Integer
|
96
|
+
:value: '5'
|
97
|
+
:kind: :req
|
98
|
+
:receiver:
|
99
|
+
:class: MethodNamedCall
|
100
|
+
:value: MethodNamedCall
|
101
|
+
- :id: 2
|
102
|
+
:event: :return
|
103
|
+
:parent_id: 1
|
104
|
+
:return_value:
|
105
|
+
:class: String
|
106
|
+
:value: 1 2 3 4 5
|
107
|
+
YAML
|
108
|
+
|
109
|
+
_, tracer = test_hook_behavior 'spec/fixtures/hook/method_named_call.rb', events_yaml do
|
110
|
+
expect(MethodNamedCall.new.call(1, 2, 3, 4, 5)).to eq('1 2 3 4 5')
|
111
|
+
end
|
112
|
+
class_map = AppMap.class_map(tracer.event_methods)
|
113
|
+
expect(Diffy::Diff.new(<<~CLASSMAP, YAML.dump(class_map)).to_s).to eq('')
|
114
|
+
---
|
115
|
+
- :name: spec/fixtures/hook/method_named_call.rb
|
116
|
+
:type: package
|
117
|
+
:children:
|
118
|
+
- :name: MethodNamedCall
|
119
|
+
:type: class
|
120
|
+
:children:
|
121
|
+
- :name: call
|
122
|
+
:type: function
|
123
|
+
:location: spec/fixtures/hook/method_named_call.rb:8
|
124
|
+
:static: false
|
125
|
+
CLASSMAP
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'can custom hook and label a function' do
|
129
|
+
events_yaml = <<~YAML
|
130
|
+
---
|
131
|
+
- :id: 1
|
132
|
+
:event: :call
|
133
|
+
:defined_class: CustomInstanceMethod
|
134
|
+
:method_id: say_default
|
135
|
+
:path: spec/fixtures/hook/custom_instance_method.rb
|
136
|
+
:lineno: 8
|
137
|
+
:static: false
|
138
|
+
:parameters: []
|
139
|
+
:receiver:
|
140
|
+
:class: CustomInstanceMethod
|
141
|
+
:value: CustomInstance Method fixture
|
142
|
+
- :id: 2
|
143
|
+
:event: :return
|
144
|
+
:parent_id: 1
|
145
|
+
:return_value:
|
146
|
+
:class: String
|
147
|
+
:value: default
|
148
|
+
YAML
|
149
|
+
|
150
|
+
config = AppMap::Config.load({
|
151
|
+
functions: [
|
152
|
+
{
|
153
|
+
package: 'hook_spec',
|
154
|
+
class: 'CustomInstanceMethod',
|
155
|
+
functions: [ :say_default ],
|
156
|
+
labels: ['cowsay']
|
157
|
+
}
|
158
|
+
]
|
159
|
+
}.deep_stringify_keys)
|
160
|
+
|
161
|
+
load 'spec/fixtures/hook/custom_instance_method.rb'
|
162
|
+
hook_cls = CustomInstanceMethod
|
163
|
+
method = hook_cls.instance_method(:say_default)
|
164
|
+
|
165
|
+
require 'appmap/hook/method'
|
166
|
+
package = config.lookup_package(hook_cls, method)
|
167
|
+
expect(package).to be
|
168
|
+
hook_method = AppMap::Hook::Method.new(package, hook_cls, method)
|
169
|
+
hook_method.activate
|
170
|
+
|
171
|
+
tracer = AppMap.tracing.trace
|
172
|
+
AppMap::Event.reset_id_counter
|
173
|
+
begin
|
174
|
+
expect(CustomInstanceMethod.new.say_default).to eq('default')
|
175
|
+
ensure
|
176
|
+
AppMap.tracing.delete(tracer)
|
177
|
+
end
|
178
|
+
|
179
|
+
events = collect_events(tracer).to_yaml
|
180
|
+
|
181
|
+
expect(Diffy::Diff.new(events_yaml, events).to_s).to eq('')
|
182
|
+
class_map = AppMap.class_map(tracer.event_methods)
|
183
|
+
expect(Diffy::Diff.new(<<~CLASSMAP, YAML.dump(class_map)).to_s).to eq('')
|
184
|
+
---
|
185
|
+
- :name: hook_spec
|
186
|
+
:type: package
|
187
|
+
:children:
|
188
|
+
- :name: CustomInstanceMethod
|
189
|
+
:type: class
|
190
|
+
:children:
|
191
|
+
- :name: say_default
|
192
|
+
:type: function
|
193
|
+
:location: spec/fixtures/hook/custom_instance_method.rb:8
|
194
|
+
:static: false
|
195
|
+
:labels:
|
196
|
+
- cowsay
|
197
|
+
CLASSMAP
|
72
198
|
end
|
73
199
|
|
74
200
|
it 'parses labels from comments' do
|
@@ -91,9 +217,6 @@ describe 'AppMap class Hooking', docker: false do
|
|
91
217
|
:labels:
|
92
218
|
- has-fn-label
|
93
219
|
:comment: "# @label has-fn-label\\n"
|
94
|
-
:source: |2
|
95
|
-
def fn_with_label
|
96
|
-
end
|
97
220
|
YAML
|
98
221
|
end
|
99
222
|
|
@@ -127,8 +250,8 @@ describe 'AppMap class Hooking', docker: false do
|
|
127
250
|
_, tracer = invoke_test_file 'spec/fixtures/hook/instance_method.rb' do
|
128
251
|
InstanceMethod.new.say_default
|
129
252
|
end
|
130
|
-
expect(tracer.event_methods.to_a.map(&:
|
131
|
-
expect(tracer.event_methods.to_a.map(&:
|
253
|
+
expect(tracer.event_methods.to_a.map(&:class_name)).to eq([ 'InstanceMethod' ])
|
254
|
+
expect(tracer.event_methods.to_a.map(&:name)).to eq([ InstanceMethod.public_instance_method(:say_default).name ])
|
132
255
|
end
|
133
256
|
|
134
257
|
it 'builds a class map of invoked methods' do
|
@@ -148,10 +271,6 @@ describe 'AppMap class Hooking', docker: false do
|
|
148
271
|
:type: function
|
149
272
|
:location: spec/fixtures/hook/instance_method.rb:8
|
150
273
|
:static: false
|
151
|
-
:source: |2
|
152
|
-
def say_default
|
153
|
-
'default'
|
154
|
-
end
|
155
274
|
YAML
|
156
275
|
end
|
157
276
|
|
@@ -744,8 +863,11 @@ describe 'AppMap class Hooking', docker: false do
|
|
744
863
|
_, _, events = test_hook_behavior 'spec/fixtures/hook/compare.rb', nil do
|
745
864
|
expect(Compare.compare('string', 'string')).to be_truthy
|
746
865
|
end
|
866
|
+
|
747
867
|
secure_compare_event = YAML.load(events).find { |evt| evt[:defined_class] == 'ActiveSupport::SecurityUtils' }
|
868
|
+
expect(secure_compare_event).to be_truthy
|
748
869
|
secure_compare_event.delete(:lineno)
|
870
|
+
secure_compare_event.delete(:path)
|
749
871
|
|
750
872
|
expect(Diffy::Diff.new(<<~YAML, secure_compare_event.to_yaml).to_s).to eq('')
|
751
873
|
---
|
@@ -753,7 +875,6 @@ describe 'AppMap class Hooking', docker: false do
|
|
753
875
|
:event: :call
|
754
876
|
:defined_class: ActiveSupport::SecurityUtils
|
755
877
|
:method_id: secure_compare
|
756
|
-
:path: lib/active_support/security_utils.rb
|
757
878
|
:static: true
|
758
879
|
:parameters:
|
759
880
|
- :name: :a
|
@@ -837,7 +958,7 @@ describe 'AppMap class Hooking', docker: false do
|
|
837
958
|
entry = cm[1][:children][0][:children][0][:children][0]
|
838
959
|
# Sanity check, make sure we got the right one
|
839
960
|
expect(entry[:name]).to eq('secure_compare')
|
840
|
-
expect(entry[:labels]).to eq(%w[
|
961
|
+
expect(entry[:labels]).to eq(%w[crypto.secure_compare])
|
841
962
|
end
|
842
963
|
end
|
843
964
|
|