statsd-instrument 3.0.0.pre2 → 3.1.1

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.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/lint.yml +22 -0
  3. data/.github/workflows/tests.yml +31 -0
  4. data/.rubocop.yml +3 -13
  5. data/CHANGELOG.md +43 -0
  6. data/Gemfile +8 -2
  7. data/README.md +6 -3
  8. data/Rakefile +7 -7
  9. data/benchmark/send-metrics-to-dev-null-log +12 -11
  10. data/benchmark/send-metrics-to-local-udp-receiver +16 -15
  11. data/bin/rake +29 -0
  12. data/bin/rubocop +29 -0
  13. data/lib/statsd-instrument.rb +1 -1
  14. data/lib/statsd/instrument.rb +112 -145
  15. data/lib/statsd/instrument/assertions.rb +200 -208
  16. data/lib/statsd/instrument/batched_udp_sink.rb +159 -0
  17. data/lib/statsd/instrument/capture_sink.rb +23 -19
  18. data/lib/statsd/instrument/client.rb +410 -306
  19. data/lib/statsd/instrument/datagram.rb +69 -65
  20. data/lib/statsd/instrument/datagram_builder.rb +81 -77
  21. data/lib/statsd/instrument/dogstatsd_datagram.rb +76 -72
  22. data/lib/statsd/instrument/dogstatsd_datagram_builder.rb +68 -64
  23. data/lib/statsd/instrument/environment.rb +88 -77
  24. data/lib/statsd/instrument/expectation.rb +96 -96
  25. data/lib/statsd/instrument/helpers.rb +11 -7
  26. data/lib/statsd/instrument/log_sink.rb +20 -16
  27. data/lib/statsd/instrument/matchers.rb +93 -74
  28. data/lib/statsd/instrument/null_sink.rb +12 -8
  29. data/lib/statsd/instrument/railtie.rb +11 -7
  30. data/lib/statsd/instrument/rubocop.rb +8 -8
  31. data/lib/statsd/instrument/rubocop/measure_as_dist_argument.rb +1 -1
  32. data/lib/statsd/instrument/rubocop/metaprogramming_positional_arguments.rb +2 -2
  33. data/lib/statsd/instrument/rubocop/metric_prefix_argument.rb +1 -1
  34. data/lib/statsd/instrument/rubocop/metric_return_value.rb +2 -2
  35. data/lib/statsd/instrument/rubocop/metric_value_keyword_argument.rb +1 -1
  36. data/lib/statsd/instrument/rubocop/positional_arguments.rb +4 -4
  37. data/lib/statsd/instrument/rubocop/singleton_configuration.rb +1 -1
  38. data/lib/statsd/instrument/rubocop/splat_arguments.rb +2 -2
  39. data/lib/statsd/instrument/statsd_datagram_builder.rb +12 -8
  40. data/lib/statsd/instrument/strict.rb +1 -6
  41. data/lib/statsd/instrument/udp_sink.rb +49 -47
  42. data/lib/statsd/instrument/version.rb +1 -1
  43. data/statsd-instrument.gemspec +4 -8
  44. data/test/assertions_test.rb +199 -167
  45. data/test/benchmark/clock_gettime.rb +1 -1
  46. data/test/benchmark/default_tags.rb +9 -9
  47. data/test/benchmark/metrics.rb +8 -8
  48. data/test/benchmark/tags.rb +4 -4
  49. data/test/capture_sink_test.rb +14 -14
  50. data/test/client_test.rb +96 -96
  51. data/test/datagram_builder_test.rb +55 -55
  52. data/test/datagram_test.rb +5 -5
  53. data/test/dogstatsd_datagram_builder_test.rb +37 -37
  54. data/test/environment_test.rb +30 -21
  55. data/test/helpers/rubocop_helper.rb +12 -9
  56. data/test/helpers_test.rb +15 -15
  57. data/test/integration_test.rb +7 -7
  58. data/test/log_sink_test.rb +4 -4
  59. data/test/matchers_test.rb +54 -54
  60. data/test/null_sink_test.rb +4 -4
  61. data/test/rubocop/measure_as_dist_argument_test.rb +2 -2
  62. data/test/rubocop/metaprogramming_positional_arguments_test.rb +2 -2
  63. data/test/rubocop/metric_prefix_argument_test.rb +2 -2
  64. data/test/rubocop/metric_return_value_test.rb +6 -6
  65. data/test/rubocop/metric_value_keyword_argument_test.rb +3 -3
  66. data/test/rubocop/positional_arguments_test.rb +12 -12
  67. data/test/rubocop/singleton_configuration_test.rb +8 -8
  68. data/test/rubocop/splat_arguments_test.rb +2 -2
  69. data/test/statsd_datagram_builder_test.rb +6 -6
  70. data/test/statsd_instrumentation_test.rb +122 -122
  71. data/test/statsd_test.rb +69 -67
  72. data/test/test_helper.rb +19 -10
  73. data/test/udp_sink_test.rb +147 -49
  74. metadata +12 -92
  75. data/.github/workflows/ci.yml +0 -49
  76. data/.rubocop-https---shopify-github-io-ruby-style-guide-rubocop-yml +0 -1027
