statsd-instrument 3.0.0.pre2 → 3.1.1

Sign up to get free protection for your applications and to get access to all the features.
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