statsd-instrument 2.3.2 → 2.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/.github/CODEOWNERS +1 -0
  3. data/.github/workflows/benchmark.yml +32 -0
  4. data/.github/workflows/ci.yml +47 -0
  5. data/.gitignore +1 -0
  6. data/.rubocop-https---shopify-github-io-ruby-style-guide-rubocop-yml +1027 -0
  7. data/.rubocop.yml +50 -0
  8. data/.yardopts +5 -0
  9. data/CHANGELOG.md +288 -2
  10. data/CONTRIBUTING.md +28 -6
  11. data/Gemfile +5 -0
  12. data/README.md +54 -46
  13. data/Rakefile +4 -2
  14. data/benchmark/README.md +29 -0
  15. data/benchmark/datagram-client +41 -0
  16. data/benchmark/send-metrics-to-dev-null-log +47 -0
  17. data/benchmark/send-metrics-to-local-udp-receiver +57 -0
  18. data/lib/statsd/instrument/assertions.rb +179 -30
  19. data/lib/statsd/instrument/backend.rb +3 -2
  20. data/lib/statsd/instrument/backends/capture_backend.rb +4 -1
  21. data/lib/statsd/instrument/backends/logger_backend.rb +3 -3
  22. data/lib/statsd/instrument/backends/null_backend.rb +2 -0
  23. data/lib/statsd/instrument/backends/udp_backend.rb +39 -45
  24. data/lib/statsd/instrument/capture_sink.rb +27 -0
  25. data/lib/statsd/instrument/client.rb +313 -0
  26. data/lib/statsd/instrument/datagram.rb +75 -0
  27. data/lib/statsd/instrument/datagram_builder.rb +101 -0
  28. data/lib/statsd/instrument/dogstatsd_datagram_builder.rb +71 -0
  29. data/lib/statsd/instrument/environment.rb +108 -29
  30. data/lib/statsd/instrument/helpers.rb +16 -8
  31. data/lib/statsd/instrument/log_sink.rb +24 -0
  32. data/lib/statsd/instrument/matchers.rb +14 -11
  33. data/lib/statsd/instrument/metric.rb +72 -45
  34. data/lib/statsd/instrument/metric_expectation.rb +32 -18
  35. data/lib/statsd/instrument/null_sink.rb +13 -0
  36. data/lib/statsd/instrument/railtie.rb +2 -1
  37. data/lib/statsd/instrument/rubocop/measure_as_dist_argument.rb +39 -0
  38. data/lib/statsd/instrument/rubocop/metaprogramming_positional_arguments.rb +42 -0
  39. data/lib/statsd/instrument/rubocop/metric_prefix_argument.rb +37 -0
  40. data/lib/statsd/instrument/rubocop/metric_return_value.rb +32 -0
  41. data/lib/statsd/instrument/rubocop/metric_value_keyword_argument.rb +36 -0
  42. data/lib/statsd/instrument/rubocop/positional_arguments.rb +99 -0
  43. data/lib/statsd/instrument/rubocop/splat_arguments.rb +31 -0
  44. data/lib/statsd/instrument/rubocop.rb +64 -0
  45. data/lib/statsd/instrument/statsd_datagram_builder.rb +14 -0
  46. data/lib/statsd/instrument/strict.rb +235 -0
  47. data/lib/statsd/instrument/udp_sink.rb +62 -0
  48. data/lib/statsd/instrument/version.rb +3 -1
  49. data/lib/statsd/instrument.rb +340 -163
  50. data/lib/statsd-instrument.rb +2 -0
  51. data/statsd-instrument.gemspec +13 -10
  52. data/test/assertions_test.rb +167 -156
  53. data/test/benchmark/clock_gettime.rb +27 -0
  54. data/test/benchmark/default_tags.rb +47 -0
  55. data/test/benchmark/metrics.rb +9 -8
  56. data/test/benchmark/tags.rb +5 -3
  57. data/test/capture_backend_test.rb +4 -2
  58. data/test/capture_sink_test.rb +44 -0
  59. data/test/client_test.rb +164 -0
  60. data/test/compatibility/dogstatsd_datagram_compatibility_test.rb +162 -0
  61. data/test/datagram_builder_test.rb +120 -0
  62. data/test/deprecations_test.rb +132 -0
  63. data/test/dogstatsd_datagram_builder_test.rb +32 -0
  64. data/test/environment_test.rb +75 -8
  65. data/test/helpers/rubocop_helper.rb +47 -0
  66. data/test/helpers_test.rb +2 -1
  67. data/test/integration_test.rb +31 -7
  68. data/test/log_sink_test.rb +37 -0
  69. data/test/logger_backend_test.rb +10 -8
  70. data/test/matchers_test.rb +42 -28
  71. data/test/metric_test.rb +18 -22
  72. data/test/null_sink_test.rb +13 -0
  73. data/test/rubocop/measure_as_dist_argument_test.rb +44 -0
  74. data/test/rubocop/metaprogramming_positional_arguments_test.rb +58 -0
  75. data/test/rubocop/metric_prefix_argument_test.rb +38 -0
  76. data/test/rubocop/metric_return_value_test.rb +78 -0
  77. data/test/rubocop/metric_value_keyword_argument_test.rb +39 -0
  78. data/test/rubocop/positional_arguments_test.rb +110 -0
  79. data/test/rubocop/splat_arguments_test.rb +27 -0
  80. data/test/statsd_datagram_builder_test.rb +22 -0
  81. data/test/statsd_instrumentation_test.rb +109 -100
  82. data/test/statsd_test.rb +113 -79
  83. data/test/test_helper.rb +12 -1
  84. data/test/udp_backend_test.rb +38 -36
  85. data/test/udp_sink_test.rb +85 -0
  86. metadata +85 -5
  87. data/.travis.yml +0 -12