data/test/statsd_test.rb CHANGED
@@ -1,33 +1,33 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'test_helper'
3
+ require "test_helper"
4
4
 
5
5
  class StatsDTest < Minitest::Test
6
6
  include StatsD::Instrument::Assertions
7
7
 
8
8
  def test_statsd_measure_with_explicit_value
9
- metric = capture_statsd_call { StatsD.measure('values.foobar', 42) }
10
- assert_equal 'values.foobar', metric.name
11
- assert_equal 42, metric.value
12
- assert_equal :ms, metric.type
9
+ metric = capture_statsd_call { StatsD.measure("values.foobar", 42) }
10
+ assert_equal("values.foobar", metric.name)
11
+ assert_equal(42, metric.value)
12
+ assert_equal(:ms, metric.type)
13
13
  end
14
14
 
15
15
  def test_statsd_measure_with_explicit_value_and_sample_rate
16
- metric = capture_statsd_call { StatsD.measure('values.foobar', 42, sample_rate: 0.1) }
17
- assert_equal 0.1, metric.sample_rate
16
+ metric = capture_statsd_call { StatsD.measure("values.foobar", 42, sample_rate: 0.1) }
17
+ assert_equal(0.1, metric.sample_rate)
18
18
  end
19
19
 
20
20
  def test_statsd_measure_with_benchmarked_block_duration
21
21
  Process.stubs(:clock_gettime).returns(5.0, 5.0 + 1.12)
22
22
  metric = capture_statsd_call do
23
- StatsD.measure('values.foobar') { 'foo' }
23
+ StatsD.measure("values.foobar") { "foo" }
24
24
  end
25
- assert_equal 1120.0, metric.value
25
+ assert_equal(1120.0, metric.value)
26
26
  end
27
27
 
28
28
  def test_statsd_measure_returns_return_value_of_block
29
- return_value = StatsD.measure('values.foobar') { 'sarah' }
30
- assert_equal 'sarah', return_value
29
+ return_value = StatsD.measure("values.foobar") { "sarah" }
30
+ assert_equal("sarah", return_value)
31
31
  end
32
32
 
33
33
  def test_statsd_measure_with_return_in_block_still_captures
@@ -35,14 +35,14 @@ class StatsDTest < Minitest::Test
35
35
  result = nil
36
36
  metric = capture_statsd_call do
37
37
  lambda = -> do
38
- StatsD.measure('values.foobar') { return 'from lambda' }
38
+ StatsD.measure("values.foobar") { return "from lambda" }
39
39
  end
40
40
 
41
41
  result = lambda.call
42
42
  end
43
43
 
44
- assert_equal 'from lambda', result
45
- assert_equal 1120.0, metric.value
44
+ assert_equal("from lambda", result)
45
+ assert_equal(1120.0, metric.value)
46
46
  end
47
47
 
