appmap 0.23.0 → 0.25.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +17 -8
  4. data/.travis.yml +6 -0
  5. data/CHANGELOG.md +19 -0
  6. data/README.md +29 -12
  7. data/Rakefile +3 -3
  8. data/appmap.gemspec +3 -1
  9. data/exe/appmap +6 -18
  10. data/lib/appmap.rb +47 -6
  11. data/lib/appmap/algorithm/prune_class_map.rb +2 -0
  12. data/lib/appmap/algorithm/stats.rb +4 -2
  13. data/lib/appmap/class_map.rb +143 -0
  14. data/lib/appmap/command/record.rb +8 -6
  15. data/lib/appmap/command/stats.rb +2 -0
  16. data/lib/appmap/command/upload.rb +4 -2
  17. data/lib/appmap/event.rb +168 -0
  18. data/lib/appmap/hook.rb +151 -0
  19. data/lib/appmap/middleware/remote_recording.rb +14 -20
  20. data/lib/appmap/rails/action_handler.rb +10 -6
  21. data/lib/appmap/rails/sql_handler.rb +10 -8
  22. data/lib/appmap/railtie.rb +31 -18
  23. data/lib/appmap/rspec.rb +238 -261
  24. data/lib/appmap/trace.rb +88 -0
  25. data/lib/appmap/version.rb +1 -1
  26. data/package-lock.json +90 -92
  27. data/spec/abstract_controller4_base_spec.rb +1 -1
  28. data/spec/abstract_controller_base_spec.rb +7 -3
  29. data/spec/config_spec.rb +25 -0
  30. data/spec/fixtures/hook/attr_accessor.rb +5 -0
  31. data/spec/fixtures/hook/class_method.rb +17 -0
  32. data/spec/fixtures/hook/constructor.rb +7 -0
  33. data/spec/fixtures/hook/exception_method.rb +11 -0
  34. data/spec/fixtures/hook/instance_method.rb +23 -0
  35. data/spec/fixtures/rails4_users_app/app/controllers/api/users_controller.rb +3 -3
  36. data/spec/fixtures/rails4_users_app/config/database.yml +2 -1
  37. data/spec/fixtures/rails4_users_app/docker-compose.yml +2 -0
  38. data/spec/fixtures/rails_users_app/.ruby-version +1 -1
  39. data/spec/fixtures/rails_users_app/app/controllers/api/users_controller.rb +2 -2
  40. data/spec/fixtures/rails_users_app/config/database.yml +2 -1
  41. data/spec/fixtures/rails_users_app/create_app +1 -0
  42. data/spec/fixtures/rails_users_app/docker-compose.yml +4 -0
  43. data/spec/fixtures/rails_users_app/spec/models/user_spec.rb +1 -1
  44. data/spec/hook_spec.rb +357 -0
  45. data/spec/rails_spec_helper.rb +25 -16
  46. data/spec/railtie_spec.rb +1 -1
  47. data/spec/record_sql_rails_pg_spec.rb +1 -2
  48. data/spec/remote_recording_spec.rb +117 -0
  49. data/spec/spec_helper.rb +1 -0
  50. data/test/cli_test.rb +7 -36
  51. data/test/fixtures/cli_record_test/appmap.yml +2 -1
  52. data/test/fixtures/cli_record_test/lib/cli_record_test/main.rb +4 -2
  53. data/test/test_helper.rb +0 -42
  54. metadata +46 -62
  55. data/exe/_appmap-record-self +0 -49
  56. data/lib/appmap/command/inspect.rb +0 -14
  57. data/lib/appmap/config.rb +0 -65
  58. data/lib/appmap/config/directory.rb +0 -65
  59. data/lib/appmap/config/file.rb +0 -13
  60. data/lib/appmap/config/named_function.rb +0 -21
  61. data/lib/appmap/config/package_dir.rb +0 -52
  62. data/lib/appmap/config/path.rb +0 -25
  63. data/lib/appmap/feature.rb +0 -262
  64. data/lib/appmap/inspect.rb +0 -91
  65. data/lib/appmap/inspect/inspector.rb +0 -99
  66. data/lib/appmap/inspect/parse_node.rb +0 -170
  67. data/lib/appmap/inspect/parser.rb +0 -15
  68. data/lib/appmap/parser.rb +0 -60
  69. data/lib/appmap/rspec/parse_node.rb +0 -41
  70. data/lib/appmap/rspec/parser.rb +0 -15
  71. data/lib/appmap/trace/event_handler/rack_handler_webrick.rb +0 -65
  72. data/lib/appmap/trace/tracer.rb +0 -356
  73. data/spec/fixtures/rails_users_app/bin/_appmap-record-self +0 -29
  74. data/spec/rack_handler_webrick_spec.rb +0 -59
  75. data/test/config_test.rb +0 -149
  76. data/test/explict_inspect_test.rb +0 -29
  77. data/test/fixtures/active_record_like/active_record.rb +0 -2
  78. data/test/fixtures/active_record_like/active_record/aggregations.rb +0 -4
  79. data/test/fixtures/active_record_like/active_record/association.rb +0 -4
  80. data/test/fixtures/active_record_like/active_record/associations/join_dependency.rb +0 -6
  81. data/test/fixtures/active_record_like/active_record/associations/join_dependency/join_base.rb +0 -8
  82. data/test/fixtures/active_record_like/active_record/associations/join_dependency/join_part.rb +0 -8
  83. data/test/fixtures/active_record_like/active_record/caps/caps.rb +0 -4
  84. data/test/fixtures/ignore_non_ruby_file/class.rb +0 -3
  85. data/test/fixtures/ignore_non_ruby_file/non-ruby.txt +0 -1
  86. data/test/fixtures/includes_excludes/lib/a/a_1.rb +0 -6
  87. data/test/fixtures/includes_excludes/lib/a/a_2.rb +0 -6
  88. data/test/fixtures/includes_excludes/lib/a/x/x_1.rb +0 -8
  89. data/test/fixtures/includes_excludes/lib/b/b_1.rb +0 -6
  90. data/test/fixtures/includes_excludes/lib/root_1.rb +0 -4
  91. data/test/fixtures/inspect_multiple_subdirs/module_a.rb +0 -2
  92. data/test/fixtures/inspect_multiple_subdirs/module_a/class_a.rb +0 -5
  93. data/test/fixtures/inspect_multiple_subdirs/module_b.rb +0 -2
  94. data/test/fixtures/inspect_multiple_subdirs/module_b/class_b.rb +0 -5
  95. data/test/fixtures/inspect_multiple_subdirs/module_b/class_c.rb +0 -5
  96. data/test/fixtures/inspect_package/module_a/module_b/class_in_module.rb +0 -6
  97. data/test/fixtures/parse_file/defs_static_function.rb +0 -96
  98. data/test/fixtures/parse_file/function_within_class.rb +0 -36
  99. data/test/fixtures/parse_file/include_public_methods.rb +0 -127
  100. data/test/fixtures/parse_file/instance_function.rb +0 -17
  101. data/test/fixtures/parse_file/modules.rb +0 -71
  102. data/test/fixtures/parse_file/sclass_static_function.rb +0 -88
  103. data/test/fixtures/parse_file/toplevel_class.rb +0 -13
  104. data/test/fixtures/parse_file/toplevel_function.rb +0 -14
  105. data/test/fixtures/trace_test/trace_program_1.rb +0 -44
  106. data/test/implicit_inspect_test.rb +0 -33
  107. data/test/include_exclude_test.rb +0 -48
  108. data/test/prerecorded_trace_test.rb +0 -76
  109. data/test/trace_test.rb +0 -92
