appmap 0.44.0 → 0.47.1
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 +28 -12
- data/CHANGELOG.md +42 -0
- data/README.md +39 -11
- data/README_CI.md +29 -0
- data/Rakefile +4 -2
- data/appmap.gemspec +5 -1
- data/lib/appmap/class_map.rb +7 -15
- data/lib/appmap/config.rb +203 -95
- 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 +155 -0
- data/lib/appmap/hook.rb +109 -71
- data/lib/appmap/hook/method.rb +6 -8
- data/lib/appmap/railtie.rb +5 -5
- data/lib/appmap/trace.rb +47 -6
- data/lib/appmap/util.rb +41 -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 +74 -11
- data/spec/class_map_spec.rb +3 -3
- data/spec/config_spec.rb +3 -1
- data/spec/hook_spec.rb +12 -66
- data/spec/record_net_http_spec.rb +160 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/util_spec.rb +18 -1
- metadata +16 -10
- data/lib/appmap/rails/request_handler.rb +0 -140
- data/lib/appmap/rails/sql_handler.rb +0 -150
- data/patch +0 -1447
data/spec/spec_helper.rb
CHANGED
@@ -20,3 +20,13 @@ end
|
|
20
20
|
def use_existing_data?
|
21
21
|
ENV['USE_EXISTING_DATA'] == 'true'
|
22
22
|
end
|
23
|
+
|
24
|
+
shared_context 'collect events' do
|
25
|
+
def collect_events(tracer)
|
26
|
+
[].tap do |events|
|
27
|
+
while tracer.event?
|
28
|
+
events << tracer.next_event.to_h
|
29
|
+
end
|
30
|
+
end.map(&AppMap::Util.method(:sanitize_event))
|
31
|
+
end
|
32
|
+
end
|
data/spec/util_spec.rb
CHANGED
@@ -4,8 +4,8 @@ require 'spec_helper'
|
|
4
4
|
require 'appmap/util'
|
5
5
|
|
6
6
|
describe AppMap::Util, docker: false do
|
7
|
-
let(:subject) { AppMap::Util.method(:scenario_filename) }
|
8
7
|
describe 'scenario_filename' do
|
8
|
+
let(:subject) { AppMap::Util.method(:scenario_filename) }
|
9
9
|
it 'leaves short names alone' do
|
10
10
|
expect(subject.call('foobar')).to eq('foobar.appmap.json')
|
11
11
|
end
|
@@ -18,4 +18,21 @@ describe AppMap::Util, docker: false do
|
|
18
18
|
expect(subject.call(fname, max_length: 50)).to eq('abcdefghijklmno-RAd_SFbH1sUZ_OXfwPsfzw.appmap.json')
|
19
19
|
end
|
20
20
|
end
|
21
|
+
describe 'swaggerize path' do
|
22
|
+
it 'replaces rails-style parameters' do
|
23
|
+
expect(AppMap::Util.swaggerize_path('/org/:org_id(.:format)')).to eq('/org/{org_id}')
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'strips the format specifier' do
|
27
|
+
expect(AppMap::Util.swaggerize_path('/org(.:format)')).to eq('/org')
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'ignores malformed parameter specs' do
|
31
|
+
expect(AppMap::Util.swaggerize_path('/org/o:rg_id')).to eq('/org/o:rg_id')
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'ignores already swaggerized paths' do
|
35
|
+
expect(AppMap::Util.swaggerize_path('/org/{org_id}')).to eq('/org/{org_id}')
|
36
|
+
end
|
37
|
+
end
|
21
38
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: appmap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.47.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kevin Gilpin
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-05-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -304,7 +304,7 @@ dependencies:
|
|
304
304
|
- - ">="
|
305
305
|
- !ruby/object:Gem::Version
|
306
306
|
version: '0'
|
307
|
-
description:
|
307
|
+
description:
|
308
308
|
email:
|
309
309
|
- kgilpin@gmail.com
|
310
310
|
executables: []
|
@@ -315,6 +315,7 @@ files:
|
|
315
315
|
- ".dockerignore"
|
316
316
|
- ".gitignore"
|
317
317
|
- ".rbenv-gemsets"
|
318
|
+
- ".releaserc.yml"
|
318
319
|
- ".rubocop.yml"
|
319
320
|
- ".travis.yml"
|
320
321
|
- CHANGELOG.md
|
@@ -323,6 +324,7 @@ files:
|
|
323
324
|
- Gemfile
|
324
325
|
- LICENSE.txt
|
325
326
|
- README.md
|
327
|
+
- README_CI.md
|
326
328
|
- Rakefile
|
327
329
|
- appmap.gemspec
|
328
330
|
- appmap.yml
|
@@ -344,14 +346,17 @@ files:
|
|
344
346
|
- lib/appmap/config.rb
|
345
347
|
- lib/appmap/cucumber.rb
|
346
348
|
- lib/appmap/event.rb
|
349
|
+
- lib/appmap/handler/function.rb
|
350
|
+
- lib/appmap/handler/net_http.rb
|
351
|
+
- lib/appmap/handler/rails/request_handler.rb
|
352
|
+
- lib/appmap/handler/rails/sql_handler.rb
|
353
|
+
- lib/appmap/handler/rails/template.rb
|
347
354
|
- lib/appmap/hook.rb
|
348
355
|
- lib/appmap/hook/method.rb
|
349
356
|
- lib/appmap/metadata.rb
|
350
357
|
- lib/appmap/middleware/remote_recording.rb
|
351
358
|
- lib/appmap/minitest.rb
|
352
359
|
- lib/appmap/open.rb
|
353
|
-
- lib/appmap/rails/request_handler.rb
|
354
|
-
- lib/appmap/rails/sql_handler.rb
|
355
360
|
- lib/appmap/railtie.rb
|
356
361
|
- lib/appmap/record.rb
|
357
362
|
- lib/appmap/rspec.rb
|
@@ -377,7 +382,7 @@ files:
|
|
377
382
|
- lore/public/stylesheets/style.css
|
378
383
|
- package-lock.json
|
379
384
|
- package.json
|
380
|
-
-
|
385
|
+
- release.sh
|
381
386
|
- spec/abstract_controller_base_spec.rb
|
382
387
|
- spec/class_map_spec.rb
|
383
388
|
- spec/config_spec.rb
|
@@ -547,6 +552,7 @@ files:
|
|
547
552
|
- spec/open_spec.rb
|
548
553
|
- spec/rails_spec_helper.rb
|
549
554
|
- spec/railtie_spec.rb
|
555
|
+
- spec/record_net_http_spec.rb
|
550
556
|
- spec/record_sql_rails_pg_spec.rb
|
551
557
|
- spec/remote_recording_spec.rb
|
552
558
|
- spec/spec_helper.rb
|
@@ -600,7 +606,7 @@ homepage: https://github.com/applandinc/appmap-ruby
|
|
600
606
|
licenses:
|
601
607
|
- MIT
|
602
608
|
metadata: {}
|
603
|
-
post_install_message:
|
609
|
+
post_install_message:
|
604
610
|
rdoc_options: []
|
605
611
|
require_paths:
|
606
612
|
- lib
|
@@ -615,8 +621,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
615
621
|
- !ruby/object:Gem::Version
|
616
622
|
version: '0'
|
617
623
|
requirements: []
|
618
|
-
rubygems_version: 3.0.
|
619
|
-
signing_key:
|
624
|
+
rubygems_version: 3.0.8
|
625
|
+
signing_key:
|
620
626
|
specification_version: 4
|
621
627
|
summary: Record the operation of a Ruby program, using the AppLand 'AppMap' format.
|
622
628
|
test_files: []
|
@@ -1,140 +0,0 @@
|
|
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
|
-
# Host and User-Agent will just introduce needless variation.
|
10
|
-
# Content-Type and Authorization get their own fields in the request.
|
11
|
-
IGNORE_HEADERS = %w[host user_agent content_type authorization].map(&:upcase).map {|h| "HTTP_#{h}"}.freeze
|
12
|
-
|
13
|
-
class << self
|
14
|
-
def selected_headers(env)
|
15
|
-
# Rack prepends HTTP_ to all client-sent headers.
|
16
|
-
matching_headers = env
|
17
|
-
.select { |k,v| k.start_with? 'HTTP_'}
|
18
|
-
.reject { |k,v| IGNORE_HEADERS.member?(k) }
|
19
|
-
.reject { |k,v| v.blank? }
|
20
|
-
.each_with_object({}) do |kv, memo|
|
21
|
-
key = kv[0].sub(/^HTTP_/, '').split('_').map(&:capitalize).join('-')
|
22
|
-
value = kv[1]
|
23
|
-
memo[key] = value
|
24
|
-
end
|
25
|
-
matching_headers.blank? ? nil : matching_headers
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
class HTTPServerRequest < AppMap::Event::MethodEvent
|
30
|
-
attr_accessor :normalized_path_info, :request_method, :path_info, :params, :mime_type, :headers, :authorization
|
31
|
-
|
32
|
-
def initialize(request)
|
33
|
-
super AppMap::Event.next_id_counter, :call, Thread.current.object_id
|
34
|
-
|
35
|
-
self.request_method = request.request_method
|
36
|
-
self.normalized_path_info = normalized_path(request)
|
37
|
-
self.mime_type = request.headers['Content-Type']
|
38
|
-
self.headers = RequestHandler.selected_headers(request.env)
|
39
|
-
self.authorization = request.headers['Authorization']
|
40
|
-
self.path_info = request.path_info.split('?')[0]
|
41
|
-
# ActionDispatch::Http::ParameterFilter is deprecated
|
42
|
-
parameter_filter_cls = \
|
43
|
-
if defined?(ActiveSupport::ParameterFilter)
|
44
|
-
ActiveSupport::ParameterFilter
|
45
|
-
else
|
46
|
-
ActionDispatch::Http::ParameterFilter
|
47
|
-
end
|
48
|
-
self.params = parameter_filter_cls.new(::Rails.application.config.filter_parameters).filter(request.params)
|
49
|
-
end
|
50
|
-
|
51
|
-
def to_h
|
52
|
-
super.tap do |h|
|
53
|
-
h[:http_server_request] = {
|
54
|
-
request_method: request_method,
|
55
|
-
path_info: path_info,
|
56
|
-
mime_type: mime_type,
|
57
|
-
normalized_path_info: normalized_path_info,
|
58
|
-
authorization: authorization,
|
59
|
-
headers: headers,
|
60
|
-
}.compact
|
61
|
-
|
62
|
-
h[:message] = params.keys.map do |key|
|
63
|
-
val = params[key]
|
64
|
-
{
|
65
|
-
name: key,
|
66
|
-
class: val.class.name,
|
67
|
-
value: self.class.display_string(val),
|
68
|
-
object_id: val.__id__,
|
69
|
-
}.tap do |message|
|
70
|
-
properties = object_properties(val)
|
71
|
-
message[:properties] = properties if properties
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
private
|
78
|
-
|
79
|
-
def normalized_path(request, router = ::Rails.application.routes.router)
|
80
|
-
router.recognize request do |route, _|
|
81
|
-
app = route.app
|
82
|
-
next unless app.matches? request
|
83
|
-
return normalized_path request, app.rack_app.routes.router if app.engine?
|
84
|
-
|
85
|
-
return route.path.spec.to_s
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
class HTTPServerResponse < AppMap::Event::MethodReturnIgnoreValue
|
91
|
-
attr_accessor :status, :mime_type, :headers
|
92
|
-
|
93
|
-
def initialize(response, parent_id, elapsed)
|
94
|
-
super AppMap::Event.next_id_counter, :return, Thread.current.object_id
|
95
|
-
|
96
|
-
self.status = response.status
|
97
|
-
self.mime_type = response.headers['Content-Type']
|
98
|
-
self.parent_id = parent_id
|
99
|
-
self.elapsed = elapsed
|
100
|
-
self.headers = RequestHandler.selected_headers(response.headers)
|
101
|
-
end
|
102
|
-
|
103
|
-
def to_h
|
104
|
-
super.tap do |h|
|
105
|
-
h[:http_server_response] = {
|
106
|
-
status: status,
|
107
|
-
mime_type: mime_type,
|
108
|
-
headers: headers
|
109
|
-
}.compact
|
110
|
-
end
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
class HookMethod < AppMap::Hook::Method
|
115
|
-
def initialize
|
116
|
-
# ActionController::Instrumentation has issued start_processing.action_controller and
|
117
|
-
# process_action.action_controller since Rails 3. Therefore it's a stable place to hook
|
118
|
-
# the request. Rails controller notifications can't be used directly because they don't
|
119
|
-
# provide response headers, and we want the Content-Type.
|
120
|
-
super(nil, ActionController::Instrumentation, ActionController::Instrumentation.instance_method(:process_action))
|
121
|
-
end
|
122
|
-
|
123
|
-
protected
|
124
|
-
|
125
|
-
def before_hook(receiver, defined_class, _) # args
|
126
|
-
call_event = HTTPServerRequest.new(receiver.request)
|
127
|
-
# http_server_request events are i/o and do not require a package name.
|
128
|
-
AppMap.tracing.record_event call_event, defined_class: defined_class, method: hook_method
|
129
|
-
[ call_event, TIME_NOW.call ]
|
130
|
-
end
|
131
|
-
|
132
|
-
def after_hook(receiver, call_event, start_time, _, _) # return_value, exception
|
133
|
-
elapsed = TIME_NOW.call - start_time
|
134
|
-
return_event = HTTPServerResponse.new receiver.response, call_event.id, elapsed
|
135
|
-
AppMap.tracing.record_event return_event
|
136
|
-
end
|
137
|
-
end
|
138
|
-
end
|
139
|
-
end
|
140
|
-
end
|
@@ -1,150 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'appmap/event'
|
4
|
-
|
5
|
-
module AppMap
|
6
|
-
module Rails
|
7
|
-
class SQLHandler
|
8
|
-
class SQLCall < AppMap::Event::MethodCall
|
9
|
-
attr_accessor :payload
|
10
|
-
|
11
|
-
def initialize(payload)
|
12
|
-
super AppMap::Event.next_id_counter, :call, Thread.current.object_id
|
13
|
-
|
14
|
-
self.payload = payload
|
15
|
-
end
|
16
|
-
|
17
|
-
def to_h
|
18
|
-
super.tap do |h|
|
19
|
-
h[:sql_query] = {
|
20
|
-
sql: payload[:sql],
|
21
|
-
database_type: payload[:database_type]
|
22
|
-
}.tap do |sql_query|
|
23
|
-
%i[server_version].each do |attribute|
|
24
|
-
sql_query[attribute] = payload[attribute] if payload[attribute]
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
class SQLReturn < AppMap::Event::MethodReturnIgnoreValue
|
32
|
-
def initialize(parent_id, elapsed)
|
33
|
-
super AppMap::Event.next_id_counter, :return, Thread.current.object_id
|
34
|
-
|
35
|
-
self.parent_id = parent_id
|
36
|
-
self.elapsed = elapsed
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
module SQLExaminer
|
41
|
-
class << self
|
42
|
-
def examine(payload, sql:)
|
43
|
-
return unless (examiner = build_examiner)
|
44
|
-
|
45
|
-
payload[:server_version] = examiner.server_version
|
46
|
-
payload[:database_type] = examiner.database_type.to_s
|
47
|
-
end
|
48
|
-
|
49
|
-
protected
|
50
|
-
|
51
|
-
def build_examiner
|
52
|
-
if defined?(Sequel)
|
53
|
-
SequelExaminer.new
|
54
|
-
elsif defined?(ActiveRecord)
|
55
|
-
ActiveRecordExaminer.new
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
class SequelExaminer
|
61
|
-
def server_version
|
62
|
-
Sequel::Model.db.server_version
|
63
|
-
end
|
64
|
-
|
65
|
-
def database_type
|
66
|
-
Sequel::Model.db.database_type.to_sym
|
67
|
-
end
|
68
|
-
|
69
|
-
def execute_query(sql)
|
70
|
-
Sequel::Model.db[sql].all
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
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
|
-
|
84
|
-
def server_version
|
85
|
-
ActiveRecord::Base.connection.try(:database_version) || issue_warning
|
86
|
-
end
|
87
|
-
|
88
|
-
def database_type
|
89
|
-
type = ActiveRecord::Base.connection.adapter_name.downcase.to_sym
|
90
|
-
type = :postgres if type == :postgresql
|
91
|
-
|
92
|
-
type
|
93
|
-
end
|
94
|
-
|
95
|
-
def execute_query(sql)
|
96
|
-
ActiveRecord::Base.connection.execute(sql).inject([]) { |memo, r| memo << r; memo }
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
def call(_, started, finished, _, payload) # (name, started, finished, unique_id, payload)
|
102
|
-
return if AppMap.tracing.empty?
|
103
|
-
|
104
|
-
reentry_key = "#{self.class.name}#call"
|
105
|
-
return if Thread.current[reentry_key] == true
|
106
|
-
|
107
|
-
Thread.current[reentry_key] = true
|
108
|
-
begin
|
109
|
-
sql = payload[:sql].strip
|
110
|
-
|
111
|
-
# Detect whether a function call within a specified filename is present in the call stack.
|
112
|
-
find_in_backtrace = lambda do |file_name, function_name = nil|
|
113
|
-
Thread.current.backtrace.find do |line|
|
114
|
-
tokens = line.split(':')
|
115
|
-
matches_file = tokens.find { |t| t.rindex(file_name) == (t.length - file_name.length) }
|
116
|
-
matches_function = function_name.nil? || tokens.find { |t| t == "in `#{function_name}'" }
|
117
|
-
matches_file && matches_function
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
# Ignore SQL calls which are made while establishing a new connection.
|
122
|
-
#
|
123
|
-
# Example:
|
124
|
-
# /path/to/ruby/2.6.0/gems/sequel-5.20.0/lib/sequel/connection_pool.rb:122:in `make_new'
|
125
|
-
return if find_in_backtrace.call('lib/sequel/connection_pool.rb', 'make_new')
|
126
|
-
# lib/active_record/connection_adapters/abstract/connection_pool.rb:811:in `new_connection'
|
127
|
-
return if find_in_backtrace.call('lib/active_record/connection_adapters/abstract/connection_pool.rb', 'new_connection')
|
128
|
-
|
129
|
-
# Ignore SQL calls which are made while inspecting the DB schema.
|
130
|
-
#
|
131
|
-
# Example:
|
132
|
-
# /path/to/ruby/2.6.0/gems/sequel-5.20.0/lib/sequel/model/base.rb:812:in `get_db_schema'
|
133
|
-
return if find_in_backtrace.call('lib/sequel/model/base.rb', 'get_db_schema')
|
134
|
-
# /usr/local/bundle/gems/activerecord-5.2.3/lib/active_record/model_schema.rb:466:in `load_schema!'
|
135
|
-
return if find_in_backtrace.call('lib/active_record/model_schema.rb', 'load_schema!')
|
136
|
-
return if find_in_backtrace.call('lib/active_model/attribute_methods.rb', 'define_attribute_methods')
|
137
|
-
return if find_in_backtrace.call('lib/active_record/connection_adapters/schema_cache.rb')
|
138
|
-
|
139
|
-
SQLExaminer.examine payload, sql: sql
|
140
|
-
|
141
|
-
call = SQLCall.new(payload)
|
142
|
-
AppMap.tracing.record_event(call)
|
143
|
-
AppMap.tracing.record_event(SQLReturn.new(call.id, finished - started))
|
144
|
-
ensure
|
145
|
-
Thread.current[reentry_key] = nil
|
146
|
-
end
|
147
|
-
end
|
148
|
-
end
|
149
|
-
end
|
150
|
-
end
|
data/patch
DELETED
@@ -1,1447 +0,0 @@
|
|
1
|
-
diff --git a/.travis.yml b/.travis.yml
|
2
|
-
index ab6ccca..f165339 100644
|
3
|
-
--- a/.travis.yml
|
4
|
-
+++ b/.travis.yml
|
5
|
-
@@ -13,11 +13,26 @@ services:
|
6
|
-
# necessary.
|
7
|
-
before_script:
|
8
|
-
- unset RAILS_ENV
|
9
|
-
-
|
10
|
-
+
|
11
|
-
+cache:
|
12
|
-
+ bundler: true
|
13
|
-
+ directories:
|
14
|
-
+ - $HOME/docker
|
15
|
-
+
|
16
|
-
+# https://stackoverflow.com/a/41975912
|
17
|
-
+before_cache:
|
18
|
-
+ # Save tagged docker images
|
19
|
-
+ - >
|
20
|
-
+ mkdir -p $HOME/docker && docker images -a --filter='dangling=false' --format '{{.Repository}}:{{.Tag}} {{.ID}}'
|
21
|
-
+ | xargs -n 2 -t sh -c 'test -e $HOME/docker/$1.tar.gz || docker save $0 | gzip -2 > $HOME/docker/$1.tar.gz'
|
22
|
-
+
|
23
|
-
+before_install:
|
24
|
-
+ # Load cached docker images
|
25
|
-
+ - if [[ -d $HOME/docker ]]; then ls $HOME/docker/*.tar.gz | xargs -I {file} sh -c "zcat {file} | docker load"; fi
|
26
|
-
+
|
27
|
-
jobs:
|
28
|
-
include:
|
29
|
-
- stage: test
|
30
|
-
script:
|
31
|
-
- mkdir tmp
|
32
|
-
- bundle exec rake test
|
33
|
-
-
|
34
|
-
diff --git a/CHANGELOG.md b/CHANGELOG.md
|
35
|
-
index e3ea210..4a25fba 100644
|
36
|
-
--- a/CHANGELOG.md
|
37
|
-
+++ b/CHANGELOG.md
|
38
|
-
@@ -1,3 +1,12 @@
|
39
|
-
+# v0.44.0
|
40
|
-
+
|
41
|
-
+* Support recording and labeling of indivudal functions via `functions:` section in *appmap.yml*.
|
42
|
-
+* Remove deprecated `exe/appmap`.
|
43
|
-
+* Add `test_status` and `exception` fields to AppMap metadata.
|
44
|
-
+* Write AppMap file atomically, by writing to a temp file first and then moving it into place.
|
45
|
-
+* Remove printing of `Inventory.json` file.
|
46
|
-
+* Remove source code from `classMap`.
|
47
|
-
+
|
48
|
-
# v0.43.0
|
49
|
-
|
50
|
-
* Record `name` and `class` of each entry in Hash-like parameters, messages, and return values.
|
51
|
-
diff --git a/README.md b/README.md
|
52
|
-
index b9e6eca..e5674d9 100644
|
53
|
-
--- a/README.md
|
54
|
-
+++ b/README.md
|
55
|
-
@@ -110,6 +110,9 @@ name: my_project
|
56
|
-
packages:
|
57
|
-
- path: app/controllers
|
58
|
-
- path: app/models
|
59
|
-
+ # Exclude sub-paths within the package path
|
60
|
-
+ exclude:
|
61
|
-
+ - concerns/accessor
|
62
|
-
- path: app/jobs
|
63
|
-
- path: app/helpers
|
64
|
-
# Include the gems that you want to see in the dependency maps.
|
65
|
-
@@ -118,15 +121,22 @@ packages:
|
66
|
-
- gem: devise
|
67
|
-
- gem: aws-sdk
|
68
|
-
- gem: will_paginate
|
69
|
-
+# Global exclusion of a class name
|
70
|
-
exclude:
|
71
|
-
- MyClass
|
72
|
-
- MyClass#my_instance_method
|
73
|
-
- MyClass.my_class_method
|
74
|
-
+functions:
|
75
|
-
+- packages: myapp
|
76
|
-
+ class: ControllerHelper
|
77
|
-
+ function: logged_in_user
|
78
|
-
+ labels: [ authentication ]
|
79
|
-
```
|
80
|
-
|
81
|
-
* **name** Provides the project name (required)
|
82
|
-
* **packages** A list of source code directories which should be recorded.
|
83
|
-
* **exclude** A list of classes and/or methods to definitively exclude from recording.
|
84
|
-
+* **functions** A list of specific functions, scoped by package and class, to record.
|
85
|
-
|
86
|
-
**packages**
|
87
|
-
|
88
|
-
@@ -145,6 +155,11 @@ Each entry in the `packages` list is a YAML object which has the following keys:
|
89
|
-
|
90
|
-
Optional list of fully qualified class and method names. Separate class and method names with period (`.`) for class methods and hash (`#`) for instance methods.
|
91
|
-
|
92
|
-
+**functions**
|
93
|
-
+
|
94
|
-
+Optional list of `class, function` pairs. The `package` name is used to place the function within the class map, and does not have to match
|
95
|
-
+the folder or gem name. The primary use of `functions` is to apply specific labels to functions whose source code is not accessible (e.g., it's in a Gem).
|
96
|
-
+For functions which are part of the application code, use `@label` or `@labels` in code comments to apply labels.
|
97
|
-
|
98
|
-
# Labels
|
99
|
-
|
100
|
-
@@ -344,7 +359,7 @@ Each interactive diagram links directly to the source code, and the information
|
101
|
-
# AppMap Swagger
|
102
|
-
|
103
|
-
[appmap_swagger](https://github.com/applandinc/appmap_swagger-ruby) is a tool to generate Swagger files from AppMap data. With `appmap_swagger`, you can add Swagger to your Ruby or Ruby on Rails project, with no need to write or modify code. Use the Swagger UI to interact with your web services API as you build it, and use diffs of Swagger to perform code review of web service changes.
|
104
|
-
-
|
105
|
-
+n
|
106
|
-
# Uploading AppMaps
|
107
|
-
|
108
|
-
[https://app.land](https://app.land) can be used to store, analyze, and share AppMaps.
|
109
|
-
diff --git a/appmap.gemspec b/appmap.gemspec
|
110
|
-
index 7a01cb9..8b2af31 100644
|
111
|
-
--- a/appmap.gemspec
|
112
|
-
+++ b/appmap.gemspec
|
113
|
-
@@ -20,8 +20,6 @@ Gem::Specification.new do |spec|
|
114
|
-
")
|
115
|
-
spec.extensions << "ext/appmap/extconf.rb"
|
116
|
-
|
117
|
-
- spec.bindir = 'exe'
|
118
|
-
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
119
|
-
spec.require_paths = ['lib']
|
120
|
-
|
121
|
-
spec.add_dependency 'activesupport'
|
122
|
-
diff --git a/exe/appmap b/exe/appmap
|
123
|
-
deleted file mode 100755
|
124
|
-
index c6bd228..0000000
|
125
|
-
--- a/exe/appmap
|
126
|
-
+++ /dev/null
|
127
|
-
@@ -1,154 +0,0 @@
|
128
|
-
-#!/usr/bin/env ruby
|
129
|
-
-# frozen_string_literal: true
|
130
|
-
-
|
131
|
-
-require 'gli'
|
132
|
-
-
|
133
|
-
-ENV['APPMAP_INITIALIZE'] = 'false'
|
134
|
-
-
|
135
|
-
-require 'appmap'
|
136
|
-
-require 'appmap/version'
|
137
|
-
-
|
138
|
-
-# AppMap CLI.
|
139
|
-
-module AppMap
|
140
|
-
- class App
|
141
|
-
- extend GLI::App
|
142
|
-
-
|
143
|
-
- program_desc 'AppMap client'
|
144
|
-
-
|
145
|
-
- version AppMap::VERSION
|
146
|
-
-
|
147
|
-
- subcommand_option_handling :normal
|
148
|
-
- arguments :strict
|
149
|
-
- preserve_argv true
|
150
|
-
-
|
151
|
-
- class << self
|
152
|
-
- protected
|
153
|
-
-
|
154
|
-
- def default_appmap_file
|
155
|
-
- ENV['APPMAP_FILE'] || 'appmap.json'
|
156
|
-
- end
|
157
|
-
-
|
158
|
-
- def output_file_flag(c, default_value: nil)
|
159
|
-
- c.desc 'Name of the output file'
|
160
|
-
- c.long_desc <<~DESC
|
161
|
-
- Use a single dash '-' for stdout.
|
162
|
-
- DESC
|
163
|
-
- c.default_value default_value if default_value
|
164
|
-
- c.arg_name 'filename'
|
165
|
-
- c.flag %i[o output]
|
166
|
-
- end
|
167
|
-
- end
|
168
|
-
-
|
169
|
-
- desc 'AppMap configuration file name'
|
170
|
-
- default_value ENV['APPMAP_CONFIG'] || 'appmap.yml'
|
171
|
-
- arg_name 'filename'
|
172
|
-
- flag %i[c config]
|
173
|
-
-
|
174
|
-
- desc 'Record the execution of a program and generate an AppMap.'
|
175
|
-
- arg_name 'program'
|
176
|
-
- command :record do |c|
|
177
|
-
- output_file_flag(c, default_value: default_appmap_file)
|
178
|
-
-
|
179
|
-
- c.action do |_, _, args|
|
180
|
-
- # My subcommand name
|
181
|
-
- ARGV.shift
|
182
|
-
-
|
183
|
-
- # Consume the :output option, if provided
|
184
|
-
- if %w[-o --output].find { |arg_name| ARGV[0] == arg_name.to_s }
|
185
|
-
- ARGV.shift
|
186
|
-
- ARGV.shift
|
187
|
-
- end
|
188
|
-
-
|
189
|
-
- # Name of the program to execute. GLI will ensure that it's present.
|
190
|
-
- program = args.shift or help_now!("'program' argument is required")
|
191
|
-
-
|
192
|
-
- # Also pop the program name from ARGV, because the command will use raw ARGV
|
193
|
-
- # to load the extra arguments into this Ruby process.
|
194
|
-
- ARGV.shift
|
195
|
-
-
|
196
|
-
- require 'appmap/command/record'
|
197
|
-
- AppMap::Command::Record.new(@config, program).perform do |version, metadata, class_map, events|
|
198
|
-
- @output_file.write JSON.generate(version: version,
|
199
|
-
- metadata: metadata,
|
200
|
-
- classMap: class_map,
|
201
|
-
- events: events)
|
202
|
-
- end
|
203
|
-
- end
|
204
|
-
- end
|
205
|
-
-
|
206
|
-
- desc 'Calculate and print statistics of scenario files.'
|
207
|
-
- arg_name 'filename'
|
208
|
-
- command :stats do |c|
|
209
|
-
- output_file_flag(c, default_value: '-')
|
210
|
-
-
|
211
|
-
- c.desc 'Display format for the result (text | json)'
|
212
|
-
- c.default_value 'text'
|
213
|
-
- c.flag %i[f format]
|
214
|
-
-
|
215
|
-
- c.desc 'Maximum number of lines to display for each stat'
|
216
|
-
- c.flag %i[l limit]
|
217
|
-
-
|
218
|
-
- c.action do |_, options, args|
|
219
|
-
- require 'appmap/command/stats'
|
220
|
-
-
|
221
|
-
- limit = options[:limit].to_i if options[:limit]
|
222
|
-
-
|
223
|
-
- # Name of the file to analyze. GLI will ensure that it's present.
|
224
|
-
- filenames = args
|
225
|
-
- help_now!("'filename' argument is required") if filenames.empty?
|
226
|
-
-
|
227
|
-
- require 'appmap/algorithm/stats'
|
228
|
-
- result = filenames.inject(::AppMap::Algorithm::Stats::Result.new([], [])) do |stats_result, filename|
|
229
|
-
- appmap = begin
|
230
|
-
- JSON.parse(File.read(filename))
|
231
|
-
- rescue JSON::ParserError
|
232
|
-
- STDERR.puts "#{filename} is not valid JSON : #{$!}"
|
233
|
-
- nil
|
234
|
-
- end
|
235
|
-
- stats_result.tap do
|
236
|
-
- if appmap
|
237
|
-
- limit = options[:limit].to_i if options[:limit]
|
238
|
-
- stats_for_file = AppMap::Command::Stats.new(appmap).perform(limit: limit)
|
239
|
-
- stats_result.merge!(stats_for_file)
|
240
|
-
- end
|
241
|
-
- end
|
242
|
-
- end
|
243
|
-
-
|
244
|
-
- result.sort!
|
245
|
-
- result.limit!(limit) if limit
|
246
|
-
-
|
247
|
-
- display = case options[:format]
|
248
|
-
- when 'json'
|
249
|
-
- JSON.pretty_generate(result.as_json)
|
250
|
-
- else
|
251
|
-
- result.as_text
|
252
|
-
- end
|
253
|
-
- @output_file.write display
|
254
|
-
- end
|
255
|
-
- end
|
256
|
-
-
|
257
|
-
- pre do |global, _, options, _|
|
258
|
-
- @config = interpret_config_option(global[:config])
|
259
|
-
- @output_file = interpret_output_file_option(options[:output])
|
260
|
-
-
|
261
|
-
- true
|
262
|
-
- end
|
263
|
-
-
|
264
|
-
- class << self
|
265
|
-
- protected
|
266
|
-
-
|
267
|
-
- def interpret_config_option(fname)
|
268
|
-
- AppMap.initialize fname
|
269
|
-
- end
|
270
|
-
-
|
271
|
-
- def interpret_output_file_option(file_name)
|
272
|
-
- Hash.new { |_, fname| -> { File.new(fname, 'w') } }.tap do |open_output_file|
|
273
|
-
- open_output_file[nil] = -> { nil }
|
274
|
-
- open_output_file['-'] = -> { $stdout }
|
275
|
-
- end[file_name].call
|
276
|
-
- end
|
277
|
-
- end
|
278
|
-
- end
|
279
|
-
-end
|
280
|
-
-
|
281
|
-
-exit AppMap::App.run(ARGV)
|
282
|
-
diff --git a/lib/appmap.rb b/lib/appmap.rb
|
283
|
-
index ccf7bb5..998513c 100644
|
284
|
-
--- a/lib/appmap.rb
|
285
|
-
+++ b/lib/appmap.rb
|
286
|
-
@@ -43,6 +43,7 @@ module AppMap
|
287
|
-
# Call this function before the program code is loaded by the Ruby VM, otherwise
|
288
|
-
# the load events won't be seen and the hooks won't activate.
|
289
|
-
def initialize(config_file_path = 'appmap.yml')
|
290
|
-
+ raise "AppMap configuration file #{config_file_path} does not exist" unless ::File.exists?(config_file_path)
|
291
|
-
warn "Configuring AppMap from path #{config_file_path}"
|
292
|
-
Config.load_from_file(config_file_path).tap do |configuration|
|
293
|
-
self.configuration = configuration
|
294
|
-
@@ -50,11 +51,6 @@ module AppMap
|
295
|
-
end
|
296
|
-
end
|
297
|
-
|
298
|
-
- # Whether to include source and comments in all class maps.
|
299
|
-
- def include_source?
|
300
|
-
- ENV['APPMAP_SOURCE'] == 'true'
|
301
|
-
- end
|
302
|
-
-
|
303
|
-
# Used to start tracing, stop tracing, and record events.
|
304
|
-
def tracing
|
305
|
-
@tracing ||= Trace::Tracing.new
|
306
|
-
@@ -88,8 +84,8 @@ module AppMap
|
307
|
-
end
|
308
|
-
|
309
|
-
# Builds a class map from a config and a list of Ruby methods.
|
310
|
-
- def class_map(methods, options = {})
|
311
|
-
- ClassMap.build_from_methods(methods, options)
|
312
|
-
+ def class_map(methods)
|
313
|
-
+ ClassMap.build_from_methods(methods)
|
314
|
-
end
|
315
|
-
|
316
|
-
# Returns default metadata detected from the Ruby system and from the
|
317
|
-
diff --git a/lib/appmap/class_map.rb b/lib/appmap/class_map.rb
|
318
|
-
index 4d19a27..0023c1c 100644
|
319
|
-
--- a/lib/appmap/class_map.rb
|
320
|
-
+++ b/lib/appmap/class_map.rb
|
321
|
-
@@ -71,17 +71,17 @@ module AppMap
|
322
|
-
end
|
323
|
-
|
324
|
-
class << self
|
325
|
-
- def build_from_methods(methods, options = {})
|
326
|
-
+ def build_from_methods(methods)
|
327
|
-
root = Types::Root.new
|
328
|
-
methods.each do |method|
|
329
|
-
- add_function root, method, options
|
330
|
-
+ add_function root, method
|
331
|
-
end
|
332
|
-
root.children.map(&:to_h)
|
333
|
-
end
|
334
|
-
|
335
|
-
protected
|
336
|
-
|
337
|
-
- def add_function(root, method, include_source: true)
|
338
|
-
+ def add_function(root, method)
|
339
|
-
package = method.package
|
340
|
-
static = method.static
|
341
|
-
|
342
|
-
@@ -113,16 +113,13 @@ module AppMap
|
343
|
-
[ method.defined_class, static ? '.' : '#', method.name ].join
|
344
|
-
end
|
345
|
-
|
346
|
-
- source, comment = begin
|
347
|
-
- [ method.source, method.comment ]
|
348
|
-
+ comment = begin
|
349
|
-
+ method.comment
|
350
|
-
rescue MethodSource::SourceNotFoundError
|
351
|
-
- [ nil, nil, ]
|
352
|
-
+ nil
|
353
|
-
end
|
354
|
-
|
355
|
-
- if include_source
|
356
|
-
- function_info[:source] = source unless source.blank?
|
357
|
-
- function_info[:comment] = comment unless comment.blank?
|
358
|
-
- end
|
359
|
-
+ function_info[:comment] = comment unless comment.blank?
|
360
|
-
|
361
|
-
function_info[:labels] = parse_labels(comment) + (package.labels || [])
|
362
|
-
object_infos << function_info
|
363
|
-
diff --git a/lib/appmap/command/record.rb b/lib/appmap/command/record.rb
|
364
|
-
index 5f22903..d683f63 100644
|
365
|
-
--- a/lib/appmap/command/record.rb
|
366
|
-
+++ b/lib/appmap/command/record.rb
|
367
|
-
@@ -27,7 +27,7 @@ module AppMap
|
368
|
-
event_thread.join
|
369
|
-
yield AppMap::APPMAP_FORMAT_VERSION,
|
370
|
-
AppMap.detect_metadata,
|
371
|
-
- AppMap.class_map(tracer.event_methods, include_source: AppMap.include_source?),
|
372
|
-
+ AppMap.class_map(tracer.event_methods),
|
373
|
-
events
|
374
|
-
end
|
375
|
-
|
376
|
-
diff --git a/lib/appmap/config.rb b/lib/appmap/config.rb
|
377
|
-
index aa208d6..b4c8841 100644
|
378
|
-
--- a/lib/appmap/config.rb
|
379
|
-
+++ b/lib/appmap/config.rb
|
380
|
-
@@ -49,44 +49,89 @@ module AppMap
|
381
|
-
end
|
382
|
-
end
|
383
|
-
|
384
|
-
- Hook = Struct.new(:method_names, :package) do
|
385
|
-
+ Function = Struct.new(:package, :cls, :labels, :function_names) do
|
386
|
-
+ def to_h
|
387
|
-
+ {
|
388
|
-
+ package: package,
|
389
|
-
+ class: cls,
|
390
|
-
+ labels: labels,
|
391
|
-
+ functions: function_names.map(&:to_sym)
|
392
|
-
+ }.compact
|
393
|
-
+ end
|
394
|
-
end
|
395
|
-
|
396
|
-
- OPENSSL_PACKAGES = Package.build_from_path('openssl', package_name: 'openssl', labels: %w[security crypto])
|
397
|
-
+ class Hook
|
398
|
-
+ attr_reader :method_names, :package
|
399
|
-
+
|
400
|
-
+ def initialize(method_names, package)
|
401
|
-
+ @method_names = method_names
|
402
|
-
+ @package = package
|
403
|
-
+ end
|
404
|
-
+
|
405
|
-
+ def to_h
|
406
|
-
+ {
|
407
|
-
+ package: package.name,
|
408
|
-
+ method_names: method_names
|
409
|
-
+ }
|
410
|
-
+ end
|
411
|
-
+ end
|
412
|
-
+
|
413
|
-
+ OPENSSL_PACKAGES = ->(labels) { Package.build_from_path('openssl', package_name: 'openssl', labels: labels) }
|
414
|
-
|
415
|
-
# Methods that should always be hooked, with their containing
|
416
|
-
# package and labels that should be applied to them.
|
417
|
-
HOOKED_METHODS = {
|
418
|
-
- 'ActiveSupport::SecurityUtils' => Hook.new(:secure_compare, Package.build_from_path('active_support', labels: %w[provider.secure_compare])),
|
419
|
-
+ 'ActiveSupport::SecurityUtils' => Hook.new(:secure_compare, Package.build_from_path('active_support', labels: %w[crypto.secure_compare])),
|
420
|
-
'ActionView::Renderer' => Hook.new(:render, Package.build_from_path('action_view', labels: %w[mvc.view])),
|
421
|
-
- 'ActionDispatch::Cookies::CookieJar' => Hook.new(%i[[]= clear update delete recycle], Package.build_from_path('action_pack', labels: %w[provider.http.cookie])),
|
422
|
-
- 'ActionDispatch::Cookies::EncryptedCookieJar' => Hook.new(%i[[]=], Package.build_from_path('action_pack', labels: %w[provider.http.cookie crypto])),
|
423
|
-
- 'CanCan::ControllerAdditions' => Hook.new(%i[authorize! can? cannot?], Package.build_from_path('cancancan', labels: %w[provider.authorization])),
|
424
|
-
- 'CanCan::Ability' => Hook.new(%i[authorize!], Package.build_from_path('cancancan', labels: %w[provider.authorization])),
|
425
|
-
+ 'ActionDispatch::Request::Session' => Hook.new(%i[destroy [] dig values []= clear update delete fetch merge], Package.build_from_path('action_pack', labels: %w[http.session])),
|
426
|
-
+ 'ActionDispatch::Cookies::CookieJar' => Hook.new(%i[[]= clear update delete recycle], Package.build_from_path('action_pack', labels: %w[http.cookie])),
|
427
|
-
+ 'ActionDispatch::Cookies::EncryptedCookieJar' => Hook.new(%i[[]=], Package.build_from_path('action_pack', labels: %w[http.cookie crypto.encrypt])),
|
428
|
-
+ 'CanCan::ControllerAdditions' => Hook.new(%i[authorize! can? cannot?], Package.build_from_path('cancancan', labels: %w[security.authorization])),
|
429
|
-
+ 'CanCan::Ability' => Hook.new(%i[authorize!], Package.build_from_path('cancancan', labels: %w[security.authorization])),
|
430
|
-
+ 'ActionController::Instrumentation' => [
|
431
|
-
+ Hook.new(%i[process_action send_file send_data redirect_to], Package.build_from_path('action_view', labels: %w[mvc.controller])),
|
432
|
-
+ Hook.new(%i[render], Package.build_from_path('action_view', labels: %w[mvc.view])),
|
433
|
-
+ ]
|
434
|
-
}.freeze
|
435
|
-
|
436
|
-
BUILTIN_METHODS = {
|
437
|
-
- 'OpenSSL::PKey::PKey' => Hook.new(:sign, OPENSSL_PACKAGES),
|
438
|
-
- 'OpenSSL::X509::Request' => Hook.new(%i[sign verify], OPENSSL_PACKAGES),
|
439
|
-
- 'OpenSSL::PKCS5' => Hook.new(%i[pbkdf2_hmac_sha1 pbkdf2_hmac], OPENSSL_PACKAGES),
|
440
|
-
- 'OpenSSL::Cipher' => Hook.new(%i[encrypt decrypt final], OPENSSL_PACKAGES),
|
441
|
-
- 'OpenSSL::X509::Certificate' => Hook.new(:sign, OPENSSL_PACKAGES),
|
442
|
-
- 'Net::HTTP' => Hook.new(:request, Package.build_from_path('net/http', package_name: 'net/http', labels: %w[protocol.http io])),
|
443
|
-
- 'Net::SMTP' => Hook.new(:send, Package.build_from_path('net/smtp', package_name: 'net/smtp', labels: %w[protocol.smtp protocol.email io])),
|
444
|
-
- 'Net::POP3' => Hook.new(:mails, Package.build_from_path('net/pop3', package_name: 'net/pop', labels: %w[protocol.pop protocol.email io])),
|
445
|
-
- 'Net::IMAP' => Hook.new(:send_command, Package.build_from_path('net/imap', package_name: 'net/imap', labels: %w[protocol.imap protocol.email io])),
|
446
|
-
- 'Marshal' => Hook.new(%i[dump load], Package.build_from_path('marshal', labels: %w[format.marshal provider.serialization])),
|
447
|
-
- 'Psych' => Hook.new(%i[dump dump_stream load load_stream parse parse_stream], Package.build_from_path('yaml', package_name: 'psych', labels: %w[format.yaml provider.serialization])),
|
448
|
-
- 'JSON::Ext::Parser' => Hook.new(:parse, Package.build_from_path('json', package_name: 'json', labels: %w[format.json provider.serialization])),
|
449
|
-
- 'JSON::Ext::Generator::State' => Hook.new(:generate, Package.build_from_path('json', package_name: 'json', labels: %w[format.json provider.serialization])),
|
450
|
-
+ 'OpenSSL::PKey::PKey' => Hook.new(:sign, OPENSSL_PACKAGES.(%w[crypto.pkey])),
|
451
|
-
+ 'OpenSSL::X509::Request' => Hook.new(%i[sign verify], OPENSSL_PACKAGES.(%w[crypto.x509])),
|
452
|
-
+ 'OpenSSL::PKCS5' => Hook.new(%i[pbkdf2_hmac_sha1 pbkdf2_hmac], OPENSSL_PACKAGES.(%w[crypto.pkcs5])),
|
453
|
-
+ 'OpenSSL::Cipher' => [
|
454
|
-
+ Hook.new(%i[encrypt], OPENSSL_PACKAGES.(%w[crypto.encrypt])),
|
455
|
-
+ Hook.new(%i[decrypt], OPENSSL_PACKAGES.(%w[crypto.decrypt]))
|
456
|
-
+ ],
|
457
|
-
+ 'ActiveSupport::Callbacks::CallbackSequence' => [
|
458
|
-
+ Hook.new(:invoke_before, Package.build_from_path('active_support', package_name: 'active_support', labels: %w[mvc.before_action])),
|
459
|
-
+ Hook.new(:invoke_after, Package.build_from_path('active_support', package_name: 'active_support', labels: %w[mvc.after_action])),
|
460
|
-
+ ],
|
461
|
-
+ 'OpenSSL::X509::Certificate' => Hook.new(:sign, OPENSSL_PACKAGES.(%w[crypto.x509])),
|
462
|
-
+ 'Net::HTTP' => Hook.new(:request, Package.build_from_path('net/http', package_name: 'net/http', labels: %w[protocol.http])),
|
463
|
-
+ 'Net::SMTP' => Hook.new(:send, Package.build_from_path('net/smtp', package_name: 'net/smtp', labels: %w[protocol.email.smtp])),
|
464
|
-
+ 'Net::POP3' => Hook.new(:mails, Package.build_from_path('net/pop3', package_name: 'net/pop', labels: %w[protocol.email.pop])),
|
465
|
-
+ 'Net::IMAP' => Hook.new(:send_command, Package.build_from_path('net/imap', package_name: 'net/imap', labels: %w[protocol.email.imap])),
|
466
|
-
+ 'Marshal' => Hook.new(%i[dump load], Package.build_from_path('marshal', labels: %w[format.marshal])),
|
467
|
-
+ 'Psych' => Hook.new(%i[dump dump_stream load load_stream parse parse_stream], Package.build_from_path('yaml', package_name: 'psych', labels: %w[format.yaml])),
|
468
|
-
+ 'JSON::Ext::Parser' => Hook.new(:parse, Package.build_from_path('json', package_name: 'json', labels: %w[format.json])),
|
469
|
-
+ 'JSON::Ext::Generator::State' => Hook.new(:generate, Package.build_from_path('json', package_name: 'json', labels: %w[format.json])),
|
470
|
-
}.freeze
|
471
|
-
|
472
|
-
- attr_reader :name, :packages, :exclude
|
473
|
-
+ attr_reader :name, :packages, :exclude, :builtin_methods
|
474
|
-
|
475
|
-
- def initialize(name, packages = [], exclude = [])
|
476
|
-
+ def initialize(name, packages, exclude: [], functions: [])
|
477
|
-
@name = name
|
478
|
-
@packages = packages
|
479
|
-
@exclude = exclude
|
480
|
-
+ @builtin_methods = BUILTIN_METHODS
|
481
|
-
+ @functions = functions
|
482
|
-
+ @hooked_methods = HOOKED_METHODS.dup
|
483
|
-
+ functions.each do |func|
|
484
|
-
+ package_options = {}
|
485
|
-
+ package_options[:labels] = func.labels if func.labels
|
486
|
-
+ @hooked_methods[func.cls] ||= []
|
487
|
-
+ @hooked_methods[func.cls] << Hook.new(func.function_names, Package.build_from_path(func.package, package_options))
|
488
|
-
+ end
|
489
|
-
end
|
490
|
-
|
491
|
-
class << self
|
492
|
-
@@ -98,6 +143,16 @@ module AppMap
|
493
|
-
|
494
|
-
# Loads configuration from a Hash.
|
495
|
-
def load(config_data)
|
496
|
-
+ functions = (config_data['functions'] || []).map do |function_data|
|
497
|
-
+ package = function_data['package']
|
498
|
-
+ cls = function_data['class']
|
499
|
-
+ functions = function_data['function'] || function_data['functions']
|
500
|
-
+ raise 'AppMap class configuration should specify package, class and function(s)' unless package && cls && functions
|
501
|
-
+ functions = Array(functions).map(&:to_sym)
|
502
|
-
+ labels = function_data['label'] || function_data['labels']
|
503
|
-
+ labels = Array(labels).map(&:to_s) if labels
|
504
|
-
+ Function.new(package, cls, labels, functions)
|
505
|
-
+ end
|
506
|
-
packages = (config_data['packages'] || []).map do |package|
|
507
|
-
gem = package['gem']
|
508
|
-
path = package['path']
|
509
|
-
@@ -112,7 +167,8 @@ module AppMap
|
510
|
-
Package.build_from_path(path, exclude: package['exclude'] || [], shallow: package['shallow'])
|
511
|
-
end
|
512
|
-
end.compact
|
513
|
-
- Config.new config_data['name'], packages, config_data['exclude'] || []
|
514
|
-
+ exclude = config_data['exclude'] || []
|
515
|
-
+ Config.new config_data['name'], packages, exclude: exclude, functions: functions
|
516
|
-
end
|
517
|
-
end
|
518
|
-
|
519
|
-
@@ -120,6 +176,7 @@ module AppMap
|
520
|
-
{
|
521
|
-
name: name,
|
522
|
-
packages: packages.map(&:to_h),
|
523
|
-
+ functions: @functions.map(&:to_h),
|
524
|
-
exclude: exclude
|
525
|
-
}
|
526
|
-
end
|
527
|
-
@@ -164,14 +221,17 @@ module AppMap
|
528
|
-
end
|
529
|
-
|
530
|
-
def find_package(defined_class, method_name)
|
531
|
-
- hook = find_hook(defined_class)
|
532
|
-
- return nil unless hook
|
533
|
-
+ hooks = find_hooks(defined_class)
|
534
|
-
+ return nil unless hooks
|
535
|
-
|
536
|
-
- Array(hook.method_names).include?(method_name) ? hook.package : nil
|
537
|
-
+ hook = Array(hooks).find do |hook|
|
538
|
-
+ Array(hook.method_names).include?(method_name)
|
539
|
-
+ end
|
540
|
-
+ hook ? hook.package : nil
|
541
|
-
end
|
542
|
-
|
543
|
-
- def find_hook(defined_class)
|
544
|
-
- HOOKED_METHODS[defined_class] || BUILTIN_METHODS[defined_class]
|
545
|
-
+ def find_hooks(defined_class)
|
546
|
-
+ Array(@hooked_methods[defined_class] || @builtin_methods[defined_class])
|
547
|
-
end
|
548
|
-
end
|
549
|
-
end
|
550
|
-
diff --git a/lib/appmap/cucumber.rb b/lib/appmap/cucumber.rb
|
551
|
-
index 7b59c0a..eaf4ac3 100644
|
552
|
-
--- a/lib/appmap/cucumber.rb
|
553
|
-
+++ b/lib/appmap/cucumber.rb
|
554
|
-
@@ -50,7 +50,7 @@ module AppMap
|
555
|
-
appmap['metadata'] = update_metadata(scenario, appmap['metadata'])
|
556
|
-
scenario_filename = AppMap::Util.scenario_filename(appmap['metadata']['name'])
|
557
|
-
|
558
|
-
- File.write(File.join(APPMAP_OUTPUT_DIR, scenario_filename), JSON.generate(appmap))
|
559
|
-
+ AppMap::Util.write_appmap(File.join(APPMAP_OUTPUT_DIR, scenario_filename), JSON.generate(appmap))
|
560
|
-
end
|
561
|
-
|
562
|
-
def enabled?
|
563
|
-
diff --git a/lib/appmap/hook.rb b/lib/appmap/hook.rb
|
564
|
-
index dd919d9..f141ac7 100644
|
565
|
-
--- a/lib/appmap/hook.rb
|
566
|
-
+++ b/lib/appmap/hook.rb
|
567
|
-
@@ -32,6 +32,7 @@ module AppMap
|
568
|
-
end
|
569
|
-
|
570
|
-
attr_reader :config
|
571
|
-
+
|
572
|
-
def initialize(config)
|
573
|
-
@config = config
|
574
|
-
end
|
575
|
-
@@ -59,6 +60,10 @@ module AppMap
|
576
|
-
|
577
|
-
hook = lambda do |hook_cls|
|
578
|
-
lambda do |method_id|
|
579
|
-
+ # Don't try and trace the AppMap methods or there will be
|
580
|
-
+ # a stack overflow in the defined hook method.
|
581
|
-
+ return if (hook_cls&.name || '').split('::')[0] == AppMap.name
|
582
|
-
+
|
583
|
-
method = begin
|
584
|
-
hook_cls.public_instance_method(method_id)
|
585
|
-
rescue NameError
|
586
|
-
@@ -78,11 +83,9 @@ module AppMap
|
587
|
-
config.always_hook?(hook_cls, method.name) ||
|
588
|
-
config.included_by_location?(method)
|
589
|
-
|
590
|
-
- hook_method = Hook::Method.new(config.package_for_method(method), hook_cls, method)
|
591
|
-
+ package = config.package_for_method(method)
|
592
|
-
|
593
|
-
- # Don't try and trace the AppMap methods or there will be
|
594
|
-
- # a stack overflow in the defined hook method.
|
595
|
-
- next if /\AAppMap[:\.]/.match?(hook_method.method_display_name)
|
596
|
-
+ hook_method = Hook::Method.new(package, hook_cls, method)
|
597
|
-
|
598
|
-
hook_method.activate
|
599
|
-
end
|
600
|
-
@@ -112,25 +115,27 @@ module AppMap
|
601
|
-
end
|
602
|
-
end
|
603
|
-
|
604
|
-
- Config::BUILTIN_METHODS.each do |class_name, hook|
|
605
|
-
- require hook.package.package_name if hook.package.package_name
|
606
|
-
- Array(hook.method_names).each do |method_name|
|
607
|
-
- method_name = method_name.to_sym
|
608
|
-
+ config.builtin_methods.each do |class_name, hooks|
|
609
|
-
+ Array(hooks).each do |hook|
|
610
|
-
+ require hook.package.package_name if hook.package.package_name
|
611
|
-
+ Array(hook.method_names).each do |method_name|
|
612
|
-
+ method_name = method_name.to_sym
|
613
|
-
|
614
|
-
- cls = class_from_string.(class_name)
|
615
|
-
- method = \
|
616
|
-
- begin
|
617
|
-
- cls.instance_method(method_name)
|
618
|
-
- rescue NameError
|
619
|
-
- cls.method(method_name) rescue nil
|
620
|
-
- end
|
621
|
-
+ cls = class_from_string.(class_name)
|
622
|
-
+ method = \
|
623
|
-
+ begin
|
624
|
-
+ cls.instance_method(method_name)
|
625
|
-
+ rescue NameError
|
626
|
-
+ cls.method(method_name) rescue nil
|
627
|
-
+ end
|
628
|
-
|
629
|
-
- next if config.never_hook?(method)
|
630
|
-
+ next if config.never_hook?(method)
|
631
|
-
|
632
|
-
- if method
|
633
|
-
- Hook::Method.new(hook.package, cls, method).activate
|
634
|
-
- else
|
635
|
-
- warn "Method #{method_name} not found on #{cls.name}"
|
636
|
-
+ if method
|
637
|
-
+ Hook::Method.new(hook.package, cls, method).activate
|
638
|
-
+ else
|
639
|
-
+ warn "Method #{method_name} not found on #{cls.name}"
|
640
|
-
+ end
|
641
|
-
end
|
642
|
-
end
|
643
|
-
end
|
644
|
-
diff --git a/lib/appmap/middleware/remote_recording.rb b/lib/appmap/middleware/remote_recording.rb
|
645
|
-
index 83affc0..f695d25 100644
|
646
|
-
--- a/lib/appmap/middleware/remote_recording.rb
|
647
|
-
+++ b/lib/appmap/middleware/remote_recording.rb
|
648
|
-
@@ -67,7 +67,7 @@ module AppMap
|
649
|
-
|
650
|
-
response = JSON.generate \
|
651
|
-
version: AppMap::APPMAP_FORMAT_VERSION,
|
652
|
-
- classMap: AppMap.class_map(tracer.event_methods, include_source: AppMap.include_source?),
|
653
|
-
+ classMap: AppMap.class_map(tracer.event_methods),
|
654
|
-
metadata: metadata,
|
655
|
-
events: @events
|
656
|
-
|
657
|
-
diff --git a/lib/appmap/minitest.rb b/lib/appmap/minitest.rb
|
658
|
-
index dc88bac..cf4a4d2 100644
|
659
|
-
--- a/lib/appmap/minitest.rb
|
660
|
-
+++ b/lib/appmap/minitest.rb
|
661
|
-
@@ -26,8 +26,9 @@ module AppMap
|
662
|
-
end
|
663
|
-
|
664
|
-
|
665
|
-
- def finish
|
666
|
-
+ def finish(exception)
|
667
|
-
warn "Finishing recording of test #{test.class}.#{test.name}" if AppMap::Minitest::LOG
|
668
|
-
+ warn "Exception: #{exception}" if exception && AppMap::Minitest::LOG
|
669
|
-
|
670
|
-
events = []
|
671
|
-
AppMap.tracing.delete @trace
|
672
|
-
@@ -36,15 +37,17 @@ module AppMap
|
673
|
-
|
674
|
-
AppMap::Minitest.add_event_methods @trace.event_methods
|
675
|
-
|
676
|
-
- class_map = AppMap.class_map(@trace.event_methods, include_source: AppMap.include_source?)
|
677
|
-
+ class_map = AppMap.class_map(@trace.event_methods)
|
678
|
-
|
679
|
-
feature_group = test.class.name.underscore.split('_')[0...-1].join('_').capitalize
|
680
|
-
feature_name = test.name.split('_')[1..-1].join(' ')
|
681
|
-
scenario_name = [ feature_group, feature_name ].join(' ')
|
682
|
-
|
683
|
-
- AppMap::Minitest.save scenario_name,
|
684
|
-
- class_map,
|
685
|
-
- source_location,
|
686
|
-
+ AppMap::Minitest.save name: scenario_name,
|
687
|
-
+ class_map: class_map,
|
688
|
-
+ source_location: source_location,
|
689
|
-
+ test_status: exception ? 'failed' : 'succeeded',
|
690
|
-
+ exception: exception,
|
691
|
-
events: events
|
692
|
-
end
|
693
|
-
end
|
694
|
-
@@ -63,11 +66,11 @@ module AppMap
|
695
|
-
@recordings_by_test[test.object_id] = Recording.new(test, name)
|
696
|
-
end
|
697
|
-
|
698
|
-
- def end_test(test)
|
699
|
-
+ def end_test(test, exception:)
|
700
|
-
recording = @recordings_by_test.delete(test.object_id)
|
701
|
-
return warn "No recording found for #{test}" unless recording
|
702
|
-
|
703
|
-
- recording.finish
|
704
|
-
+ recording.finish exception
|
705
|
-
end
|
706
|
-
|
707
|
-
def config
|
708
|
-
@@ -78,9 +81,9 @@ module AppMap
|
709
|
-
@event_methods += event_methods
|
710
|
-
end
|
711
|
-
|
712
|
-
- def save(example_name, class_map, source_location, events: nil, labels: nil)
|
713
|
-
+ def save(name:, class_map:, source_location:, test_status:, exception:, events:)
|
714
|
-
metadata = AppMap::Minitest.metadata.tap do |m|
|
715
|
-
- m[:name] = example_name
|
716
|
-
+ m[:name] = name
|
717
|
-
m[:source_location] = source_location
|
718
|
-
m[:app] = AppMap.configuration.name
|
719
|
-
m[:frameworks] ||= []
|
720
|
-
@@ -91,6 +94,13 @@ module AppMap
|
721
|
-
m[:recorder] = {
|
722
|
-
name: 'minitest'
|
723
|
-
}
|
724
|
-
+ m[:test_status] = test_status
|
725
|
-
+ if exception
|
726
|
-
+ m[:exception] = {
|
727
|
-
+ class: exception.class.name,
|
728
|
-
+ message: exception.to_s
|
729
|
-
+ }
|
730
|
-
+ end
|
731
|
-
end
|
732
|
-
|
733
|
-
appmap = {
|
734
|
-
@@ -99,14 +109,9 @@ module AppMap
|
735
|
-
classMap: class_map,
|
736
|
-
events: events
|
737
|
-
}.compact
|
738
|
-
- fname = AppMap::Util.scenario_filename(example_name)
|
739
|
-
+ fname = AppMap::Util.scenario_filename(name)
|
740
|
-
|
741
|
-
- File.write(File.join(APPMAP_OUTPUT_DIR, fname), JSON.generate(appmap))
|
742
|
-
- end
|
743
|
-
-
|
744
|
-
- def print_inventory
|
745
|
-
- class_map = AppMap.class_map(@event_methods)
|
746
|
-
- save 'Inventory', class_map, labels: %w[inventory]
|
747
|
-
+ AppMap::Util.write_appmap(File.join(APPMAP_OUTPUT_DIR, fname), JSON.generate(appmap))
|
748
|
-
end
|
749
|
-
|
750
|
-
def enabled?
|
751
|
-
@@ -115,9 +120,6 @@ module AppMap
|
752
|
-
|
753
|
-
def run
|
754
|
-
init
|
755
|
-
- at_exit do
|
756
|
-
- print_inventory
|
757
|
-
- end
|
758
|
-
end
|
759
|
-
end
|
760
|
-
end
|
761
|
-
@@ -135,7 +137,7 @@ if AppMap::Minitest.enabled?
|
762
|
-
begin
|
763
|
-
run_without_hook
|
764
|
-
ensure
|
765
|
-
- AppMap::Minitest.end_test self
|
766
|
-
+ AppMap::Minitest.end_test self, exception: $!
|
767
|
-
end
|
768
|
-
end
|
769
|
-
end
|
770
|
-
diff --git a/lib/appmap/record.rb b/lib/appmap/record.rb
|
771
|
-
index f42da60..bb3ea94 100644
|
772
|
-
--- a/lib/appmap/record.rb
|
773
|
-
+++ b/lib/appmap/record.rb
|
774
|
-
@@ -23,5 +23,5 @@ at_exit do
|
775
|
-
'classMap' => AppMap.class_map(tracer.event_methods),
|
776
|
-
'events' => events
|
777
|
-
}
|
778
|
-
- File.write 'appmap.json', JSON.generate(appmap)
|
779
|
-
+ AppMap::Util.write_appmap('appmap.json', JSON.generate(appmap))
|
780
|
-
end
|
781
|
-
diff --git a/lib/appmap/rspec.rb b/lib/appmap/rspec.rb
|
782
|
-
index bbc3781..79c0c66 100644
|
783
|
-
--- a/lib/appmap/rspec.rb
|
784
|
-
+++ b/lib/appmap/rspec.rb
|
785
|
-
@@ -94,8 +94,9 @@ module AppMap
|
786
|
-
result
|
787
|
-
end
|
788
|
-
|
789
|
-
- def finish
|
790
|
-
+ def finish(exception)
|
791
|
-
warn "Finishing recording of example #{example}" if AppMap::RSpec::LOG
|
792
|
-
+ warn "Exception: #{exception}" if exception && AppMap::RSpec::LOG
|
793
|
-
|
794
|
-
events = []
|
795
|
-
AppMap.tracing.delete @trace
|
796
|
-
@@ -104,7 +105,7 @@ module AppMap
|
797
|
-
|
798
|
-
AppMap::RSpec.add_event_methods @trace.event_methods
|
799
|
-
|
800
|
-
- class_map = AppMap.class_map(@trace.event_methods, include_source: AppMap.include_source?)
|
801
|
-
+ class_map = AppMap.class_map(@trace.event_methods)
|
802
|
-
|
803
|
-
description = []
|
804
|
-
scope = ScopeExample.new(example)
|
805
|
-
@@ -127,9 +128,11 @@ module AppMap
|
806
|
-
|
807
|
-
full_description = normalize.call(description.join(' '))
|
808
|
-
|
809
|
-
- AppMap::RSpec.save full_description,
|
810
|
-
- class_map,
|
811
|
-
- source_location,
|
812
|
-
+ AppMap::RSpec.save name: full_description,
|
813
|
-
+ class_map: class_map,
|
814
|
-
+ source_location: source_location,
|
815
|
-
+ test_status: exception ? 'failed' : 'succeeded',
|
816
|
-
+ exception: exception,
|
817
|
-
events: events
|
818
|
-
end
|
819
|
-
end
|
820
|
-
@@ -148,11 +151,11 @@ module AppMap
|
821
|
-
@recordings_by_example[example.object_id] = Recording.new(example)
|
822
|
-
end
|
823
|
-
|
824
|
-
- def end_spec(example)
|
825
|
-
+ def end_spec(example, exception:)
|
826
|
-
recording = @recordings_by_example.delete(example.object_id)
|
827
|
-
return warn "No recording found for #{example}" unless recording
|
828
|
-
|
829
|
-
- recording.finish
|
830
|
-
+ recording.finish exception
|
831
|
-
end
|
832
|
-
|
833
|
-
def config
|
834
|
-
@@ -163,12 +166,11 @@ module AppMap
|
835
|
-
@event_methods += event_methods
|
836
|
-
end
|
837
|
-
|
838
|
-
- def save(example_name, class_map, source_location, events: nil, labels: nil)
|
839
|
-
+ def save(name:, class_map:, source_location:, test_status:, exception:, events:)
|
840
|
-
metadata = AppMap::RSpec.metadata.tap do |m|
|
841
|
-
- m[:name] = example_name
|
842
|
-
+ m[:name] = name
|
843
|
-
m[:source_location] = source_location
|
844
|
-
m[:app] = AppMap.configuration.name
|
845
|
-
- m[:labels] = labels if labels
|
846
|
-
m[:frameworks] ||= []
|
847
|
-
m[:frameworks] << {
|
848
|
-
name: 'rspec',
|
849
|
-
@@ -177,6 +179,13 @@ module AppMap
|
850
|
-
m[:recorder] = {
|
851
|
-
name: 'rspec'
|
852
|
-
}
|
853
|
-
+ m[:test_status] = test_status
|
854
|
-
+ if exception
|
855
|
-
+ m[:exception] = {
|
856
|
-
+ class: exception.class.name,
|
857
|
-
+ message: exception.to_s
|
858
|
-
+ }
|
859
|
-
+ end
|
860
|
-
end
|
861
|
-
|
862
|
-
appmap = {
|
863
|
-
@@ -185,14 +194,9 @@ module AppMap
|
864
|
-
classMap: class_map,
|
865
|
-
events: events
|
866
|
-
}.compact
|
867
|
-
- fname = AppMap::Util.scenario_filename(example_name)
|
868
|
-
-
|
869
|
-
- File.write(File.join(APPMAP_OUTPUT_DIR, fname), JSON.generate(appmap))
|
870
|
-
- end
|
871
|
-
+ fname = AppMap::Util.scenario_filename(name)
|
872
|
-
|
873
|
-
- def print_inventory
|
874
|
-
- class_map = AppMap.class_map(@event_methods)
|
875
|
-
- save 'Inventory', class_map, labels: %w[inventory]
|
876
|
-
+ AppMap::Util.write_appmap(File.join(APPMAP_OUTPUT_DIR, fname), JSON.generate(appmap))
|
877
|
-
end
|
878
|
-
|
879
|
-
def enabled?
|
880
|
-
@@ -201,9 +205,6 @@ module AppMap
|
881
|
-
|
882
|
-
def run
|
883
|
-
init
|
884
|
-
- at_exit do
|
885
|
-
- print_inventory
|
886
|
-
- end
|
887
|
-
end
|
888
|
-
end
|
889
|
-
end
|
890
|
-
@@ -225,7 +226,7 @@ if AppMap::RSpec.enabled?
|
891
|
-
begin
|
892
|
-
instance_exec(&fn)
|
893
|
-
ensure
|
894
|
-
- AppMap::RSpec.end_spec example
|
895
|
-
+ AppMap::RSpec.end_spec example, exception: $!
|
896
|
-
end
|
897
|
-
end
|
898
|
-
end
|
899
|
-
diff --git a/lib/appmap/util.rb b/lib/appmap/util.rb
|
900
|
-
index 133d54a..cd08ded 100644
|
901
|
-
--- a/lib/appmap/util.rb
|
902
|
-
+++ b/lib/appmap/util.rb
|
903
|
-
@@ -71,6 +71,22 @@ module AppMap
|
904
|
-
|
905
|
-
event
|
906
|
-
end
|
907
|
-
+
|
908
|
-
+ # Atomically writes AppMap data to +filename+.
|
909
|
-
+ def write_appmap(filename, appmap)
|
910
|
-
+ require 'fileutils'
|
911
|
-
+ require 'tmpdir'
|
912
|
-
+
|
913
|
-
+ # This is what Ruby Tempfile does; but we don't want the file to be unlinked.
|
914
|
-
+ mode = File::RDWR | File::CREAT | File::EXCL
|
915
|
-
+ ::Dir::Tmpname.create([ 'appmap_', '.json' ]) do |tmpname|
|
916
|
-
+ tempfile = File.open(tmpname, mode)
|
917
|
-
+ tempfile.write(appmap)
|
918
|
-
+ tempfile.close
|
919
|
-
+ # Atomically move the tempfile into place.
|
920
|
-
+ FileUtils.mv tempfile.path, filename
|
921
|
-
+ end
|
922
|
-
+ end
|
923
|
-
end
|
924
|
-
end
|
925
|
-
end
|
926
|
-
diff --git a/lib/appmap/version.rb b/lib/appmap/version.rb
|
927
|
-
index ff7d53b..8471d09 100644
|
928
|
-
--- a/lib/appmap/version.rb
|
929
|
-
+++ b/lib/appmap/version.rb
|
930
|
-
@@ -3,7 +3,7 @@
|
931
|
-
module AppMap
|
932
|
-
URL = 'https://github.com/applandinc/appmap-ruby'
|
933
|
-
|
934
|
-
- VERSION = '0.43.0'
|
935
|
-
+ VERSION = '0.44.0'
|
936
|
-
|
937
|
-
APPMAP_FORMAT_VERSION = '1.4'
|
938
|
-
end
|
939
|
-
diff --git a/spec/abstract_controller_base_spec.rb b/spec/abstract_controller_base_spec.rb
|
940
|
-
index 26e58b1..2aed976 100644
|
941
|
-
--- a/spec/abstract_controller_base_spec.rb
|
942
|
-
+++ b/spec/abstract_controller_base_spec.rb
|
943
|
-
@@ -27,6 +27,8 @@ describe 'Rails' do
|
944
|
-
end
|
945
|
-
|
946
|
-
let(:appmap) { JSON.parse File.read File.join tmpdir, 'appmap/rspec', appmap_json_file }
|
947
|
-
+ let(:appmap_json_path) { File.join(tmpdir, 'appmap/rspec', appmap_json_file) }
|
948
|
-
+ let(:appmap) { JSON.parse File.read(appmap_json_path) }
|
949
|
-
let(:events) { appmap['events'] }
|
950
|
-
|
951
|
-
describe 'an API route' do
|
952
|
-
@@ -35,10 +37,6 @@ describe 'Rails' do
|
953
|
-
'Api_UsersController_POST_api_users_with_required_parameters_creates_a_user.appmap.json'
|
954
|
-
end
|
955
|
-
|
956
|
-
- it 'inventory file is printed' do
|
957
|
-
- expect(File).to exist(File.join(tmpdir, 'appmap/rspec/Inventory.appmap.json'))
|
958
|
-
- end
|
959
|
-
-
|
960
|
-
it 'http_server_request is recorded in the appmap' do
|
961
|
-
expect(events).to include(
|
962
|
-
hash_including(
|
963
|
-
diff --git a/spec/class_map_spec.rb b/spec/class_map_spec.rb
|
964
|
-
index 343c8b9..4f60708 100644
|
965
|
-
--- a/spec/class_map_spec.rb
|
966
|
-
+++ b/spec/class_map_spec.rb
|
967
|
-
@@ -4,18 +4,10 @@ require 'spec_helper'
|
968
|
-
|
969
|
-
describe 'AppMap::ClassMap' do
|
970
|
-
describe '.build_from_methods' do
|
971
|
-
- it 'includes source code if available' do
|
972
|
-
- map = AppMap.class_map([scoped_method(method(:test_method))])
|
973
|
-
+ it 'includes method comment' do
|
974
|
-
+ map = AppMap.class_map([scoped_method((method :test_method))])
|
975
|
-
function = dig_map(map, 5)[0]
|
976
|
-
- expect(function[:source]).to include 'test method body'
|
977
|
-
- expect(function[:comment]).to include 'test method comment'
|
978
|
-
- end
|
979
|
-
-
|
980
|
-
- it 'can omit source code even if available' do
|
981
|
-
- map = AppMap.class_map([scoped_method((method :test_method))], include_source: false)
|
982
|
-
- function = dig_map(map, 5)[0]
|
983
|
-
- expect(function).to_not include(:source)
|
984
|
-
- expect(function).to_not include(:comment)
|
985
|
-
+ expect(function).to include(:comment)
|
986
|
-
end
|
987
|
-
|
988
|
-
# test method comment
|
989
|
-
diff --git a/spec/config_spec.rb b/spec/config_spec.rb
|
990
|
-
index 5eeaac6..6657f1a 100644
|
991
|
-
--- a/spec/config_spec.rb
|
992
|
-
+++ b/spec/config_spec.rb
|
993
|
-
@@ -17,10 +17,40 @@ describe AppMap::Config, docker: false do
|
994
|
-
path: 'path-2',
|
995
|
-
exclude: [ 'exclude-1' ]
|
996
|
-
}
|
997
|
-
+ ],
|
998
|
-
+ functions: [
|
999
|
-
+ {
|
1000
|
-
+ package: 'pkg',
|
1001
|
-
+ class: 'cls',
|
1002
|
-
+ function: 'fn',
|
1003
|
-
+ label: 'lbl'
|
1004
|
-
+ }
|
1005
|
-
]
|
1006
|
-
}.deep_stringify_keys!
|
1007
|
-
config = AppMap::Config.load(config_data)
|
1008
|
-
|
1009
|
-
- expect(config.to_h.deep_stringify_keys!).to eq(config_data)
|
1010
|
-
+ config_expectation = {
|
1011
|
-
+ exclude: [],
|
1012
|
-
+ name: 'test',
|
1013
|
-
+ packages: [
|
1014
|
-
+ {
|
1015
|
-
+ path: 'path-1'
|
1016
|
-
+ },
|
1017
|
-
+ {
|
1018
|
-
+ path: 'path-2',
|
1019
|
-
+ exclude: [ 'exclude-1' ]
|
1020
|
-
+ }
|
1021
|
-
+ ],
|
1022
|
-
+ functions: [
|
1023
|
-
+ {
|
1024
|
-
+ package: 'pkg',
|
1025
|
-
+ class: 'cls',
|
1026
|
-
+ functions: [ :fn ],
|
1027
|
-
+ labels: ['lbl']
|
1028
|
-
+ }
|
1029
|
-
+ ]
|
1030
|
-
+ }.deep_stringify_keys!
|
1031
|
-
+
|
1032
|
-
+ expect(config.to_h.deep_stringify_keys!).to eq(config_expectation)
|
1033
|
-
end
|
1034
|
-
end
|
1035
|
-
diff --git a/spec/fixtures/hook/custom_instance_method.rb b/spec/fixtures/hook/custom_instance_method.rb
|
1036
|
-
new file mode 100644
|
1037
|
-
index 0000000..285db81
|
1038
|
-
--- /dev/null
|
1039
|
-
+++ b/spec/fixtures/hook/custom_instance_method.rb
|
1040
|
-
@@ -0,0 +1,11 @@
|
1041
|
-
+# frozen_string_literal: true
|
1042
|
-
+
|
1043
|
-
+class CustomInstanceMethod
|
1044
|
-
+ def to_s
|
1045
|
-
+ 'CustomInstance Method fixture'
|
1046
|
-
+ end
|
1047
|
-
+
|
1048
|
-
+ def say_default
|
1049
|
-
+ 'default'
|
1050
|
-
+ end
|
1051
|
-
+end
|
1052
|
-
diff --git a/spec/fixtures/hook/method_named_call.rb b/spec/fixtures/hook/method_named_call.rb
|
1053
|
-
new file mode 100644
|
1054
|
-
index 0000000..69a2cc5
|
1055
|
-
--- /dev/null
|
1056
|
-
+++ b/spec/fixtures/hook/method_named_call.rb
|
1057
|
-
@@ -0,0 +1,11 @@
|
1058
|
-
+# frozen_string_literal: true
|
1059
|
-
+
|
1060
|
-
+class MethodNamedCall
|
1061
|
-
+ def to_s
|
1062
|
-
+ 'MethodNamedCall'
|
1063
|
-
+ end
|
1064
|
-
+
|
1065
|
-
+ def call(a, b, c, d, e)
|
1066
|
-
+ [ a, b, c, d, e ].join(' ')
|
1067
|
-
+ end
|
1068
|
-
+end
|
1069
|
-
diff --git a/spec/hook_spec.rb b/spec/hook_spec.rb
|
1070
|
-
index e4cbc99..8afb9f1 100644
|
1071
|
-
--- a/spec/hook_spec.rb
|
1072
|
-
+++ b/spec/hook_spec.rb
|
1073
|
-
@@ -64,13 +64,144 @@ describe 'AppMap class Hooking', docker: false do
|
1074
|
-
it 'excludes named classes and methods' do
|
1075
|
-
load 'spec/fixtures/hook/exclude.rb'
|
1076
|
-
package = AppMap::Config::Package.build_from_path('spec/fixtures/hook/exclude.rb')
|
1077
|
-
- config = AppMap::Config.new('hook_spec', [ package ], %w[ExcludeTest])
|
1078
|
-
+ config = AppMap::Config.new('hook_spec', [ package ], exclude: %w[ExcludeTest])
|
1079
|
-
AppMap.configuration = config
|
1080
|
-
|
1081
|
-
expect(config.never_hook?(ExcludeTest.new.method(:instance_method))).to be_truthy
|
1082
|
-
expect(config.never_hook?(ExcludeTest.method(:cls_method))).to be_truthy
|
1083
|
-
end
|
1084
|
-
|
1085
|
-
+ it "handles an instance method named 'call' without issues" do
|
1086
|
-
+ events_yaml = <<~YAML
|
1087
|
-
+ ---
|
1088
|
-
+ - :id: 1
|
1089
|
-
+ :event: :call
|
1090
|
-
+ :defined_class: MethodNamedCall
|
1091
|
-
+ :method_id: call
|
1092
|
-
+ :path: spec/fixtures/hook/method_named_call.rb
|
1093
|
-
+ :lineno: 8
|
1094
|
-
+ :static: false
|
1095
|
-
+ :parameters:
|
1096
|
-
+ - :name: :a
|
1097
|
-
+ :class: Integer
|
1098
|
-
+ :value: '1'
|
1099
|
-
+ :kind: :req
|
1100
|
-
+ - :name: :b
|
1101
|
-
+ :class: Integer
|
1102
|
-
+ :value: '2'
|
1103
|
-
+ :kind: :req
|
1104
|
-
+ - :name: :c
|
1105
|
-
+ :class: Integer
|
1106
|
-
+ :value: '3'
|
1107
|
-
+ :kind: :req
|
1108
|
-
+ - :name: :d
|
1109
|
-
+ :class: Integer
|
1110
|
-
+ :value: '4'
|
1111
|
-
+ :kind: :req
|
1112
|
-
+ - :name: :e
|
1113
|
-
+ :class: Integer
|
1114
|
-
+ :value: '5'
|
1115
|
-
+ :kind: :req
|
1116
|
-
+ :receiver:
|
1117
|
-
+ :class: MethodNamedCall
|
1118
|
-
+ :value: MethodNamedCall
|
1119
|
-
+ - :id: 2
|
1120
|
-
+ :event: :return
|
1121
|
-
+ :parent_id: 1
|
1122
|
-
+ :return_value:
|
1123
|
-
+ :class: String
|
1124
|
-
+ :value: 1 2 3 4 5
|
1125
|
-
+ YAML
|
1126
|
-
+
|
1127
|
-
+ _, tracer = test_hook_behavior 'spec/fixtures/hook/method_named_call.rb', events_yaml do
|
1128
|
-
+ expect(MethodNamedCall.new.call(1, 2, 3, 4, 5)).to eq('1 2 3 4 5')
|
1129
|
-
+ end
|
1130
|
-
+ class_map = AppMap.class_map(tracer.event_methods)
|
1131
|
-
+ expect(Diffy::Diff.new(<<~CLASSMAP, YAML.dump(class_map)).to_s).to eq('')
|
1132
|
-
+ ---
|
1133
|
-
+ - :name: spec/fixtures/hook/method_named_call.rb
|
1134
|
-
+ :type: package
|
1135
|
-
+ :children:
|
1136
|
-
+ - :name: MethodNamedCall
|
1137
|
-
+ :type: class
|
1138
|
-
+ :children:
|
1139
|
-
+ - :name: call
|
1140
|
-
+ :type: function
|
1141
|
-
+ :location: spec/fixtures/hook/method_named_call.rb:8
|
1142
|
-
+ :static: false
|
1143
|
-
+ CLASSMAP
|
1144
|
-
+ end
|
1145
|
-
+
|
1146
|
-
+ it 'can custom hook and label a function' do
|
1147
|
-
+ events_yaml = <<~YAML
|
1148
|
-
+ ---
|
1149
|
-
+ - :id: 1
|
1150
|
-
+ :event: :call
|
1151
|
-
+ :defined_class: CustomInstanceMethod
|
1152
|
-
+ :method_id: say_default
|
1153
|
-
+ :path: spec/fixtures/hook/custom_instance_method.rb
|
1154
|
-
+ :lineno: 8
|
1155
|
-
+ :static: false
|
1156
|
-
+ :parameters: []
|
1157
|
-
+ :receiver:
|
1158
|
-
+ :class: CustomInstanceMethod
|
1159
|
-
+ :value: CustomInstance Method fixture
|
1160
|
-
+ - :id: 2
|
1161
|
-
+ :event: :return
|
1162
|
-
+ :parent_id: 1
|
1163
|
-
+ :return_value:
|
1164
|
-
+ :class: String
|
1165
|
-
+ :value: default
|
1166
|
-
+ YAML
|
1167
|
-
+
|
1168
|
-
+ config = AppMap::Config.load({
|
1169
|
-
+ functions: [
|
1170
|
-
+ {
|
1171
|
-
+ package: 'hook_spec',
|
1172
|
-
+ class: 'CustomInstanceMethod',
|
1173
|
-
+ functions: [ :say_default ],
|
1174
|
-
+ labels: ['cowsay']
|
1175
|
-
+ }
|
1176
|
-
+ ]
|
1177
|
-
+ }.deep_stringify_keys)
|
1178
|
-
+
|
1179
|
-
+ load 'spec/fixtures/hook/custom_instance_method.rb'
|
1180
|
-
+ hook_cls = CustomInstanceMethod
|
1181
|
-
+ method = hook_cls.instance_method(:say_default)
|
1182
|
-
+
|
1183
|
-
+ require 'appmap/hook/method'
|
1184
|
-
+ hook_method = AppMap::Hook::Method.new(config.package_for_method(method), hook_cls, method)
|
1185
|
-
+ hook_method.activate
|
1186
|
-
+
|
1187
|
-
+ tracer = AppMap.tracing.trace
|
1188
|
-
+ AppMap::Event.reset_id_counter
|
1189
|
-
+ begin
|
1190
|
-
+ expect(CustomInstanceMethod.new.say_default).to eq('default')
|
1191
|
-
+ ensure
|
1192
|
-
+ AppMap.tracing.delete(tracer)
|
1193
|
-
+ end
|
1194
|
-
+
|
1195
|
-
+ events = collect_events(tracer).to_yaml
|
1196
|
-
+
|
1197
|
-
+ expect(Diffy::Diff.new(events_yaml, events).to_s).to eq('')
|
1198
|
-
+ class_map = AppMap.class_map(tracer.event_methods)
|
1199
|
-
+ expect(Diffy::Diff.new(<<~CLASSMAP, YAML.dump(class_map)).to_s).to eq('')
|
1200
|
-
+ ---
|
1201
|
-
+ - :name: hook_spec
|
1202
|
-
+ :type: package
|
1203
|
-
+ :children:
|
1204
|
-
+ - :name: CustomInstanceMethod
|
1205
|
-
+ :type: class
|
1206
|
-
+ :children:
|
1207
|
-
+ - :name: say_default
|
1208
|
-
+ :type: function
|
1209
|
-
+ :location: spec/fixtures/hook/custom_instance_method.rb:8
|
1210
|
-
+ :static: false
|
1211
|
-
+ :labels:
|
1212
|
-
+ - cowsay
|
1213
|
-
+ CLASSMAP
|
1214
|
-
+ end
|
1215
|
-
+
|
1216
|
-
it 'parses labels from comments' do
|
1217
|
-
_, tracer = invoke_test_file 'spec/fixtures/hook/labels.rb' do
|
1218
|
-
ClassWithLabel.new.fn_with_label
|
1219
|
-
@@ -91,9 +222,6 @@ describe 'AppMap class Hooking', docker: false do
|
1220
|
-
:labels:
|
1221
|
-
- has-fn-label
|
1222
|
-
:comment: "# @label has-fn-label\\n"
|
1223
|
-
- :source: |2
|
1224
|
-
- def fn_with_label
|
1225
|
-
- end
|
1226
|
-
YAML
|
1227
|
-
end
|
1228
|
-
|
1229
|
-
@@ -148,10 +276,6 @@ describe 'AppMap class Hooking', docker: false do
|
1230
|
-
:type: function
|
1231
|
-
:location: spec/fixtures/hook/instance_method.rb:8
|
1232
|
-
:static: false
|
1233
|
-
- :source: |2
|
1234
|
-
- def say_default
|
1235
|
-
- 'default'
|
1236
|
-
- end
|
1237
|
-
YAML
|
1238
|
-
end
|
1239
|
-
|
1240
|
-
@@ -746,6 +870,7 @@ describe 'AppMap class Hooking', docker: false do
|
1241
|
-
end
|
1242
|
-
secure_compare_event = YAML.load(events).find { |evt| evt[:defined_class] == 'ActiveSupport::SecurityUtils' }
|
1243
|
-
secure_compare_event.delete(:lineno)
|
1244
|
-
+ secure_compare_event.delete(:path)
|
1245
|
-
|
1246
|
-
expect(Diffy::Diff.new(<<~YAML, secure_compare_event.to_yaml).to_s).to eq('')
|
1247
|
-
---
|
1248
|
-
@@ -753,7 +878,6 @@ describe 'AppMap class Hooking', docker: false do
|
1249
|
-
:event: :call
|
1250
|
-
:defined_class: ActiveSupport::SecurityUtils
|
1251
|
-
:method_id: secure_compare
|
1252
|
-
- :path: lib/active_support/security_utils.rb
|
1253
|
-
:static: true
|
1254
|
-
:parameters:
|
1255
|
-
- :name: :a
|
1256
|
-
@@ -837,7 +961,7 @@ describe 'AppMap class Hooking', docker: false do
|
1257
|
-
entry = cm[1][:children][0][:children][0][:children][0]
|
1258
|
-
# Sanity check, make sure we got the right one
|
1259
|
-
expect(entry[:name]).to eq('secure_compare')
|
1260
|
-
- expect(entry[:labels]).to eq(%w[provider.secure_compare])
|
1261
|
-
+ expect(entry[:labels]).to eq(%w[crypto.secure_compare])
|
1262
|
-
end
|
1263
|
-
end
|
1264
|
-
|
1265
|
-
diff --git a/test/cli_test.rb b/test/cli_test.rb
|
1266
|
-
deleted file mode 100755
|
1267
|
-
index 2ea654c..0000000
|
1268
|
-
--- a/test/cli_test.rb
|
1269
|
-
+++ /dev/null
|
1270
|
-
@@ -1,116 +0,0 @@
|
1271
|
-
-#!/usr/bin/env ruby
|
1272
|
-
-# frozen_string_literal: true
|
1273
|
-
-
|
1274
|
-
-require 'test_helper'
|
1275
|
-
-require 'English'
|
1276
|
-
-
|
1277
|
-
-class CLITest < Minitest::Test
|
1278
|
-
- OUTPUT_FILENAME = File.expand_path('../tmp/appmap.json', __dir__)
|
1279
|
-
- STATS_OUTPUT_FILENAME = File.expand_path('../tmp/stats.txt', __dir__)
|
1280
|
-
-
|
1281
|
-
- def setup
|
1282
|
-
- FileUtils.rm_f OUTPUT_FILENAME
|
1283
|
-
- FileUtils.rm_f STATS_OUTPUT_FILENAME
|
1284
|
-
- end
|
1285
|
-
-
|
1286
|
-
- def test_record
|
1287
|
-
- output = Dir.chdir 'test/fixtures/cli_record_test' do
|
1288
|
-
- `#{File.expand_path '../exe/appmap', __dir__} record -o #{OUTPUT_FILENAME} ./lib/cli_record_test/main.rb`.strip
|
1289
|
-
- end
|
1290
|
-
-
|
1291
|
-
- assert_equal 0, $CHILD_STATUS.exitstatus
|
1292
|
-
- assert File.file?(OUTPUT_FILENAME), "#{OUTPUT_FILENAME} does not exist"
|
1293
|
-
- assert_equal 'Hello', output
|
1294
|
-
- output = JSON.parse(File.read(OUTPUT_FILENAME))
|
1295
|
-
- assert output['classMap'], 'Output should contain classMap'
|
1296
|
-
- assert output['events'], 'Output should contain events'
|
1297
|
-
- end
|
1298
|
-
-
|
1299
|
-
- def test_stats_to_file
|
1300
|
-
- Dir.chdir 'test/fixtures/cli_record_test' do
|
1301
|
-
- `#{File.expand_path '../exe/appmap', __dir__} record -o #{OUTPUT_FILENAME} ./lib/cli_record_test/main.rb`.strip
|
1302
|
-
- end
|
1303
|
-
- assert_equal 0, $CHILD_STATUS.exitstatus
|
1304
|
-
-
|
1305
|
-
- output = Dir.chdir 'test/fixtures/cli_record_test' do
|
1306
|
-
- `#{File.expand_path '../exe/appmap', __dir__} stats -o #{STATS_OUTPUT_FILENAME} #{OUTPUT_FILENAME}`.strip
|
1307
|
-
- end
|
1308
|
-
- assert_equal 0, $CHILD_STATUS.exitstatus
|
1309
|
-
- assert_equal '', output
|
1310
|
-
- assert File.file?(OUTPUT_FILENAME), "#{OUTPUT_FILENAME} does not exist"
|
1311
|
-
- end
|
1312
|
-
-
|
1313
|
-
-
|
1314
|
-
- def test_stats_text
|
1315
|
-
- Dir.chdir 'test/fixtures/cli_record_test' do
|
1316
|
-
- `#{File.expand_path '../exe/appmap', __dir__} record -o #{OUTPUT_FILENAME} ./lib/cli_record_test/main.rb`.strip
|
1317
|
-
- end
|
1318
|
-
- assert_equal 0, $CHILD_STATUS.exitstatus
|
1319
|
-
-
|
1320
|
-
- output = Dir.chdir 'test/fixtures/cli_record_test' do
|
1321
|
-
- `#{File.expand_path '../exe/appmap', __dir__} stats -o - #{OUTPUT_FILENAME}`.strip
|
1322
|
-
- end
|
1323
|
-
-
|
1324
|
-
- assert_equal 0, $CHILD_STATUS.exitstatus
|
1325
|
-
- assert_equal <<~OUTPUT.strip, output.strip
|
1326
|
-
- Class frequency:
|
1327
|
-
- ----------------
|
1328
|
-
- 1 Main
|
1329
|
-
-
|
1330
|
-
- Method frequency:
|
1331
|
-
- ----------------
|
1332
|
-
- 1 Main.say_hello
|
1333
|
-
- OUTPUT
|
1334
|
-
- end
|
1335
|
-
-
|
1336
|
-
- def test_stats_json
|
1337
|
-
- Dir.chdir 'test/fixtures/cli_record_test' do
|
1338
|
-
- `#{File.expand_path '../exe/appmap', __dir__} record -o #{OUTPUT_FILENAME} ./lib/cli_record_test/main.rb`.strip
|
1339
|
-
- end
|
1340
|
-
- assert_equal 0, $CHILD_STATUS.exitstatus
|
1341
|
-
-
|
1342
|
-
- output = Dir.chdir 'test/fixtures/cli_record_test' do
|
1343
|
-
- `#{File.expand_path '../exe/appmap', __dir__} stats -f json -o - #{OUTPUT_FILENAME}`.strip
|
1344
|
-
- end
|
1345
|
-
-
|
1346
|
-
- assert_equal 0, $CHILD_STATUS.exitstatus
|
1347
|
-
- assert_equal <<~OUTPUT.strip, output.strip
|
1348
|
-
- {
|
1349
|
-
- "class_frequency": [
|
1350
|
-
- {
|
1351
|
-
- "name": "Main",
|
1352
|
-
- "count": 1
|
1353
|
-
- }
|
1354
|
-
- ],
|
1355
|
-
- "method_frequency": [
|
1356
|
-
- {
|
1357
|
-
- "name": "Main.say_hello",
|
1358
|
-
- "count": 1
|
1359
|
-
- }
|
1360
|
-
- ]
|
1361
|
-
- }
|
1362
|
-
- OUTPUT
|
1363
|
-
- end
|
1364
|
-
-
|
1365
|
-
- def test_record_to_default_location
|
1366
|
-
- Dir.chdir 'test/fixtures/cli_record_test' do
|
1367
|
-
- system({ 'APPMAP_FILE' => OUTPUT_FILENAME }, "#{File.expand_path '../exe/appmap', __dir__} record ./lib/cli_record_test/main.rb")
|
1368
|
-
- end
|
1369
|
-
-
|
1370
|
-
- assert_equal 0, $CHILD_STATUS.exitstatus
|
1371
|
-
- assert File.file?(OUTPUT_FILENAME), 'appmap.json does not exist'
|
1372
|
-
- end
|
1373
|
-
-
|
1374
|
-
- def test_record_to_stdout
|
1375
|
-
- output = Dir.chdir 'test/fixtures/cli_record_test' do
|
1376
|
-
- `#{File.expand_path '../exe/appmap', __dir__} record -o - ./lib/cli_record_test/main.rb`
|
1377
|
-
- end
|
1378
|
-
-
|
1379
|
-
- assert_equal 0, $CHILD_STATUS.exitstatus
|
1380
|
-
- # Event path
|
1381
|
-
- assert_includes output, %("path":"lib/cli_record_test/main.rb")
|
1382
|
-
- # Function location
|
1383
|
-
- assert_includes output, %("location":"lib/cli_record_test/main.rb:3")
|
1384
|
-
- assert !File.file?(OUTPUT_FILENAME), "#{OUTPUT_FILENAME} should not exist"
|
1385
|
-
- end
|
1386
|
-
-end
|
1387
|
-
diff --git a/test/expectations/openssl_test_key_sign1.json b/test/expectations/openssl_test_key_sign1.json
|
1388
|
-
index 6489b47..0613a69 100644
|
1389
|
-
--- a/test/expectations/openssl_test_key_sign1.json
|
1390
|
-
+++ b/test/expectations/openssl_test_key_sign1.json
|
1391
|
-
@@ -11,8 +11,7 @@
|
1392
|
-
"name": "sign",
|
1393
|
-
"type": "function",
|
1394
|
-
"location": "lib/openssl_key_sign.rb:10",
|
1395
|
-
- "static": true,
|
1396
|
-
- "source": " def Example.sign\n key = OpenSSL::PKey::RSA.new 2048\n\n document = 'the document'\n\n digest = OpenSSL::Digest::SHA256.new\n key.sign digest, document\n end\n"
|
1397
|
-
+ "static": true
|
1398
|
-
}
|
1399
|
-
]
|
1400
|
-
}
|
1401
|
-
@@ -40,8 +39,7 @@
|
1402
|
-
"location": "OpenSSL::PKey::PKey#sign",
|
1403
|
-
"static": false,
|
1404
|
-
"labels": [
|
1405
|
-
- "security",
|
1406
|
-
- "crypto"
|
1407
|
-
+ "crypto.pkey"
|
1408
|
-
]
|
1409
|
-
}
|
1410
|
-
]
|
1411
|
-
diff --git a/test/gem_test.rb b/test/gem_test.rb
|
1412
|
-
index 6cae910..1342c2a 100644
|
1413
|
-
--- a/test/gem_test.rb
|
1414
|
-
+++ b/test/gem_test.rb
|
1415
|
-
@@ -26,7 +26,7 @@ class MinitestTest < Minitest::Test
|
1416
|
-
assert_equal 2, events.size
|
1417
|
-
assert_equal 'call', events.first['event']
|
1418
|
-
assert_equal 'default_parser', events.first['method_id']
|
1419
|
-
- assert_equal "#{Gem.loaded_specs['parser'].gem_dir}/lib/parser/base.rb", events.first['path']
|
1420
|
-
+ assert_match /\lib\/parser\/base\.rb$/, events.first['path']
|
1421
|
-
assert_equal 'return', events.second['event']
|
1422
|
-
assert_equal 1, events.second['parent_id']
|
1423
|
-
end
|
1424
|
-
diff --git a/test/rspec_test.rb b/test/rspec_test.rb
|
1425
|
-
index 72ed029..b30618b 100644
|
1426
|
-
--- a/test/rspec_test.rb
|
1427
|
-
+++ b/test/rspec_test.rb
|
1428
|
-
@@ -18,19 +18,6 @@ class RSpecTest < Minitest::Test
|
1429
|
-
end
|
1430
|
-
end
|
1431
|
-
|
1432
|
-
- def test_inventory
|
1433
|
-
- perform_test 'plain_hello_spec' do
|
1434
|
-
- appmap_file = 'tmp/appmap/rspec/Inventory.appmap.json'
|
1435
|
-
-
|
1436
|
-
- assert File.file?(appmap_file), 'appmap output file does not exist'
|
1437
|
-
- appmap = JSON.parse(File.read(appmap_file))
|
1438
|
-
- assert_equal AppMap::APPMAP_FORMAT_VERSION, appmap['version']
|
1439
|
-
- assert_includes appmap.keys, 'metadata'
|
1440
|
-
- metadata = appmap['metadata']
|
1441
|
-
- assert_equal 'Inventory', metadata['name']
|
1442
|
-
- end
|
1443
|
-
- end
|
1444
|
-
-
|
1445
|
-
def test_record_decorated_rspec
|
1446
|
-
perform_test 'decorated_hello_spec' do
|
1447
|
-
appmap_file = 'tmp/appmap/rspec/Hello_says_hello.appmap.json'
|