48
48
  def test_statsd_measure_with_exception_in_block_still_captures
@@ -50,73 +50,74 @@ class StatsDTest < Minitest::Test
50
50
  result = nil
51
51
  metric = capture_statsd_call do
52
52
  lambda = -> do
53
- StatsD.measure('values.foobar') { raise 'from lambda' }
53
+ StatsD.measure("values.foobar") { raise "from lambda" }
54
54
  end
55
55
 
56
56
  begin
57
57
  result = lambda.call
58
- rescue # rubocop:disable Lint/HandleExceptions:
58
+ rescue
59
+ # noop
59
60
  end
60
61
  end
61
62
 
62
- assert_nil result
63
- assert_equal 1120.0, metric.value
63
+ assert_nil(result)
64
+ assert_equal(1120.0, metric.value)
64
65
  end
65
66
 
66
67
  def test_statsd_increment
67
- metric = capture_statsd_call { StatsD.increment('values.foobar', 3) }
68
- assert_equal :c, metric.type
69
- assert_equal 'values.foobar', metric.name
70
- assert_equal 3, metric.value
68
+ metric = capture_statsd_call { StatsD.increment("values.foobar", 3) }
69
+ assert_equal(:c, metric.type)
70
+ assert_equal("values.foobar", metric.name)
71
+ assert_equal(3, metric.value)
71
72
  end
72
73
 
73
74
  def test_statsd_increment_with_hash_argument
74
- metric = capture_statsd_call { StatsD.increment('values.foobar', tags: ['test']) }
75
- assert_equal StatsD.singleton_client.default_sample_rate, metric.sample_rate
76
- assert_equal ['test'], metric.tags
77
- assert_equal 1, metric.value
75
+ metric = capture_statsd_call { StatsD.increment("values.foobar", tags: ["test"]) }
76
+ assert_equal(StatsD.singleton_client.default_sample_rate, metric.sample_rate)
77
+ assert_equal(["test"], metric.tags)
78
+ assert_equal(1, metric.value)
78
79
  end
79
80
 
80
81
  def test_statsd_gauge
81
- metric = capture_statsd_call { StatsD.gauge('values.foobar', 12) }
82
- assert_equal :g, metric.type
83
- assert_equal 'values.foobar', metric.name
84
- assert_equal 12, metric.value
82
+ metric = capture_statsd_call { StatsD.gauge("values.foobar", 12) }
83
+ assert_equal(:g, metric.type)
84
+ assert_equal("values.foobar", metric.name)
85
+ assert_equal(12, metric.value)
85
86
  end
86
87
 
87
88
  def test_statsd_gauge_without_value
88
- assert_raises(ArgumentError) { StatsD.gauge('values.foobar') }
89
+ assert_raises(ArgumentError) { StatsD.gauge("values.foobar") }
89
90
  end
90
91
 
91
92
  def test_statsd_set
92
- metric = capture_statsd_call { StatsD.set('values.foobar', 'unique_identifier') }
93
- assert_equal :s, metric.type
94
- assert_equal 'values.foobar', metric.name
95
- assert_equal 'unique_identifier', metric.value
93
+ metric = capture_statsd_call { StatsD.set("values.foobar", "unique_identifier") }
94
+ assert_equal(:s, metric.type)
95
+ assert_equal("values.foobar", metric.name)
96
+ assert_equal("unique_identifier", metric.value)
96
97
  end
97
98
 
98
99
  def test_statsd_histogram
99
- metric = capture_statsd_call { StatsD.histogram('values.foobar', 42) }
100
- assert_equal :h, metric.type
101
- assert_equal 'values.foobar', metric.name
102
- assert_equal 42, metric.value
100
+ metric = capture_statsd_call { StatsD.histogram("values.foobar", 42) }
101
+ assert_equal(:h, metric.type)
102
+ assert_equal("values.foobar", metric.name)
103
+ assert_equal(42, metric.value)
103
104
  end
104
105
 
105
106
  def test_statsd_distribution