data/test/statsd_test.rb CHANGED
@@ -1,166 +1,177 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
 
3
5
  class StatsDTest < Minitest::Test
4
6
  include StatsD::Instrument::Assertions
5
7
 
8
+ def teardown
9
+ StatsD.default_tags = nil
10
+ end
11
+
6
12
  def test_statsd_passed_collections_to_backend
7
13
  StatsD.backend.expects(:collect_metric).with(instance_of(StatsD::Instrument::Metric))
8
14
  StatsD.increment('test')
9
15
  end
10
16
 
11
17
  def test_statsd_measure_with_explicit_value
12
- result = nil
13
- metric = capture_statsd_call { result = StatsD.measure('values.foobar', 42) }
14
- assert_equal metric, result
18
+ metric = capture_statsd_call { StatsD.measure('values.foobar', 42) }
15
19
  assert_equal 'values.foobar', metric.name
16
20
  assert_equal 42, metric.value
17
21
  assert_equal :ms, metric.type
18
22
  end
19
23
 
20
- def test_statsd_measure_with_explicit_value_and_distribution_override
21
- metric = capture_statsd_call { result = StatsD.measure('values.foobar', 42, as_dist: true) }
22
- assert_equal :d, metric.type
23
- end
24
-
25
- def test_statsd_measure_with_explicit_value_as_keyword_argument
26
- result = nil
27
- metric = capture_statsd_call { result = StatsD.measure('values.foobar', value: 42) }
28
- assert_equal metric, result
29
- assert_equal 'values.foobar', metric.name
30
- assert_equal 42, metric.value
31
- assert_equal :ms, metric.type
32
- end
33
-
34
- def test_statsd_measure_with_explicit_value_keyword_and_distribution_override
35
- metric = capture_statsd_call { result = StatsD.measure('values.foobar', value: 42, as_dist: true) }
36
- assert_equal :d, metric.type
37
- end
38
-
39
24
  def test_statsd_measure_without_value_or_block
40
25
  assert_raises(ArgumentError) { StatsD.measure('values.foobar', tags: 123) }
41
26
  end
42
27
 
43
28
  def test_statsd_measure_with_explicit_value_and_sample_rate
44
- metric = capture_statsd_call { StatsD.measure('values.foobar', 42, :sample_rate => 0.1) }
29
+ metric = capture_statsd_call { StatsD.measure('values.foobar', 42, sample_rate: 0.1) }
45
30
  assert_equal 0.1, metric.sample_rate
46
31
  end
47
32
 
48
33
  def test_statsd_measure_with_benchmarked_block_duration
