appmap 0.43.0 → 0.44.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.
@@ -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,10 +37,6 @@ 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(
@@ -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 source code if available' do
8
- map = AppMap.class_map([scoped_method(method(:test_method))])
7
+ it 'includes method comment' do
8
+ map = AppMap.class_map([scoped_method((method :test_method))])
9
9
  function = dig_map(map, 5)[0]
10
- expect(function[:source]).to include 'test method body'
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
data/spec/config_spec.rb CHANGED
@@ -17,10 +17,40 @@ 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
- expect(config.to_h.deep_stringify_keys!).to eq(config_data)
32
+ config_expectation = {
33
+ exclude: [],
34
+ name: 'test',
35
+ packages: [
36
+ {
37
+ path: 'path-1'
38
+ },
39
+ {
40
+ path: 'path-2',
41
+ exclude: [ 'exclude-1' ]
42
+ }
43
+ ],
44
+ functions: [
45
+ {
46
+ package: 'pkg',
47
+ class: 'cls',
48
+ functions: [ :fn ],
49
+ labels: ['lbl']
50
+ }
51
+ ]
52
+ }.deep_stringify_keys!
53
+
54
+ expect(config.to_h.deep_stringify_keys!).to eq(config_expectation)
25
55
  end
26
56
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CustomInstanceMethod
4
+ def to_s
5
+ 'CustomInstance Method fixture'
6
+ end
7
+
8
+ def say_default
9
+ 'default'
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class MethodNamedCall
4
+ def to_s
5
+ 'MethodNamedCall'
6
+ end
7
+
8
+ def call(a, b, c, d, e)
9
+ [ a, b, c, d, e ].join(' ')
10
+ end
11
+ end
data/spec/hook_spec.rb CHANGED
@@ -64,13 +64,144 @@ describe 'AppMap class Hooking', docker: false do
64
64
  it 'excludes named classes and methods' do
65
65
  load 'spec/fixtures/hook/exclude.rb'
66
66
  package = AppMap::Config::Package.build_from_path('spec/fixtures/hook/exclude.rb')
67
- config = AppMap::Config.new('hook_spec', [ package ], %w[ExcludeTest])
67
+ config = AppMap::Config.new('hook_spec', [ package ], exclude: %w[ExcludeTest])
68
68
  AppMap.configuration = config
69
69
 
70
70
  expect(config.never_hook?(ExcludeTest.new.method(:instance_method))).to be_truthy
71
71
  expect(config.never_hook?(ExcludeTest.method(:cls_method))).to be_truthy
72
72
  end
73
73
 
74
+ it "handles an instance method named 'call' without issues" do
75
+ events_yaml = <<~YAML
76
+ ---
77
+ - :id: 1
78
+ :event: :call
79
+ :defined_class: MethodNamedCall
80
+ :method_id: call
81
+ :path: spec/fixtures/hook/method_named_call.rb
82
+ :lineno: 8
83
+ :static: false
84
+ :parameters:
85
+ - :name: :a
86
+ :class: Integer
87
+ :value: '1'
88
+ :kind: :req
89
+ - :name: :b
90
+ :class: Integer
91
+ :value: '2'
92
+ :kind: :req
93
+ - :name: :c
94
+ :class: Integer
95
+ :value: '3'
96
+ :kind: :req
97
+ - :name: :d
98
+ :class: Integer
99
+ :value: '4'
100
+ :kind: :req
101
+ - :name: :e
102
+ :class: Integer
103
+ :value: '5'
104
+ :kind: :req
105
+ :receiver:
106
+ :class: MethodNamedCall
107
+ :value: MethodNamedCall
108
+ - :id: 2
109
+ :event: :return
110
+ :parent_id: 1
111
+ :return_value:
112
+ :class: String
113
+ :value: 1 2 3 4 5
114
+ YAML
115
+
116
+ _, tracer = test_hook_behavior 'spec/fixtures/hook/method_named_call.rb', events_yaml do
117
+ expect(MethodNamedCall.new.call(1, 2, 3, 4, 5)).to eq('1 2 3 4 5')
118
+ end
119
+ class_map = AppMap.class_map(tracer.event_methods)
120
+ expect(Diffy::Diff.new(<<~CLASSMAP, YAML.dump(class_map)).to_s).to eq('')
121
+ ---
122
+ - :name: spec/fixtures/hook/method_named_call.rb
123
+ :type: package
124
+ :children:
125
+ - :name: MethodNamedCall
126
+ :type: class
127
+ :children:
128
+ - :name: call
129
+ :type: function
130
+ :location: spec/fixtures/hook/method_named_call.rb:8
131
+ :static: false
132
+ CLASSMAP
133
+ end
134
+
135
+ it 'can custom hook and label a function' do
136
+ events_yaml = <<~YAML
137
+ ---
138
+ - :id: 1
139
+ :event: :call
140
+ :defined_class: CustomInstanceMethod
141
+ :method_id: say_default
142
+ :path: spec/fixtures/hook/custom_instance_method.rb
143
+ :lineno: 8
144
+ :static: false
145
+ :parameters: []
146
+ :receiver:
147
+ :class: CustomInstanceMethod
148
+ :value: CustomInstance Method fixture
149
+ - :id: 2
150
+ :event: :return
151
+ :parent_id: 1
152
+ :return_value:
153
+ :class: String
154
+ :value: default
155
+ YAML
156
+
157
+ config = AppMap::Config.load({
158
+ functions: [
159
+ {
160
+ package: 'hook_spec',
161
+ class: 'CustomInstanceMethod',
162
+ functions: [ :say_default ],
163
+ labels: ['cowsay']
164
+ }
165
+ ]
166
+ }.deep_stringify_keys)
167
+
168
+ load 'spec/fixtures/hook/custom_instance_method.rb'
169
+ hook_cls = CustomInstanceMethod
170
+ method = hook_cls.instance_method(:say_default)
171
+
172
+ require 'appmap/hook/method'
173
+ hook_method = AppMap::Hook::Method.new(config.package_for_method(method), hook_cls, method)
174
+ hook_method.activate
175
+
176
+ tracer = AppMap.tracing.trace
177
+ AppMap::Event.reset_id_counter
178
+ begin
179
+ expect(CustomInstanceMethod.new.say_default).to eq('default')
180
+ ensure
181
+ AppMap.tracing.delete(tracer)
182
+ end
183
+
184
+ events = collect_events(tracer).to_yaml
185
+
186
+ expect(Diffy::Diff.new(events_yaml, events).to_s).to eq('')
187
+ class_map = AppMap.class_map(tracer.event_methods)
188
+ expect(Diffy::Diff.new(<<~CLASSMAP, YAML.dump(class_map)).to_s).to eq('')
189
+ ---
190
+ - :name: hook_spec
191
+ :type: package
192
+ :children:
193
+ - :name: CustomInstanceMethod
194
+ :type: class
195
+ :children:
196
+ - :name: say_default
197
+ :type: function
198
+ :location: spec/fixtures/hook/custom_instance_method.rb:8
199
+ :static: false
200
+ :labels:
201
+ - cowsay
202
+ CLASSMAP
203
+ end
204
+
74
205
  it 'parses labels from comments' do
75
206
  _, tracer = invoke_test_file 'spec/fixtures/hook/labels.rb' do
76
207
  ClassWithLabel.new.fn_with_label
@@ -91,9 +222,6 @@ describe 'AppMap class Hooking', docker: false do
91
222
  :labels:
92
223
  - has-fn-label
93
224
  :comment: "# @label has-fn-label\\n"
94
- :source: |2
95
- def fn_with_label
96
- end
97
225
  YAML
98
226
  end
99
227
 
@@ -148,10 +276,6 @@ describe 'AppMap class Hooking', docker: false do
148
276
  :type: function
149
277
  :location: spec/fixtures/hook/instance_method.rb:8
150
278
  :static: false
151
- :source: |2
152
- def say_default
153
- 'default'
154
- end
155
279
  YAML
156
280
  end
157
281
 
@@ -746,6 +870,7 @@ describe 'AppMap class Hooking', docker: false do
746
870
  end
747
871
  secure_compare_event = YAML.load(events).find { |evt| evt[:defined_class] == 'ActiveSupport::SecurityUtils' }
748
872
  secure_compare_event.delete(:lineno)
873
+ secure_compare_event.delete(:path)
749
874
 
750
875
  expect(Diffy::Diff.new(<<~YAML, secure_compare_event.to_yaml).to_s).to eq('')
751
876
  ---
@@ -753,7 +878,6 @@ describe 'AppMap class Hooking', docker: false do
753
878
  :event: :call
754
879
  :defined_class: ActiveSupport::SecurityUtils
755
880
  :method_id: secure_compare
756
- :path: lib/active_support/security_utils.rb
757
881
  :static: true
758
882
  :parameters:
759
883
  - :name: :a
@@ -837,7 +961,7 @@ describe 'AppMap class Hooking', docker: false do
837
961
  entry = cm[1][:children][0][:children][0][:children][0]
838
962
  # Sanity check, make sure we got the right one
839
963
  expect(entry[:name]).to eq('secure_compare')
840
- expect(entry[:labels]).to eq(%w[provider.secure_compare])
964
+ expect(entry[:labels]).to eq(%w[crypto.secure_compare])
841
965
  end
842
966
  end
843
967
 
@@ -11,8 +11,7 @@
11
11
  "name": "sign",
12
12
  "type": "function",
13
13
  "location": "lib/openssl_key_sign.rb:10",
14
- "static": true,
15
- "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"
14
+ "static": true
16
15
  }