106
- metric = capture_statsd_call { StatsD.distribution('values.foobar', 42) }
107
- assert_equal :d, metric.type
108
- assert_equal 'values.foobar', metric.name
109
- assert_equal 42, metric.value
107
+ metric = capture_statsd_call { StatsD.distribution("values.foobar", 42) }
108
+ assert_equal(:d, metric.type)
109
+ assert_equal("values.foobar", metric.name)
110
+ assert_equal(42, metric.value)
110
111
  end
111
112
 
112
113
  def test_statsd_distribution_with_benchmarked_block_duration
113
114
  Process.stubs(:clock_gettime).returns(5.0, 5.0 + 1.12)
114
115
  metric = capture_statsd_call do
115
- result = StatsD.distribution('values.foobar') { 'foo' }
116
- assert_equal 'foo', result
116
+ result = StatsD.distribution("values.foobar") { "foo" }
117
+ assert_equal("foo", result)
117
118
  end
118
- assert_equal :d, metric.type
119
- assert_equal 1120.0, metric.value
119
+ assert_equal(:d, metric.type)
120
+ assert_equal(1120.0, metric.value)
120
121
  end
121
122
 
122
123
  def test_statsd_distribution_with_return_in_block_still_captures
@@ -124,16 +125,16 @@ class StatsDTest < Minitest::Test
124
125
  result = nil
125
126
  metric = capture_statsd_call do
126
127
  lambda = -> do
127
- StatsD.distribution('values.foobar') { return 'from lambda' }
128
+ StatsD.distribution("values.foobar") { return "from lambda" }
128
129
  flunk("This code should not be reached")
129
130
  end
130
131
 
131
132
  result = lambda.call
132
133
  end
133
134
 
134
- assert_equal 'from lambda', result
135
- assert_equal :d, metric.type
136
- assert_equal 1120.0, metric.value
135
+ assert_equal("from lambda", result)
136
+ assert_equal(:d, metric.type)
137
+ assert_equal(1120.0, metric.value)
137
138
  end
138
139
 
139
140
  def test_statsd_distribution_with_exception_in_block_still_captures
@@ -141,44 +142,45 @@ class StatsDTest < Minitest::Test
141
142
  result = nil
142
143
  metric = capture_statsd_call do
143
144
  lambda = -> do
144
- StatsD.distribution('values.foobar') { raise 'from lambda' }
145
+ StatsD.distribution("values.foobar") { raise "from lambda" }
145
146
  end
146
147
 
147
148
  begin
148
149
  result = lambda.call
149
- rescue # rubocop:disable Lint/HandleExceptions
150
+ rescue
151
+ # noop
150
152
  end
151
153
  end
152
154
 
153
- assert_nil result
154
- assert_equal :d, metric.type
155
- assert_equal 1120.0, metric.value
155
+ assert_nil(result)
156
+ assert_equal(:d, metric.type)
157
+ assert_equal(1120.0, metric.value)
156
158
  end
157
159
 
158
160
  def test_statsd_distribution_with_block_and_options
159
161
  Process.stubs(:clock_gettime).returns(5.0, 5.0 + 1.12)
160
162
  metric = capture_statsd_call do
161
- StatsD.distribution('values.foobar', tags: ['test'], sample_rate: 0.9) { 'foo' }
163
+ StatsD.distribution("values.foobar", tags: ["test"], sample_rate: 0.9) { "foo" }
162
164
  end
163
- assert_equal 1120.0, metric.value
164
- assert_equal 'values.foobar', metric.name
165
- assert_equal 0.9, metric.sample_rate
166
- assert_equal ['test'], metric.tags
165
+ assert_equal(1120.0, metric.value)
166
+ assert_equal("values.foobar", metric.name)
167
+ assert_equal(0.9, metric.sample_rate)
168
+ assert_equal(["test"], metric.tags)
167
169
  end
168
170
 
169
171
  def test_statsd_distribution_returns_return_value_of_block