49
- StatsD::Instrument.stubs(:duration).returns(1.12)
34
+ Process.stubs(:clock_gettime).returns(5.0, 5.0 + 1.12)
50
35
  metric = capture_statsd_call do
51
36
  StatsD.measure('values.foobar') { 'foo' }
52
37
  end
53
38
  assert_equal 1120.0, metric.value
54
39
  end
55
40
 
56
- def test_statsd_measure_use_distribution_override_for_a_block
57
- metric = capture_statsd_call do
58
- StatsD.measure('values.foobar', as_dist: true) { 'foo' }
59
- end
60
- assert_equal :d, metric.type
61
- end
62
-
63
41
  def test_statsd_measure_returns_return_value_of_block
64
42
  return_value = StatsD.measure('values.foobar') { 'sarah' }
65
43
  assert_equal 'sarah', return_value
66
44
  end
67
45
 
68
- def test_statsd_measure_returns_return_value_of_block_even_if_nil
69
- return_value = StatsD.measure('values.foobar', as_dist: true) { nil }
70
- assert_nil return_value
46
+ def test_statsd_measure_with_return_in_block_still_captures
47
+ Process.stubs(:clock_gettime).returns(5.0, 6.12)
48
+ result = nil
49
+ metric = capture_statsd_call do
50
+ lambda = -> do
51
+ StatsD.measure('values.foobar') { return 'from lambda' }
52
+ end
53
+
54
+ result = lambda.call
55
+ end
56
+
57
+ assert_equal 'from lambda', result
58
+ assert_equal 1120.0, metric.value
71
59
  end
72
60
 
73
- def test_statsd_increment
61
+ def test_statsd_measure_with_exception_in_block_still_captures
62
+ Process.stubs(:clock_gettime).returns(5.0, 6.12)
74
63
  result = nil
75
- metric = capture_statsd_call { result = StatsD.increment('values.foobar', 3) }
76
- assert_equal metric, result
64
+ metric = capture_statsd_call do
65
+ lambda = -> do
66
+ StatsD.measure('values.foobar') { raise 'from lambda' }
67
+ end
68
+
69
+ begin
70
+ result = lambda.call
71
+ rescue # rubocop:disable Lint/HandleExceptions:
72
+ end
73
+ end
74
+
75
+ assert_nil result
76
+ assert_equal 1120.0, metric.value
77
+ end
78
+
79
+ def test_statsd_increment
80
+ metric = capture_statsd_call { StatsD.increment('values.foobar', 3) }
77
81
  assert_equal :c, metric.type
78
82
  assert_equal 'values.foobar', metric.name
79
83
  assert_equal 3, metric.value
80
84
  end
81
85
 
82
86
  def test_statsd_increment_with_hash_argument
83
- metric = capture_statsd_call { StatsD.increment('values.foobar', :tags => ['test']) }
87
+ metric = capture_statsd_call { StatsD.increment('values.foobar', tags: ['test']) }
84
88
  assert_equal StatsD.default_sample_rate, metric.sample_rate
85
89
  assert_equal ['test'], metric.tags
86
90
  assert_equal 1, metric.value
87
91
  end
88
92
 
89
- def test_statsd_increment_with_value_as_keyword_argument
90
- metric = capture_statsd_call { StatsD.increment('values.foobar', :value => 2) }
91
- assert_equal StatsD.default_sample_rate, metric.sample_rate
92
- assert_equal 2, metric.value
93
- end
94
-
95
- def test_statsd_increment_with_multiple_arguments
96
- metric = capture_statsd_call { StatsD.increment('values.foobar', 12, nil, ['test']) }
97
- assert_equal StatsD.default_sample_rate, metric.sample_rate
98
- assert_equal ['test'], metric.tags
99
- assert_equal 12, metric.value
100
- end
101
-
102
93
  def test_statsd_gauge
103
- result = nil
104
- metric = capture_statsd_call { result = StatsD.gauge('values.foobar', 12) }
105
- assert_equal metric, result
94
+ metric = capture_statsd_call { StatsD.gauge('values.foobar', 12) }
106
95
  assert_equal :g, metric.type
107
96
  assert_equal 'values.foobar', metric.name
108
97
  assert_equal 12, metric.value
109
98
  end
110
99
 