17
16
  ]
18
17
  }
@@ -40,8 +39,7 @@
40
39
  "location": "OpenSSL::PKey::PKey#sign",
41
40
  "static": false,
42
41
  "labels": [
43
- "security",
44
- "crypto"
42
+ "crypto.pkey"
45
43
  ]
46
44
  }
47
45
  ]
data/test/gem_test.rb CHANGED
@@ -26,7 +26,7 @@ class MinitestTest < Minitest::Test
26
26
  assert_equal 2, events.size
27
27
  assert_equal 'call', events.first['event']
28
28
  assert_equal 'default_parser', events.first['method_id']
29
- assert_equal "#{Gem.loaded_specs['parser'].gem_dir}/lib/parser/base.rb", events.first['path']
29
+ assert_match /\lib\/parser\/base\.rb$/, events.first['path']
30
30
  assert_equal 'return', events.second['event']
31
31
  assert_equal 1, events.second['parent_id']
32
32
  end
data/test/rspec_test.rb CHANGED
@@ -18,19 +18,6 @@ class RSpecTest < Minitest::Test
18
18
  end
19
19
  end
20
20
 
21
- def test_inventory
22
- perform_test 'plain_hello_spec' do
23
- appmap_file = 'tmp/appmap/rspec/Inventory.appmap.json'
24
-
25
- assert File.file?(appmap_file), 'appmap output file does not exist'
26
- appmap = JSON.parse(File.read(appmap_file))
27
- assert_equal AppMap::APPMAP_FORMAT_VERSION, appmap['version']
28
- assert_includes appmap.keys, 'metadata'
29
- metadata = appmap['metadata']
30
- assert_equal 'Inventory', metadata['name']
31
- end
32
- end
33
-
34
21
  def test_record_decorated_rspec
