appmap 0.43.0 → 0.44.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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)