appmap 0.26.1 → 0.32.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -3
- data/CHANGELOG.md +37 -0
- data/README.md +170 -29
- data/Rakefile +1 -1
- data/exe/appmap +3 -1
- data/lib/appmap.rb +56 -35
- data/lib/appmap/algorithm/stats.rb +2 -1
- data/lib/appmap/class_map.rb +21 -28
- data/lib/appmap/command/record.rb +2 -61
- data/lib/appmap/config.rb +89 -0
- data/lib/appmap/cucumber.rb +89 -0
- data/lib/appmap/event.rb +28 -19
- data/lib/appmap/hook.rb +56 -128
- data/lib/appmap/hook/method.rb +78 -0
- data/lib/appmap/metadata.rb +62 -0
- data/lib/appmap/middleware/remote_recording.rb +2 -6
- data/lib/appmap/minitest.rb +141 -0
- data/lib/appmap/rails/action_handler.rb +7 -7
- data/lib/appmap/rails/sql_handler.rb +10 -8
- data/lib/appmap/railtie.rb +2 -2
- data/lib/appmap/record.rb +27 -0
- data/lib/appmap/rspec.rb +20 -38
- data/lib/appmap/trace.rb +19 -11
- data/lib/appmap/util.rb +59 -0
- data/lib/appmap/version.rb +1 -1
- data/package-lock.json +3 -3
- data/spec/abstract_controller4_base_spec.rb +1 -1
- data/spec/abstract_controller_base_spec.rb +9 -2
- data/spec/config_spec.rb +3 -3
- data/spec/fixtures/hook/compare.rb +7 -0
- data/spec/fixtures/hook/singleton_method.rb +54 -0
- data/spec/fixtures/rails_users_app/Gemfile +1 -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/hook_spec.rb +228 -53
- data/spec/rails_spec_helper.rb +2 -0
- data/spec/record_sql_rails_pg_spec.rb +56 -33
- data/spec/rspec_feature_metadata_spec.rb +2 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/util_spec.rb +21 -0
- data/test/cli_test.rb +4 -4
- data/test/cucumber_test.rb +72 -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/minitest_test.rb +38 -0
- data/test/record_process_test.rb +35 -0
- data/test/test_helper.rb +1 -0
- metadata +39 -3
- data/spec/fixtures/hook/class_method.rb +0 -17
data/spec/rails_spec_helper.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'rails_spec_helper'
|
2
2
|
|
3
|
-
describe '
|
3
|
+
describe 'SQL events' do
|
4
4
|
before(:all) { @fixture_dir = 'spec/fixtures/rails_users_app' }
|
5
5
|
include_context 'Rails app pg database'
|
6
6
|
|
@@ -14,54 +14,77 @@ describe 'Record SQL queries in a Rails app' do
|
|
14
14
|
end
|
15
15
|
|
16
16
|
let(:tmpdir) { "tmp/spec/record_sql_rails_pg_spec" }
|
17
|
-
let(:appmap) { JSON.parse(File.read(appmap_json)).to_yaml }
|
18
17
|
|
19
|
-
|
18
|
+
describe 'fields' do
|
20
19
|
let(:test_line_number) { 8 }
|
21
20
|
let(:appmap_json) { File.join(tmpdir, 'appmap/rspec/Api_UsersController_POST_api_users_with_required_parameters_creates_a_user.appmap.json') }
|
21
|
+
let(:orm_module) { 'sequel' }
|
22
|
+
let(:appmap) { JSON.parse(File.read(appmap_json)) }
|
23
|
+
describe 'on a call event' do
|
24
|
+
let(:event) do
|
25
|
+
appmap['events'].find do |event|
|
26
|
+
event['event'] == 'call' &&
|
27
|
+
event.keys.include?('sql_query')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
it 'do not include function-only fields' do
|
31
|
+
expect(event.keys).to_not include('defined_class')
|
32
|
+
expect(event.keys).to_not include('method_id')
|
33
|
+
expect(event.keys).to_not include('path')
|
34
|
+
expect(event.keys).to_not include('lineno')
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe 'in a Rails app' do
|
40
|
+
let(:appmap) { JSON.parse(File.read(appmap_json)).to_yaml }
|
41
|
+
context 'while creating a new record' do
|
42
|
+
let(:test_line_number) { 8 }
|
43
|
+
let(:appmap_json) { File.join(tmpdir, 'appmap/rspec/Api_UsersController_POST_api_users_with_required_parameters_creates_a_user.appmap.json') }
|
22
44
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
45
|
+
context 'using Sequel ORM' do
|
46
|
+
let(:orm_module) { 'sequel' }
|
47
|
+
it 'detects the sql INSERT' do
|
48
|
+
expect(appmap).to include(<<-SQL_QUERY.strip)
|
27
49
|
sql_query:
|
28
50
|
sql: INSERT INTO "users" ("login") VALUES ('alice') RETURNING *
|
29
|
-
|
51
|
+
SQL_QUERY
|
52
|
+
end
|
30
53
|
end
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
expect(appmap).to include(<<-SQL_QUERY.strip)
|
54
|
+
context 'using ActiveRecord ORM' do
|
55
|
+
let(:orm_module) { 'activerecord' }
|
56
|
+
it 'detects the sql INSERT' do
|
57
|
+
expect(appmap).to include(<<-SQL_QUERY.strip)
|
36
58
|
sql_query:
|
37
59
|
sql: INSERT INTO "users" ("login") VALUES ($1) RETURNING "id"
|
38
|
-
|
60
|
+
SQL_QUERY
|
61
|
+
end
|
39
62
|
end
|
40
63
|
end
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
expect(appmap).to include(<<-SQL_QUERY.strip)
|
64
|
+
|
65
|
+
context 'while listing records' do
|
66
|
+
let(:test_line_number) { 23 }
|
67
|
+
let(:appmap_json) { File.join(tmpdir, 'appmap/rspec/Api_UsersController_GET_api_users_lists_the_users.appmap.json') }
|
68
|
+
|
69
|
+
context 'using Sequel ORM' do
|
70
|
+
let(:orm_module) { 'sequel' }
|
71
|
+
it 'detects the sql SELECT' do
|
72
|
+
expect(appmap).to include(<<-SQL_QUERY.strip)
|
51
73
|
sql_query:
|
52
74
|
sql: SELECT * FROM "users"
|
53
|
-
|
54
|
-
|
55
|
-
|
75
|
+
SQL_QUERY
|
76
|
+
|
77
|
+
expect(appmap).to include('sql:')
|
78
|
+
end
|
56
79
|
end
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
expect(appmap).to include(<<-SQL_QUERY.strip)
|
80
|
+
context 'using ActiveRecord ORM' do
|
81
|
+
let(:orm_module) { 'activerecord' }
|
82
|
+
it 'detects the sql SELECT' do
|
83
|
+
expect(appmap).to include(<<-SQL_QUERY.strip)
|
62
84
|
sql_query:
|
63
85
|
sql: SELECT "users".* FROM "users"
|
64
|
-
|
86
|
+
SQL_QUERY
|
87
|
+
end
|
65
88
|
end
|
66
89
|
end
|
67
90
|
end
|
data/spec/spec_helper.rb
CHANGED
data/spec/util_spec.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'appmap/util'
|
5
|
+
|
6
|
+
describe AppMap::Util, docker: false do
|
7
|
+
let(:subject) { AppMap::Util.method(:scenario_filename) }
|
8
|
+
describe 'scenario_filename' do
|
9
|
+
it 'leaves short names alone' do
|
10
|
+
expect(subject.call('foobar')).to eq('foobar.appmap.json')
|
11
|
+
end
|
12
|
+
it 'has a customizable suffix' do
|
13
|
+
expect(subject.call('foobar', extension: '.json')).to eq('foobar.json')
|
14
|
+
end
|
15
|
+
it 'limits the filename length' do
|
16
|
+
fname = (0...104).map { |i| ((i % 26) + 97).chr }.join
|
17
|
+
|
18
|
+
expect(subject.call(fname, max_length: 50)).to eq('abcdefghijklmno-RAd_SFbH1sUZ_OXfwPsfzw.appmap.json')
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/test/cli_test.rb
CHANGED
@@ -55,11 +55,11 @@ class CLITest < Minitest::Test
|
|
55
55
|
assert_equal <<~OUTPUT.strip, output.strip
|
56
56
|
Class frequency:
|
57
57
|
----------------
|
58
|
-
|
58
|
+
1 Main
|
59
59
|
|
60
60
|
Method frequency:
|
61
61
|
----------------
|
62
|
-
|
62
|
+
1 Main.say_hello
|
63
63
|
OUTPUT
|
64
64
|
end
|
65
65
|
|
@@ -79,13 +79,13 @@ class CLITest < Minitest::Test
|
|
79
79
|
"class_frequency": [
|
80
80
|
{
|
81
81
|
"name": "Main",
|
82
|
-
"count":
|
82
|
+
"count": 1
|
83
83
|
}
|
84
84
|
],
|
85
85
|
"method_frequency": [
|
86
86
|
{
|
87
87
|
"name": "Main.say_hello",
|
88
|
-
"count":
|
88
|
+
"count": 1
|
89
89
|
}
|
90
90
|
]
|
91
91
|
}
|
@@ -0,0 +1,72 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'test_helper'
|
5
|
+
require 'English'
|
6
|
+
|
7
|
+
class CucumberTest < Minitest::Test
|
8
|
+
def perform_test(dir)
|
9
|
+
Bundler.with_clean_env do
|
10
|
+
Dir.chdir "test/fixtures/#{dir}" do
|
11
|
+
FileUtils.rm_rf 'tmp'
|
12
|
+
system 'bundle config --local local.appmap ../../..'
|
13
|
+
system 'bundle'
|
14
|
+
system({ 'APPMAP' => 'true' }, %(bundle exec cucumber))
|
15
|
+
|
16
|
+
yield
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_cucumber
|
22
|
+
perform_test 'cucumber_recorder' do
|
23
|
+
appmap_file = 'tmp/appmap/cucumber/Say_hello.appmap.json'
|
24
|
+
|
25
|
+
assert File.file?(appmap_file),
|
26
|
+
%(appmap output file does not exist in #{Dir.new('tmp/appmap/cucumber').entries.join(', ')})
|
27
|
+
appmap = JSON.parse(File.read(appmap_file))
|
28
|
+
assert_equal AppMap::APPMAP_FORMAT_VERSION, appmap['version']
|
29
|
+
assert_includes appmap.keys, 'metadata'
|
30
|
+
metadata = appmap['metadata']
|
31
|
+
|
32
|
+
assert_equal 'say_hello', metadata['feature_group']
|
33
|
+
assert_equal 'I can say hello', metadata['feature']
|
34
|
+
assert_equal 'Say hello', metadata['name']
|
35
|
+
assert_includes metadata.keys, 'client'
|
36
|
+
assert_equal({ name: 'appmap', url: AppMap::URL, version: AppMap::VERSION }.stringify_keys, metadata['client'])
|
37
|
+
assert_includes metadata.keys, 'recorder'
|
38
|
+
assert_equal({ name: 'cucumber' }.stringify_keys, metadata['recorder'])
|
39
|
+
|
40
|
+
assert_includes metadata.keys, 'frameworks'
|
41
|
+
cucumber = metadata['frameworks'].select {|f| f['name'] == 'cucumber'}
|
42
|
+
assert_equal 1, cucumber.count
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_cucumber4
|
47
|
+
perform_test 'cucumber4_recorder' do
|
48
|
+
appmap_file = 'tmp/appmap/cucumber/Say_hello.appmap.json'
|
49
|
+
|
50
|
+
assert File.file?(appmap_file),
|
51
|
+
%(appmap output file does not exist in #{Dir.new('tmp/appmap/cucumber').entries.join(', ')})
|
52
|
+
appmap = JSON.parse(File.read(appmap_file))
|
53
|
+
assert_equal AppMap::APPMAP_FORMAT_VERSION, appmap['version']
|
54
|
+
assert_includes appmap.keys, 'metadata'
|
55
|
+
metadata = appmap['metadata']
|
56
|
+
|
57
|
+
assert_equal 'say_hello', metadata['feature_group']
|
58
|
+
# In cucumber4, there's no access to the feature name from within the executing scenario
|
59
|
+
# (as far as I can tell).
|
60
|
+
assert_equal 'Say hello', metadata['feature']
|
61
|
+
assert_equal 'Say hello', metadata['name']
|
62
|
+
assert_includes metadata.keys, 'client'
|
63
|
+
assert_equal({ name: 'appmap', url: AppMap::URL, version: AppMap::VERSION }.stringify_keys, metadata['client'])
|
64
|
+
assert_includes metadata.keys, 'recorder'
|
65
|
+
assert_equal({ name: 'cucumber' }.stringify_keys, metadata['recorder'])
|
66
|
+
|
67
|
+
assert_includes metadata.keys, 'frameworks'
|
68
|
+
cucumber = metadata['frameworks'].select {|f| f['name'] == 'cucumber'}
|
69
|
+
assert_equal 1, cucumber.count
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'test_helper'
|
5
|
+
require 'English'
|
6
|
+
|
7
|
+
class MinitestTest < Minitest::Test
|
8
|
+
def perform_test(test_name)
|
9
|
+
Bundler.with_clean_env do
|
10
|
+
Dir.chdir 'test/fixtures/minitest_recorder' do
|
11
|
+
FileUtils.rm_rf 'tmp'
|
12
|
+
system 'bundle config --local local.appmap ../../..'
|
13
|
+
system 'bundle'
|
14
|
+
system({ 'APPMAP' => 'true' }, %(bundle exec ruby -Ilib -Itest test/#{test_name}_test.rb))
|
15
|
+
|
16
|
+
yield
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_hello
|
22
|
+
perform_test 'hello' do
|
23
|
+
appmap_file = 'tmp/appmap/minitest/Hello_hello.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 'minitest_recorder', metadata['app']
|
31
|
+
assert_equal 'minitest', metadata['recorder']['name']
|
32
|
+
assert_equal 'ruby', metadata['language']['name']
|
33
|
+
assert_equal 'Hello', metadata['feature_group']
|
34
|
+
assert_equal 'hello', metadata['feature']
|
35
|
+
assert_equal 'Hello hello', metadata['name']
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|