35
22
  perform_test 'decorated_hello_spec' do
36
23
  appmap_file = 'tmp/appmap/rspec/Hello_says_hello.appmap.json'
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.43.0
4
+ version: 0.44.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Gilpin
8
8
  autorequire:
9
- bindir: exe
9
+ bindir: bin
10
10
  cert_chain: []
11
- date: 2021-03-31 00:00:00.000000000 Z
11
+ date: 2021-04-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -307,8 +307,7 @@ dependencies:
307
307
  description:
308
308
  email:
309
309
  - kgilpin@gmail.com
310
- executables:
311
- - appmap
310
+ executables: []
312
311
  extensions:
313
312
  - ext/appmap/extconf.rb
314
313
  extra_rdoc_files: []
@@ -334,7 +333,6 @@ files:
334
333
  - examples/mock_webapp/lib/mock_webapp/controller.rb
335
334
  - examples/mock_webapp/lib/mock_webapp/request.rb
336
335
  - examples/mock_webapp/lib/mock_webapp/user.rb
337
- - exe/appmap
338
336
  - ext/appmap/appmap.c
339
337
  - ext/appmap/extconf.rb
340
338
  - lib/appmap.rb
@@ -379,16 +377,19 @@ files:
379
377
  - lore/public/stylesheets/style.css
380
378
  - package-lock.json
381
379
  - package.json
380
+ - patch
382
381
  - spec/abstract_controller_base_spec.rb
383
382
  - spec/class_map_spec.rb
384
383
  - spec/config_spec.rb
385
384
  - spec/fixtures/hook/attr_accessor.rb
386
385
  - spec/fixtures/hook/compare.rb
387
386
  - spec/fixtures/hook/constructor.rb
387
+ - spec/fixtures/hook/custom_instance_method.rb
388
388
  - spec/fixtures/hook/exception_method.rb
389
389
  - spec/fixtures/hook/exclude.rb
390
390
  - spec/fixtures/hook/instance_method.rb
391
391
  - spec/fixtures/hook/labels.rb
392
+ - spec/fixtures/hook/method_named_call.rb
392
393
  - spec/fixtures/hook/singleton_method.rb
393
394
  - spec/fixtures/rack_users_app/.dockerignore
394
395
  - spec/fixtures/rack_users_app/.gitignore
@@ -550,7 +551,6 @@ files:
550
551
  - spec/remote_recording_spec.rb
551
552
  - spec/spec_helper.rb
552
553
  - spec/util_spec.rb
553
- - test/cli_test.rb
554
554
  - test/cucumber_test.rb
555
555
  - test/expectations/openssl_test_key_sign1.json
556
556
  - test/expectations/openssl_test_key_sign2.json
