appmap 0.33.0 → 0.34.5
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/.gitignore +1 -1
 - data/.rbenv-gemsets +1 -0
 - data/CHANGELOG.md +27 -0
 - data/README.md +17 -2
 - data/Rakefile +10 -3
 - data/appmap.gemspec +5 -0
 - data/ext/appmap/appmap.c +95 -0
 - data/ext/appmap/extconf.rb +6 -0
 - data/lib/appmap.rb +3 -0
 - data/lib/appmap/class_map.rb +11 -6
 - data/lib/appmap/config.rb +51 -25
 - data/lib/appmap/cucumber.rb +19 -2
 - data/lib/appmap/hook.rb +44 -16
 - data/lib/appmap/hook/method.rb +53 -25
 - data/lib/appmap/rails/sql_handler.rb +5 -10
 - data/lib/appmap/rspec.rb +1 -1
 - data/lib/appmap/util.rb +18 -1
 - data/lib/appmap/version.rb +1 -1
 - data/spec/fixtures/hook/instance_method.rb +4 -0
 - data/spec/fixtures/hook/singleton_method.rb +21 -12
 - data/spec/hook_spec.rb +148 -13
 - data/test/cli_test.rb +10 -0
 - data/test/fixtures/openssl_recorder/Gemfile +3 -0
 - data/test/fixtures/openssl_recorder/appmap.yml +3 -0
 - data/test/fixtures/openssl_recorder/lib/openssl_cert_sign.rb +94 -0
 - data/test/fixtures/openssl_recorder/lib/openssl_encrypt.rb +34 -0
 - data/test/fixtures/openssl_recorder/lib/openssl_key_sign.rb +28 -0
 - data/test/openssl_test.rb +203 -0
 - metadata +55 -3
 
    
        data/lib/appmap/hook.rb
    CHANGED
    
    | 
         @@ -6,23 +6,21 @@ module AppMap 
     | 
|
| 
       6 
6 
     | 
    
         
             
              class Hook
         
     | 
| 
       7 
7 
     | 
    
         
             
                LOG = (ENV['DEBUG'] == 'true')
         
     | 
| 
       8 
8 
     | 
    
         | 
| 
      
 9 
     | 
    
         
            +
                @unbound_method_arity = ::UnboundMethod.instance_method(:arity)
         
     | 
| 
      
 10 
     | 
    
         
            +
                @method_arity = ::Method.instance_method(:arity)
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
       9 
12 
     | 
    
         
             
                class << self
         
     | 
| 
      
 13 
     | 
    
         
            +
                  def lock_builtins
         
     | 
| 
      
 14 
     | 
    
         
            +
                    return if @builtins_hooked
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                    @builtins_hooked = true
         
     | 
| 
      
 17 
     | 
    
         
            +
                  end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
       10 
19 
     | 
    
         
             
                  # Return the class, separator ('.' or '#'), and method name for
         
     | 
| 
       11 
20 
     | 
    
         
             
                  # the given method.
         
     | 
| 
       12 
21 
     | 
    
         
             
                  def qualify_method_name(method)
         
     | 
| 
       13 
22 
     | 
    
         
             
                    if method.owner.singleton_class?
         
     | 
| 
       14 
     | 
    
         
            -
                       
     | 
| 
       15 
     | 
    
         
            -
                      # #<Class:Foo> or
         
     | 
| 
       16 
     | 
    
         
            -
                      # #<Class:#<Bar:0x0123ABC>>. Retrieve the name of
         
     | 
| 
       17 
     | 
    
         
            -
                      # the class from the string.
         
     | 
| 
       18 
     | 
    
         
            -
                      #
         
     | 
| 
       19 
     | 
    
         
            -
                      # (There really isn't a better way to do this. The
         
     | 
| 
       20 
     | 
    
         
            -
                      # singleton's reference to the class it was created
         
     | 
| 
       21 
     | 
    
         
            -
                      # from is stored in an instance variable named
         
     | 
| 
       22 
     | 
    
         
            -
                      # '__attached__'. It doesn't have the '@' prefix, so
         
     | 
| 
       23 
     | 
    
         
            -
                      # it's internal only, and not accessible from user
         
     | 
| 
       24 
     | 
    
         
            -
                      # code.)
         
     | 
| 
       25 
     | 
    
         
            -
                      class_name = /#<Class:((#<(?<cls>.*?):)|((?<cls>.*?)>))/.match(method.owner.to_s)['cls']
         
     | 
| 
      
 23 
     | 
    
         
            +
                      class_name = singleton_method_owner_name(method)
         
     | 
| 
       26 
24 
     | 
    
         
             
                      [ class_name, '.', method.name ]
         
     | 
| 
       27 
25 
     | 
    
         
             
                    else
         
     | 
| 
       28 
26 
     | 
    
         
             
                      [ method.owner.name, '#', method.name ]
         
     | 
| 
         @@ -39,6 +37,8 @@ module AppMap 
     | 
|
| 
       39 
37 
     | 
    
         
             
                def enable &block
         
     | 
| 
       40 
38 
     | 
    
         
             
                  require 'appmap/hook/method'
         
     | 
| 
       41 
39 
     | 
    
         | 
| 
      
 40 
     | 
    
         
            +
                  hook_builtins
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
       42 
42 
     | 
    
         
             
                  tp = TracePoint.new(:end) do |trace_point|
         
     | 
| 
       43 
43 
     | 
    
         
             
                    cls = trace_point.self
         
     | 
| 
       44 
44 
     | 
    
         | 
| 
         @@ -47,12 +47,10 @@ module AppMap 
     | 
|
| 
       47 
47 
     | 
    
         | 
| 
       48 
48 
     | 
    
         
             
                    hook = lambda do |hook_cls|
         
     | 
| 
       49 
49 
     | 
    
         
             
                      lambda do |method_id|
         
     | 
| 
       50 
     | 
    
         
            -
                        next if method_id.to_s =~ /_hooked_by_appmap$/
         
     | 
| 
       51 
     | 
    
         
            -
             
     | 
| 
       52 
50 
     | 
    
         
             
                        method = hook_cls.public_instance_method(method_id)
         
     | 
| 
       53 
51 
     | 
    
         
             
                        hook_method = Hook::Method.new(hook_cls, method)
         
     | 
| 
       54 
52 
     | 
    
         | 
| 
       55 
     | 
    
         
            -
                        warn "AppMap: Examining #{ 
     | 
| 
      
 53 
     | 
    
         
            +
                        warn "AppMap: Examining #{hook_cls} #{method.name}" if LOG
         
     | 
| 
       56 
54 
     | 
    
         | 
| 
       57 
55 
     | 
    
         
             
                        disasm = RubyVM::InstructionSequence.disasm(method)
         
     | 
| 
       58 
56 
     | 
    
         
             
                        # Skip methods that have no instruction sequence, as they are obviously trivial.
         
     | 
