qubole-statsd-instrument 2.1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +7 -0
  3. data/.travis.yml +12 -0
  4. data/CHANGELOG.md +89 -0
  5. data/CONTRIBUTING.md +34 -0
  6. data/Gemfile +2 -0
  7. data/LICENSE +20 -0
  8. data/README.md +319 -0
  9. data/Rakefile +10 -0
  10. data/lib/statsd/instrument/assertions.rb +88 -0
  11. data/lib/statsd/instrument/backend.rb +17 -0
  12. data/lib/statsd/instrument/backends/capture_backend.rb +29 -0
  13. data/lib/statsd/instrument/backends/logger_backend.rb +20 -0
  14. data/lib/statsd/instrument/backends/null_backend.rb +7 -0
  15. data/lib/statsd/instrument/backends/udp_backend.rb +106 -0
  16. data/lib/statsd/instrument/environment.rb +54 -0
  17. data/lib/statsd/instrument/helpers.rb +14 -0
  18. data/lib/statsd/instrument/matchers.rb +96 -0
  19. data/lib/statsd/instrument/metric.rb +117 -0
  20. data/lib/statsd/instrument/metric_expectation.rb +67 -0
  21. data/lib/statsd/instrument/railtie.rb +14 -0
  22. data/lib/statsd/instrument/version.rb +5 -0
  23. data/lib/statsd/instrument.rb +407 -0
  24. data/lib/statsd-instrument.rb +1 -0
  25. data/shipit.rubygems.yml +1 -0
  26. data/statsd-instrument.gemspec +27 -0
  27. data/test/assertions_test.rb +329 -0
  28. data/test/benchmark/tags.rb +34 -0
  29. data/test/capture_backend_test.rb +24 -0
  30. data/test/environment_test.rb +46 -0
  31. data/test/helpers_test.rb +24 -0
  32. data/test/integration_test.rb +20 -0
  33. data/test/logger_backend_test.rb +20 -0
  34. data/test/matchers_test.rb +102 -0
  35. data/test/metric_test.rb +45 -0
  36. data/test/statsd_instrumentation_test.rb +328 -0
  37. data/test/statsd_test.rb +136 -0
  38. data/test/test_helper.rb +10 -0
  39. data/test/udp_backend_test.rb +167 -0
  40. metadata +182 -0