111
- def test_statsd_gauge_with_keyword_argument
112
- result = nil
113
- metric = capture_statsd_call { result = StatsD.gauge('values.foobar', value: 13) }
114
- assert_equal metric, result
115
- assert_equal :g, metric.type
116
- assert_equal 'values.foobar', metric.name
117
- assert_equal 13, metric.value
118
- end
119
-
120
100
  def test_statsd_gauge_without_value
121
101
  assert_raises(ArgumentError) { StatsD.gauge('values.foobar', tags: 123) }
122
102
  end
123
103
 
124
104
  def test_statsd_set
125
- result = nil
126
- metric = capture_statsd_call { result = StatsD.set('values.foobar', 'unique_identifier') }
127
- assert_equal metric, result
105
+ metric = capture_statsd_call { StatsD.set('values.foobar', 'unique_identifier') }
128
106
  assert_equal :s, metric.type
129
107
  assert_equal 'values.foobar', metric.name
130
108
  assert_equal 'unique_identifier', metric.value
131
109
  end
132
110
 
133
111
  def test_statsd_histogram
134
- result = nil
135
- metric = capture_statsd_call { result = StatsD.histogram('values.foobar', 42) }
136
- assert_equal metric, result
112
+ metric = capture_statsd_call { StatsD.histogram('values.foobar', 42) }
137
113
  assert_equal :h, metric.type
138
114
  assert_equal 'values.foobar', metric.name
139
115
  assert_equal 42, metric.value
140
116
  end
141
117
 
142
118
  def test_statsd_distribution
143
- result = nil
144
- metric = capture_statsd_call { result = StatsD.distribution('values.foobar', 42) }
145
- assert_equal metric, result
119
+ metric = capture_statsd_call { StatsD.distribution('values.foobar', 42) }
146
120
  assert_equal :d, metric.type
147
121
  assert_equal 'values.foobar', metric.name
148
122
  assert_equal 42, metric.value
149
123
  end
150
124
 
151
125
  def test_statsd_distribution_with_benchmarked_block_duration
152
- StatsD::Instrument.stubs(:duration).returns(1.12)
126
+ Process.stubs(:clock_gettime).returns(5.0, 5.0 + 1.12)
153
127
  metric = capture_statsd_call do
154
- StatsD.distribution('values.foobar') { 'foo' }
128
+ result = StatsD.distribution('values.foobar') { 'foo' }
129
+ assert_equal 'foo', result
155
130
  end
156
131
  assert_equal :d, metric.type
157
132
  assert_equal 1120.0, metric.value
158
133
  end
159
134
 
135
+ def test_statsd_distribution_with_return_in_block_still_captures
136
+ Process.stubs(:clock_gettime).returns(5.0, 5.0 + 1.12)
137
+ result = nil
138
+ metric = capture_statsd_call do
139
+ lambda = -> do
140
+ StatsD.distribution('values.foobar') { return 'from lambda' }
141
+ flunk("This code should not be reached")
142
+ end
143
+
144
+ result = lambda.call
145
+ end
146
+
147
+ assert_equal 'from lambda', result
148
+ assert_equal :d, metric.type
149
+ assert_equal 1120.0, metric.value
150
+ end
151
+
152
+ def test_statsd_distribution_with_exception_in_block_still_captures
153
+ Process.stubs(:clock_gettime).returns(5.0, 5.0 + 1.12)
154
+ result = nil
155
+ metric = capture_statsd_call do
156
+ lambda = -> do
157
+ StatsD.distribution('values.foobar') { raise 'from lambda' }
158
+ end
159
+
160
+ begin
161
+ result = lambda.call
162
+ rescue # rubocop:disable Lint/HandleExceptions
163
+ end
164
+ end
165
+
166
+ assert_nil result
167
+ assert_equal :d, metric.type
168
+ assert_equal 1120.0, metric.value
169
+ end
170
+
160
171
  def test_statsd_distribution_with_block_and_options
161
- StatsD::Instrument.stubs(:duration).returns(1.12)
172
+ Process.stubs(:clock_gettime).returns(5.0, 5.0 + 1.12)
162
173
  metric = capture_statsd_call do
163
- StatsD.distribution('values.foobar', :tags => ['test'], :sample_rate => 0.9) { 'foo' }
174
+ StatsD.distribution('values.foobar', tags: ['test'], sample_rate: 0.9) { 'foo' }
164
175
  end