170
- return_value = StatsD.distribution('values.foobar') { 'sarah' }
171
- assert_equal 'sarah', return_value
172
+ return_value = StatsD.distribution("values.foobar") { "sarah" }
173
+ assert_equal("sarah", return_value)
172
174
  end
173
175
 
174
176
  def test_statsd_measure_returns_return_value_of_block_even_if_nil
175
- return_value = StatsD.distribution('values.foobar') { nil }
176
- assert_nil return_value
177
+ return_value = StatsD.distribution("values.foobar") { nil }
178
+ assert_nil(return_value)
177
179
  end
178
180
 
179
181
  def test_statsd_duration_returns_time_in_seconds
180
182
  duration = StatsD::Instrument.duration {}
181
- assert_kind_of Float, duration
183
+ assert_kind_of(Float, duration)
182
184
  end
183
185
 
184
186
  def test_statsd_duration_does_not_swallow_exceptions
@@ -191,7 +193,7 @@ class StatsDTest < Minitest::Test
191
193
 
192
194
  def capture_statsd_call(&block)
193
195
  metrics = capture_statsd_calls(&block)
194
- assert_equal 1, metrics.length
196
+ assert_equal(1, metrics.length)
195
197
  metrics.first
196
198
  end
197
199
  end
data/test/test_helper.rb CHANGED
@@ -1,19 +1,28 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- ENV['ENV'] = 'test'
3
+ if Warning.respond_to?(:[]=)
4
+ Warning[:deprecated] = true
5
+ end
6
+
7
+ ENV["ENV"] = "test"
4
8
 
5
- require 'minitest/autorun'
6
- require 'minitest/pride'
7
- require 'mocha/setup'
8
- require 'statsd-instrument'
9
+ require "minitest/autorun"
10
+ require "minitest/pride"
11
+ require "mocha/minitest"
12
+ require "statsd-instrument"
9
13
 
10
- require_relative 'helpers/rubocop_helper'
14
+ require_relative "helpers/rubocop_helper"
11
15
 
12
- module StatsD::Instrument
13
- def self.strict_mode_enabled?
14
- StatsD::Instrument.const_defined?(:Strict) &&
15
- StatsD.singleton_class.ancestors.include?(StatsD::Instrument::Strict)
16
+ module StatsD
17
+ module Instrument
18
+ def self.strict_mode_enabled?
19
+ StatsD::Instrument.const_defined?(:Strict) &&
20
+ StatsD.singleton_class.ancestors.include?(StatsD::Instrument::Strict)
21
+ end
16
22
  end
17
23
  end
18
24
 
19
25
  StatsD.logger = Logger.new(File::NULL)
26
+
27
+ Thread.abort_on_exception = true
28
+ Thread.report_on_exception = true
@@ -1,83 +1,181 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'test_helper'
4
-
5
- class UDPSinkTest < Minitest::Test
6
- def setup
7
- @receiver = UDPSocket.new
8
- @receiver.bind('localhost', 0)
9
- @host = @receiver.addr[2]
10
- @port = @receiver.addr[1]
11
- end
12
-
13
- def teardown
14
- @receiver.close
15
- end
3
+ require "test_helper"
16
4
 
5
+ module UDPSinkTests
17
6
  def test_udp_sink_sends_data_over_udp
18
- udp_sink = StatsD::Instrument::UDPSink.new(@host, @port)
19
- udp_sink << 'foo:1|c'
7
+ udp_sink = build_sink(@host, @port)
8
+ udp_sink << "foo:1|c"
20
9
 
21
10
  datagram, _source = @receiver.recvfrom(100)
22
- assert_equal 'foo:1|c', datagram
11
+ assert_equal("foo:1|c", datagram)
12
+ end
13
+
14
+ def large_datagram
15
+ datagram = "#{"a" * 1000}:1|c"
16
+ udp_sink = build_sink(@host, @port)
17
+ udp_sink << datagram
18
+
19
+ datagram, _source = @receiver.recvfrom(1500)
20
+ assert_equal(datagram, datagram)
23
21
  end
24
22
 
25
23
  def test_sample?