@@ -3,7 +3,7 @@ require 'rails_spec_helper'
3
3
  describe 'AbstractControllerBase' do
4
4
  before(:all) { @fixture_dir = 'spec/fixtures/rails_users_app' }
5
5
  include_context 'Rails app pg database'
6
-
6
+
7
7
  around(:each) do |example|
8
8
  FileUtils.rm_rf tmpdir
9
9
  FileUtils.mkdir_p tmpdir
@@ -17,7 +17,11 @@ describe 'AbstractControllerBase' do
17
17
  let(:appmap_json) { File.join(tmpdir, 'appmap/rspec/Api_UsersController_POST_api_users_with_required_parameters_creates_a_user.appmap.json') }
18
18
 
19
19
  describe 'testing with rspec' do
20
- it 'Message fields are recorded in the appmap' do
20
+ it 'inventory file is printed' do
21
+ expect(File).to exist(File.join(tmpdir, 'appmap/rspec/Inventory.appmap.json'))
22
+ end
23
+
24
+ it 'message fields are recorded in the appmap' do
21
25
  expect(File).to exist(appmap_json)
22
26
  appmap = JSON.parse(File.read(appmap_json)).to_yaml
23
27
 
@@ -50,7 +54,7 @@ describe 'AbstractControllerBase' do
50
54
  expect(appmap).to match(<<-CREATE_CALL.strip)