165
176
  assert_equal 1120.0, metric.value
166
177
  assert_equal 'values.foobar', metric.name
@@ -179,14 +190,37 @@ class StatsDTest < Minitest::Test
179
190
  end
180
191
 
181
192
  def test_statsd_key_value
182
- result = nil
183
- metric = capture_statsd_call { result = StatsD.key_value('values.foobar', 42) }
184
- assert_equal metric, result
193
+ metric = capture_statsd_call { StatsD.key_value('values.foobar', 42) }
185
194
  assert_equal :kv, metric.type
186
195
  assert_equal 'values.foobar', metric.name
187
196
  assert_equal 42, metric.value
188
197
  end
189
198
 
199
+ def test_statsd_durarion_returns_time_in_seconds
200
+ duration = StatsD::Instrument.duration {}
201
+ assert_kind_of Float, duration
202
+ end
203
+
204
+ def test_statsd_durarion_does_not_swallow_exceptions
205
+ assert_raises(RuntimeError) do
206
+ StatsD::Instrument.duration { raise "Foo" }
207
+ end
208
+ end
209
+
210
+ def test_statsd_default_tags_get_normalized
211
+ StatsD.default_tags = { first_tag: 'first_value', second_tag: 'second_value' }
212
+ assert_equal ['first_tag:first_value', 'second_tag:second_value'], StatsD.default_tags
213
+ end
214
+
215
+ def test_name_prefix
216
+ StatsD.stubs(:prefix).returns('prefix')
217
+ m = capture_statsd_call { StatsD.increment('counter') }
218
+ assert_equal 'prefix.counter', m.name
219
+
220
+ m = capture_statsd_call { StatsD.increment('counter', no_prefix: true) }
221
+ assert_equal 'counter', m.name
222
+ end
223
+
190
224
  protected
191
225
 
192
226
  def capture_statsd_call(&block)
data/test/test_helper.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  ENV['ENV'] = 'test'
2
4
 
3
5
  require 'minitest/autorun'
@@ -7,4 +9,13 @@ require 'set'
7
9
  require 'logger'
8
10
  require 'statsd-instrument'
9
11
 
10
- StatsD.logger = Logger.new('/dev/null')
12
+ require_relative 'helpers/rubocop_helper'
13
+
14
+ module StatsD::Instrument
15
+ def self.strict_mode_enabled?
16
+ StatsD::Instrument.const_defined?(:Strict) &&
17
+ StatsD.singleton_class.ancestors.include?(StatsD::Instrument::Strict)
18
+ end
19
+ end
20
+
21
+ StatsD.logger = Logger.new(File::NULL)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
 
3
5
  class UDPBackendTest < Minitest::Test
@@ -76,7 +78,7 @@ class UDPBackendTest < Minitest::Test
76
78
  StatsD.histogram('fooh', 42.4)
77
79
  end
78
80
 
79
- def test_distribution_syntax_on_datadog
81
+ def test_distribution_syntax_on_datadog
80
82
  @backend.implementation = :datadog
81
83
  @backend.expects(:write_packet).with('fooh:42.4|d')
82
84
  StatsD.distribution('fooh', 42.4)
@@ -84,20 +86,25 @@ class UDPBackendTest < Minitest::Test
84
86
 
85
87
  def test_event_on_datadog
86
88
  @backend.implementation = :datadog
87
- @backend.expects(:write_packet).with('_e{4,3}:fooh|baz|h:localhost:3000|@0.01|#foo')
88
- StatsD.event('fooh', 'baz', hostname: 'localhost:3000', sample_rate: 0.01, tags: ["foo"])
89
+ @backend.expects(:write_packet).with('_e{4,3}:fooh|baz|h:localhost|#foo')
90
+ StatsD.event('fooh', 'baz', hostname: 'localhost', tags: ["foo"])
89
91
  end
90
92
 
91
93
  def test_event_on_datadog_escapes_newlines
92
94
  @backend.implementation = :datadog
93
- @backend.expects(:write_packet).with('_e{8,5}:fooh\\n\\n|baz\\n')
94
- StatsD.event('fooh\n\n', 'baz\n')
95
+ @backend.expects(:write_packet).with("_e{8,5}:fooh\\n\\n|baz\\n")
96
+ StatsD.event("fooh\n\n", "baz\n")
95
97
  end