26
- udp_sink = StatsD::Instrument::UDPSink.new(@host, @port)
27
- assert udp_sink.sample?(1)
28
- refute udp_sink.sample?(0)
24
+ udp_sink = build_sink(@host, @port)
25
+ assert(udp_sink.sample?(1))
26
+ refute(udp_sink.sample?(0))
29
27
 
30
28
  udp_sink.stubs(:rand).returns(0.3)
31
- assert udp_sink.sample?(0.5)
29
+ assert(udp_sink.sample?(0.5))
32
30
 
33
31
  udp_sink.stubs(:rand).returns(0.7)
34
- refute udp_sink.sample?(0.5)
32
+ refute(udp_sink.sample?(0.5))
35
33
  end
36
34
 
37
35
  def test_parallelism
38
- udp_sink = StatsD::Instrument::UDPSink.new(@host, @port)
39
- 50.times { |i| Thread.new { udp_sink << "foo:#{i}|c" << "bar:#{i}|c" } }
36
+ udp_sink = build_sink(@host, @port)
37
+ 50.times.map { |i| Thread.new { udp_sink << "foo:#{i}|c" << "bar:#{i}|c" } }
40
38
  datagrams = []
41
- 100.times do
42
- datagram, _source = @receiver.recvfrom(100)
43
- datagrams << datagram
39
+
40
+ while @receiver.wait_readable(2)
41
+ datagram, _source = @receiver.recvfrom(4000)
42
+ datagrams += datagram.split("\n")
44
43
  end
45
44
 
46
- assert_equal 100, datagrams.size
45
+ assert_equal(100, datagrams.size)
47
46
  end
48
47
 
49
- def test_socket_error_should_invalidate_socket
50
- UDPSocket.stubs(:new).returns(socket = mock('socket'))
51
-
52
- seq = sequence('connect_fail_connect_succeed')
53
- socket.expects(:connect).with('localhost', 8125).in_sequence(seq)
54
- socket.expects(:send).raises(Errno::EDESTADDRREQ).in_sequence(seq)
55
- socket.expects(:connect).with('localhost', 8125).in_sequence(seq)
56
- socket.expects(:send).returns(1).in_sequence(seq)
57
-
58
- udp_sink = StatsD::Instrument::UDPSink.new('localhost', 8125)
59
- udp_sink << 'foo:1|c'
60
- udp_sink << 'bar:1|c'
48
+ class SimpleFormatter < ::Logger::Formatter
49
+ def call(_severity, _timestamp, _progname, msg)
50
+ "#{String === msg ? msg : msg.inspect}\n"
51
+ end
61
52
  end
62
53
 
63
54
  def test_sends_datagram_in_signal_handler
64
- udp_sink = StatsD::Instrument::UDPSink.new(@host, @port)
55
+ udp_sink = build_sink(@host, @port)
56
+ Signal.trap("USR1") { udp_sink << "exiting:1|c" }
57
+
65
58
  pid = fork do
66
- Signal.trap('TERM') do
67
- udp_sink << "exiting:1|c"
68
- Process.exit!(0)
69
- end
59
+ sleep(5)
60
+ end
61
+
62
+ Signal.trap("USR1", "DEFAULT")
63
+
64
+ Process.kill("USR1", pid)
65
+ @receiver.wait_readable(1)
66
+ assert_equal("exiting:1|c", @receiver.recvfrom_nonblock(100).first)
67
+ Process.kill("KILL", pid)
68
+ rescue NotImplementedError
69
+ pass("Fork is not implemented on #{RUBY_PLATFORM}")
70
+ end
70
71
 
71
- sleep(10)
72
+ def test_sends_datagram_before_exit
73
+ udp_sink = build_sink(@host, @port)
74
+ fork do
75
+ udp_sink << "exiting:1|c"
76
+ Process.exit(0)
72
77
  end
73
78
 