data/exe/appmap DELETED
@@ -1,154 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- require 'gli'
5
-
6
- ENV['APPMAP_INITIALIZE'] = 'false'
7
-
8
- require 'appmap'
9
- require 'appmap/version'
10
-
11
- # AppMap CLI.
12
- module AppMap
13
- class App
14
- extend GLI::App
15
-
16
- program_desc 'AppMap client'
17
-
18
- version AppMap::VERSION
19
-
20
- subcommand_option_handling :normal
21
- arguments :strict
22
- preserve_argv true
23
-
24
- class << self
25
- protected
26
-
27
- def default_appmap_file
28
- ENV['APPMAP_FILE'] || 'appmap.json'
29
- end
30
-
31
- def output_file_flag(c, default_value: nil)
32
- c.desc 'Name of the output file'
33
- c.long_desc <<~DESC
34
- Use a single dash '-' for stdout.
35
- DESC
36
- c.default_value default_value if default_value
37
- c.arg_name 'filename'
38
- c.flag %i[o output]
39
- end
40
- end
41
-
42
- desc 'AppMap configuration file name'
43
- default_value ENV['APPMAP_CONFIG'] || 'appmap.yml'
44
- arg_name 'filename'
45
- flag %i[c config]
46
-
47
- desc 'Record the execution of a program and generate an AppMap.'
48
- arg_name 'program'
49
- command :record do |c|
50
- output_file_flag(c, default_value: default_appmap_file)
51
-
52
- c.action do |_, _, args|
53
- # My subcommand name
54
- ARGV.shift
55
-
56
- # Consume the :output option, if provided
57
- if %w[-o --output].find { |arg_name| ARGV[0] == arg_name.to_s }
58
- ARGV.shift
59
- ARGV.shift
60
- end
61
-
62
- # Name of the program to execute. GLI will ensure that it's present.
63
- program = args.shift or help_now!("'program' argument is required")
64
-
65
- # Also pop the program name from ARGV, because the command will use raw ARGV
66
- # to load the extra arguments into this Ruby process.
67
- ARGV.shift
68
-
69
- require 'appmap/command/record'
70
- AppMap::Command::Record.new(@config, program).perform do |version, metadata, class_map, events|
71
- @output_file.write JSON.generate(version: version,
72
- metadata: metadata,
73
- classMap: class_map,
74
- events: events)
75
- end
76
- end
77
- end
78
-
79
- desc 'Calculate and print statistics of scenario files.'
80
- arg_name 'filename'
81
- command :stats do |c|
82
- output_file_flag(c, default_value: '-')
83
-
84
- c.desc 'Display format for the result (text | json)'
85
- c.default_value 'text'
86
- c.flag %i[f format]
87
-
88
- c.desc 'Maximum number of lines to display for each stat'
89
- c.flag %i[l limit]
90
-
91
- c.action do |_, options, args|
92
- require 'appmap/command/stats'
93
-
94
- limit = options[:limit].to_i if options[:limit]
95
-
96
- # Name of the file to analyze. GLI will ensure that it's present.
97
- filenames = args
98
- help_now!("'filename' argument is required") if filenames.empty?
99
-
100
- require 'appmap/algorithm/stats'
101
- result = filenames.inject(::AppMap::Algorithm::Stats::Result.new([], [])) do |stats_result, filename|
102
- appmap = begin
103
- JSON.parse(File.read(filename))
104
- rescue JSON::ParserError
105
- STDERR.puts "#{filename} is not valid JSON : #{$!}"
106
- nil
107
- end
108
- stats_result.tap do
109
- if appmap
110
- limit = options[:limit].to_i if options[:limit]
111
- stats_for_file = AppMap::Command::Stats.new(appmap).perform(limit: limit)
112
- stats_result.merge!(stats_for_file)
113
- end
114
- end
115
- end
116
-
117
- result.sort!
118
- result.limit!(limit) if limit
119
-
120
- display = case options[:format]
121
- when 'json'
122
- JSON.pretty_generate(result.as_json)
123
- else
124
- result.as_text
125
- end
126
- @output_file.write display
127
- end
128
- end
129
-
130
- pre do |global, _, options, _|
131
- @config = interpret_config_option(global[:config])
132
- @output_file = interpret_output_file_option(options[:output])
133
-
134
- true
135
- end
136
-
137
- class << self
138
- protected
139
-
140
- def interpret_config_option(fname)
141
- AppMap.initialize fname
142
- end
143
-
144
- def interpret_output_file_option(file_name)
145
- Hash.new { |_, fname| -> { File.new(fname, 'w') } }.tap do |open_output_file|
146
- open_output_file[nil] = -> { nil }
147
- open_output_file['-'] = -> { $stdout }
148
- end[file_name].call
149
- end
150
- end
151
- end
152
- end
153
-
154
- exit AppMap::App.run(ARGV)