51
55
  event: call
52
56
  defined_class: Api::UsersController
53
- method_id: create_user
57
+ method_id: build_user
54
58
  path: app/controllers/api/users_controller.rb
55
59
  lineno: 23
56
60
  static: false
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_spec_helper'
4
+ require 'active_support/core_ext'
5
+ require 'appmap/hook'
6
+
7
+ describe AppMap::Hook::Config do
8
+ it 'loads from a Hash' do
9
+ config_data = {
10
+ name: 'test',
11
+ packages: [
12
+ {
13
+ path: 'path-1'
14
+ },
15
+ {
16
+ path: 'path-2',
17
+ exclude: [ 'exclude-1' ]
18
+ }
19
+ ]
20
+ }.deep_stringify_keys!
21
+ config = AppMap::Hook::Config.load(config_data)
22
+
23
+ expect(config.to_h.deep_stringify_keys!).to eq(config_data)
24
+ end
25
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AttrAccessor
4
+ attr_accessor :value
5
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ClassMethod
4
+ class << self
5
+ def say_default
6
+ 'default'
7
+ end
8
+ end
9
+
10
+ def ClassMethod.say_class_defined
11
+ 'defined with explicit class scope'
12
+ end
13
+
14
+ def self.say_self_defined
15
+ 'defined with self class scope'
16
+ end
17
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Constructor
4
+ def initialize(value)
5
+ @value = value
6
+ end
7
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ExceptionMethod
4
+ def to_s
5
+ 'Exception Method fixture'
6
+ end
7
+
8
+ def raise_exception
9
+ raise 'Exception occurred in raise_exception'
10
+ end
11
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ class InstanceMethod
4
+ def to_s
5
+ 'Instance Method fixture'
6
+ end
7
+
8
+ def say_default
9
+ 'default'
10
+ end
11
+
12
+ def say_echo(arg)
13
+ arg.to_s
14
+ end
15
+
16
+ def say_kw(kw: 'kw')
17
+ kw.to_s
18
+ end
19
+
20
+ def say_block(&block)
21
+ yield
22
+ end
23
+ end
@@ -6,7 +6,7 @@ module Api
6
6
  end
7
7
 
8
8
  def create
9
- @user = create_user(params.slice(:login).to_unsafe_h)
9
+ @user = build_user(params.slice(:login).to_unsafe_h)
10
10
  unless @user.valid?