96
98
 
97
99
  def test_event_on_datadog_ignores_invalid_metadata
98
100
  @backend.implementation = :datadog
99
- @backend.expects(:write_packet).with('_e{4,3}:fooh|baz')
100
- StatsD.event('fooh', 'baz', i_am_not_supported: 'not-supported')
101
+ if StatsD::Instrument.strict_mode_enabled?
102
+ assert_raises(ArgumentError) { StatsD.event('fooh', 'baz', sample_rate: 0.01) }
103
+ assert_raises(ArgumentError) { StatsD.event('fooh', 'baz', unsupported: 'foo') }
104
+ else
105
+ @backend.expects(:write_packet).with('_e{4,3}:fooh|baz')
106
+ StatsD.event('fooh', 'baz', sample_rate: 0.01, i_am_not_supported: 'not-supported')
107
+ end
101
108
  end
102
109
 
103
110
  def test_event_warns_when_not_using_datadog
@@ -109,14 +116,26 @@ class UDPBackendTest < Minitest::Test
109
116
 
110
117
  def test_service_check_on_datadog
111
118
  @backend.implementation = :datadog
112
- @backend.expects(:write_packet).with('_sc|fooh|baz|h:localhost:3000|@0.01|#foo')
113
- StatsD.service_check('fooh', 'baz', hostname: 'localhost:3000', sample_rate: 0.01, tags: ["foo"])
119
+ @backend.expects(:write_packet).with('_sc|fooh|0|h:localhost|#foo')
120
+ StatsD.service_check('fooh', 0, hostname: 'localhost', tags: ["foo"])
114
121
  end
115
122
 
116
123
  def test_service_check_on_datadog_ignores_invalid_metadata
117
124
  @backend.implementation = :datadog
118
- @backend.expects(:write_packet).with('_sc|fooh|baz')
119
- StatsD.service_check('fooh', 'baz', i_am_not_supported: 'not-supported')
125
+ if StatsD::Instrument.strict_mode_enabled?
126
+ assert_raises(ArgumentError) { StatsD.service_check('fooh', "warning", sample_rate: 0.01) }
127
+ assert_raises(ArgumentError) { StatsD.service_check('fooh', "warning", unsupported: 'foo') }
128
+ else
129
+ @backend.expects(:write_packet).with('_sc|fooh|1')
130
+ StatsD.service_check('fooh', "warning", sample_rate: 0.01, i_am_not_supported: 'not-supported')
131
+ end
132
+ end
133
+
134
+ def test_service_check_on_datadog_will_append_message_as_final_metadata_field
135
+ @backend.implementation = :datadog
136
+ @backend.expects(:write_packet).with('_sc|fooh|0|d:1230768000|#quc|m:Everything OK')
137
+ StatsD.service_check('fooh', :ok, message: "Everything OK",
138
+ timestamp: Time.parse('2009-01-01T00:00:00Z'), tags: ['quc'])
120
139
  end
121
140
 
122
141
  def test_service_check_warns_when_not_using_datadog
@@ -146,11 +165,19 @@ class UDPBackendTest < Minitest::Test
146
165
  StatsD.key_value('fooy', 42)
147
166
  end
148
167
 
168
+ # For key_value metrics (only supported by statsite), the sample rate
169
+ # part of the datagram format is (ab)used to be set to a timestamp instead.
170
+ # Changing that to `sample_rate: timestamp` does not make sense, so we
171
+ # disable the rubocop rule for positional arguments for now,
172
+ # until we figure out how we want to handle this.
173
+
174
+ # rubocop:disable StatsD/PositionalArguments
149
175
  def test_supports_key_value_with_timestamp_on_statsite
150
176
  @backend.implementation = :statsite
151
177
  @backend.expects(:write_packet).with("fooy:42|kv|@123456\n")
152
178
  StatsD.key_value('fooy', 42, 123456)
153
179
  end
180
+ # rubocop:enable StatsD/PositionalArguments
154
181
 
155
182
  def test_warn_when_using_key_value_and_not_on_statsite
156
183
  @backend.implementation = :other
@@ -183,31 +210,6 @@ class UDPBackendTest < Minitest::Test
183
210
  StatsD.increment('fail')