@@ -0,0 +1,328 @@
1
+ require 'test_helper'
2
+
3
+ module ActiveMerchant; end
4
+ class ActiveMerchant::Base
5
+ def ssl_post(arg)
6
+ if arg
7
+ 'OK'
8
+ else
9
+ raise 'Not OK'
10
+ end
11
+ end
12
+
13
+ def post_with_block(&block)
14
+ yield if block_given?
15
+ end
16
+ end
17
+
18
+ class ActiveMerchant::Gateway < ActiveMerchant::Base
19
+ def purchase(arg)
20
+ ssl_post(arg)
21
+ true
22
+ rescue
23
+ false
24
+ end
25
+
26
+ def self.sync
27
+ true
28
+ end
29
+
30
+ def self.singleton_class
31
+ class << self; self; end
32
+ end
33
+ end
34
+
35
+ class ActiveMerchant::UniqueGateway < ActiveMerchant::Base
36
+ def ssl_post(arg)
37
+ {:success => arg}
38
+ end
39
+
40
+ def purchase(arg)
41
+ ssl_post(arg)
42
+ end
43
+ end
44
+
45
+ class GatewaySubClass < ActiveMerchant::Gateway
46
+ end
47
+
48
+ class InstrumentedClass
49
+ extend StatsD::Instrument
50
+
51
+ def public_and_instrumented
52
+ end
53
+ statsd_count :public_and_instrumented, 'InstrumentedClass.public_and_instrumented'
54
+
55
+ protected
56
+
57
+ def protected_and_instrumented
58
+ end
59
+ statsd_count :protected_and_instrumented, 'InstrumentedClass.protected_and_instrumented'
60
+
61
+ private
62
+
63
+ def private_and_instrumented
64
+ end
65
+ statsd_count :private_and_instrumented, 'InstrumentedClass.private_and_instrumented'
66
+ end
67
+
68
+ ActiveMerchant::Base.extend StatsD::Instrument
69
+
70
+ class StatsDInstrumentationTest < Minitest::Test
71
+ include StatsD::Instrument::Assertions
72
+
73
+ def test_statsd_count_if
74
+ ActiveMerchant::Gateway.statsd_count_if :ssl_post, 'ActiveMerchant.Gateway.if'
75
+
76
+ assert_statsd_increment('ActiveMerchant.Gateway.if') do
77
+ ActiveMerchant::Gateway.new.purchase(true)
78
+ ActiveMerchant::Gateway.new.purchase(false)
79
+ end
80
+ ensure
81
+ ActiveMerchant::Gateway.statsd_remove_count_if :ssl_post, 'ActiveMerchant.Gateway.if'
82
+ end
83
+
84
+ def test_statsd_count_if_with_method_receiving_block
85
+ ActiveMerchant::Base.statsd_count_if :post_with_block, 'ActiveMerchant.Base.post_with_block' do |result|
86
+ result == 'true'
87
+ end
88
+
89
+ assert_statsd_increment('ActiveMerchant.Base.post_with_block') do
90
+ assert_equal 'true', ActiveMerchant::Base.new.post_with_block { 'true' }
91
+ assert_equal 'false', ActiveMerchant::Base.new.post_with_block { 'false' }
92
+ end
93
+ ensure
94
+ ActiveMerchant::Base.statsd_remove_count_if :post_with_block, 'ActiveMerchant.Base.post_with_block'
95
+ end
96
+
97
+ def test_statsd_count_if_with_block
98
+ ActiveMerchant::UniqueGateway.statsd_count_if :ssl_post, 'ActiveMerchant.Gateway.block' do |result|
99
+ result[:success]
100
+ end
101
+
102
+ assert_statsd_increment('ActiveMerchant.Gateway.block', times: 1) do
103
+ ActiveMerchant::UniqueGateway.new.purchase(true)
104
+ ActiveMerchant::UniqueGateway.new.purchase(false)
105
+ end
106
+ ensure
107
+ ActiveMerchant::UniqueGateway.statsd_remove_count_if :ssl_post, 'ActiveMerchant.Gateway.block'
108
+ end
109
+
110
+ def test_statsd_count_success
111
+ ActiveMerchant::Gateway.statsd_count_success :ssl_post, 'ActiveMerchant.Gateway', 0.5
112
+
113
+ assert_statsd_increment('ActiveMerchant.Gateway.success', sample_rate: 0.5, times: 1) do
114
+ ActiveMerchant::Gateway.new.purchase(true)
115
+ ActiveMerchant::Gateway.new.purchase(false)
116
+ end
117
+
118
+ assert_statsd_increment('ActiveMerchant.Gateway.failure', sample_rate: 0.5, times: 1) do
119
+ ActiveMerchant::Gateway.new.purchase(false)
120
+ ActiveMerchant::Gateway.new.purchase(true)
121
+ end
122
+ ensure
123
+ ActiveMerchant::Gateway.statsd_remove_count_success :ssl_post, 'ActiveMerchant.Gateway'
124
+ end
125
+
126
+ def test_statsd_count_success_with_method_receiving_block
127
+ ActiveMerchant::Base.statsd_count_success :post_with_block, 'ActiveMerchant.Base.post_with_block' do |result|
128
+ result == 'successful'
129
+ end
130
+
131
+ assert_statsd_increment('ActiveMerchant.Base.post_with_block.success', times: 1) do
132
+ assert_equal 'successful', ActiveMerchant::Base.new.post_with_block { 'successful' }
133
+ assert_equal 'not so successful', ActiveMerchant::Base.new.post_with_block { 'not so successful' }
134
+ end
135
+
136
+ assert_statsd_increment('ActiveMerchant.Base.post_with_block.failure', times: 1) do
137
+ assert_equal 'successful', ActiveMerchant::Base.new.post_with_block { 'successful' }
138
+ assert_equal 'not so successful', ActiveMerchant::Base.new.post_with_block { 'not so successful' }
139
+ end
140
+ ensure
141
+ ActiveMerchant::Base.statsd_remove_count_success :post_with_block, 'ActiveMerchant.Base.post_with_block'
142
+ end
143
+
144
+ def test_statsd_count_success_with_block
145
+ ActiveMerchant::UniqueGateway.statsd_count_success :ssl_post, 'ActiveMerchant.Gateway' do |result|
146
+ result[:success]
147
+ end
148
+
149
+ assert_statsd_increment('ActiveMerchant.Gateway.success') do
150
+ ActiveMerchant::UniqueGateway.new.purchase(true)
151
+ end
152
+
153
+ assert_statsd_increment('ActiveMerchant.Gateway.failure') do
154
+ ActiveMerchant::UniqueGateway.new.purchase(false)
155
+ end
156
+ ensure
157
+ ActiveMerchant::UniqueGateway.statsd_remove_count_success :ssl_post, 'ActiveMerchant.Gateway'
158
+ end
159
+
160
+ def test_statsd_count
161
+ ActiveMerchant::Gateway.statsd_count :ssl_post, 'ActiveMerchant.Gateway.ssl_post'
162
+
163
+ assert_statsd_increment('ActiveMerchant.Gateway.ssl_post') do
164
+ ActiveMerchant::Gateway.new.purchase(true)
165
+ end
166
+ ensure
167
+ ActiveMerchant::Gateway.statsd_remove_count :ssl_post, 'ActiveMerchant.Gateway.ssl_post'
168
+ end
169
+
170
+ def test_statsd_count_with_name_as_lambda
171
+ metric_namer = lambda { |object, args| object.class.to_s.downcase + ".insert." + args.first.to_s }
172
+ ActiveMerchant::Gateway.statsd_count(:ssl_post, metric_namer)
173
+
174
+ assert_statsd_increment('gatewaysubclass.insert.true') do
175
+ GatewaySubClass.new.purchase(true)
176
+ end
177
+ ensure
178
+ ActiveMerchant::Gateway.statsd_remove_count(:ssl_post, metric_namer)
179
+ end
180
+
181
+ def test_statsd_count_with_method_receiving_block
182
+ ActiveMerchant::Base.statsd_count :post_with_block, 'ActiveMerchant.Base.post_with_block'
183
+
184
+ assert_statsd_increment('ActiveMerchant.Base.post_with_block') do
185
+ assert_equal 'block called', ActiveMerchant::Base.new.post_with_block { 'block called' }
186
+ end
187
+ ensure
188
+ ActiveMerchant::Base.statsd_remove_count :post_with_block, 'ActiveMerchant.Base.post_with_block'
189
+ end
190
+
191
+ def test_statsd_measure
192
+ ActiveMerchant::UniqueGateway.statsd_measure :ssl_post, 'ActiveMerchant.Gateway.ssl_post', sample_rate: 0.3
193
+
194
+ assert_statsd_measure('ActiveMerchant.Gateway.ssl_post', sample_rate: 0.3) do
195
+ ActiveMerchant::UniqueGateway.new.purchase(true)
196
+ end
197
+ ensure
198
+ ActiveMerchant::UniqueGateway.statsd_remove_measure :ssl_post, 'ActiveMerchant.Gateway.ssl_post'
199
+ end
200
+
201
+ def test_statsd_measure_uses_normalized_metric_name
202
+ ActiveMerchant::UniqueGateway.statsd_measure :ssl_post, 'ActiveMerchant::Gateway.ssl_post'
203
+
204
+ assert_statsd_measure('ActiveMerchant.Gateway.ssl_post') do
205
+ ActiveMerchant::UniqueGateway.new.purchase(true)
206
+ end
207
+ ensure
208
+ ActiveMerchant::UniqueGateway.statsd_remove_measure :ssl_post, 'ActiveMerchant::Gateway.ssl_post'
209
+ end
210
+
211
+ def test_statsd_measure_yells_without_block
212
+ err = assert_raises(ArgumentError) do
213
+ assert_statsd_measure('ActiveMerchant.Gateway.ssl_post')
214
+ end
215
+ assert_equal "block must be given", err.to_s
216
+ end
217
+
218
+ def test_statsd_measure_with_method_receiving_block
219
+ ActiveMerchant::Base.statsd_measure :post_with_block, 'ActiveMerchant.Base.post_with_block'
220
+
221
+ assert_statsd_measure('ActiveMerchant.Base.post_with_block') do
222
+ assert_equal 'block called', ActiveMerchant::Base.new.post_with_block { 'block called' }
223
+ end
224
+ ensure
225
+ ActiveMerchant::Base.statsd_remove_measure :post_with_block, 'ActiveMerchant.Base.post_with_block'
226
+ end
227
+
228
+ def test_instrumenting_class_method
229
+ ActiveMerchant::Gateway.singleton_class.extend StatsD::Instrument
230
+ ActiveMerchant::Gateway.singleton_class.statsd_count :sync, 'ActiveMerchant.Gateway.sync'
231
+
232
+ assert_statsd_increment('ActiveMerchant.Gateway.sync') do
233
+ ActiveMerchant::Gateway.sync
234
+ end
235
+ ensure
236
+ ActiveMerchant::Gateway.singleton_class.statsd_remove_count :sync, 'ActiveMerchant.Gateway.sync'
237
+ end
238
+
239
+ def test_statsd_count_with_tags
240
+ ActiveMerchant::Gateway.singleton_class.extend StatsD::Instrument
241
+ ActiveMerchant::Gateway.singleton_class.statsd_count :sync, 'ActiveMerchant.Gateway.sync', tags: { key: 'value' }
242
+
243
+ assert_statsd_increment('ActiveMerchant.Gateway.sync', tags: ['key:value']) do
244
+ ActiveMerchant::Gateway.sync
245
+ end
246
+ ensure
247
+ ActiveMerchant::Gateway.singleton_class.statsd_remove_count :sync, 'ActiveMerchant.Gateway.sync'
248
+ end
249
+
250
+ def test_statsd_doesnt_change_method_scope_of_public_method
251
+ assert_scope InstrumentedClass, :public_and_instrumented, :public
252
+
253
+ assert_statsd_increment('InstrumentedClass.public_and_instrumented') do
254
+ InstrumentedClass.new.send(:public_and_instrumented)
255
+ end
256
+ end
257
+
258
+ def test_statsd_doesnt_change_method_scope_of_protected_method
259
+ assert_scope InstrumentedClass, :protected_and_instrumented, :protected
260
+
261
+ assert_statsd_increment('InstrumentedClass.protected_and_instrumented') do
262
+ InstrumentedClass.new.send(:protected_and_instrumented)
263
+ end
264
+ end
265
+
266
+ def test_statsd_doesnt_change_method_scope_of_private_method
267
+ assert_scope InstrumentedClass, :private_and_instrumented, :private
268
+
269
+ assert_statsd_increment('InstrumentedClass.private_and_instrumented') do
270
+ InstrumentedClass.new.send(:private_and_instrumented)
271
+ end
272
+ end
273
+
274
+ def test_statsd_doesnt_change_method_scope_on_removal_of_public_method
275
+ assert_scope InstrumentedClass, :public_and_instrumented, :public
276
+ InstrumentedClass.statsd_remove_count :public_and_instrumented, 'InstrumentedClass.public_and_instrumented'
277
+ assert_scope InstrumentedClass, :public_and_instrumented, :public
278
+
279
+ InstrumentedClass.statsd_count :public_and_instrumented, 'InstrumentedClass.public_and_instrumented'
280
+ end
281
+
282
+ def test_statsd_doesnt_change_method_scope_on_removal_of_protected_method
283
+ assert_scope InstrumentedClass, :protected_and_instrumented, :protected
284
+ InstrumentedClass.statsd_remove_count :protected_and_instrumented, 'InstrumentedClass.protected_and_instrumented'
285
+ assert_scope InstrumentedClass, :protected_and_instrumented, :protected
286
+
287
+ InstrumentedClass.statsd_count :protected_and_instrumented, 'InstrumentedClass.protected_and_instrumented'
288
+ end
289
+
290
+ def test_statsd_doesnt_change_method_scope_on_removal_of_private_method
291
+ assert_scope InstrumentedClass, :private_and_instrumented, :private
292
+ InstrumentedClass.statsd_remove_count :private_and_instrumented, 'InstrumentedClass.private_and_instrumented'
293
+ assert_scope InstrumentedClass, :private_and_instrumented, :private
294
+
295
+ InstrumentedClass.statsd_count :private_and_instrumented, 'InstrumentedClass.private_and_instrumented'
296
+ end
297
+
298
+ def test_statsd_works_with_prepended_modules
299
+ mod = Module.new do
300
+ define_method(:foo) { super() }
301
+ end
302
+ klass = Class.new do
303
+ prepend mod
304
+ extend StatsD::Instrument
305
+ define_method(:foo) {}
306
+ statsd_count :foo, "foo"
307
+ end
308
+
309
+ assert_statsd_increment("foo") do
310
+ klass.new.foo
311
+ end
312
+ end
313
+
314
+ private
315
+
316
+ def assert_scope(klass, method, expected_scope)
317
+ method_scope = case
318
+ when klass.private_method_defined?(method)
319
+ :private
320
+ when klass.protected_method_defined?(method)
321
+ :protected
322
+ else
323
+ :public
324
+ end
325
+
326
+ assert_equal method_scope, expected_scope, "Expected method to be #{expected_scope}"
327
+ end
328
+ end
@@ -0,0 +1,136 @@
1
+ require 'test_helper'
2
+
3
+ class StatsDTest < Minitest::Test
4
+ include StatsD::Instrument::Assertions
5
+
6
+ def test_statsd_passed_collections_to_backend
7
+ StatsD.backend.expects(:collect_metric).with(instance_of(StatsD::Instrument::Metric))
8
+ StatsD.increment('test')
9
+ end
10
+
11
+ 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
15
+ assert_equal 'values.foobar', metric.name
16
+ assert_equal 42, metric.value
17
+ assert_equal :ms, metric.type
18
+ end
19
+
20
+ def test_statsd_measure_with_explicit_value_as_keyword_argument
21
+ result = nil
22
+ metric = capture_statsd_call { result = StatsD.measure('values.foobar', value: 42) }
23
+ assert_equal metric, result
24
+ assert_equal 'values.foobar', metric.name
25
+ assert_equal 42, metric.value
26
+ assert_equal :ms, metric.type
27
+ end
28
+
29
+ def test_statsd_measure_without_value_or_block
30
+ assert_raises(ArgumentError) { StatsD.measure('values.foobar', tags: 123) }
31
+ end
32
+
33
+ def test_statsd_measure_with_explicit_value_and_sample_rate
34
+ metric = capture_statsd_call { StatsD.measure('values.foobar', 42, :sample_rate => 0.1) }
35
+ assert_equal 0.1, metric.sample_rate
36
+ end
37
+
38
+ def test_statsd_measure_with_benchmarked_block_duration
39
+ StatsD::Instrument.stubs(:duration).returns(1.12)
40
+ metric = capture_statsd_call do
41
+ StatsD.measure('values.foobar') { 'foo' }
42
+ end
43
+ assert_equal 1120.0, metric.value
44
+ end
45
+
46
+ def test_statsd_measure_returns_return_value_of_block
47
+ return_value = StatsD.measure('values.foobar') { 'sarah' }
48
+ assert_equal 'sarah', return_value
49
+ end
50
+
51
+ def test_statsd_increment
52
+ result = nil
53
+ metric = capture_statsd_call { result = StatsD.increment('values.foobar', 3) }
54
+ assert_equal metric, result
55
+ assert_equal :c, metric.type
56
+ assert_equal 'values.foobar', metric.name
57
+ assert_equal 3, metric.value
58
+ end
59
+
60
+ def test_statsd_increment_with_hash_argument
61
+ metric = capture_statsd_call { StatsD.increment('values.foobar', :tags => ['test']) }
62
+ assert_equal StatsD.default_sample_rate, metric.sample_rate
63
+ assert_equal ['test'], metric.tags
64
+ assert_equal 1, metric.value
65
+ end
66
+
67
+ def test_statsd_increment_with_value_as_keyword_argument
68
+ metric = capture_statsd_call { StatsD.increment('values.foobar', :value => 2) }
69
+ assert_equal StatsD.default_sample_rate, metric.sample_rate
70
+ assert_equal 2, metric.value
71
+ end
72
+
73
+ def test_statsd_increment_with_multiple_arguments
74
+ metric = capture_statsd_call { StatsD.increment('values.foobar', 12, nil, ['test']) }
75
+ assert_equal StatsD.default_sample_rate, metric.sample_rate
76
+ assert_equal ['test'], metric.tags
77
+ assert_equal 12, metric.value
78
+ end
79
+
80
+ def test_statsd_gauge
81
+ result = nil
82
+ metric = capture_statsd_call { result = StatsD.gauge('values.foobar', 12) }
83
+ assert_equal metric, result
84
+ assert_equal :g, metric.type
85
+ assert_equal 'values.foobar', metric.name
86
+ assert_equal 12, metric.value
87
+ end
88
+
89
+ def test_statsd_gauge_with_keyword_argument
90
+ result = nil
91
+ metric = capture_statsd_call { result = StatsD.gauge('values.foobar', value: 13) }
92
+ assert_equal metric, result
93
+ assert_equal :g, metric.type
94
+ assert_equal 'values.foobar', metric.name
95
+ assert_equal 13, metric.value
96
+ end
97
+
98
+ def test_statsd_gauge_without_value
99
+ assert_raises(ArgumentError) { StatsD.gauge('values.foobar', tags: 123) }
100
+ end
101
+
102
+ def test_statsd_set
103
+ result = nil
104
+ metric = capture_statsd_call { result = StatsD.set('values.foobar', 'unique_identifier') }
105
+ assert_equal metric, result
106
+ assert_equal :s, metric.type
107
+ assert_equal 'values.foobar', metric.name
108
+ assert_equal 'unique_identifier', metric.value
109
+ end
110
+
111
+ def test_statsd_histogram
112
+ result = nil
113
+ metric = capture_statsd_call { result = StatsD.histogram('values.foobar', 42) }
114
+ assert_equal metric, result
115
+ assert_equal :h, metric.type
116
+ assert_equal 'values.foobar', metric.name
117
+ assert_equal 42, metric.value
118
+ end
119
+
120
+ def test_statsd_key_value
121
+ result = nil
122
+ metric = capture_statsd_call { result = StatsD.key_value('values.foobar', 42) }
123
+ assert_equal metric, result
124
+ assert_equal :kv, metric.type
125
+ assert_equal 'values.foobar', metric.name
126
+ assert_equal 42, metric.value
127
+ end
128
+
129
+ protected
130
+
131
+ def capture_statsd_call(&block)
132
+ metrics = capture_statsd_calls(&block)
133
+ assert_equal 1, metrics.length
134
+ metrics.first
135
+ end
136
+ end
@@ -0,0 +1,10 @@
1
+ ENV['ENV'] = 'test'
2
+
3
+ require 'minitest/autorun'
4
+ require 'minitest/pride'
5
+ require 'mocha/setup'
6
+ require 'set'
7
+ require 'logger'
8
+ require 'statsd-instrument'
9
+
10
+ StatsD.logger = Logger.new('/dev/null')
@@ -0,0 +1,167 @@
1
+ require 'test_helper'
2
+
3
+ class UDPBackendTest < Minitest::Test
4
+ def setup
5
+ StatsD.stubs(:backend).returns(@backend = StatsD::Instrument::Backends::UDPBackend.new)
6
+ @backend.stubs(:rand).returns(0.0)
7
+
8
+ UDPSocket.stubs(:new).returns(@socket = mock('socket'))
9
+ @socket.stubs(:connect)
10
+ @socket.stubs(:send).returns(1)
11
+
12
+ StatsD.stubs(:logger).returns(@logger = mock('logger'))
13
+ end
14
+
15
+ def test_changing_host_or_port_should_create_new_socket
16
+ @socket.expects(:connect).with('localhost', 1234).once
17
+ @socket.expects(:connect).with('localhost', 2345).once
18
+ @socket.expects(:connect).with('127.0.0.1', 2345).once
19
+
20
+ @backend.server = "localhost:1234"
21
+ @backend.socket
22
+
23
+ @backend.port = 2345
24
+ @backend.socket
25
+
26
+ @backend.host = '127.0.0.1'
27
+ @backend.socket
28
+ end
29
+
30
+ def test_collect_respects_sampling_rate
31
+ @socket.expects(:send).once.returns(1)
32
+ metric = StatsD::Instrument::Metric.new(type: :c, name: 'test', sample_rate: 0.5)
33
+
34
+ @backend.stubs(:rand).returns(0.4)
35
+ @backend.collect_metric(metric)
36
+
37
+ @backend.stubs(:rand).returns(0.6)
38
+ @backend.collect_metric(metric)
39
+ end
40
+
41
+ def test_support_counter_syntax
42
+ @backend.expects(:write_packet).with('counter:1|c').once
43
+ StatsD.increment('counter', sample_rate: 1.0)
44
+
45
+ @backend.expects(:write_packet).with('counter:1|c|@0.5').once
46
+ StatsD.increment('counter', sample_rate: 0.5)
47
+ end
48
+
49
+ def test_supports_gauge_syntax
50
+ @backend.expects(:write_packet).with('fooy:1.23|g')
51
+ StatsD.gauge('fooy', 1.23)
52
+
53
+ @backend.expects(:write_packet).with('fooy:42|g|@0.01')
54
+ StatsD.gauge('fooy', 42, sample_rate: 0.01)
55
+ end
56
+
57
+ def test_supports_set_syntax
58
+ @backend.expects(:write_packet).with('unique:10.0.0.10|s')
59
+ StatsD.set('unique', '10.0.0.10')
60
+
61
+ @backend.expects(:write_packet).with('unique:10.0.0.10|s|@0.01')
62
+ StatsD.set('unique', '10.0.0.10', sample_rate: 0.01)
63
+ end
64
+
65
+ def test_support_measure_syntax
66
+ @backend.expects(:write_packet).with('duration:1.23|ms')
67
+ StatsD.measure('duration', 1.23)
68
+
69
+ @backend.expects(:write_packet).with('duration:0.42|ms|@0.01')
70
+ StatsD.measure('duration', 0.42, sample_rate: 0.01)
71
+ end
72
+
73
+ def test_histogram_syntax_on_datadog
74
+ @backend.implementation = :datadog
75
+ @backend.expects(:write_packet).with('fooh:42.4|h')
76
+ StatsD.histogram('fooh', 42.4)
77
+ end
78
+
79
+ def test_histogram_warns_if_not_using_datadog
80
+ @backend.implementation = :other
81
+ @backend.expects(:write_packet).never
82
+ @logger.expects(:warn)
83
+ StatsD.histogram('fooh', 42.4)
84
+ end
85
+
86
+ def test_supports_key_value_syntax_on_statsite
87
+ @backend.implementation = :statsite
88
+ @backend.expects(:write_packet).with("fooy:42|kv\n")
89
+ StatsD.key_value('fooy', 42)
90
+ end
91
+
92
+ def test_supports_key_value_with_timestamp_on_statsite
93
+ @backend.implementation = :statsite
94
+ @backend.expects(:write_packet).with("fooy:42|kv|@123456\n")
95
+ StatsD.key_value('fooy', 42, 123456)
96
+ end
97
+
98
+ def test_warn_when_using_key_value_and_not_on_statsite
99
+ @backend.implementation = :other
100
+ @backend.expects(:write_packet).never
101
+ @logger.expects(:warn)
102
+ StatsD.key_value('fookv', 3.33)
103
+ end
104
+
105
+ def test_support_tags_syntax_on_datadog
106
+ @backend.implementation = :datadog
107
+ @backend.expects(:write_packet).with("fooc:3|c|#topic:foo,bar")
108
+ StatsD.increment('fooc', 3, tags: ['topic:foo', 'bar'])
109
+ end
110
+
111
+ def test_support_tags_syntax_on_collectd
112
+ @backend.implementation = :collectd
113
+ @backend.expects(:write_packet).with("[topic=foo,bar]fooc:3|g")
114
+ StatsD.gauge('fooc', 3, tags: ['topic:foo','bar'])
115
+ end
116
+
117
+ def test_warn_when_using_tags_and_not_on_datadog
118
+ @backend.implementation = :other
119
+ @backend.expects(:write_packet).with("fooc:1|c")
120
+ @logger.expects(:warn)
121
+ StatsD.increment('fooc', tags: ['ignored'])
122
+ end
123
+
124
+ def test_socket_error_should_not_raise_but_log
125
+ @socket.stubs(:connect).raises(SocketError)
126
+ @logger.expects(:error)
127
+ StatsD.increment('fail')
128
+ end
129
+
130
+ def test_system_call_error_should_not_raise_but_log
131
+ @socket.stubs(:send).raises(Errno::ETIMEDOUT)
132
+ @logger.expects(:error)
133
+ StatsD.increment('fail')
134
+ end
135
+
136
+ def test_io_error_should_not_raise_but_log
137
+ @socket.stubs(:send).raises(IOError)
138
+ @logger.expects(:error)
139
+ StatsD.increment('fail')
140
+ end
141
+
142
+ def test_synchronize_in_exit_handler_handles_thread_error_and_exits_cleanly
143
+ pid = fork do
144
+ Signal.trap('TERM') do
145
+ $sent_packet = false
146
+
147
+ class << @backend.socket
148
+ def send(command, *args)
149
+ $sent_packet = true if command == 'exiting:1|c'
150
+ command.length
151
+ end
152
+ end
153
+
154
+ StatsD.increment('exiting')
155
+ Process.exit!($sent_packet)
156
+ end
157
+
158
+ sleep 100
159
+ end
160
+
161
+ Process.kill('TERM', pid)
162
+ Process.waitpid(pid)
163
+
164
+ assert $?.success?, 'socket did not write on exit'
165
+ end
166
+
167
+ end