11
11
  error = {
12
12
  code: 'invalid',
@@ -18,9 +18,9 @@ module Api
18
18
  end
19
19
  @user.save
20
20
  render json: @user, status: :created
21
- end
21
+ end
22
22
 
23
- def create_user(params)
23
+ def build_user(params)
24
24
  User.new(params)
25
25
  end
26
26
  end
@@ -1,5 +1,6 @@
1
1
  default: &default
2
- host: pg
2
+ host: <%= ENV['PGHOST'] || 'pg' %>
3
+ port: <%= ENV['PGPORT'] || 5432 %>
3
4
  user: postgres
4
5
  adapter: postgresql
5
6
  encoding: unicode
@@ -6,6 +6,8 @@ services:
6
6
  dockerfile: Dockerfile.pg
7
7
  ports:
8
8
  - "5432"
9
+ environment:
10
+ POSTGRES_HOST_AUTH_METHOD: trust
9
11
 
10
12
  app:
11
13
  build:
@@ -1 +1 @@
1
- 2.5.5
1
+ 2.6.2
@@ -6,7 +6,7 @@ module Api
6
6
  end
7
7
 
8
8
  def create
9
- @user = create_user(params.slice(:login).to_unsafe_h)
9
+ @user = build_user(params.slice(:login).to_unsafe_h)
10
10
  unless @user.valid?
11
11
  error = {
12
12
  code: 'invalid',
@@ -20,7 +20,7 @@ module Api
20
20
  render json: @user, status: :created
21
21
  end
22
22
 
23
- def create_user(params)
23
+ def build_user(params)
24
24
  User.new(params)
25
25
  end
26
26
  end
@@ -1,5 +1,6 @@
1
1
  default: &default
2
- host: pg
2
+ host: <%= ENV['PGHOST'] || 'pg' %>
3
+ port: <%= ENV['PGPORT'] || 5432 %>
3
4
  user: postgres
4
5
  adapter: postgresql
5
6
  encoding: unicode
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env bash
2
+ set -e
2
3
 
3
4
  # Just checking for a healthy container isn't enough, apparently. If
4
5
  # we don't wait here, sometimes the database is inaccessible.
@@ -6,6 +6,8 @@ services:
6
6
  dockerfile: Dockerfile.pg
7
7
  ports:
8
8
  - "5432"
9
+ environment:
10
+ POSTGRES_HOST_AUTH_METHOD: trust
9
11
 
10
12
  app:
11
13
  build:
@@ -16,6 +18,8 @@ services:
16
18
  [ "./bin/rails", "server", "-b", "0.0.0.0", "webrick" ]
17
19
  environment:
18
20
  RAILS_ENV:
21
+ ORM_MODULE:
22
+ APPMAP:
19
23
  volumes:
20
24
  - .:/src/app
21
25
  ports:
@@ -2,7 +2,7 @@ require 'rails_helper'
2
2
 
3
3
  describe User, feature_group: 'User', appmap: true do
4
4
  # TODO: appmap/rspec doesn't handle shared_examples_for 100% correctly yet.
5
- # In my tests, only one of these two tests will be emitted as an
5
+ # In my tests, only one of these two tests will be emitted as an appmap.
6
6
  shared_examples_for 'creates the user' do |username|
7
7
  let(:login) { username }
8
8
  let(:user) { User.new(login: login) }
data/spec/hook_spec.rb ADDED
@@ -0,0 +1,357 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_spec_helper'
4
+ require 'appmap/hook'
5
+ require 'appmap/event'
6
+ require 'diffy'
7
+
8
+ describe 'AppMap class Hooking' do
9
+ def collect_events(tracer)
10
+ [].tap do |events|
11
+ while tracer.event?
12
+ events << tracer.next_event.to_h
13
+ end
14
+ end.map do |event|
15
+ event.delete(:thread_id)
16
+ event.delete(:elapsed)
17
+ delete_object_id = ->(obj) { (obj || {}).delete(:object_id) }
18
+ delete_object_id.call(event[:receiver])
19
+ delete_object_id.call(event[:return_value])
20
+ (event[:parameters] || []).each(&delete_object_id)
21
+ (event[:exceptions] || []).each(&delete_object_id)
22
+
23
+ if event[:event] == :return
24
+ # These should be removed from the appmap spec
25
+ %i[defined_class method_id path lineno static].each do |obsolete_field|
26
+ event.delete(obsolete_field)
27
+ end
28
+ end
29
+ event
30
+ end.to_yaml
31
+ end
32
+
33
+ def invoke_test_file(file, &block)
34
+ package = AppMap::Hook::Package.new(file, [])
35
+ config = AppMap::Hook::Config.new('hook_spec', [ package ])
36
+ AppMap::Hook.hook(config)
37
+
38
+ tracer = AppMap.tracing.trace
39
+ AppMap::Event.reset_id_counter
40
+ begin
41
+ load file
42
+ yield
43
+ ensure
44
+ AppMap.tracing.delete(tracer)
45
+ end
46
+ [ config, tracer ]
47
+ end
48
+
49
+ def test_hook_behavior(file, events_yaml, &block)
50
+ config, tracer = invoke_test_file(file, &block)
51
+
52
+ events = collect_events(tracer)
53
+ expect(Diffy::Diff.new(events, events_yaml).to_s).to eq('')
54
+
55
+ [ config, tracer ]
56
+ end
57
+
58
+ it 'hooks an instance method that takes no arguments' do
59
+ events_yaml = <<~YAML
60
+ ---
61
+ - :id: 1
62
+ :event: :call
63
+ :defined_class: InstanceMethod
64
+ :method_id: say_default
65
+ :path: spec/fixtures/hook/instance_method.rb
66
+ :lineno: 8
67
+ :static: false
68
+ :parameters: []
69
+ :receiver:
70
+ :class: InstanceMethod
71
+ :value: Instance Method fixture
72
+ - :id: 2
73
+ :event: :return
74
+ :parent_id: 1
75
+ :return_value:
76
+ :class: String
77
+ :value: default
78
+ YAML
79
+ config, tracer = test_hook_behavior 'spec/fixtures/hook/instance_method.rb', events_yaml do
80
+ expect(InstanceMethod.new.say_default).to eq('default')
81
+ end
82
+ end
83
+
84
+ it 'collects the methods that are invoked' do
85
+ _, tracer = invoke_test_file 'spec/fixtures/hook/instance_method.rb' do
86
+ InstanceMethod.new.say_default
87
+ end
88
+ expect(tracer.event_methods.to_a.map(&:defined_class)).to eq([ 'InstanceMethod' ])
89
+ expect(tracer.event_methods.to_a.map(&:method).map(&:to_s)).to eq([ InstanceMethod.public_instance_method(:say_default).to_s ])
90
+ end
91
+
92
+ it 'builds a class map of invoked methods' do
93
+ config, tracer = invoke_test_file 'spec/fixtures/hook/instance_method.rb' do
94
+ InstanceMethod.new.say_default
95
+ end
96
+ class_map = AppMap.class_map(config, tracer.event_methods).to_yaml
97
+ expect(Diffy::Diff.new(class_map, <<~YAML).to_s).to eq('')
98
+ ---
99
+ - :name: spec/fixtures/hook/instance_method.rb
100
+ :type: package
101
+ :children:
102
+ - :name: InstanceMethod
103
+ :type: class
104
+ :children:
105
+ - :name: say_default
106
+ :type: function
107
+ :location: spec/fixtures/hook/instance_method.rb:8
108
+ :static: false
109
+ YAML
110
+ end
111
+
112
+ it 'does not hook an attr_accessor' do
113
+ events_yaml = <<~YAML
114
+ --- []
115
+ YAML
116
+ test_hook_behavior 'spec/fixtures/hook/attr_accessor.rb', events_yaml do
117
+ obj = AttrAccessor.new
118
+ obj.value = 'foo'
119
+ expect(obj.value).to eq('foo')
120
+ end
121
+ end
122
+
123
+ it 'does not hook a constructor' do
124
+ events_yaml = <<~YAML
125
+ --- []
126
+ YAML
127
+ test_hook_behavior 'spec/fixtures/hook/constructor.rb', events_yaml do
128
+ Constructor.new('foo')
129
+ end
130
+ end
131
+
132
+ it 'hooks an instance method that takes an argument' do
133
+ events_yaml = <<~YAML
134
+ ---
135
+ - :id: 1
136
+ :event: :call
137
+ :defined_class: InstanceMethod
138
+ :method_id: say_echo
139
+ :path: spec/fixtures/hook/instance_method.rb
140
+ :lineno: 12
141
+ :static: false
142
+ :parameters:
143
+ - :name: :arg
144
+ :class: String
145
+ :value: echo
146
+ :kind: :req
147
+ :receiver:
148
+ :class: InstanceMethod
149
+ :value: Instance Method fixture
150
+ - :id: 2
151
+ :event: :return
152
+ :parent_id: 1
153
+ :return_value:
154
+ :class: String
155
+ :value: echo
156
+ YAML
157
+ test_hook_behavior 'spec/fixtures/hook/instance_method.rb', events_yaml do
158
+ expect(InstanceMethod.new.say_echo('echo')).to eq('echo')
159
+ end
160
+ end
161
+
162
+ it 'hooks an instance method that takes a keyword argument' do
163
+ events_yaml = <<~YAML
164
+ ---
165
+ - :id: 1
166
+ :event: :call
167
+ :defined_class: InstanceMethod
168
+ :method_id: say_kw
169
+ :path: spec/fixtures/hook/instance_method.rb
170
+ :lineno: 16
171
+ :static: false
172
+ :parameters:
173
+ - :name: :kw
174
+ :class: Hash
175
+ :value: '{:kw=>"kw"}'
176
+ :kind: :key
177
+ :receiver:
178
+ :class: InstanceMethod
179
+ :value: Instance Method fixture
180
+ - :id: 2
181
+ :event: :return
182
+ :parent_id: 1
183
+ :return_value:
184
+ :class: String
185
+ :value: kw
186
+ YAML
187
+ test_hook_behavior 'spec/fixtures/hook/instance_method.rb', events_yaml do
188
+ expect(InstanceMethod.new.say_kw(kw: 'kw')).to eq('kw')
189
+ end
190
+ end
191
+
192
+ it 'hooks an instance method that takes a default keyword argument' do
193
+ events_yaml = <<~YAML
194
+ ---
195
+ - :id: 1
196
+ :event: :call
197
+ :defined_class: InstanceMethod
198
+ :method_id: say_kw
199
+ :path: spec/fixtures/hook/instance_method.rb
200
+ :lineno: 16
201
+ :static: false
202
+ :parameters:
203
+ - :name: :kw
204
+ :class: NilClass
205
+ :value:
206
+ :kind: :key
207
+ :receiver:
208
+ :class: InstanceMethod
209
+ :value: Instance Method fixture
210
+ - :id: 2
211
+ :event: :return
212
+ :parent_id: 1
213
+ :return_value:
214
+ :class: String
215
+ :value: kw
216
+ YAML
217
+ test_hook_behavior 'spec/fixtures/hook/instance_method.rb', events_yaml do
218
+ expect(InstanceMethod.new.say_kw).to eq('kw')
219
+ end
220
+ end
221
+
222
+ it 'hooks an instance method that takes a block argument' do
223
+ events_yaml = <<~YAML
224
+ ---
225
+ - :id: 1
226
+ :event: :call
227
+ :defined_class: InstanceMethod
228
+ :method_id: say_block
229
+ :path: spec/fixtures/hook/instance_method.rb
230
+ :lineno: 20
231
+ :static: false
232
+ :parameters:
233
+ - :name: :block
234
+ :class: NilClass
235
+ :value:
236
+ :kind: :block
237
+ :receiver:
238
+ :class: InstanceMethod
239
+ :value: Instance Method fixture
240
+ - :id: 2
241
+ :event: :return
242
+ :parent_id: 1
243
+ :return_value:
244
+ :class: String
245
+ :value: albert
246
+ YAML
247
+ test_hook_behavior 'spec/fixtures/hook/instance_method.rb', events_yaml do
248
+ expect(InstanceMethod.new.say_block { 'albert' }).to eq('albert')
249
+ end
250
+ end
251
+
252
+ it 'hooks a singleton method' do
253
+ events_yaml = <<~YAML
254
+ ---
255
+ - :id: 1
256
+ :event: :call
257
+ :defined_class: ClassMethod
258
+ :method_id: say_default
259
+ :path: spec/fixtures/hook/class_method.rb
260
+ :lineno: 5
261
+ :static: true
262
+ :parameters: []
263
+ :receiver:
264
+ :class: Class
265
+ :value: ClassMethod
266
+ - :id: 2
267
+ :event: :return
268
+ :parent_id: 1
269
+ :return_value:
270
+ :class: String
271
+ :value: default
272
+ YAML
273
+ test_hook_behavior 'spec/fixtures/hook/class_method.rb', events_yaml do
274
+ expect(ClassMethod.say_default).to eq('default')
275
+ end
276
+ end
277
+
278
+ it 'hooks a class method with explicit class name scope' do
279
+ events_yaml = <<~YAML
280
+ ---
281
+ - :id: 1
282
+ :event: :call
283
+ :defined_class: ClassMethod
284
+ :method_id: say_class_defined
285
+ :path: spec/fixtures/hook/class_method.rb
286
+ :lineno: 10
287
+ :static: true
288
+ :parameters: []
289
+ :receiver:
290
+ :class: Class
291
+ :value: ClassMethod
292
+ - :id: 2
293
+ :event: :return
294
+ :parent_id: 1
295
+ :return_value:
296
+ :class: String
297
+ :value: defined with explicit class scope
298
+ YAML
299
+ test_hook_behavior 'spec/fixtures/hook/class_method.rb', events_yaml do
300
+ expect(ClassMethod.say_class_defined).to eq('defined with explicit class scope')
301
+ end
302
+ end
303
+
304
+ it "hooks a class method with 'self' as the class name scope" do
305
+ events_yaml = <<~YAML
306
+ ---
307
+ - :id: 1
308
+ :event: :call
309
+ :defined_class: ClassMethod
310
+ :method_id: say_self_defined
311
+ :path: spec/fixtures/hook/class_method.rb
312
+ :lineno: 14
313
+ :static: true
314
+ :parameters: []
315
+ :receiver:
316
+ :class: Class
317
+ :value: ClassMethod
318
+ - :id: 2
319
+ :event: :return
320
+ :parent_id: 1
321
+ :return_value:
322
+ :class: String
323
+ :value: defined with self class scope
324
+ YAML
325
+ test_hook_behavior 'spec/fixtures/hook/class_method.rb', events_yaml do
326
+ expect(ClassMethod.say_self_defined).to eq('defined with self class scope')
327
+ end
328
+ end
329
+
330
+ it 'Reports exceptions' do
331
+ events_yaml = <<~YAML
332
+ ---
333
+ - :id: 1
334
+ :event: :call
335
+ :defined_class: ExceptionMethod
336
+ :method_id: raise_exception
337
+ :path: spec/fixtures/hook/exception_method.rb
338
+ :lineno: 8
339
+ :static: false
340
+ :parameters: []
341
+ :receiver:
342
+ :class: ExceptionMethod
343
+ :value: Exception Method fixture
344
+ - :id: 2
345
+ :event: :return
346
+ :parent_id: 1
347
+ :exceptions:
348
+ - :class: RuntimeError
349
+ :message: Exception occurred in raise_exception
350
+ :path: spec/fixtures/hook/exception_method.rb
351
+ :lineno: 9
352
+ YAML
353
+ test_hook_behavior 'spec/fixtures/hook/exception_method.rb', events_yaml do
354
+ ExceptionMethod.new.raise_exception
355
+ end
356
+ end
357
+ end