74
- Process.kill('TERM', pid)
75
- _, exit_status = Process.waitpid2(pid)
79
+ @receiver.wait_readable(1)
80
+ assert_equal("exiting:1|c", @receiver.recvfrom_nonblock(100).first)
81
+ rescue NotImplementedError
82
+ pass("Fork is not implemented on #{RUBY_PLATFORM}")
83
+ end
76
84
 
77
- assert_equal 0, exit_status, "The forked process did not exit cleanly"
78
- assert_equal "exiting:1|c", @receiver.recvfrom_nonblock(100).first
85
+ def test_sends_datagram_when_termed
86
+ udp_sink = build_sink(@host, @port)
87
+ fork do
88
+ udp_sink << "exiting:1|c"
89
+ Process.kill("TERM", Process.pid)
90
+ end
79
91
 
92
+ @receiver.wait_readable(1)
93
+ assert_equal("exiting:1|c", @receiver.recvfrom_nonblock(100).first)
80
94
  rescue NotImplementedError
81
95
  pass("Fork is not implemented on #{RUBY_PLATFORM}")
82
96
  end
97
+
98
+ private
99
+
100
+ def build_sink(host = @host, port = @port)
101
+ @sink_class.new(host, port)
102
+ end
103
+
104
+ class UDPSinkTest < Minitest::Test
105
+ include UDPSinkTests
106
+
107
+ def setup
108
+ @receiver = UDPSocket.new
109
+ @receiver.bind("localhost", 0)
110
+ @host = @receiver.addr[2]
111
+ @port = @receiver.addr[1]
112
+ @sink_class = StatsD::Instrument::UDPSink
113
+ end
114
+
115
+ def teardown
116
+ @receiver.close
117
+ end
118
+
119
+ def test_socket_error_should_invalidate_socket
120
+ previous_logger = StatsD.logger
121
+ begin
122
+ logs = StringIO.new
123
+ StatsD.logger = Logger.new(logs)
124
+ StatsD.logger.formatter = SimpleFormatter.new
125
+ UDPSocket.stubs(:new).returns(socket = mock("socket"))
126
+
127
+ seq = sequence("connect_fail_connect_succeed")
128
+ socket.expects(:connect).with("localhost", 8125).in_sequence(seq)
129
+ socket.expects(:send).raises(Errno::EDESTADDRREQ).in_sequence(seq)
130
+ socket.expects(:connect).with("localhost", 8125).in_sequence(seq)
131
+ socket.expects(:send).returns(1).in_sequence(seq)
132
+
133
+ udp_sink = build_sink("localhost", 8125)
134
+ udp_sink << "foo:1|c"
135
+ udp_sink << "bar:1|c"
136
+
137
+ assert_equal(
138
+ "[#{@sink_class}] Resetting connection because of " \
139
+ "Errno::EDESTADDRREQ: Destination address required\n",
140
+ logs.string,
141
+ )
142
+ ensure
143
+ StatsD.logger = previous_logger
144
+ end
145
+ end
146
+ end
147
+
148
+ class BatchedUDPSinkTest < Minitest::Test
149
+ include UDPSinkTests
150
+
151
+ def setup
152
+ @receiver = UDPSocket.new
153
+ @receiver.bind("localhost", 0)
154
+ @host = @receiver.addr[2]
155
+ @port = @receiver.addr[1]
156
+ @sink_class = StatsD::Instrument::BatchedUDPSink
157
+ end
158
+
159
+ def teardown
160
+ @receiver.close
161
+ end
162
+
163
+ def test_parallelism_buffering
164
+ udp_sink = build_sink(@host, @port)
165
+ 50.times.map do |i|
166
+ Thread.new do
167
+ udp_sink << "foo:#{i}|c" << "bar:#{i}|c" << "baz:#{i}|c" << "plop:#{i}|c"
168
+ end
169
+ end
170
+
171
+ datagrams = []
172
+
173
+ while @receiver.wait_readable(2)
174
+ datagram, _source = @receiver.recvfrom(1000)
175
+ datagrams += datagram.split("\n")
176
+ end
177
+
178
+ assert_equal(200, datagrams.size)
179
+ end
180
+ end
83
181
  end