statsd-instrument 2.3.2 → 2.6.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/.github/CODEOWNERS +1 -0
 - data/.github/workflows/benchmark.yml +32 -0
 - data/.github/workflows/ci.yml +47 -0
 - data/.gitignore +1 -0
 - data/.rubocop-https---shopify-github-io-ruby-style-guide-rubocop-yml +1027 -0
 - data/.rubocop.yml +50 -0
 - data/.yardopts +5 -0
 - data/CHANGELOG.md +288 -2
 - data/CONTRIBUTING.md +28 -6
 - data/Gemfile +5 -0
 - data/README.md +54 -46
 - data/Rakefile +4 -2
 - data/benchmark/README.md +29 -0
 - data/benchmark/datagram-client +41 -0
 - data/benchmark/send-metrics-to-dev-null-log +47 -0
 - data/benchmark/send-metrics-to-local-udp-receiver +57 -0
 - data/lib/statsd/instrument/assertions.rb +179 -30
 - data/lib/statsd/instrument/backend.rb +3 -2
 - data/lib/statsd/instrument/backends/capture_backend.rb +4 -1
 - data/lib/statsd/instrument/backends/logger_backend.rb +3 -3
 - data/lib/statsd/instrument/backends/null_backend.rb +2 -0
 - data/lib/statsd/instrument/backends/udp_backend.rb +39 -45
 - data/lib/statsd/instrument/capture_sink.rb +27 -0
 - data/lib/statsd/instrument/client.rb +313 -0
 - data/lib/statsd/instrument/datagram.rb +75 -0
 - data/lib/statsd/instrument/datagram_builder.rb +101 -0
 - data/lib/statsd/instrument/dogstatsd_datagram_builder.rb +71 -0
 - data/lib/statsd/instrument/environment.rb +108 -29
 - data/lib/statsd/instrument/helpers.rb +16 -8
 - data/lib/statsd/instrument/log_sink.rb +24 -0
 - data/lib/statsd/instrument/matchers.rb +14 -11
 - data/lib/statsd/instrument/metric.rb +72 -45
 - data/lib/statsd/instrument/metric_expectation.rb +32 -18
 - data/lib/statsd/instrument/null_sink.rb +13 -0
 - data/lib/statsd/instrument/railtie.rb +2 -1
 - data/lib/statsd/instrument/rubocop/measure_as_dist_argument.rb +39 -0
 - data/lib/statsd/instrument/rubocop/metaprogramming_positional_arguments.rb +42 -0
 - data/lib/statsd/instrument/rubocop/metric_prefix_argument.rb +37 -0
 - data/lib/statsd/instrument/rubocop/metric_return_value.rb +32 -0
 - data/lib/statsd/instrument/rubocop/metric_value_keyword_argument.rb +36 -0
 - data/lib/statsd/instrument/rubocop/positional_arguments.rb +99 -0
 - data/lib/statsd/instrument/rubocop/splat_arguments.rb +31 -0
 - data/lib/statsd/instrument/rubocop.rb +64 -0
 - data/lib/statsd/instrument/statsd_datagram_builder.rb +14 -0
 - data/lib/statsd/instrument/strict.rb +235 -0
 - data/lib/statsd/instrument/udp_sink.rb +62 -0
 - data/lib/statsd/instrument/version.rb +3 -1
 - data/lib/statsd/instrument.rb +340 -163
 - data/lib/statsd-instrument.rb +2 -0
 - data/statsd-instrument.gemspec +13 -10
 - data/test/assertions_test.rb +167 -156
 - data/test/benchmark/clock_gettime.rb +27 -0
 - data/test/benchmark/default_tags.rb +47 -0
 - data/test/benchmark/metrics.rb +9 -8
 - data/test/benchmark/tags.rb +5 -3
 - data/test/capture_backend_test.rb +4 -2
 - data/test/capture_sink_test.rb +44 -0
 - data/test/client_test.rb +164 -0
 - data/test/compatibility/dogstatsd_datagram_compatibility_test.rb +162 -0
 - data/test/datagram_builder_test.rb +120 -0
 - data/test/deprecations_test.rb +132 -0
 - data/test/dogstatsd_datagram_builder_test.rb +32 -0
 - data/test/environment_test.rb +75 -8
 - data/test/helpers/rubocop_helper.rb +47 -0
 - data/test/helpers_test.rb +2 -1
 - data/test/integration_test.rb +31 -7
 - data/test/log_sink_test.rb +37 -0
 - data/test/logger_backend_test.rb +10 -8
 - data/test/matchers_test.rb +42 -28
 - data/test/metric_test.rb +18 -22
 - data/test/null_sink_test.rb +13 -0
 - data/test/rubocop/measure_as_dist_argument_test.rb +44 -0
 - data/test/rubocop/metaprogramming_positional_arguments_test.rb +58 -0
 - data/test/rubocop/metric_prefix_argument_test.rb +38 -0
 - data/test/rubocop/metric_return_value_test.rb +78 -0
 - data/test/rubocop/metric_value_keyword_argument_test.rb +39 -0
 - data/test/rubocop/positional_arguments_test.rb +110 -0
 - data/test/rubocop/splat_arguments_test.rb +27 -0
 - data/test/statsd_datagram_builder_test.rb +22 -0
 - data/test/statsd_instrumentation_test.rb +109 -100
 - data/test/statsd_test.rb +113 -79
 - data/test/test_helper.rb +12 -1
 - data/test/udp_backend_test.rb +38 -36
 - data/test/udp_sink_test.rb +85 -0
 - metadata +85 -5
 - data/.travis.yml +0 -12
 
    
        data/test/benchmark/tags.rb
    CHANGED
    
    | 
         @@ -1,9 +1,11 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            require 'statsd-instrument'
         
     | 
| 
       2 
4 
     | 
    
         
             
            require 'benchmark/ips'
         
     | 
| 
       3 
5 
     | 
    
         | 
| 
       4 
6 
     | 
    
         
             
            Benchmark.ips do |bench|
         
     | 
| 
       5 
7 
     | 
    
         
             
              bench.report("normalized tags with simple hash") do
         
     | 