| 
         @@ -63,7 +61,7 @@ module AppMap 
     | 
|
| 
       63 
61 
     | 
    
         
             
                        next if /\AAppMap[:\.]/.match?(hook_method.method_display_name)
         
     | 
| 
       64 
62 
     | 
    
         | 
| 
       65 
63 
     | 
    
         
             
                        next unless \
         
     | 
| 
       66 
     | 
    
         
            -
                          config.always_hook?( 
     | 
| 
      
 64 
     | 
    
         
            +
                          config.always_hook?(hook_cls, method.name) ||
         
     | 
| 
       67 
65 
     | 
    
         
             
                          config.included_by_location?(method)
         
     | 
| 
       68 
66 
     | 
    
         | 
| 
       69 
67 
     | 
    
         
             
                        hook_method.activate
         
     | 
| 
         @@ -76,5 +74,35 @@ module AppMap 
     | 
|
| 
       76 
74 
     | 
    
         | 
| 
       77 
75 
     | 
    
         
             
                  tp.enable(&block)
         
     | 
| 
       78 
76 
     | 
    
         
             
                end
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                def hook_builtins
         
     | 
| 
      
 79 
     | 
    
         
            +
                  return unless self.class.lock_builtins
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
                  class_from_string = lambda do |fq_class|
         
     | 
| 
      
 82 
     | 
    
         
            +
                    fq_class.split('::').inject(Object) do |mod, class_name|
         
     | 
| 
      
 83 
     | 
    
         
            +
                      mod.const_get(class_name)
         
     | 
| 
      
 84 
     | 
    
         
            +
                    end
         
     | 
| 
      
 85 
     | 
    
         
            +
                  end
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                  Config::BUILTIN_METHODS.each do |class_name, hook|
         
     | 
| 
      
 88 
     | 
    
         
            +
                    require hook.package.package_name if hook.package.package_name
         
     | 
| 
      
 89 
     | 
    
         
            +
                    Array(hook.method_names).each do |method_name|
         
     | 
| 
      
 90 
     | 
    
         
            +
                      method_name = method_name.to_sym
         
     | 
| 
      
 91 
     | 
    
         
            +
                      cls = class_from_string.(class_name)
         
     | 
| 
      
 92 
     | 
    
         
            +
                      method = \
         
     | 
| 
      
 93 
     | 
    
         
            +
                        begin
         
     | 
| 
      
 94 
     | 
    
         
            +
                          cls.instance_method(method_name)
         
     | 
| 
      
 95 
     | 
    
         
            +
                        rescue NameError
         
     | 
| 
      
 96 
     | 
    
         
            +
                          cls.method(method_name) rescue nil
         
     | 
| 
      
 97 
     | 
    
         
            +
                        end
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
                      if method
         
     | 
| 
      
 100 
     | 
    
         
            +
                        Hook::Method.new(cls, method).activate
         
     | 
| 
      
 101 
     | 
    
         
            +
                      else
         
     | 
| 
      
 102 
     | 
    
         
            +
                        warn "Method #{method_name} not found on #{cls.name}" 
         
     | 
| 
      
 103 
     | 
    
         
            +
                      end
         
     | 
| 
      
 104 
     | 
    
         
            +
                    end
         
     | 
| 
      
 105 
     | 
    
         
            +
                  end
         
     | 
| 
      
 106 
     | 
    
         
            +
                end
         
     | 
| 
       79 
107 
     | 
    
         
             
              end
         
     | 
| 
       80 
108 
     | 
    
         
             
            end
         
     | 
    
        data/lib/appmap/hook/method.rb
    CHANGED
    
    | 
         @@ -1,63 +1,91 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            module AppMap
         
     | 
| 
       2 
2 
     | 
    
         
             
              class Hook
         
     | 
| 
       3 
3 
     | 
    
         
             
                class Method
         
     | 
| 
       4 
     | 
    
         
            -
                  attr_reader :hook_class, :hook_method 
     | 
| 
      
 4 
     | 
    
         
            +
                  attr_reader :hook_class, :hook_method
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                  # +method_display_name+ may be nil if name resolution gets
         
     | 
| 
      
 7 
     | 
    
         
            +
                  # deferred until runtime (e.g. for a singleton method on an
         
     | 
| 
      
 8 
     | 
    
         
            +
                  # embedded Struct).
         
     | 
| 
      
 9 
     | 
    
         
            +
                  attr_reader :method_display_name
         
     | 
| 
       5 
10 
     | 
    
         | 
| 
       6 
11 
     | 
    
         
             
                  HOOK_DISABLE_KEY = 'AppMap::Hook.disable'
         
     | 
| 
       7 
12 
     | 
    
         
             
                  private_constant :HOOK_DISABLE_KEY
         
     | 
| 
       8 
13 
     | 
    
         | 
| 
      
 14 
     | 
    
         
            +
                  # Grab the definition of Time.now here, to avoid interfering
         
     | 
| 
      
 15 
     | 
    
         
            +
                  # with the method we're hooking.
         
     | 
| 
      
 16 
     | 
    
         
            +
                  TIME_NOW = Time.method(:now)
         
     | 
| 
      
 17 
     | 
    
         
            +
                  private_constant :TIME_NOW
         
     | 
| 
      
 18 
     | 
    
         
            +
                  
         
     | 
| 
       9 
19 
     | 
    
         
             
                  def initialize(hook_class, hook_method)
         
     | 
| 
       10 
20 
     | 
    
         
             
                    @hook_class = hook_class
         
     | 
| 
       11 
21 
     | 
    
         
             
                    @hook_method = hook_method
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                    # Get the class for the method, if it's known.
         
     | 
| 
       12 
24 
     | 
    
         
             
                    @defined_class, method_symbol = Hook.qualify_method_name(@hook_method)
         
     | 
| 
       13 
     | 
    
         
            -
                    @method_display_name = [@defined_class, method_symbol, @hook_method.name].join
         
     | 
| 
      
 25 
     | 
    
         
            +
                    @method_display_name = [@defined_class, method_symbol, @hook_method.name].join if @defined_class
         
     | 
| 
       14 
26 
     | 
    
         
             
                  end
         
     | 
| 
       15 
27 
     | 
    
         | 
| 
       16 
28 
     | 
    
         
             
                  def activate
         
     | 
| 
       17 
     | 
    
         
            -
                     
     | 
| 
      
 29 
     | 
    
         
            +
                    if Hook::LOG
         
     | 
| 
      
 30 
     | 
    
         
            +
                      msg = if method_display_name
         
     | 
| 
      
 31 
     | 
    
         
            +
                              "#{method_display_name}"
         
     | 
| 
      
 32 
     | 
    
         
            +
                            else
         
     | 
| 
      
 33 
     | 
    
         
            +
                              "#{hook_method.name} (class resolution deferred)"
         
     | 
| 
      
 34 
     | 
    
         
            +
                            end
         
     | 
| 
      
 35 
     | 
    
         
            +
                      warn "AppMap: Hooking " + msg
         
     | 
| 
      
 36 
     | 
    
         
            +
                    end
         
     | 
| 
       18 
37 
     | 
    
         | 
| 
      
 38 
     | 
    
         
            +
                    defined_class = @defined_class
         
     | 
| 
       19 
39 
     | 
    
         
             
                    hook_method = self.hook_method
         
     | 
| 
       20 
40 
     | 
    
         
             
                    before_hook = self.method(:before_hook)
         
     | 
| 
       21 
41 
     | 
    
         
             
                    after_hook = self.method(:after_hook)
         
     | 
| 
       22 
42 
     | 
    
         
             
                    with_disabled_hook = self.method(:with_disabled_hook)
         
     | 
| 
       23 
43 
     | 
    
         | 
| 
       24 
     | 
    
         
            -
                     
     | 
| 
       25 
     | 
    
         
            -
             
     | 
| 
      
 44 
     | 
    
         
            +
                    hook_method_def = nil
         
     | 
| 
      
 45 
     | 
    
         
            +
                    hook_class.instance_eval do 
         
     | 
| 
      
 46 
     | 
    
         
            +
                      hook_method_def = Proc.new do |*args, &block|
         
     | 
| 
      
 47 
     | 
    
         
            +
                        instance_method = hook_method.bind(self).to_proc
         
     | 
| 
       26 
48 
     | 
    
         | 
| 
       27 
     | 
    
         
            -
             
     | 
| 
       28 
     | 
    
         
            -
             
     | 
| 
       29 
     | 
    
         
            -
             
     | 
| 
      
 49 
     | 
    
         
            +
                        # We may not have gotten the class for the method during
         
     | 
| 
      
 50 
     | 
    
         
            +
                        # initialization (e.g. for a singleton method on an embedded
         
     | 
| 
      
 51 
     | 
    
         
            +
                        # struct), so make sure we have it now.
         
     | 
| 
      
 52 
     | 
    
         
            +
                        defined_class,_ = Hook.qualify_method_name(hook_method) unless defined_class
         
     | 
| 
       30 
53 
     | 
    
         | 
| 
       31 
     | 
    
         
            -
             
     | 
| 
       32 
     | 
    
         
            -
                         
     | 
| 
       33 
     | 
    
         
            -
             
     | 
| 
       34 
     | 
    
         
            -
             
     | 
| 
       35 
     | 
    
         
            -
             
     | 
| 
       36 
     | 
    
         
            -
             
     | 
| 
       37 
     | 
    
         
            -
                         
     | 
| 
       38 
     | 
    
         
            -
             
     | 
| 
       39 
     | 
    
         
            -
                        exception =  
     | 
| 
       40 
     | 
    
         
            -
                         
     | 
| 
       41 
     | 
    
         
            -
             
     | 
| 
       42 
     | 
    
         
            -
                         
     | 
| 
       43 
     | 
    
         
            -
                           
     | 
| 
      
 54 
     | 
    
         
            +
                        hook_disabled = Thread.current[HOOK_DISABLE_KEY]
         
     | 
| 
      
 55 
     | 
    
         
            +
                        enabled = true if !hook_disabled && AppMap.tracing.enabled?
         
     | 
| 
      
 56 
     | 
    
         
            +
                        return instance_method.call(*args, &block) unless enabled
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                        call_event, start_time = with_disabled_hook.() do
         
     | 
| 
      
 59 
     | 
    
         
            +
                          before_hook.(self, defined_class, args)
         
     | 
| 
      
 60 
     | 
    
         
            +
                        end
         
     | 
| 
      
 61 
     | 
    
         
            +
                        return_value = nil
         
     | 
| 
      
 62 
     | 
    
         
            +
                        exception = nil
         
     | 
| 
      
 63 
     | 
    
         
            +
                        begin
         
     | 
| 
      
 64 
     | 
    
         
            +
                          return_value = instance_method.(*args, &block)
         
     | 
| 
      
 65 
     | 
    
         
            +
                        rescue
         
     | 
| 
      
 66 
     | 
    
         
            +
                          exception = $ERROR_INFO
         
     | 
| 
      
 67 
     | 
    
         
            +
                          raise
         
     | 
| 
      
 68 
     | 
    
         
            +
                        ensure
         
     | 
| 
      
 69 
     | 
    
         
            +
                          with_disabled_hook.() do
         
     | 
| 
      
 70 
     | 
    
         
            +
                            after_hook.(call_event, start_time, return_value, exception)
         
     | 
| 
      
 71 
     | 
    
         
            +
                          end
         
     | 
| 
       44 
72 
     | 
    
         
             
                        end
         
     | 
| 
       45 
73 
     | 
    
         
             
                      end
         
     | 
| 
       46 
74 
     | 
    
         
             
                    end
         
     | 
| 
      
 75 
     | 
    
         
            +
                    hook_class.define_method_with_arity(hook_method.name, hook_method.arity, hook_method_def)
         
     | 
| 
       47 
76 
     | 
    
         
             
                  end
         
     | 
| 
       48 
     | 
    
         
            -
             
     | 
| 
       49 
77 
     | 
    
         
             
                  protected
         
     | 
| 
       50 
78 
     | 
    
         | 
| 
       51 
     | 
    
         
            -
                  def before_hook(receiver, args)
         
     | 
| 
      
 79 
     | 
    
         
            +
                  def before_hook(receiver, defined_class, args)
         
     | 
| 
       52 
80 
     | 
    
         
             
                    require 'appmap/event'
         
     | 
| 
       53 
81 
     | 
    
         
             
                    call_event = AppMap::Event::MethodCall.build_from_invocation(defined_class, hook_method, receiver, args)
         
     | 
| 
       54 
82 
     | 
    
         
             
                    AppMap.tracing.record_event call_event, defined_class: defined_class, method: hook_method
         
     | 
| 
       55 
     | 
    
         
            -
                    [ call_event,  
     | 
| 
      
 83 
     | 
    
         
            +
                    [ call_event, TIME_NOW.call ]
         
     | 
| 
       56 
84 
     | 
    
         
             
                  end
         
     | 
| 
       57 
85 
     | 
    
         | 
| 
       58 
86 
     | 
    
         
             
                  def after_hook(call_event, start_time, return_value, exception)
         
     | 
| 
       59 
87 
     | 
    
         
             
                    require 'appmap/event'
         
     | 
| 
       60 
     | 
    
         
            -
                    elapsed =  
     | 
| 
      
 88 
     | 
    
         
            +
                    elapsed = TIME_NOW.call - start_time
         
     | 
| 
       61 
89 
     | 
    
         
             
                    return_event = \
         
     | 
| 
       62 
90 
     | 
    
         
             
                      AppMap::Event::MethodReturn.build_from_invocation call_event.id, elapsed, return_value, exception
         
     | 
| 
       63 
91 
     | 
    
         
             
                    AppMap.tracing.record_event return_event
         
     | 
| 
         @@ -73,20 +73,15 @@ module AppMap 
     | 
|
| 
       73 
73 
     | 
    
         | 
| 
       74 
74 
     | 
    
         
             
                    class ActiveRecordExaminer
         
     | 
| 
       75 
75 
     | 
    
         
             
                      def server_version
         
     | 
| 
       76 
     | 
    
         
            -
                         
     | 
| 
       77 
     | 
    
         
            -
             
     | 
| 
       78 
     | 
    
         
            -
                          ActiveRecord::Base.connection.postgresql_version
         
     | 
| 
       79 
     | 
    
         
            -
                        when :sqlite
         
     | 
| 
       80 
     | 
    
         
            -
                          ActiveRecord::Base.connection.database_version.to_s
         
     | 
| 
       81 
     | 
    
         
            -
                        else
         
     | 
| 
       82 
     | 
    
         
            -
                          warn "Unable to determine database version for #{database_type.inspect}"
         
     | 
| 
       83 
     | 
    
         
            -
                        end
         
     | 
| 
      
 76 
     | 
    
         
            +
                        ActiveRecord::Base.connection.try(:database_version) ||\
         
     | 
| 
      
 77 
     | 
    
         
            +
                          warn("Unable to determine database version for #{database_type.inspect}")
         
     | 
| 
       84 
78 
     | 
    
         
             
                      end
         
     | 
| 
       85 
79 
     | 
    
         | 
| 
       86 
80 
     | 
    
         
             
                      def database_type
         
     | 
| 
       87 
     | 
    
         
            -
                         
     | 
| 
      
 81 
     | 
    
         
            +
                        type = ActiveRecord::Base.connection.adapter_name.downcase.to_sym
         
     | 
| 
      
 82 
     | 
    
         
            +
                        type = :postgres if type == :postgresql
         
     | 
| 
       88 
83 
     | 
    
         | 
| 
       89 
     | 
    
         
            -
                         
     | 
| 
      
 84 
     | 
    
         
            +
                        type
         
     | 
| 
       90 
85 
     | 
    
         
             
                      end
         
     | 
| 
       91 
86 
     | 
    
         | 
| 
       92 
87 
     | 
    
         
             
                      def execute_query(sql)
         
     | 
    
        data/lib/appmap/rspec.rb
    CHANGED
    
    | 
         @@ -154,7 +154,7 @@ module AppMap 
     | 
|
| 
       154 
154 
     | 
    
         
             
                    end
         
     | 
| 
       155 
155 
     | 
    
         | 
| 
       156 
156 
     | 
    
         
             
                    labels = labels.map(&:to_s).map(&:strip).reject(&:blank?).map(&:downcase).uniq
         
     | 
| 
       157 
     | 
    
         
            -
                    description.reject!(&:nil?).reject(&:blank?)
         
     | 
| 
      
 157 
     | 
    
         
            +
                    description.reject!(&:nil?).reject!(&:blank?)
         
     | 
| 
       158 
158 
     | 
    
         
             
                    default_description = description.last
         
     | 
| 
       159 
159 
     | 
    
         
             
                    description.reverse!
         
     | 
| 
       160 
160 
     | 
    
         | 
    
        data/lib/appmap/util.rb
    CHANGED
    
    | 
         @@ -36,6 +36,23 @@ module AppMap 
     | 
|
| 
       36 
36 
     | 
    
         
             
                    [ fname, extension ].join
         
     | 
| 
       37 
37 
     | 
    
         
             
                  end
         
     | 
| 
       38 
38 
     | 
    
         | 
| 
      
 39 
     | 
    
         
            +
                  # sanitize_paths removes ephemeral values from objects with
         
     | 
| 
      
 40 
     | 
    
         
            +
                  # embedded paths (e.g. an event or a classmap), making events
         
     | 
| 
      
 41 
     | 
    
         
            +
                  # easier to compare across runs.
         
     | 
| 
      
 42 
     | 
    
         
            +
                  def sanitize_paths(h)
         
     | 
| 
      
 43 
     | 
    
         
            +
                    require 'hashie'
         
     | 
| 
      
 44 
     | 
    
         
            +
                    h.extend(Hashie::Extensions::DeepLocate)
         
     | 
| 
      
 45 
     | 
    
         
            +
                    keys = %i(path location)
         
     | 
| 
      
 46 
     | 
    
         
            +
                    h.deep_locate ->(k,v,o) {
         
     | 
| 
      
 47 
     | 
    
         
            +
                      next unless keys.include?(k)
         
     | 
| 
      
 48 
     | 
    
         
            +
                      
         
     | 
| 
      
 49 
     | 
    
         
            +
                      fix = ->(v) {v.gsub(%r{#{Gem.dir}/gems/.*(?=lib)}, '')}
         
     | 
| 
      
 50 
     | 
    
         
            +
                      keys.each {|k| o[k] = fix.(o[k]) if o[k] }
         
     | 
| 
      
 51 
     | 
    
         
            +
                    }
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                    h
         
     | 
| 
      
 54 
     | 
    
         
            +
                  end
         
     | 
| 
      
 55 
     | 
    
         
            +
                  
         
     | 
| 
       39 
56 
     | 
    
         
             
                  # sanitize_event removes ephemeral values from an event, making
         
     | 
| 
       40 
57 
     | 
    
         
             
                  # events easier to compare across runs.
         
     | 
| 
       41 
58 
     | 
    
         
             
                  def sanitize_event(event, &block)
         
     | 
| 
         @@ -49,7 +66,7 @@ module AppMap 
     | 
|
| 
       49 
66 
     | 
    
         | 
| 
       50 
67 
     | 
    
         
             
                    case event[:event]
         
     | 
| 
       51 
68 
     | 
    
         
             
                    when :call
         
     | 
| 
       52 
     | 
    
         
            -
                      event 
     | 
| 
      
 69 
     | 
    
         
            +
                      sanitize_paths(event)
         
     | 
| 
       53 
70 
     | 
    
         
             
                    end
         
     | 
| 
       54 
71 
     | 
    
         | 
| 
       55 
72 
     | 
    
         
             
                    event
         
     | 
    
        data/lib/appmap/version.rb
    CHANGED
    
    
| 
         @@ -15,6 +15,20 @@ class SingletonMethod 
     | 
|
| 
       15 
15 
     | 
    
         
             
                'defined with self class scope'
         
     | 
| 
       16 
16 
     | 
    
         
             
              end
         
     | 
| 
       17 
17 
     | 
    
         | 
| 
      
 18 
     | 
    
         
            +
              module AddMethod
         
     | 
| 
      
 19 
     | 
    
         
            +
                def self.included(base)
         
     | 
| 
      
 20 
     | 
    
         
            +
                  base.module_eval do
         
     | 
| 
      
 21 
     | 
    
         
            +
                    define_method "added_method" do
         
     | 
| 
      
 22 
     | 
    
         
            +
                      _added_method
         
     | 
| 
      
 23 
     | 
    
         
            +
                    end
         
     | 
| 
      
 24 
     | 
    
         
            +
                  end
         
     | 
| 
      
 25 
     | 
    
         
            +
                end
         
     | 
| 
      
 26 
     | 
    
         
            +
                
         
     | 
| 
      
 27 
     | 
    
         
            +
                def _added_method
         
     | 
| 
      
 28 
     | 
    
         
            +
                  'defined by including a module'
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
              end
         
     | 
| 
      
 31 
     | 
    
         
            +
              
         
     | 
| 
       18 
32 
     | 
    
         
             
              # When called, do_include calls +include+ to bring in the module
         
     | 
| 
       19 
33 
     | 
    
         
             
              # AddMethod. AddMethod defines a new instance method, which gets
         
     | 
| 
       20 
34 
     | 
    
         
             
              # added to the singleton class of SingletonMethod.
         
     | 
| 
         @@ -32,23 +46,18 @@ class SingletonMethod 
     | 
|
| 
       32 
46 
     | 
    
         
             
                  end
         
     | 
| 
       33 
47 
     | 
    
         
             
                end
         
     | 
| 
       34 
48 
     | 
    
         
             
              end
         
     | 
| 
       35 
     | 
    
         
            -
              
         
     | 
| 
       36 
     | 
    
         
            -
              def to_s
         
     | 
| 
       37 
     | 
    
         
            -
                'Singleton Method fixture'
         
     | 
| 
       38 
     | 
    
         
            -
              end
         
     | 
| 
       39 
     | 
    
         
            -
            end
         
     | 
| 
       40 
49 
     | 
    
         | 
| 
       41 
     | 
    
         
            -
             
     | 
| 
       42 
     | 
    
         
            -
             
     | 
| 
       43 
     | 
    
         
            -
             
     | 
| 
       44 
     | 
    
         
            -
             
     | 
| 
       45 
     | 
    
         
            -
                    _added_method
         
     | 
| 
      
 50 
     | 
    
         
            +
              STRUCT_TEST = Struct.new(:attr) do
         
     | 
| 
      
 51 
     | 
    
         
            +
                class << self
         
     | 
| 
      
 52 
     | 
    
         
            +
                  def say_struct_singleton
         
     | 
| 
      
 53 
     | 
    
         
            +
                    'singleton for a struct'
         
     | 
| 
       46 
54 
     | 
    
         
             
                  end
         
     | 
| 
       47 
55 
     | 
    
         
             
                end
         
     | 
| 
       48 
56 
     | 
    
         
             
              end
         
     | 
| 
       49 
57 
     | 
    
         | 
| 
       50 
     | 
    
         
            -
              def  
     | 
| 
       51 
     | 
    
         
            -
                ' 
     | 
| 
      
 58 
     | 
    
         
            +
              def to_s
         
     | 
| 
      
 59 
     | 
    
         
            +
                'Singleton Method fixture'
         
     | 
| 
       52 
60 
     | 
    
         
             
              end
         
     | 
| 
       53 
61 
     | 
    
         
             
            end
         
     | 
| 
       54 
62 
     | 
    
         | 
| 
      
 63 
     | 
    
         
            +
             
     | 
    
        data/spec/hook_spec.rb
    CHANGED
    
    | 
         @@ -22,12 +22,12 @@ describe 'AppMap class Hooking', docker: false do 
     | 
|
| 
       22 
22 
     | 
    
         
             
                  while tracer.event?
         
     | 
| 
       23 
23 
     | 
    
         
             
                    events << tracer.next_event.to_h
         
     | 
| 
       24 
24 
     | 
    
         
             
                  end
         
     | 
| 
       25 
     | 
    
         
            -
                end.map(&AppMap::Util.method(:sanitize_event)) 
     | 
| 
      
 25 
     | 
    
         
            +
                end.map(&AppMap::Util.method(:sanitize_event))
         
     | 
| 
       26 
26 
     | 
    
         
             
              end
         
     | 
| 
       27 
27 
     | 
    
         | 
| 
       28 
28 
     | 
    
         
             
              def invoke_test_file(file, setup: nil, &block)
         
     | 
| 
       29 
29 
     | 
    
         
             
                AppMap.configuration = nil
         
     | 
| 
       30 
     | 
    
         
            -
                package = AppMap::Package.new(file 
     | 
| 
      
 30 
     | 
    
         
            +
                package = AppMap::Config::Package.new(file)
         
     | 
| 
       31 
31 
     | 
    
         
             
                config = AppMap::Config.new('hook_spec', [ package ])
         
     | 
| 
       32 
32 
     | 
    
         
             
                AppMap.configuration = config
         
     | 
| 
       33 
33 
     | 
    
         
             
                tracer = nil
         
     | 
| 
         @@ -50,8 +50,9 @@ describe 'AppMap class Hooking', docker: false do 
     | 
|
| 
       50 
50 
     | 
    
         
             
              def test_hook_behavior(file, events_yaml, setup: nil, &block)
         
     | 
| 
       51 
51 
     | 
    
         
             
                config, tracer = invoke_test_file(file, setup: setup, &block)
         
     | 
| 
       52 
52 
     | 
    
         | 
| 
       53 
     | 
    
         
            -
                events = collect_events(tracer)
         
     | 
| 
       54 
     | 
    
         
            -
             
     | 
| 
      
 53 
     | 
    
         
            +
                events = collect_events(tracer).to_yaml
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                expect(Diffy::Diff.new(events_yaml, events).to_s).to eq('')
         
     | 
| 
       55 
56 
     | 
    
         | 
| 
       56 
57 
     | 
    
         
             
                [ config, tracer ]
         
     | 
| 
       57 
58 
     | 
    
         
             
              end
         
     | 
| 
         @@ -99,7 +100,7 @@ describe 'AppMap class Hooking', docker: false do 
     | 
|
| 
       99 
100 
     | 
    
         
             
                  InstanceMethod.new.say_default
         
     | 
| 
       100 
101 
     | 
    
         
             
                end
         
     | 
| 
       101 
102 
     | 
    
         
             
                class_map = AppMap.class_map(tracer.event_methods).to_yaml
         
     | 
| 
       102 
     | 
    
         
            -
                expect(Diffy::Diff.new( 
     | 
| 
      
 103 
     | 
    
         
            +
                expect(Diffy::Diff.new(<<~YAML, class_map).to_s).to eq('')
         
     | 
| 
       103 
104 
     | 
    
         
             
                ---
         
     | 
| 
       104 
105 
     | 
    
         
             
                - :name: spec/fixtures/hook/instance_method.rb
         
     | 
| 
       105 
106 
     | 
    
         
             
                  :type: package
         
     | 
| 
         @@ -341,7 +342,7 @@ describe 'AppMap class Hooking', docker: false do 
     | 
|
| 
       341 
342 
     | 
    
         
             
                  :defined_class: SingletonMethod
         
     | 
| 
       342 
343 
     | 
    
         
             
                  :method_id: added_method
         
     | 
| 
       343 
344 
     | 
    
         
             
                  :path: spec/fixtures/hook/singleton_method.rb
         
     | 
| 
       344 
     | 
    
         
            -
                  :lineno:  
     | 
| 
      
 345 
     | 
    
         
            +
                  :lineno: 21
         
     | 
| 
       345 
346 
     | 
    
         
             
                  :static: false
         
     | 
| 
       346 
347 
     | 
    
         
             
                  :parameters: []
         
     | 
| 
       347 
348 
     | 
    
         
             
                  :receiver:
         
     | 
| 
         @@ -349,10 +350,10 @@ describe 'AppMap class Hooking', docker: false do 
     | 
|
| 
       349 
350 
     | 
    
         
             
                    :value: Singleton Method fixture
         
     | 
| 
       350 
351 
     | 
    
         
             
                - :id: 2
         
     | 
| 
       351 
352 
     | 
    
         
             
                  :event: :call
         
     | 
| 
       352 
     | 
    
         
            -
                  :defined_class: AddMethod
         
     | 
| 
      
 353 
     | 
    
         
            +
                  :defined_class: SingletonMethod::AddMethod
         
     | 
| 
       353 
354 
     | 
    
         
             
                  :method_id: _added_method
         
     | 
| 
       354 
355 
     | 
    
         
             
                  :path: spec/fixtures/hook/singleton_method.rb
         
     | 
| 
       355 
     | 
    
         
            -
                  :lineno:  
     | 
| 
      
 356 
     | 
    
         
            +
                  :lineno: 27
         
     | 
| 
       356 
357 
     | 
    
         
             
                  :static: false
         
     | 
| 
       357 
358 
     | 
    
         
             
                  :parameters: []
         
     | 
| 
       358 
359 
     | 
    
         
             
                  :receiver:
         
     | 
| 
         @@ -394,10 +395,44 @@ describe 'AppMap class Hooking', docker: false do 
     | 
|
| 
       394 
395 
     | 
    
         
             
                load 'spec/fixtures/hook/singleton_method.rb'
         
     | 
| 
       395 
396 
     | 
    
         
             
                setup = -> { SingletonMethod.new_with_instance_method }
         
     | 
| 
       396 
397 
     | 
    
         
             
                test_hook_behavior 'spec/fixtures/hook/singleton_method.rb', events_yaml, setup: setup do |s|
         
     | 
| 
      
 398 
     | 
    
         
            +
                  # Make sure we're testing the right thing
         
     | 
| 
      
 399 
     | 
    
         
            +
                  say_instance_defined = s.method(:say_instance_defined)
         
     | 
| 
      
 400 
     | 
    
         
            +
                  expect(say_instance_defined.owner.to_s).to start_with('#<Class:#<SingletonMethod:')
         
     | 
| 
      
 401 
     | 
    
         
            +
             
     | 
| 
      
 402 
     | 
    
         
            +
                  # Verify the native extension works as expected
         
     | 
| 
      
 403 
     | 
    
         
            +
                  expect(AppMap::Hook.singleton_method_owner_name(say_instance_defined)).to eq('SingletonMethod')
         
     | 
| 
      
 404 
     | 
    
         
            +
                  
         
     | 
| 
       397 
405 
     | 
    
         
             
                  expect(s.say_instance_defined).to eq('defined for an instance')
         
     | 
| 
       398 
406 
     | 
    
         
             
                end
         
     | 
| 
       399 
407 
     | 
    
         
             
              end
         
     | 
| 
       400 
408 
     | 
    
         | 
| 
      
 409 
     | 
    
         
            +
              it 'hooks a singleton method on an embedded struct' do
         
     | 
| 
      
 410 
     | 
    
         
            +
                events_yaml = <<~YAML
         
     | 
| 
      
 411 
     | 
    
         
            +
                ---
         
     | 
| 
      
 412 
     | 
    
         
            +
                - :id: 1
         
     | 
| 
      
 413 
     | 
    
         
            +
                  :event: :call
         
     | 
| 
      
 414 
     | 
    
         
            +
                  :defined_class: SingletonMethod::STRUCT_TEST
         
     | 
| 
      
 415 
     | 
    
         
            +
                  :method_id: say_struct_singleton
         
     | 
| 
      
 416 
     | 
    
         
            +
                  :path: spec/fixtures/hook/singleton_method.rb
         
     | 
| 
      
 417 
     | 
    
         
            +
                  :lineno: 52
         
     | 
| 
      
 418 
     | 
    
         
            +
                  :static: true
         
     | 
| 
      
 419 
     | 
    
         
            +
                  :parameters: []
         
     | 
| 
      
 420 
     | 
    
         
            +
                  :receiver:
         
     | 
| 
      
 421 
     | 
    
         
            +
                    :class: Class
         
     | 
| 
      
 422 
     | 
    
         
            +
                    :value: SingletonMethod::STRUCT_TEST
         
     | 
| 
      
 423 
     | 
    
         
            +
                - :id: 2
         
     | 
| 
      
 424 
     | 
    
         
            +
                  :event: :return
         
     | 
| 
      
 425 
     | 
    
         
            +
                  :parent_id: 1
         
     | 
| 
      
 426 
     | 
    
         
            +
                  :return_value:
         
     | 
| 
      
 427 
     | 
    
         
            +
                    :class: String
         
     | 
| 
      
 428 
     | 
    
         
            +
                    :value: singleton for a struct
         
     | 
| 
      
 429 
     | 
    
         
            +
                YAML
         
     | 
| 
      
 430 
     | 
    
         
            +
             
     | 
| 
      
 431 
     | 
    
         
            +
                test_hook_behavior 'spec/fixtures/hook/singleton_method.rb', events_yaml do
         
     | 
| 
      
 432 
     | 
    
         
            +
                  expect(SingletonMethod::STRUCT_TEST.say_struct_singleton).to eq('singleton for a struct')
         
     | 
| 
      
 433 
     | 
    
         
            +
                end
         
     | 
| 
      
 434 
     | 
    
         
            +
              end
         
     | 
| 
      
 435 
     | 
    
         
            +
              
         
     | 
| 
       401 
436 
     | 
    
         
             
              it 'Reports exceptions' do
         
     | 
| 
       402 
437 
     | 
    
         
             
                events_yaml = <<~YAML
         
     | 
| 
       403 
438 
     | 
    
         
             
                ---
         
     | 
| 
         @@ -465,7 +500,7 @@ describe 'AppMap class Hooking', docker: false do 
     | 
|
| 
       465 
500 
     | 
    
         
             
                    :event: :call
         
     | 
| 
       466 
501 
     | 
    
         
             
                    :defined_class: ActiveSupport::SecurityUtils
         
     | 
| 
       467 
502 
     | 
    
         
             
                    :method_id: secure_compare
         
     | 
| 
       468 
     | 
    
         
            -
                    :path:  
     | 
| 
      
 503 
     | 
    
         
            +
                    :path: lib/active_support/security_utils.rb
         
     | 
| 
       469 
504 
     | 
    
         
             
                    :lineno: 26
         
     | 
| 
       470 
505 
     | 
    
         
             
                    :static: true
         
     | 
| 
       471 
506 
     | 
    
         
             
                    :parameters:
         
     | 
| 
         @@ -481,12 +516,52 @@ describe 'AppMap class Hooking', docker: false do 
     | 
|
| 
       481 
516 
     | 
    
         
             
                      :class: Module
         
     | 
| 
       482 
517 
     | 
    
         
             
                      :value: ActiveSupport::SecurityUtils
         
     | 
| 
       483 
518 
     | 
    
         
             
                  - :id: 3
         
     | 
| 
      
 519 
     | 
    
         
            +
                    :event: :call
         
     | 
| 
      
 520 
     | 
    
         
            +
                    :defined_class: Digest::Instance
         
     | 
| 
      
 521 
     | 
    
         
            +
                    :method_id: digest
         
     | 
| 
      
 522 
     | 
    
         
            +
                    :path: Digest::Instance#digest
         
     | 
| 
      
 523 
     | 
    
         
            +
                    :static: false
         
     | 
| 
      
 524 
     | 
    
         
            +
                    :parameters:
         
     | 
| 
      
 525 
     | 
    
         
            +
                    - :name: arg
         
     | 
| 
      
 526 
     | 
    
         
            +
                      :class: String
         
     | 
| 
      
 527 
     | 
    
         
            +
                      :value: string
         
     | 
| 
      
 528 
     | 
    
         
            +
                      :kind: :rest
         
     | 
| 
      
 529 
     | 
    
         
            +
                    :receiver:
         
     | 
| 
      
 530 
     | 
    
         
            +
                      :class: Digest::SHA256
         
     | 
| 
      
 531 
     | 
    
         
            +
                      :value: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
         
     | 
| 
      
 532 
     | 
    
         
            +
                  - :id: 4
         
     | 
| 
      
 533 
     | 
    
         
            +
                    :event: :return
         
     | 
| 
      
 534 
     | 
    
         
            +
                    :parent_id: 3
         
     | 
| 
      
 535 
     | 
    
         
            +
                    :return_value:
         
     | 
| 
      
 536 
     | 
    
         
            +
                      :class: String
         
     | 
| 
      
 537 
     | 
    
         
            +
                      :value: "G2__)__qc____X____3_].\\x02y__.___/_"
         
     | 
| 
      
 538 
     | 
    
         
            +
                  - :id: 5
         
     | 
| 
      
 539 
     | 
    
         
            +
                    :event: :call
         
     | 
| 
      
 540 
     | 
    
         
            +
                    :defined_class: Digest::Instance
         
     | 
| 
      
 541 
     | 
    
         
            +
                    :method_id: digest
         
     | 
| 
      
 542 
     | 
    
         
            +
                    :path: Digest::Instance#digest
         
     | 
| 
      
 543 
     | 
    
         
            +
                    :static: false
         
     | 
| 
      
 544 
     | 
    
         
            +
                    :parameters:
         
     | 
| 
      
 545 
     | 
    
         
            +
                    - :name: arg
         
     | 
| 
      
 546 
     | 
    
         
            +
                      :class: String
         
     | 
| 
      
 547 
     | 
    
         
            +
                      :value: string
         
     | 
| 
      
 548 
     | 
    
         
            +
                      :kind: :rest
         
     | 
| 
      
 549 
     | 
    
         
            +
                    :receiver:
         
     | 
| 
      
 550 
     | 
    
         
            +
                      :class: Digest::SHA256
         
     | 
| 
      
 551 
     | 
    
         
            +
                      :value: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
         
     | 
| 
      
 552 
     | 
    
         
            +
                  - :id: 6
         
     | 
| 
      
 553 
     | 
    
         
            +
                    :event: :return
         
     | 
| 
      
 554 
     | 
    
         
            +
                    :parent_id: 5
         
     | 
| 
      
 555 
     | 
    
         
            +
                    :return_value:
         
     | 
| 
      
 556 
     | 
    
         
            +
                      :class: String
         
     | 
| 
      
 557 
     | 
    
         
            +
                      :value: "G2__)__qc____X____3_].\\x02y__.___/_"
         
     | 
| 
      
 558 
     | 
    
         
            +
                  - :id: 7
         
     | 
| 
       484 
559 
     | 
    
         
             
                    :event: :return
         
     | 
| 
       485 
560 
     | 
    
         
             
                    :parent_id: 2
         
     | 
| 
       486 
561 
     | 
    
         
             
                    :return_value:
         
     | 
| 
       487 
562 
     | 
    
         
             
                      :class: TrueClass
         
     | 
| 
       488 
563 
     | 
    
         
             
                      :value: 'true'
         
     | 
| 
       489 
     | 
    
         
            -
                  - :id:  
     | 
| 
      
 564 
     | 
    
         
            +
                  - :id: 8
         
     | 
| 
       490 
565 
     | 
    
         
             
                    :event: :return
         
     | 
| 
       491 
566 
     | 
    
         
             
                    :parent_id: 1
         
     | 
| 
       492 
567 
     | 
    
         
             
                    :return_value:
         
     | 
| 
         @@ -523,22 +598,82 @@ describe 'AppMap class Hooking', docker: false do 
     | 
|
| 
       523 
598 
     | 
    
         
             
                        :children:
         
     | 
| 
       524 
599 
     | 
    
         
             
                        - :name: secure_compare
         
     | 
| 
       525 
600 
     | 
    
         
             
                          :type: function
         
     | 
| 
       526 
     | 
    
         
            -
                          :location:  
     | 
| 
      
 601 
     | 
    
         
            +
                          :location: lib/active_support/security_utils.rb:26
         
     | 
| 
       527 
602 
     | 
    
         
             
                          :static: true
         
     | 
| 
       528 
603 
     | 
    
         
             
                          :labels:
         
     | 
| 
       529 
604 
     | 
    
         
             
                          - security
         
     | 
| 
      
 605 
     | 
    
         
            +
                          - crypto
         
     | 
| 
      
 606 
     | 
    
         
            +
                  - :name: openssl
         
     | 
| 
      
 607 
     | 
    
         
            +
                    :type: package
         
     | 
| 
      
 608 
     | 
    
         
            +
                    :children:
         
     | 
| 
      
 609 
     | 
    
         
            +
                    - :name: Digest
         
     | 
| 
      
 610 
     | 
    
         
            +
                      :type: class
         
     | 
| 
      
 611 
     | 
    
         
            +
                      :children:
         
     | 
| 
      
 612 
     | 
    
         
            +
                      - :name: Instance
         
     | 
| 
      
 613 
     | 
    
         
            +
                        :type: class
         
     | 
| 
      
 614 
     | 
    
         
            +
                        :children:
         
     | 
| 
      
 615 
     | 
    
         
            +
                        - :name: digest
         
     | 
| 
      
 616 
     | 
    
         
            +
                          :type: function
         
     | 
| 
      
 617 
     | 
    
         
            +
                          :location: Digest::Instance#digest
         
     | 
| 
      
 618 
     | 
    
         
            +
                          :static: false
         
     | 
| 
      
 619 
     | 
    
         
            +
                          :labels:
         
     | 
| 
      
 620 
     | 
    
         
            +
                          - security
         
     | 
| 
      
 621 
     | 
    
         
            +
                          - crypto
         
     | 
| 
       530 
622 
     | 
    
         
             
                  YAML
         
     | 
| 
       531 
623 
     | 
    
         | 
| 
       532 
624 
     | 
    
         
             
                  config, tracer = invoke_test_file 'spec/fixtures/hook/compare.rb' do
         
     | 
| 
       533 
625 
     | 
    
         
             
                    expect(Compare.compare('string', 'string')).to be_truthy
         
     | 
| 
       534 
626 
     | 
    
         
             
                  end
         
     | 
| 
       535 
     | 
    
         
            -
                  cm = AppMap::ClassMap.build_from_methods(config, tracer.event_methods)
         
     | 
| 
      
 627 
     | 
    
         
            +
                  cm = AppMap::Util.sanitize_paths(AppMap::ClassMap.build_from_methods(config, tracer.event_methods))
         
     | 
| 
       536 
628 
     | 
    
         
             
                  entry = cm[1][:children][0][:children][0][:children][0]
         
     | 
| 
       537 
629 
     | 
    
         
             
                  # Sanity check, make sure we got the right one
         
     | 
| 
       538 
630 
     | 
    
         
             
                  expect(entry[:name]).to eq('secure_compare')
         
     | 
| 
       539 
631 
     | 
    
         
             
                  spec = Gem::Specification.find_by_name('activesupport')
         
     | 
| 
       540 
632 
     | 
    
         
             
                  entry[:location].gsub!(spec.base_dir + '/', '')
         
     | 
| 
       541 
     | 
    
         
            -
                  expect(Diffy::Diff.new(cm.to_yaml 
     | 
| 
      
 633 
     | 
    
         
            +
                  expect(Diffy::Diff.new(classmap_yaml, cm.to_yaml).to_s).to eq('')
         
     | 
| 
      
 634 
     | 
    
         
            +
                end
         
     | 
| 
      
 635 
     | 
    
         
            +
              end
         
     | 
| 
      
 636 
     | 
    
         
            +
             
     | 
| 
      
 637 
     | 
    
         
            +
              it "doesn't cause expectations on Time.now to fail" do
         
     | 
| 
      
 638 
     | 
    
         
            +
                events_yaml = <<~YAML
         
     | 
| 
      
 639 
     | 
    
         
            +
                ---
         
     | 
| 
      
 640 
     | 
    
         
            +
                - :id: 1
         
     | 
| 
      
 641 
     | 
    
         
            +
                  :event: :call
         
     | 
| 
      
 642 
     | 
    
         
            +
                  :defined_class: InstanceMethod
         
     | 
| 
      
 643 
     | 
    
         
            +
                  :method_id: say_the_time
         
     | 
| 
      
 644 
     | 
    
         
            +
                  :path: spec/fixtures/hook/instance_method.rb
         
     | 
| 
      
 645 
     | 
    
         
            +
                  :lineno: 24
         
     | 
| 
      
 646 
     | 
    
         
            +
                  :static: false
         
     | 
| 
      
 647 
     | 
    
         
            +
                  :parameters: []
         
     | 
| 
      
 648 
     | 
    
         
            +
                  :receiver:
         
     | 
| 
      
 649 
     | 
    
         
            +
                    :class: InstanceMethod
         
     | 
| 
      
 650 
     | 
    
         
            +
                    :value: Instance Method fixture
         
     | 
| 
      
 651 
     | 
    
         
            +
                - :id: 2
         
     | 
| 
      
 652 
     | 
    
         
            +
                  :event: :return
         
     | 
| 
      
 653 
     | 
    
         
            +
                  :parent_id: 1
         
     | 
| 
      
 654 
     | 
    
         
            +
                  :return_value:
         
     | 
| 
      
 655 
     | 
    
         
            +
                    :class: String
         
     | 
| 
      
 656 
     | 
    
         
            +
                    :value: '2020-01-01 00:00:00 +0000'
         
     | 
| 
      
 657 
     | 
    
         
            +
                YAML
         
     | 
| 
      
 658 
     | 
    
         
            +
                test_hook_behavior 'spec/fixtures/hook/instance_method.rb', events_yaml do
         
     | 
| 
      
 659 
     | 
    
         
            +
                  require 'timecop'
         
     | 
| 
      
 660 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 661 
     | 
    
         
            +
                    tz = ENV['TZ']
         
     | 
| 
      
 662 
     | 
    
         
            +
                    ENV['TZ'] = 'UTC'
         
     | 
| 
      
 663 
     | 
    
         
            +
                    Timecop.freeze(Time.utc('2020-01-01')) do
         
     | 
| 
      
 664 
     | 
    
         
            +
                      expect(Time).to receive(:now).exactly(3).times.and_call_original
         
     | 
| 
      
 665 
     | 
    
         
            +
                      expect(InstanceMethod.new.say_the_time).to be
         
     | 
| 
      
 666 
     | 
    
         
            +
                    end
         
     | 
| 
      
 667 
     | 
    
         
            +
                  ensure
         
     | 
| 
      
 668 
     | 
    
         
            +
                    ENV['TZ'] = tz
         
     | 
| 
      
 669 
     | 
    
         
            +
                  end
         
     | 
| 
      
 670 
     | 
    
         
            +
                end
         
     | 
| 
      
 671 
     | 
    
         
            +
              end
         
     | 
| 
      
 672 
     | 
    
         
            +
             
     | 
| 
      
 673 
     | 
    
         
            +
              it "preserves the arity of hooked methods" do
         
     | 
| 
      
 674 
     | 
    
         
            +
                invoke_test_file 'spec/fixtures/hook/instance_method.rb' do
         
     | 
| 
      
 675 
     | 
    
         
            +
                  expect(InstanceMethod.instance_method(:say_echo).arity).to be(1)
         
     | 
| 
      
 676 
     | 
    
         
            +
                  expect(InstanceMethod.new.method(:say_echo).arity).to be(1)
         
     | 
| 
       542 
677 
     | 
    
         
             
                end
         
     | 
| 
       543 
678 
     | 
    
         
             
              end
         
     | 
| 
       544 
679 
     | 
    
         
             
            end
         
     |