appmap 0.43.0 → 0.47.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/.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
|
|