appmap 0.31.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.
- 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
|