OLD | NEW |
| (Empty) |
1 # Copyright 2015 The Chromium Authors. All rights reserved. | |
2 # Use of this source code is governed by a BSD-style license that can be | |
3 # found in the LICENSE file. | |
4 | |
5 import sys | |
6 import unittest | |
7 | |
8 import mock | |
9 from infra_libs.ts_mon.protos import metrics_pb2 | |
10 | |
11 | |
12 from infra_libs.ts_mon.common import distribution | |
13 from infra_libs.ts_mon.common import errors | |
14 from infra_libs.ts_mon.common import interface | |
15 from infra_libs.ts_mon.common import metric_store | |
16 from infra_libs.ts_mon.common import metrics | |
17 from infra_libs.ts_mon.common import targets | |
18 from infra_libs.ts_mon.common.test import stubs | |
19 from infra_libs.ts_mon.protos import metrics_pb2 | |
20 | |
21 | |
22 class TestBase(unittest.TestCase): | |
23 def setUp(self): | |
24 super(TestBase, self).setUp() | |
25 target = targets.TaskTarget('test_service', 'test_job', | |
26 'test_region', 'test_host') | |
27 self.mock_state = interface.State(target=target) | |
28 self.state_patcher = mock.patch('infra_libs.ts_mon.common.interface.state', | |
29 new=self.mock_state) | |
30 self.state_patcher.start() | |
31 | |
32 def tearDown(self): | |
33 self.state_patcher.stop() | |
34 super(TestBase, self).tearDown() | |
35 | |
36 | |
37 class MetricTest(TestBase): | |
38 def test_name_property(self): | |
39 m1 = metrics.Metric('/foo', fields={'asdf': 1}) | |
40 self.assertEquals(m1.name, 'foo') | |
41 | |
42 def test_init_too_may_fields(self): | |
43 fields = {str(i): str(i) for i in xrange(8)} | |
44 with self.assertRaises(errors.MonitoringTooManyFieldsError) as e: | |
45 metrics.Metric('test', fields=fields) | |
46 self.assertEquals(e.exception.metric, 'test') | |
47 self.assertEquals(len(e.exception.fields), 8) | |
48 | |
49 def test_serialize(self): | |
50 t = targets.DeviceTarget('reg', 'role', 'net', 'host') | |
51 m = metrics.StringMetric('test') | |
52 m.set('val') | |
53 p = metrics_pb2.MetricsCollection() | |
54 m.serialize_to(p, 1234, (('bar', 1), ('baz', False)), m.get(), t) | |
55 return str(p).splitlines() | |
56 | |
57 def test_serialize_with_description(self): | |
58 t = targets.DeviceTarget('reg', 'role', 'net', 'host') | |
59 m = metrics.StringMetric('test', description='a custom description') | |
60 m.set('val') | |
61 p = metrics_pb2.MetricsCollection() | |
62 m.serialize_to(p, 1234, (('bar', 1), ('baz', False)), m.get(), t) | |
63 return str(p).splitlines() | |
64 | |
65 def test_serialize_with_units(self): | |
66 t = targets.DeviceTarget('reg', 'role', 'net', 'host') | |
67 m = metrics.GaugeMetric('test', units=metrics.MetricsDataUnits.SECONDS) | |
68 m.set(1) | |
69 p = metrics_pb2.MetricsCollection() | |
70 m.serialize_to(p, 1234, (('bar', 1), ('baz', False)), m.get(), t) | |
71 self.assertEquals(p.data[0].units, metrics.MetricsDataUnits.SECONDS) | |
72 return str(p).splitlines() | |
73 | |
74 def test_serialize_too_many_fields(self): | |
75 m = metrics.StringMetric('test', fields={'a': 1, 'b': 2, 'c': 3, 'd': 4}) | |
76 m.set('val', fields={'e': 5, 'f': 6, 'g': 7}) | |
77 with self.assertRaises(errors.MonitoringTooManyFieldsError): | |
78 m.set('val', fields={'e': 5, 'f': 6, 'g': 7, 'h': 8}) | |
79 | |
80 def test_populate_field_values(self): | |
81 pb1 = metrics_pb2.MetricsData() | |
82 m1 = metrics.Metric('foo', fields={'asdf': 1}) | |
83 m1._populate_fields(pb1, m1._normalized_fields) | |
84 self.assertEquals(pb1.fields[0].name, 'asdf') | |
85 self.assertEquals(pb1.fields[0].int_value, 1) | |
86 | |
87 pb2 = metrics_pb2.MetricsData() | |
88 m2 = metrics.Metric('bar', fields={'qwer': True}) | |
89 m2._populate_fields(pb2, m2._normalized_fields) | |
90 self.assertEquals(pb2.fields[0].name, 'qwer') | |
91 self.assertEquals(pb2.fields[0].bool_value, True) | |
92 | |
93 pb3 = metrics_pb2.MetricsData() | |
94 m3 = metrics.Metric('baz', fields={'zxcv': 'baz'}) | |
95 m3._populate_fields(pb3, m3._normalized_fields) | |
96 self.assertEquals(pb3.fields[0].name, 'zxcv') | |
97 self.assertEquals(pb3.fields[0].string_value, 'baz') | |
98 | |
99 def test_invalid_field_value(self): | |
100 pb = metrics_pb2.MetricsData() | |
101 m = metrics.Metric('test', fields={'pi': 3.14}) | |
102 with self.assertRaises(errors.MonitoringInvalidFieldTypeError) as e: | |
103 m._populate_fields(pb, m._normalized_fields) | |
104 self.assertEquals(e.exception.metric, 'test') | |
105 self.assertEquals(e.exception.field, 'pi') | |
106 self.assertEquals(e.exception.value, 3.14) | |
107 | |
108 def test_register_unregister(self): | |
109 self.assertEquals(0, len(self.mock_state.metrics)) | |
110 m = metrics.Metric('test', fields={'pi': 3.14}) | |
111 self.assertEquals(1, len(self.mock_state.metrics)) | |
112 m.unregister() | |
113 self.assertEquals(0, len(self.mock_state.metrics)) | |
114 | |
115 def test_reset(self): | |
116 m = metrics.StringMetric('test') | |
117 self.assertIsNone(m.get()) | |
118 m.set('foo') | |
119 self.assertEqual('foo', m.get()) | |
120 m.reset() | |
121 self.assertIsNone(m.get()) | |
122 | |
123 | |
124 class StringMetricTest(TestBase): | |
125 | |
126 def test_populate_value(self): | |
127 pb = metrics_pb2.MetricsData() | |
128 m = metrics.StringMetric('test') | |
129 m._populate_value(pb, 'foo', 1234) | |
130 self.assertEquals(pb.string_value, 'foo') | |
131 | |
132 def test_set(self): | |
133 m = metrics.StringMetric('test') | |
134 m.set('hello world') | |
135 self.assertEquals(m.get(), 'hello world') | |
136 | |
137 def test_non_string_raises(self): | |
138 m = metrics.StringMetric('test') | |
139 with self.assertRaises(errors.MonitoringInvalidValueTypeError): | |
140 m.set(object()) | |
141 | |
142 def test_is_cumulative(self): | |
143 m = metrics.StringMetric('test') | |
144 self.assertFalse(m.is_cumulative()) | |
145 | |
146 | |
147 class BooleanMetricTest(TestBase): | |
148 | |
149 def test_populate_value(self): | |
150 pb = metrics_pb2.MetricsData() | |
151 m = metrics.BooleanMetric('test') | |
152 m._populate_value(pb, True, 1234) | |
153 self.assertEquals(pb.boolean_value, True) | |
154 | |
155 def test_set(self): | |
156 m = metrics.BooleanMetric('test') | |
157 m.set(False) | |
158 self.assertEquals(m.get(), False) | |
159 | |
160 def test_non_bool_raises(self): | |
161 m = metrics.BooleanMetric('test') | |
162 with self.assertRaises(errors.MonitoringInvalidValueTypeError): | |
163 m.set(object()) | |
164 with self.assertRaises(errors.MonitoringInvalidValueTypeError): | |
165 m.set('True') | |
166 with self.assertRaises(errors.MonitoringInvalidValueTypeError): | |
167 m.set(123) | |
168 | |
169 def test_is_cumulative(self): | |
170 m = metrics.BooleanMetric('test') | |
171 self.assertFalse(m.is_cumulative()) | |
172 | |
173 | |
174 class CounterMetricTest(TestBase): | |
175 | |
176 def test_populate_value(self): | |
177 pb = metrics_pb2.MetricsData() | |
178 m = metrics.CounterMetric('test') | |
179 m._populate_value(pb, 1, 1234) | |
180 self.assertEquals(pb.counter, 1) | |
181 | |
182 def test_set(self): | |
183 m = metrics.CounterMetric('test') | |
184 m.set(10) | |
185 self.assertEquals(m.get(), 10) | |
186 | |
187 def test_increment(self): | |
188 m = metrics.CounterMetric('test') | |
189 m.set(1) | |
190 self.assertEquals(m.get(), 1) | |
191 m.increment() | |
192 self.assertEquals(m.get(), 2) | |
193 m.increment_by(3) | |
194 self.assertAlmostEquals(m.get(), 5) | |
195 | |
196 def test_decrement_raises(self): | |
197 m = metrics.CounterMetric('test') | |
198 m.set(1) | |
199 with self.assertRaises(errors.MonitoringDecreasingValueError): | |
200 m.set(0) | |
201 with self.assertRaises(errors.MonitoringDecreasingValueError): | |
202 m.increment_by(-1) | |
203 | |
204 def test_non_int_raises(self): | |
205 m = metrics.CounterMetric('test') | |
206 with self.assertRaises(errors.MonitoringInvalidValueTypeError): | |
207 m.set(object()) | |
208 with self.assertRaises(errors.MonitoringInvalidValueTypeError): | |
209 m.set(1.5) | |
210 with self.assertRaises(errors.MonitoringInvalidValueTypeError): | |
211 m.increment_by(1.5) | |
212 | |
213 def test_multiple_field_values(self): | |
214 m = metrics.CounterMetric('test') | |
215 m.increment({'foo': 'bar'}) | |
216 m.increment({'foo': 'baz'}) | |
217 m.increment({'foo': 'bar'}) | |
218 self.assertIsNone(None) | |
219 self.assertEquals(2, m.get({'foo': 'bar'})) | |
220 self.assertEquals(1, m.get({'foo': 'baz'})) | |
221 | |
222 def test_override_fields(self): | |
223 m = metrics.CounterMetric('test', fields={'foo': 'bar'}) | |
224 m.increment() | |
225 m.increment({'foo': 'baz'}) | |
226 self.assertEquals(1, m.get()) | |
227 self.assertEquals(1, m.get({'foo': 'bar'})) | |
228 self.assertEquals(1, m.get({'foo': 'baz'})) | |
229 | |
230 def test_start_timestamp(self): | |
231 t = targets.DeviceTarget('reg', 'role', 'net', 'host') | |
232 m = metrics.CounterMetric('test', fields={'foo': 'bar'}) | |
233 m.increment() | |
234 p = metrics_pb2.MetricsCollection() | |
235 m.serialize_to(p, 1234, (), m.get(), t) | |
236 self.assertEquals(1234000000, p.data[0].start_timestamp_us) | |
237 | |
238 def test_is_cumulative(self): | |
239 m = metrics.CounterMetric('test') | |
240 self.assertTrue(m.is_cumulative()) | |
241 | |
242 def test_get_all(self): | |
243 m = metrics.CounterMetric('test') | |
244 m.increment() | |
245 m.increment({'foo': 'bar'}) | |
246 m.increment({'foo': 'baz', 'moo': 'wibble'}) | |
247 self.assertEqual([ | |
248 ((), 1), | |
249 ((('foo', 'bar'),), 1), | |
250 ((('foo', 'baz'), ('moo', 'wibble')), 1), | |
251 ], sorted(m.get_all())) | |
252 | |
253 | |
254 class GaugeMetricTest(TestBase): | |
255 | |
256 def test_populate_value(self): | |
257 pb = metrics_pb2.MetricsData() | |
258 m = metrics.GaugeMetric('test') | |
259 m._populate_value(pb, 1, 1234) | |
260 self.assertEquals(pb.gauge, 1) | |
261 | |
262 def test_set(self): | |
263 m = metrics.GaugeMetric('test') | |
264 m.set(10) | |
265 self.assertEquals(m.get(), 10) | |
266 m.set(sys.maxint + 1) | |
267 self.assertEquals(m.get(), sys.maxint + 1) | |
268 | |
269 def test_non_int_raises(self): | |
270 m = metrics.GaugeMetric('test') | |
271 with self.assertRaises(errors.MonitoringInvalidValueTypeError): | |
272 m.set(object()) | |
273 | |
274 def test_is_cumulative(self): | |
275 m = metrics.GaugeMetric('test') | |
276 self.assertFalse(m.is_cumulative()) | |
277 | |
278 | |
279 class CumulativeMetricTest(TestBase): | |
280 | |
281 def test_populate_value(self): | |
282 pb = metrics_pb2.MetricsData() | |
283 m = metrics.CumulativeMetric('test') | |
284 m._populate_value(pb, 1.618, 1234) | |
285 self.assertAlmostEquals(pb.cumulative_double_value, 1.618) | |
286 | |
287 def test_set(self): | |
288 m = metrics.CumulativeMetric('test') | |
289 m.set(3.14) | |
290 self.assertAlmostEquals(m.get(), 3.14) | |
291 | |
292 def test_decrement_raises(self): | |
293 m = metrics.CumulativeMetric('test') | |
294 m.set(3.14) | |
295 with self.assertRaises(errors.MonitoringDecreasingValueError): | |
296 m.set(0) | |
297 with self.assertRaises(errors.MonitoringDecreasingValueError): | |
298 m.increment_by(-1) | |
299 | |
300 def test_non_number_raises(self): | |
301 m = metrics.CumulativeMetric('test') | |
302 with self.assertRaises(errors.MonitoringInvalidValueTypeError): | |
303 m.set(object()) | |
304 | |
305 def test_start_timestamp(self): | |
306 t = targets.DeviceTarget('reg', 'role', 'net', 'host') | |
307 m = metrics.CumulativeMetric('test', fields={'foo': 'bar'}) | |
308 m.set(3.14) | |
309 p = metrics_pb2.MetricsCollection() | |
310 m.serialize_to(p, 1234, (), m.get(), t) | |
311 self.assertEquals(1234000000, p.data[0].start_timestamp_us) | |
312 | |
313 def test_is_cumulative(self): | |
314 m = metrics.CumulativeMetric('test') | |
315 self.assertTrue(m.is_cumulative()) | |
316 | |
317 | |
318 class FloatMetricTest(TestBase): | |
319 | |
320 def test_populate_value(self): | |
321 pb = metrics_pb2.MetricsData() | |
322 m = metrics.FloatMetric('test') | |
323 m._populate_value(pb, 1.618, 1234) | |
324 self.assertEquals(pb.noncumulative_double_value, 1.618) | |
325 | |
326 def test_set(self): | |
327 m = metrics.FloatMetric('test') | |
328 m.set(3.14) | |
329 self.assertEquals(m.get(), 3.14) | |
330 | |
331 def test_non_number_raises(self): | |
332 m = metrics.FloatMetric('test') | |
333 with self.assertRaises(errors.MonitoringInvalidValueTypeError): | |
334 m.set(object()) | |
335 | |
336 def test_is_cumulative(self): | |
337 m = metrics.FloatMetric('test') | |
338 self.assertFalse(m.is_cumulative()) | |
339 | |
340 | |
341 class RunningZeroGeneratorTest(TestBase): | |
342 | |
343 def assertZeroes(self, expected, sequence): | |
344 self.assertEquals(expected, | |
345 list(metrics.DistributionMetric._running_zero_generator(sequence))) | |
346 | |
347 def test_running_zeroes(self): | |
348 self.assertZeroes([1, -1, 1], [1, 0, 1]) | |
349 self.assertZeroes([1, -2, 1], [1, 0, 0, 1]) | |
350 self.assertZeroes([1, -3, 1], [1, 0, 0, 0, 1]) | |
351 self.assertZeroes([1, -1, 1, -1, 2], [1, 0, 1, 0, 2]) | |
352 self.assertZeroes([1, -1, 1, -2, 2], [1, 0, 1, 0, 0, 2]) | |
353 self.assertZeroes([1, -2, 1, -2, 2], [1, 0, 0, 1, 0, 0, 2]) | |
354 | |
355 def test_leading_zeroes(self): | |
356 self.assertZeroes([-1, 1], [0, 1]) | |
357 self.assertZeroes([-2, 1], [0, 0, 1]) | |
358 self.assertZeroes([-3, 1], [0, 0, 0, 1]) | |
359 | |
360 def test_trailing_zeroes(self): | |
361 self.assertZeroes([1], [1]) | |
362 self.assertZeroes([1], [1, 0]) | |
363 self.assertZeroes([1], [1, 0, 0]) | |
364 self.assertZeroes([], []) | |
365 self.assertZeroes([], [0]) | |
366 self.assertZeroes([], [0, 0]) | |
367 | |
368 | |
369 class DistributionMetricTest(TestBase): | |
370 | |
371 def test_populate_canonical(self): | |
372 pb = metrics_pb2.MetricsData() | |
373 m = metrics.DistributionMetric('test') | |
374 m._populate_value(pb, | |
375 distribution.Distribution(distribution.GeometricBucketer()), | |
376 1234) | |
377 self.assertEquals(pb.distribution.spec_type, | |
378 metrics_pb2.PrecomputedDistribution.CANONICAL_POWERS_OF_10_P_0_2) | |
379 | |
380 m._populate_value(pb, | |
381 distribution.Distribution(distribution.GeometricBucketer(2)), | |
382 1234) | |
383 self.assertEquals(pb.distribution.spec_type, | |
384 metrics_pb2.PrecomputedDistribution.CANONICAL_POWERS_OF_2) | |
385 | |
386 m._populate_value(pb, | |
387 distribution.Distribution(distribution.GeometricBucketer(10)), | |
388 1234) | |
389 self.assertEquals(pb.distribution.spec_type, | |
390 metrics_pb2.PrecomputedDistribution.CANONICAL_POWERS_OF_10) | |
391 | |
392 def test_populate_custom(self): | |
393 pb = metrics_pb2.MetricsData() | |
394 m = metrics.DistributionMetric('test') | |
395 m._populate_value(pb, | |
396 distribution.Distribution(distribution.GeometricBucketer(4)), | |
397 1234) | |
398 self.assertEquals(pb.distribution.spec_type, | |
399 metrics_pb2.PrecomputedDistribution.CUSTOM_PARAMETERIZED) | |
400 self.assertEquals(0, pb.distribution.width) | |
401 self.assertEquals(4, pb.distribution.growth_factor) | |
402 self.assertEquals(100, pb.distribution.num_buckets) | |
403 | |
404 m._populate_value(pb, | |
405 distribution.Distribution(distribution.FixedWidthBucketer(10)), | |
406 1234) | |
407 self.assertEquals(pb.distribution.spec_type, | |
408 metrics_pb2.PrecomputedDistribution.CUSTOM_PARAMETERIZED) | |
409 self.assertEquals(10, pb.distribution.width) | |
410 self.assertEquals(0, pb.distribution.growth_factor) | |
411 self.assertEquals(100, pb.distribution.num_buckets) | |
412 | |
413 def test_populate_buckets(self): | |
414 pb = metrics_pb2.MetricsData() | |
415 m = metrics.DistributionMetric('test') | |
416 d = distribution.Distribution( | |
417 distribution.FixedWidthBucketer(10)) | |
418 d.add(5) | |
419 d.add(15) | |
420 d.add(35) | |
421 d.add(65) | |
422 | |
423 m._populate_value(pb, d, 1234) | |
424 self.assertEquals([1, 1, -1, 1, -2, 1], pb.distribution.bucket) | |
425 self.assertEquals(0, pb.distribution.underflow) | |
426 self.assertEquals(0, pb.distribution.overflow) | |
427 self.assertEquals(30, pb.distribution.mean) | |
428 | |
429 pb = metrics_pb2.MetricsData() | |
430 d = distribution.Distribution( | |
431 distribution.FixedWidthBucketer(10, num_finite_buckets=1)) | |
432 d.add(5) | |
433 d.add(15) | |
434 d.add(25) | |
435 | |
436 m._populate_value(pb, d, 1234) | |
437 self.assertEquals([1], pb.distribution.bucket) | |
438 self.assertEquals(0, pb.distribution.underflow) | |
439 self.assertEquals(2, pb.distribution.overflow) | |
440 self.assertEquals(15, pb.distribution.mean) | |
441 | |
442 def test_populate_buckets_last_zero(self): | |
443 pb = metrics_pb2.MetricsData() | |
444 m = metrics.DistributionMetric('test') | |
445 d = distribution.Distribution( | |
446 distribution.FixedWidthBucketer(10, num_finite_buckets=10)) | |
447 d.add(5) | |
448 d.add(105) | |
449 | |
450 m._populate_value(pb, d, 1234) | |
451 self.assertEquals([1], pb.distribution.bucket) | |
452 self.assertEquals(1, pb.distribution.overflow) | |
453 | |
454 def test_populate_buckets_underflow(self): | |
455 pb = metrics_pb2.MetricsData() | |
456 m = metrics.DistributionMetric('test') | |
457 d = distribution.Distribution( | |
458 distribution.FixedWidthBucketer(10, num_finite_buckets=10)) | |
459 d.add(-5) | |
460 d.add(-1000000) | |
461 | |
462 m._populate_value(pb, d, 1234) | |
463 self.assertEquals([], pb.distribution.bucket) | |
464 self.assertEquals(2, pb.distribution.underflow) | |
465 self.assertEquals(0, pb.distribution.overflow) | |
466 self.assertEquals(-500002.5, pb.distribution.mean) | |
467 | |
468 def test_populate_is_cumulative(self): | |
469 pb = metrics_pb2.MetricsData() | |
470 d = distribution.Distribution( | |
471 distribution.FixedWidthBucketer(10, num_finite_buckets=10)) | |
472 m = metrics.CumulativeDistributionMetric('test') | |
473 | |
474 m._populate_value(pb, d, 1234) | |
475 self.assertTrue(pb.distribution.is_cumulative) | |
476 | |
477 m = metrics.NonCumulativeDistributionMetric('test2') | |
478 | |
479 m._populate_value(pb, d, 1234) | |
480 self.assertFalse(pb.distribution.is_cumulative) | |
481 | |
482 def test_add(self): | |
483 m = metrics.DistributionMetric('test') | |
484 m.add(1) | |
485 m.add(10) | |
486 m.add(100) | |
487 self.assertEquals({2: 1, 6: 1, 11: 1}, m.get().buckets) | |
488 self.assertEquals(111, m.get().sum) | |
489 self.assertEquals(3, m.get().count) | |
490 | |
491 def test_add_custom_bucketer(self): | |
492 m = metrics.DistributionMetric('test', | |
493 bucketer=distribution.FixedWidthBucketer(10)) | |
494 m.add(1) | |
495 m.add(10) | |
496 m.add(100) | |
497 self.assertEquals({1: 1, 2: 1, 11: 1}, m.get().buckets) | |
498 self.assertEquals(111, m.get().sum) | |
499 self.assertEquals(3, m.get().count) | |
500 | |
501 def test_set(self): | |
502 d = distribution.Distribution( | |
503 distribution.FixedWidthBucketer(10, num_finite_buckets=10)) | |
504 d.add(1) | |
505 d.add(10) | |
506 d.add(100) | |
507 | |
508 m = metrics.CumulativeDistributionMetric('test') | |
509 with self.assertRaises(TypeError): | |
510 m.set(d) | |
511 | |
512 m = metrics.NonCumulativeDistributionMetric('test2') | |
513 m.set(d) | |
514 self.assertEquals(d, m.get()) | |
515 | |
516 with self.assertRaises(errors.MonitoringInvalidValueTypeError): | |
517 m.set(1) | |
518 with self.assertRaises(errors.MonitoringInvalidValueTypeError): | |
519 m.set('foo') | |
520 | |
521 def test_start_timestamp(self): | |
522 t = targets.DeviceTarget('reg', 'role', 'net', 'host') | |
523 m = metrics.CumulativeDistributionMetric('test') | |
524 m.add(1) | |
525 m.add(5) | |
526 m.add(25) | |
527 p = metrics_pb2.MetricsCollection() | |
528 m.serialize_to(p, 1234, (), m.get(), t) | |
529 self.assertEquals(1234000000, p.data[0].start_timestamp_us) | |
530 | |
531 def test_is_cumulative(self): | |
532 cd = metrics.CumulativeDistributionMetric('test') | |
533 ncd = metrics.NonCumulativeDistributionMetric('test2') | |
534 self.assertTrue(cd.is_cumulative()) | |
535 self.assertFalse(ncd.is_cumulative()) | |
OLD | NEW |