appmap 0.31.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.dockerignore +5 -0
- data/.gitignore +17 -0
- data/.rubocop.yml +27 -0
- data/.ruby-version +1 -0
- data/.travis.yml +44 -0
- data/CHANGELOG.md +199 -0
- data/Dockerfile.appmap +5 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +300 -0
- data/Rakefile +132 -0
- data/appmap.gemspec +44 -0
- data/appmap.yml +8 -0
- data/examples/install.rb +76 -0
- data/examples/mock_webapp/Gemfile +1 -0
- data/examples/mock_webapp/appmap.yml +2 -0
- data/examples/mock_webapp/exe/mock_webapp_request +12 -0
- data/examples/mock_webapp/lib/mock_webapp/controller.rb +23 -0
- data/examples/mock_webapp/lib/mock_webapp/request.rb +12 -0
- data/examples/mock_webapp/lib/mock_webapp/user.rb +18 -0
- data/exe/appmap +154 -0
- data/lib/appmap.rb +87 -0
- data/lib/appmap/algorithm/prune_class_map.rb +67 -0
- data/lib/appmap/algorithm/stats.rb +91 -0
- data/lib/appmap/class_map.rb +135 -0
- data/lib/appmap/command/record.rb +38 -0
- data/lib/appmap/command/stats.rb +14 -0
- data/lib/appmap/config.rb +91 -0
- data/lib/appmap/cucumber.rb +89 -0
- data/lib/appmap/event.rb +168 -0
- data/lib/appmap/hook.rb +130 -0
- data/lib/appmap/metadata.rb +62 -0
- data/lib/appmap/middleware/remote_recording.rb +114 -0
- data/lib/appmap/minitest.rb +141 -0
- data/lib/appmap/rails/action_handler.rb +91 -0
- data/lib/appmap/rails/sql_handler.rb +145 -0
- data/lib/appmap/railtie.rb +45 -0
- data/lib/appmap/record.rb +27 -0
- data/lib/appmap/rspec.rb +301 -0
- data/lib/appmap/trace.rb +96 -0
- data/lib/appmap/util.rb +40 -0
- data/lib/appmap/version.rb +9 -0
- data/lore/pages/2019-05-21-install-and-record/index.pug +51 -0
- data/lore/pages/2019-05-21-install-and-record/install_example_appmap.png +0 -0
- data/lore/pages/2019-05-21-install-and-record/metadata.yml +5 -0
- data/lore/pages/layout.pug +66 -0
- data/lore/public/lib/bootstrap-4.1.3/css/bootstrap-grid.css +1912 -0
- data/lore/public/lib/bootstrap-4.1.3/css/bootstrap-grid.css.map +1 -0
- data/lore/public/lib/bootstrap-4.1.3/css/bootstrap-grid.min.css +7 -0
- data/lore/public/lib/bootstrap-4.1.3/css/bootstrap-grid.min.css.map +1 -0
- data/lore/public/lib/bootstrap-4.1.3/css/bootstrap-reboot.css +331 -0
- data/lore/public/lib/bootstrap-4.1.3/css/bootstrap-reboot.css.map +1 -0
- data/lore/public/lib/bootstrap-4.1.3/css/bootstrap-reboot.min.css +8 -0
- data/lore/public/lib/bootstrap-4.1.3/css/bootstrap-reboot.min.css.map +1 -0
- data/lore/public/lib/bootstrap-4.1.3/css/bootstrap.css +9030 -0
- data/lore/public/lib/bootstrap-4.1.3/css/bootstrap.css.map +1 -0
- data/lore/public/lib/bootstrap-4.1.3/css/bootstrap.min.css +7 -0
- data/lore/public/lib/bootstrap-4.1.3/css/bootstrap.min.css.map +1 -0
- data/lore/public/stylesheets/style.css +8 -0
- data/package-lock.json +1064 -0
- data/package.json +24 -0
- data/spec/abstract_controller4_base_spec.rb +67 -0
- data/spec/abstract_controller_base_spec.rb +72 -0
- data/spec/config_spec.rb +25 -0
- data/spec/fixtures/hook/attr_accessor.rb +5 -0
- data/spec/fixtures/hook/compare.rb +7 -0
- data/spec/fixtures/hook/constructor.rb +7 -0
- data/spec/fixtures/hook/exception_method.rb +11 -0
- data/spec/fixtures/hook/instance_method.rb +23 -0
- data/spec/fixtures/hook/openssl_sign.rb +87 -0
- data/spec/fixtures/hook/singleton_method.rb +54 -0
- data/spec/fixtures/rack_users_app/.dockerignore +2 -0
- data/spec/fixtures/rack_users_app/.gitignore +2 -0
- data/spec/fixtures/rack_users_app/Dockerfile +32 -0
- data/spec/fixtures/rack_users_app/Gemfile +10 -0
- data/spec/fixtures/rack_users_app/appmap.yml +3 -0
- data/spec/fixtures/rack_users_app/config.ru +2 -0
- data/spec/fixtures/rack_users_app/docker-compose.yml +9 -0
- data/spec/fixtures/rack_users_app/lib/app.rb +36 -0
- data/spec/fixtures/rails4_users_app/.gitignore +13 -0
- data/spec/fixtures/rails4_users_app/.rbenv-gemsets +2 -0
- data/spec/fixtures/rails4_users_app/.ruby-version +1 -0
- data/spec/fixtures/rails4_users_app/Dockerfile +30 -0
- data/spec/fixtures/rails4_users_app/Dockerfile.pg +3 -0
- data/spec/fixtures/rails4_users_app/Gemfile +77 -0
- data/spec/fixtures/rails4_users_app/README.rdoc +28 -0
- data/spec/fixtures/rails4_users_app/Rakefile +6 -0
- data/spec/fixtures/rails4_users_app/app/assets/images/.keep +0 -0
- data/spec/fixtures/rails4_users_app/app/assets/javascripts/application.js +16 -0
- data/spec/fixtures/rails4_users_app/app/assets/stylesheets/application.css +15 -0
- data/spec/fixtures/rails4_users_app/app/controllers/api/users_controller.rb +27 -0
- data/spec/fixtures/rails4_users_app/app/controllers/application_controller.rb +5 -0
- data/spec/fixtures/rails4_users_app/app/controllers/concerns/.keep +0 -0
- data/spec/fixtures/rails4_users_app/app/controllers/health_controller.rb +5 -0
- data/spec/fixtures/rails4_users_app/app/controllers/users_controller.rb +5 -0
- data/spec/fixtures/rails4_users_app/app/helpers/application_helper.rb +2 -0
- data/spec/fixtures/rails4_users_app/app/mailers/.keep +0 -0
- data/spec/fixtures/rails4_users_app/app/models/.keep +0 -0
- data/spec/fixtures/rails4_users_app/app/models/concerns/.keep +0 -0
- data/spec/fixtures/rails4_users_app/app/models/user.rb +18 -0
- data/spec/fixtures/rails4_users_app/app/views/layouts/application.html.haml +7 -0
- data/spec/fixtures/rails4_users_app/app/views/users/index.html.haml +7 -0
- data/spec/fixtures/rails4_users_app/appmap.yml +3 -0
- data/spec/fixtures/rails4_users_app/bin/rails +9 -0
- data/spec/fixtures/rails4_users_app/bin/setup +29 -0
- data/spec/fixtures/rails4_users_app/bin/spring +17 -0
- data/spec/fixtures/rails4_users_app/config.ru +4 -0
- data/spec/fixtures/rails4_users_app/config/application.rb +26 -0
- data/spec/fixtures/rails4_users_app/config/boot.rb +3 -0
- data/spec/fixtures/rails4_users_app/config/database.yml +18 -0
- data/spec/fixtures/rails4_users_app/config/environment.rb +5 -0
- data/spec/fixtures/rails4_users_app/config/environments/development.rb +41 -0
- data/spec/fixtures/rails4_users_app/config/environments/production.rb +79 -0
- data/spec/fixtures/rails4_users_app/config/environments/test.rb +42 -0
- data/spec/fixtures/rails4_users_app/config/initializers/assets.rb +11 -0
- data/spec/fixtures/rails4_users_app/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/fixtures/rails4_users_app/config/initializers/cookies_serializer.rb +3 -0
- data/spec/fixtures/rails4_users_app/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/fixtures/rails4_users_app/config/initializers/inflections.rb +16 -0
- data/spec/fixtures/rails4_users_app/config/initializers/mime_types.rb +4 -0
- data/spec/fixtures/rails4_users_app/config/initializers/session_store.rb +3 -0
- data/spec/fixtures/rails4_users_app/config/initializers/to_time_preserves_timezone.rb +10 -0
- data/spec/fixtures/rails4_users_app/config/initializers/wrap_parameters.rb +14 -0
- data/spec/fixtures/rails4_users_app/config/locales/en.yml +23 -0
- data/spec/fixtures/rails4_users_app/config/routes.rb +12 -0
- data/spec/fixtures/rails4_users_app/config/secrets.yml +22 -0
- data/spec/fixtures/rails4_users_app/create_app +23 -0
- data/spec/fixtures/rails4_users_app/db/migrate/20191127112304_create_users.rb +10 -0
- data/spec/fixtures/rails4_users_app/db/schema.rb +26 -0
- data/spec/fixtures/rails4_users_app/db/seeds.rb +7 -0
- data/spec/fixtures/rails4_users_app/docker-compose.yml +26 -0
- data/spec/fixtures/rails4_users_app/lib/assets/.keep +0 -0
- data/spec/fixtures/rails4_users_app/lib/tasks/.keep +0 -0
- data/spec/fixtures/rails4_users_app/log/.keep +0 -0
- data/spec/fixtures/rails4_users_app/public/404.html +67 -0
- data/spec/fixtures/rails4_users_app/public/422.html +67 -0
- data/spec/fixtures/rails4_users_app/public/500.html +66 -0
- data/spec/fixtures/rails4_users_app/public/favicon.ico +0 -0
- data/spec/fixtures/rails4_users_app/public/robots.txt +5 -0
- data/spec/fixtures/rails4_users_app/spec/controllers/users_controller_api_spec.rb +49 -0
- data/spec/fixtures/rails4_users_app/spec/rails_helper.rb +95 -0
- data/spec/fixtures/rails4_users_app/spec/spec_helper.rb +96 -0
- data/spec/fixtures/rails4_users_app/test/fixtures/users.yml +9 -0
- data/spec/fixtures/rails_users_app/.dockerignore +1 -0
- data/spec/fixtures/rails_users_app/.gitignore +39 -0
- data/spec/fixtures/rails_users_app/.rspec +1 -0
- data/spec/fixtures/rails_users_app/.ruby-version +1 -0
- data/spec/fixtures/rails_users_app/Dockerfile +29 -0
- data/spec/fixtures/rails_users_app/Dockerfile.pg +3 -0
- data/spec/fixtures/rails_users_app/Gemfile +52 -0
- data/spec/fixtures/rails_users_app/Rakefile +6 -0
- data/spec/fixtures/rails_users_app/app/controllers/api/users_controller.rb +27 -0
- data/spec/fixtures/rails_users_app/app/controllers/application_controller.rb +2 -0
- data/spec/fixtures/rails_users_app/app/controllers/concerns/.keep +0 -0
- data/spec/fixtures/rails_users_app/app/controllers/health_controller.rb +5 -0
- data/spec/fixtures/rails_users_app/app/controllers/users_controller.rb +5 -0
- data/spec/fixtures/rails_users_app/app/models/activerecord/user.rb +18 -0
- data/spec/fixtures/rails_users_app/app/models/concerns/.keep +0 -0
- data/spec/fixtures/rails_users_app/app/models/sequel/user.rb +25 -0
- data/spec/fixtures/rails_users_app/app/views/layouts/application.html.haml +7 -0
- data/spec/fixtures/rails_users_app/app/views/users/index.html.haml +7 -0
- data/spec/fixtures/rails_users_app/appmap.yml +3 -0
- data/spec/fixtures/rails_users_app/bin/appmap +29 -0
- data/spec/fixtures/rails_users_app/bin/byebug +29 -0
- data/spec/fixtures/rails_users_app/bin/gli +29 -0
- data/spec/fixtures/rails_users_app/bin/htmldiff +29 -0
- data/spec/fixtures/rails_users_app/bin/ldiff +29 -0
- data/spec/fixtures/rails_users_app/bin/nokogiri +29 -0
- data/spec/fixtures/rails_users_app/bin/rackup +29 -0
- data/spec/fixtures/rails_users_app/bin/rails +4 -0
- data/spec/fixtures/rails_users_app/bin/rake +29 -0
- data/spec/fixtures/rails_users_app/bin/rspec +29 -0
- data/spec/fixtures/rails_users_app/bin/ruby-parse +29 -0
- data/spec/fixtures/rails_users_app/bin/ruby-rewrite +29 -0
- data/spec/fixtures/rails_users_app/bin/sequel +29 -0
- data/spec/fixtures/rails_users_app/bin/setup +25 -0
- data/spec/fixtures/rails_users_app/bin/sprockets +29 -0
- data/spec/fixtures/rails_users_app/bin/thor +29 -0
- data/spec/fixtures/rails_users_app/bin/update +25 -0
- data/spec/fixtures/rails_users_app/config.ru +5 -0
- data/spec/fixtures/rails_users_app/config/application.rb +51 -0
- data/spec/fixtures/rails_users_app/config/boot.rb +3 -0
- data/spec/fixtures/rails_users_app/config/credentials.yml.enc +1 -0
- data/spec/fixtures/rails_users_app/config/database.yml +18 -0
- data/spec/fixtures/rails_users_app/config/environment.rb +5 -0
- data/spec/fixtures/rails_users_app/config/environments/development.rb +40 -0
- data/spec/fixtures/rails_users_app/config/environments/production.rb +68 -0
- data/spec/fixtures/rails_users_app/config/environments/test.rb +36 -0
- data/spec/fixtures/rails_users_app/config/initializers/application_controller_renderer.rb +8 -0
- data/spec/fixtures/rails_users_app/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/fixtures/rails_users_app/config/initializers/cors.rb +16 -0
- data/spec/fixtures/rails_users_app/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/fixtures/rails_users_app/config/initializers/inflections.rb +16 -0
- data/spec/fixtures/rails_users_app/config/initializers/mime_types.rb +4 -0
- data/spec/fixtures/rails_users_app/config/initializers/record_button.rb +3 -0
- data/spec/fixtures/rails_users_app/config/initializers/wrap_parameters.rb +9 -0
- data/spec/fixtures/rails_users_app/config/locales/en.yml +33 -0
- data/spec/fixtures/rails_users_app/config/routes.rb +11 -0
- data/spec/fixtures/rails_users_app/create_app +27 -0
- data/spec/fixtures/rails_users_app/db/migrate/20190728211408_create_users.rb +9 -0
- data/spec/fixtures/rails_users_app/db/schema.rb +23 -0
- data/spec/fixtures/rails_users_app/docker-compose.yml +28 -0
- data/spec/fixtures/rails_users_app/features/api_users.feature +13 -0
- data/spec/fixtures/rails_users_app/features/support/env.rb +4 -0
- data/spec/fixtures/rails_users_app/features/support/hooks.rb +11 -0
- data/spec/fixtures/rails_users_app/features/support/steps.rb +18 -0
- data/spec/fixtures/rails_users_app/lib/tasks/.keep +0 -0
- data/spec/fixtures/rails_users_app/log/.keep +0 -0
- data/spec/fixtures/rails_users_app/public/robots.txt +1 -0
- data/spec/fixtures/rails_users_app/spec/controllers/users_controller_api_spec.rb +29 -0
- data/spec/fixtures/rails_users_app/spec/models/user_spec.rb +39 -0
- data/spec/fixtures/rails_users_app/spec/rails_helper.rb +66 -0
- data/spec/fixtures/rails_users_app/spec/spec_helper.rb +96 -0
- data/spec/fixtures/rails_users_app/users_app/.gitignore +20 -0
- data/spec/hook_spec.rb +576 -0
- data/spec/rails_spec_helper.rb +60 -0
- data/spec/railtie_spec.rb +44 -0
- data/spec/record_sql_rails4_pg_spec.rb +76 -0
- data/spec/record_sql_rails_pg_spec.rb +68 -0
- data/spec/remote_recording_spec.rb +117 -0
- data/spec/rspec_feature_metadata_spec.rb +32 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/util_spec.rb +21 -0
- data/test/cli_test.rb +116 -0
- data/test/cucumber_test.rb +72 -0
- data/test/fixtures/cli_record_test/appmap.yml +3 -0
- data/test/fixtures/cli_record_test/lib/cli_record_test/main.rb +9 -0
- data/test/fixtures/cucumber4_recorder/Gemfile +5 -0
- data/test/fixtures/cucumber4_recorder/appmap.yml +3 -0
- data/test/fixtures/cucumber4_recorder/features/say_hello.feature +5 -0
- data/test/fixtures/cucumber4_recorder/features/support/env.rb +5 -0
- data/test/fixtures/cucumber4_recorder/features/support/hooks.rb +11 -0
- data/test/fixtures/cucumber4_recorder/features/support/steps.rb +9 -0
- data/test/fixtures/cucumber4_recorder/lib/hello.rb +7 -0
- data/test/fixtures/cucumber_recorder/Gemfile +5 -0
- data/test/fixtures/cucumber_recorder/appmap.yml +3 -0
- data/test/fixtures/cucumber_recorder/features/say_hello.feature +5 -0
- data/test/fixtures/cucumber_recorder/features/support/env.rb +5 -0
- data/test/fixtures/cucumber_recorder/features/support/hooks.rb +11 -0
- data/test/fixtures/cucumber_recorder/features/support/steps.rb +9 -0
- data/test/fixtures/cucumber_recorder/lib/hello.rb +7 -0
- data/test/fixtures/minitest_recorder/Gemfile +5 -0
- data/test/fixtures/minitest_recorder/appmap.yml +3 -0
- data/test/fixtures/minitest_recorder/lib/hello.rb +5 -0
- data/test/fixtures/minitest_recorder/test/hello_test.rb +12 -0
- data/test/fixtures/process_recorder/appmap.yml +3 -0
- data/test/fixtures/process_recorder/hello.rb +9 -0
- data/test/fixtures/rspec_recorder/Gemfile +5 -0
- data/test/fixtures/rspec_recorder/appmap.yml +3 -0
- data/test/fixtures/rspec_recorder/lib/hello.rb +5 -0
- data/test/fixtures/rspec_recorder/spec/decorated_hello_spec.rb +21 -0
- data/test/fixtures/rspec_recorder/spec/labeled_hello_spec.rb +9 -0
- data/test/fixtures/rspec_recorder/spec/plain_hello_spec.rb +9 -0
- data/test/minitest_test.rb +38 -0
- data/test/record_process_test.rb +35 -0
- data/test/rspec_test.rb +82 -0
- data/test/test_helper.rb +4 -0
- metadata +525 -0
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AppMap
|
4
|
+
# Railtie connects the AppMap recorder to Rails-specific features.
|
5
|
+
class Railtie < ::Rails::Railtie
|
6
|
+
config.appmap = ActiveSupport::OrderedOptions.new
|
7
|
+
|
8
|
+
initializer 'appmap.init' do |_| # params: app
|
9
|
+
require 'appmap'
|
10
|
+
end
|
11
|
+
|
12
|
+
# appmap.subscribe subscribes to ActiveSupport Notifications so that they can be recorded as
|
13
|
+
# AppMap events.
|
14
|
+
initializer 'appmap.subscribe', after: 'appmap.init' do |_| # params: app
|
15
|
+
require 'appmap/rails/sql_handler'
|
16
|
+
require 'appmap/rails/action_handler'
|
17
|
+
ActiveSupport::Notifications.subscribe 'sql.sequel', AppMap::Rails::SQLHandler.new
|
18
|
+
ActiveSupport::Notifications.subscribe 'sql.active_record', AppMap::Rails::SQLHandler.new
|
19
|
+
ActiveSupport::Notifications.subscribe \
|
20
|
+
'start_processing.action_controller', AppMap::Rails::ActionHandler::HTTPServerRequest.new
|
21
|
+
ActiveSupport::Notifications.subscribe \
|
22
|
+
'process_action.action_controller', AppMap::Rails::ActionHandler::HTTPServerResponse.new
|
23
|
+
end
|
24
|
+
|
25
|
+
# appmap.trace begins recording an AppMap trace and writes it to appmap.json.
|
26
|
+
# This behavior is only activated if the configuration setting app.config.appmap.enabled
|
27
|
+
# is truthy.
|
28
|
+
initializer 'appmap.trace', after: 'appmap.subscribe' do |app|
|
29
|
+
lambda do
|
30
|
+
return unless app.config.appmap.enabled
|
31
|
+
|
32
|
+
require 'appmap/command/record'
|
33
|
+
require 'json'
|
34
|
+
AppMap::Command::Record.new(AppMap.configuration).perform do |version, metadata, class_map, events|
|
35
|
+
appmap = JSON.generate \
|
36
|
+
version: version,
|
37
|
+
metadata: metadata,
|
38
|
+
classMap: class_map,
|
39
|
+
events: events
|
40
|
+
File.open('appmap.json', 'w').write(appmap)
|
41
|
+
end
|
42
|
+
end.call
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'appmap'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
tracer = AppMap.tracing.trace
|
7
|
+
|
8
|
+
at_exit do
|
9
|
+
AppMap.tracing.delete(tracer)
|
10
|
+
|
11
|
+
events = [].tap do |event_list|
|
12
|
+
event_list << tracer.next_event.to_h while tracer.event?
|
13
|
+
end
|
14
|
+
|
15
|
+
metadata = AppMap.detect_metadata
|
16
|
+
metadata[:recorder] = {
|
17
|
+
name: 'record_process'
|
18
|
+
}
|
19
|
+
|
20
|
+
appmap = {
|
21
|
+
'version' => AppMap::APPMAP_FORMAT_VERSION,
|
22
|
+
'metadata' => metadata,
|
23
|
+
'classMap' => AppMap.class_map(tracer.event_methods),
|
24
|
+
'events' => events
|
25
|
+
}
|
26
|
+
File.write 'appmap.json', JSON.generate(appmap)
|
27
|
+
end
|
data/lib/appmap/rspec.rb
ADDED
@@ -0,0 +1,301 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'appmap/util'
|
4
|
+
|
5
|
+
module AppMap
|
6
|
+
# Integration of AppMap with RSpec. When enabled with APPMAP=true, the AppMap tracer will
|
7
|
+
# be activated around each scenario which has the metadata key `:appmap`.
|
8
|
+
module RSpec
|
9
|
+
APPMAP_OUTPUT_DIR = 'tmp/appmap/rspec'
|
10
|
+
LOG = false
|
11
|
+
|
12
|
+
def self.metadata
|
13
|
+
AppMap.detect_metadata
|
14
|
+
end
|
15
|
+
|
16
|
+
module FeatureAnnotations
|
17
|
+
def feature
|
18
|
+
return nil unless annotations
|
19
|
+
|
20
|
+
annotations[:feature]
|
21
|
+
end
|
22
|
+
|
23
|
+
def labels
|
24
|
+
labels = metadata[:appmap]
|
25
|
+
if labels.is_a?(Array)
|
26
|
+
labels
|
27
|
+
elsif labels.is_a?(String) || labels.is_a?(Symbol)
|
28
|
+
[ labels ]
|
29
|
+
else
|
30
|
+
[]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def feature_group
|
35
|
+
return nil unless annotations
|
36
|
+
|
37
|
+
annotations[:feature_group]
|
38
|
+
end
|
39
|
+
|
40
|
+
def annotations
|
41
|
+
metadata.tap do |md|
|
42
|
+
description_args_hashes.each do |h|
|
43
|
+
md.merge! h
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
protected
|
49
|
+
|
50
|
+
def metadata
|
51
|
+
return {} unless example_obj.respond_to?(:metadata)
|
52
|
+
|
53
|
+
example_obj.metadata
|
54
|
+
end
|
55
|
+
|
56
|
+
def description_args_hashes
|
57
|
+
return [] unless example_obj.respond_to?(:metadata)
|
58
|
+
|
59
|
+
(example_obj.metadata[:description_args] || []).select { |arg| arg.is_a?(Hash) }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# ScopeExample and ScopeExampleGroup is a way to handle the weird way that RSpec
|
64
|
+
# stores the nested example names.
|
65
|
+
ScopeExample = Struct.new(:example) do
|
66
|
+
include FeatureAnnotations
|
67
|
+
|
68
|
+
alias_method :example_obj, :example
|
69
|
+
|
70
|
+
def description?
|
71
|
+
true
|
72
|
+
end
|
73
|
+
|
74
|
+
def description
|
75
|
+
example.description
|
76
|
+
end
|
77
|
+
|
78
|
+
def parent
|
79
|
+
ScopeExampleGroup.new(example.example_group)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# As you can see here, the way that RSpec stores the example description and
|
84
|
+
# represents the example group hierarchy is pretty weird.
|
85
|
+
ScopeExampleGroup = Struct.new(:example_group) do
|
86
|
+
include FeatureAnnotations
|
87
|
+
|
88
|
+
alias_method :example_obj, :example_group
|
89
|
+
|
90
|
+
def description_args
|
91
|
+
# Don't stringify any hashes that RSpec considers part of the example group description.
|
92
|
+
example_group.metadata[:description_args].reject { |arg| arg.is_a?(Hash) }
|
93
|
+
end
|
94
|
+
|
95
|
+
def description?
|
96
|
+
return true if example_group.respond_to?(:described_class) && example_group.described_class
|
97
|
+
|
98
|
+
return true if example_group.respond_to?(:description) && !description_args.empty?
|
99
|
+
|
100
|
+
false
|
101
|
+
end
|
102
|
+
|
103
|
+
def description
|
104
|
+
description? ? description_args.join(' ') : nil
|
105
|
+
end
|
106
|
+
|
107
|
+
def parent
|
108
|
+
# An example group always has a parent; but it might be 'self'...
|
109
|
+
|
110
|
+
# DEPRECATION WARNING: `Module#parent` has been renamed to `module_parent`. `parent` is deprecated and will be
|
111
|
+
# removed in Rails 6.1. (called from parent at /Users/kgilpin/source/appland/appmap-ruby/lib/appmap/rspec.rb:110)
|
112
|
+
example_group_parent = \
|
113
|
+
if example_group.respond_to?(:module_parent)
|
114
|
+
example_group.module_parent
|
115
|
+
else
|
116
|
+
example_group.parent
|
117
|
+
end
|
118
|
+
|
119
|
+
example_group_parent != example_group ? ScopeExampleGroup.new(example_group_parent) : nil
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
Recording = Struct.new(:example) do
|
124
|
+
def initialize(example)
|
125
|
+
super
|
126
|
+
|
127
|
+
warn "Starting recording of example #{example}" if AppMap::RSpec::LOG
|
128
|
+
@trace = AppMap.tracing.trace
|
129
|
+
end
|
130
|
+
|
131
|
+
def finish
|
132
|
+
warn "Finishing recording of example #{example}" if AppMap::RSpec::LOG
|
133
|
+
|
134
|
+
events = []
|
135
|
+
AppMap.tracing.delete @trace
|
136
|
+
|
137
|
+
events << @trace.next_event.to_h while @trace.event?
|
138
|
+
|
139
|
+
AppMap::RSpec.add_event_methods @trace.event_methods
|
140
|
+
|
141
|
+
class_map = AppMap.class_map(@trace.event_methods)
|
142
|
+
|
143
|
+
description = []
|
144
|
+
scope = ScopeExample.new(example)
|
145
|
+
feature_group = feature = nil
|
146
|
+
|
147
|
+
labels = []
|
148
|
+
while scope
|
149
|
+
labels += scope.labels
|
150
|
+
description << scope.description
|
151
|
+
feature ||= scope.feature
|
152
|
+
feature_group ||= scope.feature_group
|
153
|
+
scope = scope.parent
|
154
|
+
end
|
155
|
+
|
156
|
+
labels = labels.map(&:to_s).map(&:strip).reject(&:blank?).map(&:downcase).uniq
|
157
|
+
description.reject!(&:nil?).reject(&:blank?)
|
158
|
+
default_description = description.last
|
159
|
+
description.reverse!
|
160
|
+
|
161
|
+
normalize = lambda do |desc|
|
162
|
+
desc.gsub('it should behave like', '')
|
163
|
+
.gsub(/Controller$/, '')
|
164
|
+
.gsub(/\s+/, ' ')
|
165
|
+
.strip
|
166
|
+
end
|
167
|
+
|
168
|
+
full_description = normalize.call(description.join(' '))
|
169
|
+
|
170
|
+
compute_feature_name = lambda do
|
171
|
+
return 'unknown' if description.empty?
|
172
|
+
|
173
|
+
feature_description = description.dup
|
174
|
+
num_tokens = [2, feature_description.length - 1].min
|
175
|
+
feature_description[0...num_tokens].map(&:strip).join(' ')
|
176
|
+
end
|
177
|
+
|
178
|
+
feature_group ||= normalize.call(default_description).underscore.gsub('/', '_').humanize
|
179
|
+
feature_name = feature || compute_feature_name.call if feature_group
|
180
|
+
feature_name = normalize.call(feature_name) if feature_name
|
181
|
+
|
182
|
+
AppMap::RSpec.save full_description,
|
183
|
+
class_map,
|
184
|
+
events: events,
|
185
|
+
feature_name: feature_name,
|
186
|
+
feature_group_name: feature_group,
|
187
|
+
labels: labels.blank? ? nil : labels
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
@recordings_by_example = {}
|
192
|
+
@event_methods = Set.new
|
193
|
+
|
194
|
+
class << self
|
195
|
+
def init
|
196
|
+
warn 'Configuring AppMap recorder for RSpec'
|
197
|
+
|
198
|
+
FileUtils.mkdir_p APPMAP_OUTPUT_DIR
|
199
|
+
end
|
200
|
+
|
201
|
+
def begin_spec(example)
|
202
|
+
@recordings_by_example[example.object_id] = Recording.new(example)
|
203
|
+
end
|
204
|
+
|
205
|
+
def end_spec(example)
|
206
|
+
recording = @recordings_by_example.delete(example.object_id)
|
207
|
+
return warn "No recording found for #{example}" unless recording
|
208
|
+
|
209
|
+
recording.finish
|
210
|
+
end
|
211
|
+
|
212
|
+
def config
|
213
|
+
@config or raise "AppMap is not configured"
|
214
|
+
end
|
215
|
+
|
216
|
+
def add_event_methods(event_methods)
|
217
|
+
@event_methods += event_methods
|
218
|
+
end
|
219
|
+
|
220
|
+
def save(example_name, class_map, events: nil, feature_name: nil, feature_group_name: nil, labels: nil)
|
221
|
+
metadata = AppMap::RSpec.metadata.tap do |m|
|
222
|
+
m[:name] = example_name
|
223
|
+
m[:app] = AppMap.configuration.name
|
224
|
+
m[:feature] = feature_name if feature_name
|
225
|
+
m[:feature_group] = feature_group_name if feature_group_name
|
226
|
+
m[:labels] = labels if labels
|
227
|
+
m[:frameworks] ||= []
|
228
|
+
m[:frameworks] << {
|
229
|
+
name: 'rspec',
|
230
|
+
version: Gem.loaded_specs['rspec-core']&.version&.to_s
|
231
|
+
}
|
232
|
+
m[:recorder] = {
|
233
|
+
name: 'rspec'
|
234
|
+
}
|
235
|
+
end
|
236
|
+
|
237
|
+
appmap = {
|
238
|
+
version: AppMap::APPMAP_FORMAT_VERSION,
|
239
|
+
metadata: metadata,
|
240
|
+
classMap: class_map,
|
241
|
+
events: events
|
242
|
+
}.compact
|
243
|
+
fname = AppMap::Util.scenario_filename(example_name)
|
244
|
+
|
245
|
+
File.write(File.join(APPMAP_OUTPUT_DIR, fname), JSON.generate(appmap))
|
246
|
+
end
|
247
|
+
|
248
|
+
def print_inventory
|
249
|
+
class_map = AppMap.class_map(@event_methods)
|
250
|
+
save 'Inventory', class_map, labels: %w[inventory]
|
251
|
+
end
|
252
|
+
|
253
|
+
def enabled?
|
254
|
+
ENV['APPMAP'] == 'true'
|
255
|
+
end
|
256
|
+
|
257
|
+
def run
|
258
|
+
init
|
259
|
+
at_exit do
|
260
|
+
print_inventory
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
if AppMap::RSpec.enabled?
|
268
|
+
require 'appmap'
|
269
|
+
require 'active_support/inflector/transliterate'
|
270
|
+
require 'rspec/core'
|
271
|
+
require 'rspec/core/example'
|
272
|
+
|
273
|
+
module RSpec
|
274
|
+
module Core
|
275
|
+
class Example
|
276
|
+
class << self
|
277
|
+
def wrap_example_block(example, fn)
|
278
|
+
proc do
|
279
|
+
AppMap::RSpec.begin_spec example
|
280
|
+
begin
|
281
|
+
instance_exec(&fn)
|
282
|
+
ensure
|
283
|
+
AppMap::RSpec.end_spec example
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
def self.new(*arguments, &block)
|
290
|
+
warn "Wrapping example_block for #{name}" if AppMap::RSpec::LOG
|
291
|
+
allocate.tap do |obj|
|
292
|
+
arguments[arguments.length - 1] = wrap_example_block(obj, arguments.last) if arguments.last.is_a?(Proc)
|
293
|
+
obj.send :initialize, *arguments, &block
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
AppMap::RSpec.run
|
301
|
+
end
|
data/lib/appmap/trace.rb
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AppMap
|
4
|
+
module Trace
|
5
|
+
class ScopedMethod < SimpleDelegator
|
6
|
+
attr_reader :defined_class, :static
|
7
|
+
|
8
|
+
def initialize(defined_class, method, static)
|
9
|
+
@defined_class = defined_class
|
10
|
+
@static = static
|
11
|
+
super(method)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class Tracing
|
16
|
+
def initialize
|
17
|
+
@Tracing = []
|
18
|
+
end
|
19
|
+
|
20
|
+
def empty?
|
21
|
+
@Tracing.empty?
|
22
|
+
end
|
23
|
+
|
24
|
+
def trace(enable: true)
|
25
|
+
Tracer.new.tap do |tracer|
|
26
|
+
@Tracing << tracer
|
27
|
+
tracer.enable if enable
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def enabled?
|
32
|
+
@Tracing.any?(&:enabled?)
|
33
|
+
end
|
34
|
+
|
35
|
+
def record_event(event, defined_class: nil, method: nil)
|
36
|
+
@Tracing.each do |tracer|
|
37
|
+
tracer.record_event(event, defined_class: defined_class, method: method)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def delete(tracer)
|
42
|
+
return unless @Tracing.member?(tracer)
|
43
|
+
|
44
|
+
@Tracing.delete(tracer)
|
45
|
+
tracer.disable
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class Tracer
|
51
|
+
# Records the events which happen in a program.
|
52
|
+
def initialize
|
53
|
+
@events = []
|
54
|
+
@methods = Set.new
|
55
|
+
@enabled = false
|
56
|
+
end
|
57
|
+
|
58
|
+
def enable
|
59
|
+
@enabled = true
|
60
|
+
end
|
61
|
+
|
62
|
+
def enabled?
|
63
|
+
@enabled
|
64
|
+
end
|
65
|
+
|
66
|
+
# Private function. Use AppMap.tracing#delete.
|
67
|
+
def disable # :nodoc:
|
68
|
+
@enabled = false
|
69
|
+
end
|
70
|
+
|
71
|
+
# Record a program execution event.
|
72
|
+
#
|
73
|
+
# The event should be one of the MethodEvent subclasses.
|
74
|
+
def record_event(event, defined_class: nil, method: nil)
|
75
|
+
return unless @enabled
|
76
|
+
|
77
|
+
@events << event
|
78
|
+
@methods << Trace::ScopedMethod.new(defined_class, method, event.static) if (defined_class && method && event.event == :call)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Gets a unique list of the methods that were invoked by the program.
|
82
|
+
def event_methods
|
83
|
+
@methods.to_a
|
84
|
+
end
|
85
|
+
|
86
|
+
# Whether there is an event available for processing.
|
87
|
+
def event?
|
88
|
+
!@events.empty?
|
89
|
+
end
|
90
|
+
|
91
|
+
# Gets the next available event, if any.
|
92
|
+
def next_event
|
93
|
+
@events.shift
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|