184
211
  end
185
212
 
186
- def test_synchronize_in_exit_handler_handles_thread_error_and_exits_cleanly
187
- pid = fork do
188
- Signal.trap('TERM') do
189
- $sent_packet = false
190
-
191
- class << @backend.socket
192
- def send(command, *args)
193
- $sent_packet = true if command == 'exiting:1|c'
194
- command.length
195
- end
196
- end
197
-
198
- StatsD.increment('exiting')
199
- Process.exit!($sent_packet)
200
- end
201
-
202
- sleep 100
203
- end
204
-
205
- Process.kill('TERM', pid)
206
- Process.waitpid(pid)
207
-
208
- assert $?.success?, 'socket did not write on exit'
209
- end
210
-
211
213
  def test_socket_error_should_invalidate_socket
212
214
  seq = sequence('fail_then_succeed')
213
215
 
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ require 'statsd/instrument/client'
6
+
7
+ class UDPSinktest < Minitest::Test
8
+ def setup
9
+ @receiver = UDPSocket.new
10
+ @receiver.bind('localhost', 0)
11
+ @host = @receiver.addr[2]
12
+ @port = @receiver.addr[1]
13
+ end
14
+
15
+ def teardown
16
+ @receiver.close
17
+ end
18
+
19
+ def test_udp_sink_sends_data_over_udp
20
+ udp_sink = StatsD::Instrument::UDPSink.new(@host, @port)
21
+ udp_sink << 'foo:1|c'
22
+
23
+ datagram, _source = @receiver.recvfrom(100)
24
+ assert_equal 'foo:1|c', datagram
25
+ end
26
+
27
+ def test_sample?
28
+ udp_sink = StatsD::Instrument::UDPSink.new(@host, @port)
29
+ assert udp_sink.sample?(1)
30
+ refute udp_sink.sample?(0)
31
+
32
+ udp_sink.stubs(:rand).returns(0.3)
33
+ assert udp_sink.sample?(0.5)
34
+
35
+ udp_sink.stubs(:rand).returns(0.7)
36
+ refute udp_sink.sample?(0.5)
37
+ end
38
+
39
+ def test_parallelism
40
+ udp_sink = StatsD::Instrument::UDPSink.new(@host, @port)
41
+ 50.times { |i| Thread.new { udp_sink << "foo:#{i}|c" << "bar:#{i}|c" } }
42
+ datagrams = []
43
+ 100.times do
44
+ datagram, _source = @receiver.recvfrom(100)
45
+ datagrams << datagram
46
+ end
47
+
48
+ assert_equal 100, datagrams.size
49
+ end
50
+
51
+ def test_socket_error_should_invalidate_socket
52
+ UDPSocket.stubs(:new).returns(socket = mock('socket'))
53
+
54
+ seq = sequence('connect_fail_connect_succeed')
55
+ socket.expects(:connect).with('localhost', 8125).in_sequence(seq)
56
+ socket.expects(:send).raises(Errno::EDESTADDRREQ).in_sequence(seq)
57
+ socket.expects(:connect).with('localhost', 8125).in_sequence(seq)
58
+ socket.expects(:send).returns(1).in_sequence(seq)
59
+
60
+ udp_sink = StatsD::Instrument::UDPSink.new('localhost', 8125)
61
+ udp_sink << 'foo:1|c'
62
+ udp_sink << 'bar:1|c'
63
+ end
64
+
65
+ def test_sends_datagram_in_signal_handler
66
+ udp_sink = StatsD::Instrument::UDPSink.new(@host, @port)
67
+ pid = fork do
68
+ Signal.trap('TERM') do
69
+ udp_sink << "exiting:1|c"
70
+ Process.exit!(0)
71
+ end
72
+
73
+ sleep(10)
74
+ end
75
+
76
+ Process.kill('TERM', pid)
77
+ _, exit_status = Process.waitpid2(pid)
78
+
79
+ assert_equal 0, exit_status, "The forked process did not exit cleanly"
80
+ assert_equal "exiting:1|c", @receiver.recvfrom_nonblock(100).first
81
+
82
+ rescue NotImplementedError
83
+ pass("Fork is not implemented on #{RUBY_PLATFORM}")
84
+ end
85
+ end