| 
       6 
     | 
    
         
            -
                StatsD::Instrument::Metric.normalize_tags(: 
     | 
| 
      
 8 
     | 
    
         
            +
                StatsD::Instrument::Metric.normalize_tags(tag: 'value')
         
     | 
| 
       7 
9 
     | 
    
         
             
              end
         
     | 
| 
       8 
10 
     | 
    
         | 
| 
       9 
11 
     | 
    
         
             
              bench.report("normalized tags with simple array") do
         
     | 
| 
         @@ -11,14 +13,14 @@ Benchmark.ips do |bench| 
     | 
|
| 
       11 
13 
     | 
    
         
             
              end
         
     | 
| 
       12 
14 
     | 
    
         | 
| 
       13 
15 
     | 
    
         
             
              bench.report("normalized tags with large hash") do
         
     | 
| 
       14 
     | 
    
         
            -
                StatsD::Instrument::Metric.normalize_tags( 
     | 
| 
      
 16 
     | 
    
         
            +
                StatsD::Instrument::Metric.normalize_tags(
         
     | 
| 
       15 
17 
     | 
    
         
             
                  mobile: true,
         
     | 
| 
       16 
18 
     | 
    
         
             
                  pod: "1",
         
     | 
| 
       17 
19 
     | 
    
         
             
                  protocol: "https",
         
     | 
| 
       18 
20 
     | 
    
         
             
                  country: "Langbortistan",
         
     | 
| 
       19 
21 
     | 
    
         
             
                  complete: true,
         
     | 
| 
       20 
22 
     | 
    
         
             
                  shop: "omg shop that has a longer name",
         
     | 
| 
       21 
     | 
    
         
            -
                 
     | 
| 
      
 23 
     | 
    
         
            +
                )
         
     | 
| 
       22 
24 
     | 
    
         
             
              end
         
     | 
| 
       23 
25 
     | 
    
         | 
| 
       24 
26 
     | 
    
         
             
              bench.report("normalized tags with large array") do
         
     | 
| 
         @@ -1,10 +1,12 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            require 'test_helper'
         
     | 
| 
       2 
4 
     | 
    
         | 
| 
       3 
5 
     | 
    
         
             
            class CaptureBackendTest < Minitest::Test
         
     | 
| 
       4 
6 
     | 
    
         
             
              def setup
         
     | 
| 
       5 
7 
     | 
    
         
             
                @backend = StatsD::Instrument::Backends::CaptureBackend.new
         
     | 
| 
       6 
     | 
    
         
            -
                @metric1 = StatsD::Instrument::Metric 
     | 
| 
       7 
     | 
    
         
            -
                @metric2 = StatsD::Instrument::Metric 
     | 
| 
      
 8 
     | 
    
         
            +
                @metric1 = StatsD::Instrument::Metric.new(type: :c, name: 'mock.counter')
         
     | 
| 
      
 9 
     | 
    
         
            +
                @metric2 = StatsD::Instrument::Metric.new(type: :ms, name: 'mock.measure', value: 123)
         
     | 
| 
       8 
10 
     | 
    
         
             
              end
         
     | 
| 
       9 
11 
     | 
    
         | 
| 
       10 
12 
     | 
    
         
             
              def test_collecting_metric
         
     | 
| 
         @@ -0,0 +1,44 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'test_helper'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            require 'statsd/instrument/client'
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            class CaptureSinktest < Minitest::Test
         
     | 
| 
      
 8 
     | 
    
         
            +
              def test_capture_sink_captures_datagram_instances
         
     | 
| 
      
 9 
     | 
    
         
            +
                capture_sink = StatsD::Instrument::CaptureSink.new(parent: [])
         
     | 
| 
      
 10 
     | 
    
         
            +
                capture_sink << 'foo:1|c'
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                assert_equal 1, capture_sink.datagrams.length
         
     | 
| 
      
 13 
     | 
    
         
            +
                assert_kind_of StatsD::Instrument::Datagram, capture_sink.datagrams.first
         
     | 
| 
      
 14 
     | 
    
         
            +
                assert_equal 'foo:1|c', capture_sink.datagrams.first.source
         
     | 
| 
      
 15 
     | 
    
         
            +
              end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
              def test_capture_sink_sends_datagrams_to_parent
         
     | 
| 
      
 18 
     | 
    
         
            +
                parent = []
         
     | 
| 
      
 19 
     | 
    
         
            +
                capture_sink = StatsD::Instrument::CaptureSink.new(parent: parent)
         
     | 
| 
      
 20 
     | 
    
         
            +
                capture_sink << 'foo:1|c' << 'bar:1|c'
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                assert_equal ['foo:1|c', 'bar:1|c'], parent
         
     | 
| 
      
 23 
     | 
    
         
            +
              end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
              def test_nesting_capture_sink_instances
         
     | 
| 
      
 26 
     | 
    
         
            +
                null_sink = StatsD::Instrument::NullSink.new
         
     | 
| 
      
 27 
     | 
    
         
            +
                outer_capture_sink = StatsD::Instrument::CaptureSink.new(parent: null_sink)
         
     | 
| 
      
 28 
     | 
    
         
            +
                inner_capture_sink = StatsD::Instrument::CaptureSink.new(parent: outer_capture_sink)
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                outer_capture_sink << 'foo:1|c'
         
     | 
| 
      
 31 
     | 
    
         
            +
                inner_capture_sink << 'bar:1|c'
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                assert_equal ['foo:1|c', 'bar:1|c'], outer_capture_sink.datagrams.map(&:source)
         
     | 
| 
      
 34 
     | 
    
         
            +
                assert_equal ['bar:1|c'], inner_capture_sink.datagrams.map(&:source)
         
     | 
| 
      
 35 
     | 
    
         
            +
              end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
              def test_using_a_different_datagram_class
         
     | 
| 
      
 38 
     | 
    
         
            +
                sink = StatsD::Instrument::CaptureSink.new(parent: [], datagram_class: String)
         
     | 
| 
      
 39 
     | 
    
         
            +
                sink << 'foo:1|c'
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                assert sink.datagrams.all? { |datagram| datagram.is_a?(String) }
         
     | 
| 
      
 42 
     | 
    
         
            +
                assert_equal ['foo:1|c'], sink.datagrams
         
     | 
| 
      
 43 
     | 
    
         
            +
              end
         
     | 
| 
      
 44 
     | 
    
         
            +
            end
         
     | 
    
        data/test/client_test.rb
    ADDED
    
    | 
         @@ -0,0 +1,164 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'test_helper'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            require 'statsd/instrument/client'
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            class ClientTest < Minitest::Test
         
     | 
| 
      
 8 
     | 
    
         
            +
              def setup
         
     | 
| 
      
 9 
     | 
    
         
            +
                @client = StatsD::Instrument::Client.new(datagram_builder_class: StatsD::Instrument::StatsDDatagramBuilder)
         
     | 
| 
      
 10 
     | 
    
         
            +
                @dogstatsd_client = StatsD::Instrument::Client.new(
         
     | 
| 
      
 11 
     | 
    
         
            +
                  datagram_builder_class: StatsD::Instrument::DogStatsDDatagramBuilder,
         
     | 
| 
      
 12 
     | 
    
         
            +
                )
         
     | 
| 
      
 13 
     | 
    
         
            +
              end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
              def test_capture
         
     | 
| 
      
 16 
     | 
    
         
            +
                inner_datagrams = nil
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                @client.increment('foo')
         
     | 
| 
      
 19 
     | 
    
         
            +
                outer_datagrams = @client.capture do
         
     | 
| 
      
 20 
     | 
    
         
            +
                  @client.increment('bar')
         
     | 
| 
      
 21 
     | 
    
         
            +
                  inner_datagrams = @client.capture do
         
     | 
| 
      
 22 
     | 
    
         
            +
                    @client.increment('baz')
         
     | 
| 
      
 23 
     | 
    
         
            +
                  end
         
     | 
| 
      
 24 
     | 
    
         
            +
                end
         
     | 
| 
      
 25 
     | 
    
         
            +
                @client.increment('quc')
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                assert_equal ['bar', 'baz'], outer_datagrams.map(&:name)
         
     | 
| 
      
 28 
     | 
    
         
            +
                assert_equal ['baz'], inner_datagrams.map(&:name)
         
     | 
| 
      
 29 
     | 
    
         
            +
              end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
              def test_metric_methods_return_nil
         
     | 
| 
      
 32 
     | 
    
         
            +
                assert_nil @client.increment('foo')
         
     | 
| 
      
 33 
     | 
    
         
            +
                assert_nil @client.measure('bar', 122.54)
         
     | 
| 
      
 34 
     | 
    
         
            +
                assert_nil @client.set('baz', 123)
         
     | 
| 
      
 35 
     | 
    
         
            +
                assert_nil @client.gauge('baz', 12.3)
         
     | 
| 
      
 36 
     | 
    
         
            +
              end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
              def test_increment_with_default_value
         
     | 
| 
      
 39 
     | 
    
         
            +
                datagrams = @client.capture { @client.increment('foo') }
         
     | 
| 
      
 40 
     | 
    
         
            +
                assert_equal 1, datagrams.size
         
     | 
| 
      
 41 
     | 
    
         
            +
                assert_equal 'foo:1|c', datagrams.first.source
         
     | 
| 
      
 42 
     | 
    
         
            +
              end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
              def test_measure_with_value
         
     | 
| 
      
 45 
     | 
    
         
            +
                datagrams = @client.capture { @client.measure('foo', 122.54) }
         
     | 
| 
      
 46 
     | 
    
         
            +
                assert_equal 1, datagrams.size
         
     | 
| 
      
 47 
     | 
    
         
            +
                assert_equal 'foo:122.54|ms', datagrams.first.source
         
     | 
| 
      
 48 
     | 
    
         
            +
              end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
              def test_measure_with_block
         
     | 
| 
      
 51 
     | 
    
         
            +
                Process.stubs(:clock_gettime).with(Process::CLOCK_MONOTONIC).returns(0.1, 0.2)
         
     | 
| 
      
 52 
     | 
    
         
            +
                datagrams = @client.capture do
         
     | 
| 
      
 53 
     | 
    
         
            +
                  @client.measure('foo') {}
         
     | 
| 
      
 54 
     | 
    
         
            +
                end
         
     | 
| 
      
 55 
     | 
    
         
            +
                assert_equal 1, datagrams.size
         
     | 
| 
      
 56 
     | 
    
         
            +
                assert_equal 'foo:100.0|ms', datagrams.first.source
         
     | 
| 
      
 57 
     | 
    
         
            +
              end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
              def test_gauge
         
     | 
| 
      
 60 
     | 
    
         
            +
                datagrams = @client.capture { @client.gauge('foo', 123) }
         
     | 
| 
      
 61 
     | 
    
         
            +
                assert_equal 1, datagrams.size
         
     | 
| 
      
 62 
     | 
    
         
            +
                assert_equal 'foo:123|g', datagrams.first.source
         
     | 
| 
      
 63 
     | 
    
         
            +
              end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
              def test_set
         
     | 
| 
      
 66 
     | 
    
         
            +
                datagrams = @client.capture { @client.set('foo', 12345) }
         
     | 
| 
      
 67 
     | 
    
         
            +
                assert_equal 1, datagrams.size
         
     | 
| 
      
 68 
     | 
    
         
            +
                assert_equal 'foo:12345|s', datagrams.first.source
         
     | 
| 
      
 69 
     | 
    
         
            +
              end
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
              def test_histogram
         
     | 
| 
      
 72 
     | 
    
         
            +
                datagrams = @dogstatsd_client.capture { @dogstatsd_client.histogram('foo', 12.44) }
         
     | 
| 
      
 73 
     | 
    
         
            +
                assert_equal 1, datagrams.size
         
     | 
| 
      
 74 
     | 
    
         
            +
                assert_equal 'foo:12.44|h', datagrams.first.source
         
     | 
| 
      
 75 
     | 
    
         
            +
              end
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
              def test_distribution_with_value
         
     | 
| 
      
 78 
     | 
    
         
            +
                datagrams = @dogstatsd_client.capture { @dogstatsd_client.distribution('foo', 12.44) }
         
     | 
| 
      
 79 
     | 
    
         
            +
                assert_equal 1, datagrams.size
         
     | 
| 
      
 80 
     | 
    
         
            +
                assert_equal 'foo:12.44|d', datagrams.first.source
         
     | 
| 
      
 81 
     | 
    
         
            +
              end
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
              def test_distribution_with_block
         
     | 
| 
      
 84 
     | 
    
         
            +
                Process.stubs(:clock_gettime).with(Process::CLOCK_MONOTONIC).returns(0.1, 0.2)
         
     | 
| 
      
 85 
     | 
    
         
            +
                datagrams = @dogstatsd_client.capture do
         
     | 
| 
      
 86 
     | 
    
         
            +
                  @dogstatsd_client.distribution('foo') {}
         
     | 
| 
      
 87 
     | 
    
         
            +
                end
         
     | 
| 
      
 88 
     | 
    
         
            +
                assert_equal 1, datagrams.size
         
     | 
| 
      
 89 
     | 
    
         
            +
                assert_equal "foo:100.0|d", datagrams.first.source
         
     | 
| 
      
 90 
     | 
    
         
            +
              end
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
              def test_latency_emits_ms_metric
         
     | 
| 
      
 93 
     | 
    
         
            +
                Process.stubs(:clock_gettime).with(Process::CLOCK_MONOTONIC).returns(0.1, 0.2)
         
     | 
| 
      
 94 
     | 
    
         
            +
                datagrams = @client.capture do
         
     | 
| 
      
 95 
     | 
    
         
            +
                  @client.latency('foo') {}
         
     | 
| 
      
 96 
     | 
    
         
            +
                end
         
     | 
| 
      
 97 
     | 
    
         
            +
                assert_equal 1, datagrams.size
         
     | 
| 
      
 98 
     | 
    
         
            +
                assert_equal "foo:100.0|ms", datagrams.first.source
         
     | 
| 
      
 99 
     | 
    
         
            +
              end
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
              def test_latency_on_dogstatsd_prefers_distribution_metric_type
         
     | 
| 
      
 102 
     | 
    
         
            +
                Process.stubs(:clock_gettime).with(Process::CLOCK_MONOTONIC).returns(0.1, 0.2)
         
     | 
| 
      
 103 
     | 
    
         
            +
                datagrams = @dogstatsd_client.capture do
         
     | 
| 
      
 104 
     | 
    
         
            +
                  @dogstatsd_client.latency('foo') {}
         
     | 
| 
      
 105 
     | 
    
         
            +
                end
         
     | 
| 
      
 106 
     | 
    
         
            +
                assert_equal 1, datagrams.size
         
     | 
| 
      
 107 
     | 
    
         
            +
                assert_equal "foo:100.0|d", datagrams.first.source
         
     | 
| 
      
 108 
     | 
    
         
            +
              end
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
              def test_latency_calls_block_even_when_not_sending_a_sample
         
     | 
| 
      
 111 
     | 
    
         
            +
                called = false
         
     | 
| 
      
 112 
     | 
    
         
            +
                @client.capture do
         
     | 
| 
      
 113 
     | 
    
         
            +
                  @client.latency('foo', sample_rate: 0) { called = true }
         
     | 
| 
      
 114 
     | 
    
         
            +
                end
         
     | 
| 
      
 115 
     | 
    
         
            +
                assert called, "The block should have been called"
         
     | 
| 
      
 116 
     | 
    
         
            +
              end
         
     | 
| 
      
 117 
     | 
    
         
            +
             
     | 
| 
      
 118 
     | 
    
         
            +
              def test_service_check
         
     | 
| 
      
 119 
     | 
    
         
            +
                datagrams = @dogstatsd_client.capture { @dogstatsd_client.service_check('service', :ok) }
         
     | 
| 
      
 120 
     | 
    
         
            +
                assert_equal 1, datagrams.size
         
     | 
| 
      
 121 
     | 
    
         
            +
                assert_equal "_sc|service|0", datagrams.first.source
         
     | 
| 
      
 122 
     | 
    
         
            +
              end
         
     | 
| 
      
 123 
     | 
    
         
            +
             
     | 
| 
      
 124 
     | 
    
         
            +
              def test_event
         
     | 
| 
      
 125 
     | 
    
         
            +
                datagrams = @dogstatsd_client.capture { @dogstatsd_client.event('service', "event\ndescription") }
         
     | 
| 
      
 126 
     | 
    
         
            +
                assert_equal 1, datagrams.size
         
     | 
| 
      
 127 
     | 
    
         
            +
                assert_equal "_e{7,18}:service|event\\ndescription", datagrams.first.source
         
     | 
| 
      
 128 
     | 
    
         
            +
              end
         
     | 
| 
      
 129 
     | 
    
         
            +
             
     | 
| 
      
 130 
     | 
    
         
            +
              def test_no_prefix
         
     | 
| 
      
 131 
     | 
    
         
            +
                client = StatsD::Instrument::Client.new(prefix: 'foo')
         
     | 
| 
      
 132 
     | 
    
         
            +
                datagrams = client.capture do
         
     | 
| 
      
 133 
     | 
    
         
            +
                  client.increment('bar')
         
     | 
| 
      
 134 
     | 
    
         
            +
                  client.increment('bar', no_prefix: true)
         
     | 
| 
      
 135 
     | 
    
         
            +
                end
         
     | 
| 
      
 136 
     | 
    
         
            +
             
     | 
| 
      
 137 
     | 
    
         
            +
                assert_equal 2, datagrams.size
         
     | 
| 
      
 138 
     | 
    
         
            +
                assert_equal "foo.bar", datagrams[0].name
         
     | 
| 
      
 139 
     | 
    
         
            +
                assert_equal "bar", datagrams[1].name
         
     | 
| 
      
 140 
     | 
    
         
            +
              end
         
     | 
| 
      
 141 
     | 
    
         
            +
             
     | 
| 
      
 142 
     | 
    
         
            +
              def test_sampling
         
     | 
| 
      
 143 
     | 
    
         
            +
                mock_sink = mock('sink')
         
     | 
| 
      
 144 
     | 
    
         
            +
                mock_sink.stubs(:sample?).returns(false, true, false, false, true)
         
     | 
| 
      
 145 
     | 
    
         
            +
                mock_sink.expects(:<<).twice
         
     | 
| 
      
 146 
     | 
    
         
            +
             
     | 
| 
      
 147 
     | 
    
         
            +
                client = StatsD::Instrument::Client.new(sink: mock_sink)
         
     | 
| 
      
 148 
     | 
    
         
            +
                5.times { client.increment('metric') }
         
     | 
| 
      
 149 
     | 
    
         
            +
              end
         
     | 
| 
      
 150 
     | 
    
         
            +
             
     | 
| 
      
 151 
     | 
    
         
            +
              def test_clone_with_prefix_option
         
     | 
| 
      
 152 
     | 
    
         
            +
                # Both clients will use the same sink.
         
     | 
| 
      
 153 
     | 
    
         
            +
                mock_sink = mock('sink')
         
     | 
| 
      
 154 
     | 
    
         
            +
                mock_sink.stubs(:sample?).returns(true)
         
     | 
| 
      
 155 
     | 
    
         
            +
                mock_sink.expects(:<<).with("metric:1|c").returns(mock_sink)
         
     | 
| 
      
 156 
     | 
    
         
            +
                mock_sink.expects(:<<).with("foo.metric:1|c").returns(mock_sink)
         
     | 
| 
      
 157 
     | 
    
         
            +
             
     | 
| 
      
 158 
     | 
    
         
            +
                original_client = StatsD::Instrument::Client.new(sink: mock_sink)
         
     | 
| 
      
 159 
     | 
    
         
            +
                client_with_other_options = original_client.clone_with_options(prefix: 'foo')
         
     | 
| 
      
 160 
     | 
    
         
            +
             
     | 
| 
      
 161 
     | 
    
         
            +
                original_client.increment('metric')
         
     | 
| 
      
 162 
     | 
    
         
            +
                client_with_other_options.increment('metric')
         
     | 
| 
      
 163 
     | 
    
         
            +
              end
         
     | 
| 
      
 164 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,162 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'test_helper'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'statsd/instrument/client'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            module Compatibility
         
     | 
| 
      
 7 
     | 
    
         
            +
              class DogStatsDDatagramCompatibilityTest < Minitest::Test
         
     | 
| 
      
 8 
     | 
    
         
            +
                def setup
         
     | 
| 
      
 9 
     | 
    
         
            +
                  StatsD::Instrument::UDPSink.any_instance.stubs(:sample?).returns(true)
         
     | 
| 
      
 10 
     | 
    
         
            +
                  StatsD::Instrument::Backends::UDPBackend.any_instance.stubs(:rand).returns(0)
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                  @server = UDPSocket.new
         
     | 
| 
      
 13 
     | 
    
         
            +
                  @server.bind('localhost', 0)
         
     | 
| 
      
 14 
     | 
    
         
            +
                  @host = @server.addr[2]
         
     | 
| 
      
 15 
     | 
    
         
            +
                  @port = @server.addr[1]
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                def teardown
         
     | 
| 
      
 19 
     | 
    
         
            +
                  @server.close
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                def test_increment_compatibility
         
     | 
| 
      
 23 
     | 
    
         
            +
                  assert_equal_datagrams { |client| client.increment('counter') }
         
     | 
| 
      
 24 
     | 
    
         
            +
                  assert_equal_datagrams { |client| client.increment('counter', no_prefix: true) }
         
     | 
| 
      
 25 
     | 
    
         
            +
                  assert_equal_datagrams { |client| client.increment('counter', 12) }
         
     | 
| 
      
 26 
     | 
    
         
            +
                  assert_equal_datagrams { |client| client.increment('counter', sample_rate: 0.1) }
         
     | 
| 
      
 27 
     | 
    
         
            +
                  assert_equal_datagrams { |client| client.increment('counter', tags: ['foo', 'bar']) }
         
     | 
| 
      
 28 
     | 
    
         
            +
                  assert_equal_datagrams { |client| client.increment('counter', tags: { foo: 'bar' }) }
         
     | 
| 
      
 29 
     | 
    
         
            +
                  assert_equal_datagrams { |client| client.increment('counter', sample_rate: 0.1, tags: ['quc']) }
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                def test_measure_compatibility
         
     | 
| 
      
 33 
     | 
    
         
            +
                  assert_equal_datagrams { |client| client.measure('timing', 12.34) }
         
     | 
| 
      
 34 
     | 
    
         
            +
                  assert_equal_datagrams { |client| client.measure('timing', 0.01, no_prefix: true) }
         
     | 
| 
      
 35 
     | 
    
         
            +
                  assert_equal_datagrams { |client| client.measure('timing', 0.12, sample_rate: 0.1) }
         
     | 
| 
      
 36 
     | 
    
         
            +
                  assert_equal_datagrams { |client| client.measure('timing', 0.12, tags: ['foo', 'bar']) }
         
     | 
| 
      
 37 
     | 
    
         
            +
                end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                def test_measure_with_block_compatibility
         
     | 
| 
      
 40 
     | 
    
         
            +
                  Process.stubs(:clock_gettime).with(Process::CLOCK_MONOTONIC).returns(12.1)
         
     | 
| 
      
 41 
     | 
    
         
            +
                  assert_equal_datagrams do |client|
         
     | 
| 
      
 42 
     | 
    
         
            +
                    return_value = client.measure('timing', tags: ['foo'], sample_rate: 0.1) { 'foo' }
         
     | 
| 
      
 43 
     | 
    
         
            +
                    assert_equal 'foo', return_value
         
     | 
| 
      
 44 
     | 
    
         
            +
                  end
         
     | 
| 
      
 45 
     | 
    
         
            +
                end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                def test_gauge_compatibility
         
     | 
| 
      
 48 
     | 
    
         
            +
                  assert_equal_datagrams { |client| client.gauge('current', 1234) }
         
     | 
| 
      
 49 
     | 
    
         
            +
                  assert_equal_datagrams { |client| client.gauge('current', 1234, no_prefix: true) }
         
     | 
| 
      
 50 
     | 
    
         
            +
                  assert_equal_datagrams { |client| client.gauge('current', 1234, sample_rate: 0.1) }
         
     | 
| 
      
 51 
     | 
    
         
            +
                  assert_equal_datagrams { |client| client.gauge('current', 1234, tags: ['foo', 'bar']) }
         
     | 
| 
      
 52 
     | 
    
         
            +
                  assert_equal_datagrams { |client| client.gauge('current', 1234, tags: { foo: 'bar' }) }
         
     | 
| 
      
 53 
     | 
    
         
            +
                  assert_equal_datagrams { |client| client.gauge('current', 1234, sample_rate: 0.1, tags: ['quc']) }
         
     | 
| 
      
 54 
     | 
    
         
            +
                end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                def test_set_compatibility
         
     | 
| 
      
 57 
     | 
    
         
            +
                  assert_equal_datagrams { |client| client.set('unique', 'foo') }
         
     | 
| 
      
 58 
     | 
    
         
            +
                  assert_equal_datagrams { |client| client.set('unique', 'foo', no_prefix: true) }
         
     | 
| 
      
 59 
     | 
    
         
            +
                  assert_equal_datagrams { |client| client.set('unique', 'foo', sample_rate: 0.1) }
         
     | 
| 
      
 60 
     | 
    
         
            +
                  assert_equal_datagrams { |client| client.set('unique', '1234', tags: ['foo', 'bar']) }
         
     | 
| 
      
 61 
     | 
    
         
            +
                  assert_equal_datagrams { |client| client.set('unique', '1234', tags: { foo: 'bar' }) }
         
     | 
| 
      
 62 
     | 
    
         
            +
                  assert_equal_datagrams { |client| client.set('unique', '1234', sample_rate: 0.1, tags: ['quc']) }
         
     | 
| 
      
 63 
     | 
    
         
            +
                end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                def test_histogram_compatibility
         
     | 
| 
      
 66 
     | 
    
         
            +
                  assert_equal_datagrams { |client| client.histogram('sample', 12.44) }
         
     | 
| 
      
 67 
     | 
    
         
            +
                  assert_equal_datagrams { |client| client.histogram('sample', 12.44, no_prefix: true) }
         
     | 
| 
      
 68 
     | 
    
         
            +
                  assert_equal_datagrams { |client| client.histogram('sample', 12.44, sample_rate: 0.1) }
         
     | 
| 
      
 69 
     | 
    
         
            +
                  assert_equal_datagrams { |client| client.histogram('sample', 12.44, tags: ['foo', 'bar']) }
         
     | 
| 
      
 70 
     | 
    
         
            +
                  assert_equal_datagrams { |client| client.histogram('sample', 12.44, tags: { foo: 'bar' }) }
         
     | 
| 
      
 71 
     | 
    
         
            +
                  assert_equal_datagrams { |client| client.histogram('sample', 12.44, sample_rate: 0.1, tags: ['quc']) }
         
     | 
| 
      
 72 
     | 
    
         
            +
                end
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                def test_distribution_compatibility
         
     | 
| 
      
 75 
     | 
    
         
            +
                  assert_equal_datagrams { |client| client.distribution('sample', 12.44) }
         
     | 
| 
      
 76 
     | 
    
         
            +
                  assert_equal_datagrams { |client| client.distribution('sample', 12.44, no_prefix: true) }
         
     | 
| 
      
 77 
     | 
    
         
            +
                  assert_equal_datagrams { |client| client.distribution('sample', 12.44, sample_rate: 0.1) }
         
     | 
| 
      
 78 
     | 
    
         
            +
                  assert_equal_datagrams { |client| client.distribution('sample', 12.44, tags: ['foo', 'bar']) }
         
     | 
| 
      
 79 
     | 
    
         
            +
                  assert_equal_datagrams { |client| client.distribution('sample', 12.44, tags: { foo: 'bar' }) }
         
     | 
| 
      
 80 
     | 
    
         
            +
                  assert_equal_datagrams { |client| client.distribution('sample', 12.44, sample_rate: 0.1, tags: ['quc']) }
         
     | 
| 
      
 81 
     | 
    
         
            +
                end
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
                def test_distribution_with_block_compatibility
         
     | 
| 
      
 84 
     | 
    
         
            +
                  Process.stubs(:clock_gettime).with(Process::CLOCK_MONOTONIC).returns(12.1)
         
     | 
| 
      
 85 
     | 
    
         
            +
                  assert_equal_datagrams do |client|
         
     | 
| 
      
 86 
     | 
    
         
            +
                    return_value = client.distribution('timing', tags: ['foo'], sample_rate: 0.1) { 'foo' }
         
     | 
| 
      
 87 
     | 
    
         
            +
                    assert_equal 'foo', return_value
         
     | 
| 
      
 88 
     | 
    
         
            +
                  end
         
     | 
| 
      
 89 
     | 
    
         
            +
                end
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
                def test_service_check_compatibility
         
     | 
| 
      
 92 
     | 
    
         
            +
                  assert_equal_datagrams { |client| client.service_check('service', 0) }
         
     | 
| 
      
 93 
     | 
    
         
            +
                  assert_equal_datagrams { |client| client.service_check('service', :critical, no_prefix: true) }
         
     | 
| 
      
 94 
     | 
    
         
            +
                  assert_equal_datagrams { |client| client.event('foo', "bar\nbaz") }
         
     | 
| 
      
 95 
     | 
    
         
            +
                  assert_equal_datagrams do |client|
         
     | 
| 
      
 96 
     | 
    
         
            +
                    client.service_check('service', "ok", timestamp: Time.parse('2019-09-09T04:22:17Z'),
         
     | 
| 
      
 97 
     | 
    
         
            +
                      hostname: 'localhost', tags: ['foo'], message: 'bar')
         
     | 
| 
      
 98 
     | 
    
         
            +
                  end
         
     | 
| 
      
 99 
     | 
    
         
            +
                end
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
                def test_event_compatibility
         
     | 
| 
      
 102 
     | 
    
         
            +
                  assert_equal_datagrams { |client| client.event('foo', "bar\nbaz") }
         
     | 
| 
      
 103 
     | 
    
         
            +
                  assert_equal_datagrams { |client| client.event('foo', "bar\nbaz", no_prefix: true) }
         
     | 
| 
      
 104 
     | 
    
         
            +
                  assert_equal_datagrams do |client|
         
     | 
| 
      
 105 
     | 
    
         
            +
                    client.event('Something happend', "And it's not good", timestamp: Time.parse('2019-09-09T04:22:17Z'),
         
     | 
| 
      
 106 
     | 
    
         
            +
                      hostname: 'localhost', tags: ['foo'], alert_type: 'warning', priority: 'low',
         
     | 
| 
      
 107 
     | 
    
         
            +
                      aggregation_key: 'foo', source_type_name: 'logs')
         
     | 
| 
      
 108 
     | 
    
         
            +
                  end
         
     | 
| 
      
 109 
     | 
    
         
            +
                end
         
     | 
| 
      
 110 
     | 
    
         
            +
             
     | 
| 
      
 111 
     | 
    
         
            +
                private
         
     | 
| 
      
 112 
     | 
    
         
            +
             
     | 
| 
      
 113 
     | 
    
         
            +
                MODES = [:normal, :with_prefix, :with_default_tags]
         
     | 
| 
      
 114 
     | 
    
         
            +
             
     | 
| 
      
 115 
     | 
    
         
            +
                def assert_equal_datagrams(&block)
         
     | 
| 
      
 116 
     | 
    
         
            +
                  MODES.each do |mode|
         
     | 
| 
      
 117 
     | 
    
         
            +
                    legacy_datagram = with_legacy_client(mode) { |client| read_datagram(client, &block) }
         
     | 
| 
      
 118 
     | 
    
         
            +
                    new_datagram = with_new_client(mode) { |client| read_datagram(client, &block) }
         
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
                    assert_equal legacy_datagram, new_datagram, "The datagrams emitted were not the same in #{mode} mode"
         
     | 
| 
      
 121 
     | 
    
         
            +
                  end
         
     | 
| 
      
 122 
     | 
    
         
            +
                end
         
     | 
| 
      
 123 
     | 
    
         
            +
             
     | 
| 
      
 124 
     | 
    
         
            +
                def with_legacy_client(mode)
         
     | 
| 
      
 125 
     | 
    
         
            +
                  old_prefix = StatsD.prefix
         
     | 
| 
      
 126 
     | 
    
         
            +
                  StatsD.prefix = 'prefix' if mode == :with_prefix
         
     | 
| 
      
 127 
     | 
    
         
            +
             
     | 
| 
      
 128 
     | 
    
         
            +
                  old_default_tags = StatsD.default_tags
         
     | 
| 
      
 129 
     | 
    
         
            +
                  StatsD.default_tags = { key: 'value' } if mode == :with_default_tags
         
     | 
| 
      
 130 
     | 
    
         
            +
             
     | 
| 
      
 131 
     | 
    
         
            +
                  old_backend = StatsD.backend
         
     | 
| 
      
 132 
     | 
    
         
            +
                  new_backend = StatsD::Instrument::Backends::UDPBackend.new("#{@host}:#{@port}", :datadog)
         
     | 
| 
      
 133 
     | 
    
         
            +
                  StatsD.backend = new_backend
         
     | 
| 
      
 134 
     | 
    
         
            +
             
     | 
| 
      
 135 
     | 
    
         
            +
                  yield(StatsD)
         
     | 
| 
      
 136 
     | 
    
         
            +
                ensure
         
     | 
| 
      
 137 
     | 
    
         
            +
                  new_backend.socket.close if new_backend&.socket
         
     | 
| 
      
 138 
     | 
    
         
            +
                  StatsD.backend = old_backend
         
     | 
| 
      
 139 
     | 
    
         
            +
                  StatsD.prefix = old_prefix
         
     | 
| 
      
 140 
     | 
    
         
            +
                  StatsD.default_tags = old_default_tags
         
     | 
| 
      
 141 
     | 
    
         
            +
                end
         
     | 
| 
      
 142 
     | 
    
         
            +
             
     | 
| 
      
 143 
     | 
    
         
            +
                def with_new_client(mode)
         
     | 
| 
      
 144 
     | 
    
         
            +
                  prefix = mode == :with_prefix ? 'prefix' : nil
         
     | 
| 
      
 145 
     | 
    
         
            +
                  default_tags = mode == :with_default_tags ? { key: 'value' } : nil
         
     | 
| 
      
 146 
     | 
    
         
            +
                  client = StatsD::Instrument::Client.new(
         
     | 
| 
      
 147 
     | 
    
         
            +
                    sink: StatsD::Instrument::UDPSink.new(@host, @port),
         
     | 
| 
      
 148 
     | 
    
         
            +
                    datagram_builder_class: StatsD::Instrument::DogStatsDDatagramBuilder,
         
     | 
| 
      
 149 
     | 
    
         
            +
                    prefix: prefix,
         
     | 
| 
      
 150 
     | 
    
         
            +
                    default_tags: default_tags
         
     | 
| 
      
 151 
     | 
    
         
            +
                  )
         
     | 
| 
      
 152 
     | 
    
         
            +
             
     | 
| 
      
 153 
     | 
    
         
            +
                  yield(client)
         
     | 
| 
      
 154 
     | 
    
         
            +
                end
         
     | 
| 
      
 155 
     | 
    
         
            +
             
     | 
| 
      
 156 
     | 
    
         
            +
                def read_datagram(client)
         
     | 
| 
      
 157 
     | 
    
         
            +
                  yield(client)
         
     | 
| 
      
 158 
     | 
    
         
            +
                  data, _origin = @server.recvfrom(100)
         
     | 
| 
      
 159 
     | 
    
         
            +
                  data
         
     | 
| 
      
 160 
     | 
    
         
            +
                end
         
     | 
| 
      
 161 
     | 
    
         
            +
              end
         
     | 
| 
      
 162 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,120 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'test_helper'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            require 'statsd/instrument/client'
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            class DatagramBuilderTest < Minitest::Test
         
     | 
| 
      
 8 
     | 
    
         
            +
              def setup
         
     | 
| 
      
 9 
     | 
    
         
            +
                @datagram_builder = StatsD::Instrument::DatagramBuilder.new
         
     | 
| 
      
 10 
     | 
    
         
            +
              end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
              def test_normalize_name
         
     | 
| 
      
 13 
     | 
    
         
            +
                assert_equal 'foo', @datagram_builder.send(:normalize_name, 'foo')
         
     | 
| 
      
 14 
     | 
    
         
            +
                assert_equal 'fo_o', @datagram_builder.send(:normalize_name, 'fo|o')
         
     | 
| 
      
 15 
     | 
    
         
            +
                assert_equal 'fo_o', @datagram_builder.send(:normalize_name, 'fo@o')
         
     | 
| 
      
 16 
     | 
    
         
            +
                assert_equal 'fo_o', @datagram_builder.send(:normalize_name, 'fo:o')
         
     | 
| 
      
 17 
     | 
    
         
            +
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
              def test_normalize_unsupported_tag_names
         
     | 
| 
      
 20 
     | 
    
         
            +
                assert_equal ['ignored'], @datagram_builder.send(:normalize_tags, ['igno|re,d'])
         
     | 
| 
      
 21 
     | 
    
         
            +
                # Note: how this is interpreted by the backend is undefined.
         
     | 
| 
      
 22 
     | 
    
         
            +
                # We rely on the user to not do stuff like this if they don't want to be surprised.
         
     | 
| 
      
 23 
     | 
    
         
            +
                # We do not want to take the performance hit of normaling this.
         
     | 
| 
      
 24 
     | 
    
         
            +
                assert_equal ['lol::class:omg::lol'], @datagram_builder.send(:normalize_tags, "lol::class" => "omg::lol")
         
     | 
| 
      
 25 
     | 
    
         
            +
              end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
              def test_normalize_tags_converts_hash_to_array
         
     | 
| 
      
 28 
     | 
    
         
            +
                assert_equal ['tag:value'], @datagram_builder.send(:normalize_tags, tag: 'value')
         
     | 
| 
      
 29 
     | 
    
         
            +
                assert_equal ['tag1:v1', 'tag2:v2'], @datagram_builder.send(:normalize_tags, tag1: 'v1', tag2: 'v2')
         
     | 
| 
      
 30 
     | 
    
         
            +
              end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
              def test_c
         
     | 
| 
      
 33 
     | 
    
         
            +
                datagram = @datagram_builder.c('foo', 1, nil, nil)
         
     | 
| 
      
 34 
     | 
    
         
            +
                assert_equal "foo:1|c", datagram
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                datagram = @datagram_builder.c('fo:o', 10, 0.1, nil)
         
     | 
| 
      
 37 
     | 
    
         
            +
                assert_equal "fo_o:10|c|@0.1", datagram
         
     | 
| 
      
 38 
     | 
    
         
            +
              end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
              def test_ms
         
     | 
| 
      
 41 
     | 
    
         
            +
                datagram = @datagram_builder.ms('foo', 1, nil, nil)
         
     | 
| 
      
 42 
     | 
    
         
            +
                assert_equal "foo:1|ms", datagram
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                datagram = @datagram_builder.ms('fo:o', 10, 0.1, nil)
         
     | 
| 
      
 45 
     | 
    
         
            +
                assert_equal "fo_o:10|ms|@0.1", datagram
         
     | 
| 
      
 46 
     | 
    
         
            +
              end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
              def test_g
         
     | 
| 
      
 49 
     | 
    
         
            +
                datagram = @datagram_builder.g('foo', 1, nil, nil)
         
     | 
| 
      
 50 
     | 
    
         
            +
                assert_equal "foo:1|g", datagram
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                datagram = @datagram_builder.g('fo|o', 10, 0.01, nil)
         
     | 
| 
      
 53 
     | 
    
         
            +
                assert_equal "fo_o:10|g|@0.01", datagram
         
     | 
| 
      
 54 
     | 
    
         
            +
              end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
              def test_s
         
     | 
| 
      
 57 
     | 
    
         
            +
                datagram = @datagram_builder.s('foo', 1, nil, nil)
         
     | 
| 
      
 58 
     | 
    
         
            +
                assert_equal "foo:1|s", datagram
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                datagram = @datagram_builder.s('fo@o', 10, 0.01, nil)
         
     | 
| 
      
 61 
     | 
    
         
            +
                assert_equal "fo_o:10|s|@0.01", datagram
         
     | 
| 
      
 62 
     | 
    
         
            +
              end
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
              def test_h
         
     | 
| 
      
 65 
     | 
    
         
            +
                datagram = @datagram_builder.h('foo', 1, nil, nil)
         
     | 
| 
      
 66 
     | 
    
         
            +
                assert_equal "foo:1|h", datagram
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                datagram = @datagram_builder.h('fo@o', 10, 0.01, nil)
         
     | 
| 
      
 69 
     | 
    
         
            +
                assert_equal "fo_o:10|h|@0.01", datagram
         
     | 
| 
      
 70 
     | 
    
         
            +
              end
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
              def test_d
         
     | 
| 
      
 73 
     | 
    
         
            +
                datagram = @datagram_builder.d('foo', 1, nil, nil)
         
     | 
| 
      
 74 
     | 
    
         
            +
                assert_equal "foo:1|d", datagram
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                datagram = @datagram_builder.d('fo@o', 10, 0.01, nil)
         
     | 
| 
      
 77 
     | 
    
         
            +
                assert_equal "fo_o:10|d|@0.01", datagram
         
     | 
| 
      
 78 
     | 
    
         
            +
              end
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
              def test_tags
         
     | 
| 
      
 81 
     | 
    
         
            +
                datagram = @datagram_builder.d('foo', 10, nil, ['foo', 'bar'])
         
     | 
| 
      
 82 
     | 
    
         
            +
                assert_equal "foo:10|d|#foo,bar", datagram
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                datagram = @datagram_builder.d('foo', 10, 0.1, ['foo:bar'])
         
     | 
| 
      
 85 
     | 
    
         
            +
                assert_equal "foo:10|d|@0.1|#foo:bar", datagram
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                datagram = @datagram_builder.d('foo', 10, 1, foo: 'bar', baz: 'quc')
         
     | 
| 
      
 88 
     | 
    
         
            +
                assert_equal "foo:10|d|#foo:bar,baz:quc", datagram
         
     | 
| 
      
 89 
     | 
    
         
            +
              end
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
              def test_prefix
         
     | 
| 
      
 92 
     | 
    
         
            +
                datagram_builder = StatsD::Instrument::DatagramBuilder.new(prefix: 'foo')
         
     | 
| 
      
 93 
     | 
    
         
            +
                datagram = datagram_builder.c('bar', 1, nil, nil)
         
     | 
| 
      
 94 
     | 
    
         
            +
                assert_equal 'foo.bar:1|c', datagram
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
      
 96 
     | 
    
         
            +
                # The prefix should also be normalized
         
     | 
| 
      
 97 
     | 
    
         
            +
                datagram_builder = StatsD::Instrument::DatagramBuilder.new(prefix: 'foo|bar')
         
     | 
| 
      
 98 
     | 
    
         
            +
                datagram = datagram_builder.c('baz', 1, nil, nil)
         
     | 
| 
      
 99 
     | 
    
         
            +
                assert_equal 'foo_bar.baz:1|c', datagram
         
     | 
| 
      
 100 
     | 
    
         
            +
              end
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
      
 102 
     | 
    
         
            +
              def test_default_tags
         
     | 
| 
      
 103 
     | 
    
         
            +
                datagram_builder = StatsD::Instrument::DatagramBuilder.new(default_tags: ['foo'])
         
     | 
| 
      
 104 
     | 
    
         
            +
                datagram = datagram_builder.c('bar', 1, nil, nil)
         
     | 
| 
      
 105 
     | 
    
         
            +
                assert_equal 'bar:1|c|#foo', datagram
         
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
      
 107 
     | 
    
         
            +
                datagram = datagram_builder.c('bar', 1, nil, a: 'b')
         
     | 
| 
      
 108 
     | 
    
         
            +
                assert_equal 'bar:1|c|#a:b,foo', datagram
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
                # We do not filter out duplicates, because detecting dupes is too time consuming.
         
     | 
| 
      
 111 
     | 
    
         
            +
                # We let the server deal with the situation
         
     | 
| 
      
 112 
     | 
    
         
            +
                datagram = datagram_builder.c('bar', 1, nil, ['foo'])
         
     | 
| 
      
 113 
     | 
    
         
            +
                assert_equal 'bar:1|c|#foo,foo', datagram
         
     | 
| 
      
 114 
     | 
    
         
            +
             
     | 
| 
      
 115 
     | 
    
         
            +
                # Default tags are also normalized
         
     | 
| 
      
 116 
     | 
    
         
            +
                datagram_builder = StatsD::Instrument::DatagramBuilder.new(default_tags: ['f,o|o'])
         
     | 
| 
      
 117 
     | 
    
         
            +
                datagram = datagram_builder.c('bar', 1, nil, nil)
         
     | 
| 
      
 118 
     | 
    
         
            +
                assert_equal 'bar:1|c|#foo', datagram
         
     | 
| 
      
 119 
     | 
    
         
            +
              end
         
     | 
| 
      
 120 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,132 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'test_helper'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            class DeprecationsTest < Minitest::Test
         
     | 
| 
      
 6 
     | 
    
         
            +
              unless StatsD::Instrument.strict_mode_enabled?
         
     | 
| 
      
 7 
     | 
    
         
            +
                class InstrumentedClass
         
     | 
| 
      
 8 
     | 
    
         
            +
                  extend StatsD::Instrument
         
     | 
| 
      
 9 
     | 
    
         
            +
                  def foo; end
         
     | 
| 
      
 10 
     | 
    
         
            +
                  statsd_count :foo, 'frequency', 0.5, ['tag'] # rubocop:disable StatsD/MetaprogrammingPositionalArguments
         
     | 
| 
      
 11 
     | 
    
         
            +
                  statsd_measure :foo, 'latency', as_dist: true # rubocop:disable StatsD/MeasureAsDistArgument
         
     | 
| 
      
 12 
     | 
    
         
            +
                  statsd_count_success :foo, 'frequency', prefix: 'foo' # rubocop:disable StatsD/MetricPrefixArgument
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
              end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
              include StatsD::Instrument::Assertions
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
              def setup
         
     | 
| 
      
 19 
     | 
    
         
            +
                skip("Deprecation are not supported in strict mode") if StatsD::Instrument.strict_mode_enabled?
         
     | 
| 
      
 20 
     | 
    
         
            +
              end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
              def test__deprecated__metaprogramming_method_with_positional_arguments
         
     | 
| 
      
 23 
     | 
    
         
            +
                metrics = capture_statsd_calls { InstrumentedClass.new.foo }
         
     | 
| 
      
 24 
     | 
    
         
            +
                metric = metrics[0]
         
     | 
| 
      
 25 
     | 
    
         
            +
                assert_equal :c, metric.type
         
     | 
| 
      
 26 
     | 
    
         
            +
                assert_equal 'frequency', metric.name
         
     | 
| 
      
 27 
     | 
    
         
            +
                assert_equal 1, metric.value
         
     | 
| 
      
 28 
     | 
    
         
            +
                assert_equal 0.5, metric.sample_rate
         
     | 
| 
      
 29 
     | 
    
         
            +
                assert_equal ["tag"], metric.tags
         
     | 
| 
      
 30 
     | 
    
         
            +
              end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
              def test__deprecated__metaprogramming_statsd_measure_with_as_dist
         
     | 
| 
      
 33 
     | 
    
         
            +
                metrics = capture_statsd_calls { InstrumentedClass.new.foo }
         
     | 
| 
      
 34 
     | 
    
         
            +
                metric = metrics[1]
         
     | 
| 
      
 35 
     | 
    
         
            +
                assert_equal :d, metric.type
         
     | 
| 
      
 36 
     | 
    
         
            +
                assert_equal 'latency', metric.name
         
     | 
| 
      
 37 
     | 
    
         
            +
              end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
              def test__deprecated__metaprogramming_statsd_count_with_prefix
         
     | 
| 
      
 40 
     | 
    
         
            +
                metrics = capture_statsd_calls { InstrumentedClass.new.foo }
         
     | 
| 
      
 41 
     | 
    
         
            +
                metric = metrics[2]
         
     | 
| 
      
 42 
     | 
    
         
            +
                assert_equal :c, metric.type
         
     | 
| 
      
 43 
     | 
    
         
            +
                assert_equal 'foo.frequency.success', metric.name
         
     | 
| 
      
 44 
     | 
    
         
            +
              end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
              # rubocop:disable StatsD/MetricValueKeywordArgument
         
     | 
| 
      
 47 
     | 
    
         
            +
              def test__deprecated__statsd_measure_with_explicit_value_as_keyword_argument
         
     | 
| 
      
 48 
     | 
    
         
            +
                metric = capture_statsd_call { StatsD.measure('values.foobar', value: 42) }
         
     | 
| 
      
 49 
     | 
    
         
            +
                assert_equal 'values.foobar', metric.name
         
     | 
| 
      
 50 
     | 
    
         
            +
                assert_equal 42, metric.value
         
     | 
| 
      
 51 
     | 
    
         
            +
                assert_equal :ms, metric.type
         
     | 
| 
      
 52 
     | 
    
         
            +
              end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
              # rubocop:disable StatsD/MeasureAsDistArgument
         
     | 
| 
      
 55 
     | 
    
         
            +
              def test__deprecated__statsd_measure_with_explicit_value_keyword_and_distribution_override
         
     | 
| 
      
 56 
     | 
    
         
            +
                metric = capture_statsd_call { StatsD.measure('values.foobar', value: 42, as_dist: true) }
         
     | 
| 
      
 57 
     | 
    
         
            +
                assert_equal 42, metric.value
         
     | 
| 
      
 58 
     | 
    
         
            +
                assert_equal :d, metric.type
         
     | 
| 
      
 59 
     | 
    
         
            +
              end
         
     | 
| 
      
 60 
     | 
    
         
            +
              # rubocop:enable StatsD/MeasureAsDistArgument
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
              def test__deprecated__statsd_increment_with_value_as_keyword_argument
         
     | 
| 
      
 63 
     | 
    
         
            +
                metric = capture_statsd_call { StatsD.increment('values.foobar', value: 2) }
         
     | 
| 
      
 64 
     | 
    
         
            +
                assert_equal StatsD.default_sample_rate, metric.sample_rate
         
     | 
| 
      
 65 
     | 
    
         
            +
                assert_equal 2, metric.value
         
     | 
| 
      
 66 
     | 
    
         
            +
              end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
              def test__deprecated__statsd_gauge_with_keyword_argument
         
     | 
| 
      
 69 
     | 
    
         
            +
                metric = capture_statsd_call { StatsD.gauge('values.foobar', value: 13) }
         
     | 
| 
      
 70 
     | 
    
         
            +
                assert_equal :g, metric.type
         
     | 
| 
      
 71 
     | 
    
         
            +
                assert_equal 'values.foobar', metric.name
         
     | 
| 
      
 72 
     | 
    
         
            +
                assert_equal 13, metric.value
         
     | 
| 
      
 73 
     | 
    
         
            +
              end
         
     | 
| 
      
 74 
     | 
    
         
            +
              # rubocop:enable StatsD/MetricValueKeywordArgument
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
              # rubocop:disable StatsD/MetricReturnValue
         
     | 
| 
      
 77 
     | 
    
         
            +
              def test__deprecated__statsd_increment_retuns_metric_instance
         
     | 
| 
      
 78 
     | 
    
         
            +
                metric = StatsD.increment('key')
         
     | 
| 
      
 79 
     | 
    
         
            +
                assert_kind_of StatsD::Instrument::Metric, metric
         
     | 
| 
      
 80 
     | 
    
         
            +
                assert_equal 'key', metric.name
         
     | 
| 
      
 81 
     | 
    
         
            +
                assert_equal :c, metric.type
         
     | 
| 
      
 82 
     | 
    
         
            +
                assert_equal 1, metric.value
         
     | 
| 
      
 83 
     | 
    
         
            +
              end
         
     | 
| 
      
 84 
     | 
    
         
            +
              # rubocop:enable StatsD/MetricReturnValue
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
              # rubocop:disable StatsD/PositionalArguments
         
     | 
| 
      
 87 
     | 
    
         
            +
              def test__deprecated__statsd_increment_with_positional_argument_for_tags
         
     | 
| 
      
 88 
     | 
    
         
            +
                metric = capture_statsd_call { StatsD.increment('values.foobar', 12, nil, ['test']) }
         
     | 
| 
      
 89 
     | 
    
         
            +
                assert_equal StatsD.default_sample_rate, metric.sample_rate
         
     | 
| 
      
 90 
     | 
    
         
            +
                assert_equal ['test'], metric.tags
         
     | 
| 
      
 91 
     | 
    
         
            +
                assert_equal 12, metric.value
         
     | 
| 
      
 92 
     | 
    
         
            +
                assert_equal StatsD.default_sample_rate, metric.sample_rate
         
     | 
| 
      
 93 
     | 
    
         
            +
              end
         
     | 
| 
      
 94 
     | 
    
         
            +
              # rubocop:enable StatsD/PositionalArguments
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
      
 96 
     | 
    
         
            +
              # rubocop:disable StatsD/MeasureAsDistArgument
         
     | 
| 
      
 97 
     | 
    
         
            +
              def test__deprecated__statsd_measure_with_explicit_value_and_distribution_override
         
     | 
| 
      
 98 
     | 
    
         
            +
                metric = capture_statsd_call { StatsD.measure('values.foobar', 42, as_dist: true) }
         
     | 
| 
      
 99 
     | 
    
         
            +
                assert_equal :d, metric.type
         
     | 
| 
      
 100 
     | 
    
         
            +
              end
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
      
 102 
     | 
    
         
            +
              def test__deprecated__statsd_measure_use_distribution_override_for_a_block
         
     | 
| 
      
 103 
     | 
    
         
            +
                metric = capture_statsd_call do
         
     | 
| 
      
 104 
     | 
    
         
            +
                  StatsD.measure('values.foobar', as_dist: true) { 'foo' }
         
     | 
| 
      
 105 
     | 
    
         
            +
                end
         
     | 
| 
      
 106 
     | 
    
         
            +
                assert_equal :d, metric.type
         
     | 
| 
      
 107 
     | 
    
         
            +
              end
         
     | 
| 
      
 108 
     | 
    
         
            +
             
     | 
| 
      
 109 
     | 
    
         
            +
              def test__deprecated__statsd_measure_as_distribution_returns_return_value_of_block_even_if_nil
         
     | 
| 
      
 110 
     | 
    
         
            +
                return_value = StatsD.measure('values.foobar', as_dist: true) { nil }
         
     | 
| 
      
 111 
     | 
    
         
            +
                assert_nil return_value
         
     | 
| 
      
 112 
     | 
    
         
            +
              end
         
     | 
| 
      
 113 
     | 
    
         
            +
              # rubocop:enable StatsD/MeasureAsDistArgument
         
     | 
| 
      
 114 
     | 
    
         
            +
             
     | 
| 
      
 115 
     | 
    
         
            +
              # rubocop:disable StatsD/MetricPrefixArgument
         
     | 
| 
      
 116 
     | 
    
         
            +
              def test__deprecated__override_name_prefix
         
     | 
| 
      
 117 
     | 
    
         
            +
                m = capture_statsd_call { StatsD.increment('counter', prefix: "foobar") }
         
     | 
| 
      
 118 
     | 
    
         
            +
                assert_equal 'foobar.counter', m.name
         
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
                m = capture_statsd_call { StatsD.increment('counter', prefix: "foobar", no_prefix: true) }
         
     | 
| 
      
 121 
     | 
    
         
            +
                assert_equal 'counter', m.name
         
     | 
| 
      
 122 
     | 
    
         
            +
              end
         
     | 
| 
      
 123 
     | 
    
         
            +
              # rubocop:enable StatsD/MetricPrefixArgument
         
     | 
| 
      
 124 
     | 
    
         
            +
             
     | 
| 
      
 125 
     | 
    
         
            +
              protected
         
     | 
| 
      
 126 
     | 
    
         
            +
             
     | 
| 
      
 127 
     | 
    
         
            +
              def capture_statsd_call(&block)
         
     | 
| 
      
 128 
     | 
    
         
            +
                metrics = capture_statsd_calls(&block)
         
     | 
| 
      
 129 
     | 
    
         
            +
                assert_equal 1, metrics.length
         
     | 
| 
      
 130 
     | 
    
         
            +
                metrics.first
         
     | 
| 
      
 131 
     | 
    
         
            +
              end
         
     | 
| 
      
 132 
     | 
    
         
            +
            end
         
     |