appmap 0.28.1 → 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 +4 -4
- data/CHANGELOG.md +14 -1
- data/README.md +24 -0
- data/Rakefile +1 -1
- data/lib/appmap.rb +7 -6
- data/lib/appmap/class_map.rb +15 -23
- data/lib/appmap/config.rb +91 -0
- data/lib/appmap/hook.rb +92 -128
- data/lib/appmap/metadata.rb +1 -1
- data/lib/appmap/minitest.rb +141 -0
- data/lib/appmap/record.rb +27 -0
- data/lib/appmap/rspec.rb +1 -1
- data/lib/appmap/trace.rb +9 -1
- data/lib/appmap/version.rb +1 -1
- data/spec/config_spec.rb +3 -3
- data/spec/fixtures/hook/compare.rb +7 -0
- data/spec/fixtures/hook/openssl_sign.rb +87 -0
- data/spec/hook_spec.rb +141 -18
- data/spec/util_spec.rb +1 -1
- 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
- metadata +15 -2
data/lib/appmap/metadata.rb
CHANGED
@@ -0,0 +1,141 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'appmap/util'
|
4
|
+
|
5
|
+
module AppMap
|
6
|
+
# Integration of AppMap with Minitest. When enabled with APPMAP=true, the AppMap tracer will
|
7
|
+
# be activated around each test.
|
8
|
+
module Minitest
|
9
|
+
APPMAP_OUTPUT_DIR = 'tmp/appmap/minitest'
|
10
|
+
LOG = false
|
11
|
+
|
12
|
+
def self.metadata
|
13
|
+
AppMap.detect_metadata
|
14
|
+
end
|
15
|
+
|
16
|
+
Recording = Struct.new(:test) do
|
17
|
+
def initialize(test)
|
18
|
+
super
|
19
|
+
|
20
|
+
warn "Starting recording of test #{test.class}.#{test.name}" if AppMap::Minitest::LOG
|
21
|
+
@trace = AppMap.tracing.trace
|
22
|
+
end
|
23
|
+
|
24
|
+
def finish
|
25
|
+
warn "Finishing recording of test #{test.class}.#{test.name}" if AppMap::Minitest::LOG
|
26
|
+
|
27
|
+
events = []
|
28
|
+
AppMap.tracing.delete @trace
|
29
|
+
|
30
|
+
events << @trace.next_event.to_h while @trace.event?
|
31
|
+
|
32
|
+
AppMap::Minitest.add_event_methods @trace.event_methods
|
33
|
+
|
34
|
+
class_map = AppMap.class_map(@trace.event_methods)
|
35
|
+
|
36
|
+
feature_group = test.class.name.underscore.split('_')[0...-1].join('_').capitalize
|
37
|
+
feature_name = test.name.split('_')[1..-1].join(' ')
|
38
|
+
scenario_name = [ feature_group, feature_name ].join(' ')
|
39
|
+
|
40
|
+
AppMap::Minitest.save scenario_name,
|
41
|
+
class_map,
|
42
|
+
events: events,
|
43
|
+
feature_name: feature_name,
|
44
|
+
feature_group_name: feature_group
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
@recordings_by_test = {}
|
49
|
+
@event_methods = Set.new
|
50
|
+
|
51
|
+
class << self
|
52
|
+
def init
|
53
|
+
warn 'Configuring AppMap recorder for Minitest'
|
54
|
+
|
55
|
+
FileUtils.mkdir_p APPMAP_OUTPUT_DIR
|
56
|
+
end
|
57
|
+
|
58
|
+
def begin_test(test)
|
59
|
+
@recordings_by_test[test.object_id] = Recording.new(test)
|
60
|
+
end
|
61
|
+
|
62
|
+
def end_test(test)
|
63
|
+
recording = @recordings_by_test.delete(test.object_id)
|
64
|
+
return warn "No recording found for #{test}" unless recording
|
65
|
+
|
66
|
+
recording.finish
|
67
|
+
end
|
68
|
+
|
69
|
+
def config
|
70
|
+
@config or raise "AppMap is not configured"
|
71
|
+
end
|
72
|
+
|
73
|
+
def add_event_methods(event_methods)
|
74
|
+
@event_methods += event_methods
|
75
|
+
end
|
76
|
+
|
77
|
+
def save(example_name, class_map, events: nil, feature_name: nil, feature_group_name: nil, labels: nil)
|
78
|
+
metadata = AppMap::Minitest.metadata.tap do |m|
|
79
|
+
m[:name] = example_name
|
80
|
+
m[:app] = AppMap.configuration.name
|
81
|
+
m[:feature] = feature_name if feature_name
|
82
|
+
m[:feature_group] = feature_group_name if feature_group_name
|
83
|
+
m[:frameworks] ||= []
|
84
|
+
m[:frameworks] << {
|
85
|
+
name: 'minitest',
|
86
|
+
version: Gem.loaded_specs['minitest']&.version&.to_s
|
87
|
+
}
|
88
|
+
m[:recorder] = {
|
89
|
+
name: 'minitest'
|
90
|
+
}
|
91
|
+
end
|
92
|
+
|
93
|
+
appmap = {
|
94
|
+
version: AppMap::APPMAP_FORMAT_VERSION,
|
95
|
+
metadata: metadata,
|
96
|
+
classMap: class_map,
|
97
|
+
events: events
|
98
|
+
}.compact
|
99
|
+
fname = AppMap::Util.scenario_filename(example_name)
|
100
|
+
|
101
|
+
File.write(File.join(APPMAP_OUTPUT_DIR, fname), JSON.generate(appmap))
|
102
|
+
end
|
103
|
+
|
104
|
+
def print_inventory
|
105
|
+
class_map = AppMap.class_map(@event_methods)
|
106
|
+
save 'Inventory', class_map, labels: %w[inventory]
|
107
|
+
end
|
108
|
+
|
109
|
+
def enabled?
|
110
|
+
ENV['APPMAP'] == 'true'
|
111
|
+
end
|
112
|
+
|
113
|
+
def run
|
114
|
+
init
|
115
|
+
at_exit do
|
116
|
+
print_inventory
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
if AppMap::Minitest.enabled?
|
124
|
+
require 'appmap'
|
125
|
+
require 'minitest/test'
|
126
|
+
|
127
|
+
class ::Minitest::Test
|
128
|
+
alias run_without_hook run
|
129
|
+
|
130
|
+
def run
|
131
|
+
AppMap::Minitest.begin_test self
|
132
|
+
begin
|
133
|
+
run_without_hook
|
134
|
+
ensure
|
135
|
+
AppMap::Minitest.end_test self
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
AppMap::Minitest.run
|
141
|
+
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
CHANGED
@@ -218,7 +218,7 @@ module AppMap
|
|
218
218
|
end
|
219
219
|
|
220
220
|
def save(example_name, class_map, events: nil, feature_name: nil, feature_group_name: nil, labels: nil)
|
221
|
-
metadata = RSpec.metadata.tap do |m|
|
221
|
+
metadata = AppMap::RSpec.metadata.tap do |m|
|
222
222
|
m[:name] = example_name
|
223
223
|
m[:app] = AppMap.configuration.name
|
224
224
|
m[:feature] = feature_name if feature_name
|
data/lib/appmap/trace.rb
CHANGED
@@ -2,7 +2,15 @@
|
|
2
2
|
|
3
3
|
module AppMap
|
4
4
|
module Trace
|
5
|
-
ScopedMethod
|
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
|
6
14
|
|
7
15
|
class Tracing
|
8
16
|
def initialize
|
data/lib/appmap/version.rb
CHANGED
data/spec/config_spec.rb
CHANGED
@@ -2,9 +2,9 @@
|
|
2
2
|
|
3
3
|
require 'rails_spec_helper'
|
4
4
|
require 'active_support/core_ext'
|
5
|
-
require 'appmap/
|
5
|
+
require 'appmap/config'
|
6
6
|
|
7
|
-
describe AppMap::
|
7
|
+
describe AppMap::Config, docker: false do
|
8
8
|
it 'loads from a Hash' do
|
9
9
|
config_data = {
|
10
10
|
name: 'test',
|
@@ -18,7 +18,7 @@ describe AppMap::Hook::Config do
|
|
18
18
|
}
|
19
19
|
]
|
20
20
|
}.deep_stringify_keys!
|
21
|
-
config = AppMap::
|
21
|
+
config = AppMap::Config.load(config_data)
|
22
22
|
|
23
23
|
expect(config.to_h.deep_stringify_keys!).to eq(config_data)
|
24
24
|
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# From the manual page https://ruby-doc.org/stdlib-2.5.1/libdoc/openssl/rdoc/OpenSSL.html
|
4
|
+
|
5
|
+
require 'openssl'
|
6
|
+
|
7
|
+
module OpenSSLExample
|
8
|
+
def OpenSSLExample.example
|
9
|
+
ca_key = OpenSSL::PKey::RSA.new 2048
|
10
|
+
pass_phrase = 'my secure pass phrase goes here'
|
11
|
+
|
12
|
+
cipher = OpenSSL::Cipher.new 'AES-256-CBC'
|
13
|
+
|
14
|
+
open 'tmp/ca_key.pem', 'w', 0644 do |io|
|
15
|
+
io.write ca_key.export(cipher, pass_phrase)
|
16
|
+
end
|
17
|
+
|
18
|
+
ca_name = OpenSSL::X509::Name.parse '/CN=ca/DC=example'
|
19
|
+
|
20
|
+
ca_cert = OpenSSL::X509::Certificate.new
|
21
|
+
ca_cert.serial = 0
|
22
|
+
ca_cert.version = 2
|
23
|
+
ca_cert.not_before = Time.now
|
24
|
+
ca_cert.not_after = Time.now + 86400
|
25
|
+
|
26
|
+
ca_cert.public_key = ca_key.public_key
|
27
|
+
ca_cert.subject = ca_name
|
28
|
+
ca_cert.issuer = ca_name
|
29
|
+
|
30
|
+
extension_factory = OpenSSL::X509::ExtensionFactory.new
|
31
|
+
extension_factory.subject_certificate = ca_cert
|
32
|
+
extension_factory.issuer_certificate = ca_cert
|
33
|
+
|
34
|
+
ca_cert.add_extension extension_factory.create_extension('subjectKeyIdentifier', 'hash')
|
35
|
+
ca_cert.add_extension extension_factory.create_extension('basicConstraints', 'CA:TRUE', true)
|
36
|
+
|
37
|
+
ca_cert.add_extension extension_factory.create_extension(
|
38
|
+
'keyUsage', 'cRLSign,keyCertSign', true)
|
39
|
+
|
40
|
+
ca_cert.sign ca_key, OpenSSL::Digest::SHA1.new
|
41
|
+
|
42
|
+
open 'tmp/ca_cert.pem', 'w' do |io|
|
43
|
+
io.write ca_cert.to_pem
|
44
|
+
end
|
45
|
+
|
46
|
+
csr = OpenSSL::X509::Request.new
|
47
|
+
csr.version = 0
|
48
|
+
csr.subject = OpenSSL::X509::Name.new([ ['CN', 'the name to sign', OpenSSL::ASN1::UTF8STRING] ])
|
49
|
+
csr.public_key = ca_key.public_key
|
50
|
+
csr.sign ca_key, OpenSSL::Digest::SHA1.new
|
51
|
+
|
52
|
+
open 'tmp/csr.pem', 'w' do |io|
|
53
|
+
io.write csr.to_pem
|
54
|
+
end
|
55
|
+
|
56
|
+
csr = OpenSSL::X509::Request.new File.read 'tmp/csr.pem'
|
57
|
+
|
58
|
+
raise 'CSR can not be verified' unless csr.verify csr.public_key
|
59
|
+
|
60
|
+
csr_cert = OpenSSL::X509::Certificate.new
|
61
|
+
csr_cert.serial = 0
|
62
|
+
csr_cert.version = 2
|
63
|
+
csr_cert.not_before = Time.now
|
64
|
+
csr_cert.not_after = Time.now + 600
|
65
|
+
|
66
|
+
csr_cert.subject = csr.subject
|
67
|
+
csr_cert.public_key = csr.public_key
|
68
|
+
csr_cert.issuer = ca_cert.subject
|
69
|
+
|
70
|
+
extension_factory = OpenSSL::X509::ExtensionFactory.new
|
71
|
+
extension_factory.subject_certificate = csr_cert
|
72
|
+
extension_factory.issuer_certificate = ca_cert
|
73
|
+
|
74
|
+
csr_cert.add_extension extension_factory.create_extension('basicConstraints', 'CA:FALSE')
|
75
|
+
|
76
|
+
csr_cert.add_extension extension_factory.create_extension(
|
77
|
+
'keyUsage', 'keyEncipherment,dataEncipherment,digitalSignature')
|
78
|
+
|
79
|
+
csr_cert.add_extension extension_factory.create_extension('subjectKeyIdentifier', 'hash')
|
80
|
+
|
81
|
+
csr_cert.sign ca_key, OpenSSL::Digest::SHA1.new
|
82
|
+
|
83
|
+
open 'tmp/csr_cert.pem', 'w' do |io|
|
84
|
+
io.write csr_cert.to_pem
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
data/spec/hook_spec.rb
CHANGED
@@ -15,7 +15,7 @@ module ShowYamlNulls
|
|
15
15
|
end
|
16
16
|
Psych::Visitors::YAMLTree.prepend(ShowYamlNulls)
|
17
17
|
|
18
|
-
describe 'AppMap class Hooking' do
|
18
|
+
describe 'AppMap class Hooking', docker: false do
|
19
19
|
def collect_events(tracer)
|
20
20
|
[].tap do |events|
|
21
21
|
while tracer.event?
|
@@ -30,7 +30,10 @@ describe 'AppMap class Hooking' do
|
|
30
30
|
(event[:parameters] || []).each(&delete_object_id)
|
31
31
|
(event[:exceptions] || []).each(&delete_object_id)
|
32
32
|
|
33
|
-
|
33
|
+
case event[:event]
|
34
|
+
when :call
|
35
|
+
event[:path] = event[:path].gsub(Gem.dir + '/', '')
|
36
|
+
when :return
|
34
37
|
# These should be removed from the appmap spec
|
35
38
|
%i[defined_class method_id path lineno static].each do |obsolete_field|
|
36
39
|
event.delete(obsolete_field)
|
@@ -42,21 +45,23 @@ describe 'AppMap class Hooking' do
|
|
42
45
|
|
43
46
|
def invoke_test_file(file, setup: nil, &block)
|
44
47
|
AppMap.configuration = nil
|
45
|
-
package = AppMap::
|
46
|
-
config = AppMap::
|
48
|
+
package = AppMap::Package.new(file, [])
|
49
|
+
config = AppMap::Config.new('hook_spec', [ package ])
|
47
50
|
AppMap.configuration = config
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
51
|
+
tracer = nil
|
52
|
+
AppMap::Hook.new(config).enable do
|
53
|
+
setup_result = setup.call if setup
|
54
|
+
|
55
|
+
tracer = AppMap.tracing.trace
|
56
|
+
AppMap::Event.reset_id_counter
|
57
|
+
begin
|
58
|
+
load file
|
59
|
+
yield setup_result
|
60
|
+
ensure
|
61
|
+
AppMap.tracing.delete(tracer)
|
62
|
+
end
|
59
63
|
end
|
64
|
+
|
60
65
|
[ config, tracer ]
|
61
66
|
end
|
62
67
|
|
@@ -104,7 +109,7 @@ describe 'AppMap class Hooking' do
|
|
104
109
|
InstanceMethod.new.say_default
|
105
110
|
end
|
106
111
|
expect(tracer.event_methods.to_a.map(&:defined_class)).to eq([ 'InstanceMethod' ])
|
107
|
-
expect(tracer.event_methods.to_a.map(&:
|
112
|
+
expect(tracer.event_methods.to_a.map(&:to_s)).to eq([ InstanceMethod.public_instance_method(:say_default).to_s ])
|
108
113
|
end
|
109
114
|
|
110
115
|
it 'builds a class map of invoked methods' do
|
@@ -403,14 +408,14 @@ describe 'AppMap class Hooking' do
|
|
403
408
|
events_yaml = <<~YAML
|
404
409
|
--- []
|
405
410
|
YAML
|
406
|
-
|
411
|
+
|
407
412
|
load 'spec/fixtures/hook/singleton_method.rb'
|
408
413
|
setup = -> { SingletonMethod.new_with_instance_method }
|
409
414
|
test_hook_behavior 'spec/fixtures/hook/singleton_method.rb', events_yaml, setup: setup do |s|
|
410
415
|
expect(s.say_instance_defined).to eq('defined for an instance')
|
411
416
|
end
|
412
417
|
end
|
413
|
-
|
418
|
+
|
414
419
|
it 'Reports exceptions' do
|
415
420
|
events_yaml = <<~YAML
|
416
421
|
---
|
@@ -450,4 +455,122 @@ describe 'AppMap class Hooking' do
|
|
450
455
|
expect { ExceptionMethod.new.raise_exception }.to raise_exception
|
451
456
|
end
|
452
457
|
end
|
458
|
+
|
459
|
+
context 'OpenSSL::X509::Certificate.sign' do
|
460
|
+
# OpenSSL::X509 is not being hooked.
|
461
|
+
# This might be because the class is being loaded before AppMap, and so the TracePoint
|
462
|
+
# set by AppMap doesn't see it.
|
463
|
+
xit 'is hooked' do
|
464
|
+
events_yaml = <<~YAML
|
465
|
+
---
|
466
|
+
YAML
|
467
|
+
test_hook_behavior 'spec/fixtures/hook/openssl_sign.rb', events_yaml do
|
468
|
+
expect(OpenSSLExample.example).to be_truthy
|
469
|
+
end
|
470
|
+
end
|
471
|
+
end
|
472
|
+
|
473
|
+
context 'ActiveSupport::SecurityUtils.secure_compare' do
|
474
|
+
it 'is hooked' do
|
475
|
+
events_yaml = <<~YAML
|
476
|
+
---
|
477
|
+
- :id: 1
|
478
|
+
:event: :call
|
479
|
+
:defined_class: Compare
|
480
|
+
:method_id: compare
|
481
|
+
:path: spec/fixtures/hook/compare.rb
|
482
|
+
:lineno: 4
|
483
|
+
:static: true
|
484
|
+
:parameters:
|
485
|
+
- :name: :s1
|
486
|
+
:class: String
|
487
|
+
:value: string
|
488
|
+
:kind: :req
|
489
|
+
- :name: :s2
|
490
|
+
:class: String
|
491
|
+
:value: string
|
492
|
+
:kind: :req
|
493
|
+
:receiver:
|
494
|
+
:class: Class
|
495
|
+
:value: Compare
|
496
|
+
- :id: 2
|
497
|
+
:event: :call
|
498
|
+
:defined_class: ActiveSupport::SecurityUtils
|
499
|
+
:method_id: secure_compare
|
500
|
+
:path: gems/activesupport-6.0.3.2/lib/active_support/security_utils.rb
|
501
|
+
:lineno: 26
|
502
|
+
:static: true
|
503
|
+
:parameters:
|
504
|
+
- :name: :a
|
505
|
+
:class: String
|
506
|
+
:value: string
|
507
|
+
:kind: :req
|
508
|
+
- :name: :b
|
509
|
+
:class: String
|
510
|
+
:value: string
|
511
|
+
:kind: :req
|
512
|
+
:receiver:
|
513
|
+
:class: Module
|
514
|
+
:value: ActiveSupport::SecurityUtils
|
515
|
+
- :id: 3
|
516
|
+
:event: :return
|
517
|
+
:parent_id: 2
|
518
|
+
:return_value:
|
519
|
+
:class: TrueClass
|
520
|
+
:value: 'true'
|
521
|
+
- :id: 4
|
522
|
+
:event: :return
|
523
|
+
:parent_id: 1
|
524
|
+
:return_value:
|
525
|
+
:class: TrueClass
|
526
|
+
:value: 'true'
|
527
|
+
YAML
|
528
|
+
|
529
|
+
test_hook_behavior 'spec/fixtures/hook/compare.rb', events_yaml do
|
530
|
+
expect(Compare.compare('string', 'string')).to be_truthy
|
531
|
+
end
|
532
|
+
end
|
533
|
+
|
534
|
+
it 'gets labeled in the classmap' do
|
535
|
+
classmap_yaml = <<~YAML
|
536
|
+
---
|
537
|
+
- :name: spec/fixtures/hook/compare.rb
|
538
|
+
:type: package
|
539
|
+
:children:
|
540
|
+
- :name: Compare
|
541
|
+
:type: class
|
542
|
+
:children:
|
543
|
+
- :name: compare
|
544
|
+
:type: function
|
545
|
+
:location: spec/fixtures/hook/compare.rb:4
|
546
|
+
:static: true
|
547
|
+
- :name: active_support
|
548
|
+
:type: package
|
549
|
+
:children:
|
550
|
+
- :name: ActiveSupport
|
551
|
+
:type: class
|
552
|
+
:children:
|
553
|
+
- :name: SecurityUtils
|
554
|
+
:type: class
|
555
|
+
:children:
|
556
|
+
- :name: secure_compare
|
557
|
+
:type: function
|
558
|
+
:location: gems/activesupport-6.0.3.2/lib/active_support/security_utils.rb:26
|
559
|
+
:static: true
|
560
|
+
:labels:
|
561
|
+
- security
|
562
|
+
YAML
|
563
|
+
|
564
|
+
config, tracer = invoke_test_file 'spec/fixtures/hook/compare.rb' do
|
565
|
+
expect(Compare.compare('string', 'string')).to be_truthy
|
566
|
+
end
|
567
|
+
cm = AppMap::ClassMap.build_from_methods(config, tracer.event_methods)
|
568
|
+
entry = cm[1][:children][0][:children][0][:children][0]
|
569
|
+
# Sanity check, make sure we got the right one
|
570
|
+
expect(entry[:name]).to eq('secure_compare')
|
571
|
+
spec = Gem::Specification.find_by_name('activesupport')
|
572
|
+
entry[:location].gsub!(spec.base_dir + '/', '')
|
573
|
+
expect(Diffy::Diff.new(cm.to_yaml, classmap_yaml).to_s).to eq('')
|
574
|
+
end
|
575
|
+
end
|
453
576
|
end
|