blob: e15ef1fe2825af9ab095dcc554eb181e6e042baa [file] [log] [blame]
# Copyright 2015 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import mock
import sys
import time
import unittest
from infra_libs.ts_mon.common import distribution
from infra_libs.ts_mon.common import errors
from infra_libs.ts_mon.common import interface
from infra_libs.ts_mon.common import metric_store
from infra_libs.ts_mon.common import metrics
from infra_libs.ts_mon.common import targets
from infra_libs.ts_mon.protos import metrics_pb2
class TestBase(unittest.TestCase):
def setUp(self):
super(TestBase, self).setUp()
target = targets.TaskTarget('test_service', 'test_job',
'test_region', 'test_host')
self.mock_state = interface.State(target=target)
self.state_patcher = mock.patch('infra_libs.ts_mon.common.interface.state',
new=self.mock_state)
self.state_patcher.start()
self.mock_state.target = targets.TaskTarget(
service_name='service', job_name='job', region='region',
hostname='hostname', task_num=0)
self.time_fn = mock.create_autospec(time.time, spec_set=True)
self.mock_state.store = metric_store.InProcessMetricStore(
self.mock_state, self.time_fn)
def tearDown(self):
self.state_patcher.stop()
super(TestBase, self).tearDown()
def _test_proto(self, metric, set_fn, value_type, stream_kind):
self.time_fn.return_value = 100.3
interface.register(metric)
set_fn(metric)
self.time_fn.return_value = 1000.6
proto = list(interface._generate_proto())[0]
data_set = proto.metrics_collection[0].metrics_data_set[0]
data = data_set.data[0]
self.assertEqual(stream_kind, data_set.stream_kind)
self.assertEqual(value_type, data_set.value_type)
self.assertEqual(100, data.start_timestamp.seconds)
self.assertEqual(1000, data.end_timestamp.seconds)
self.assertFalse(data_set.annotations.HasField('unit'))
return data
class MetricTest(TestBase):
def test_properties(self):
field_spec = [metrics.StringField('string')]
m1 = metrics.Metric('/foo', 'foo', field_spec, 'us')
self.assertEquals(m1.name, 'foo')
self.assertEquals(m1.field_spec, field_spec)
self.assertEquals(m1.units, 'us')
def test_equality(self):
field_spec = [metrics.StringField('string')]
m = metrics.Metric('/foo', 'foo', field_spec, 'us')
self.assertEquals(m, m)
def test_init_too_many_fields(self):
fields = [metrics.StringField('field%d' % i) for i in range(8)]
with self.assertRaises(errors.MonitoringTooManyFieldsError) as e:
metrics.Metric('test', 'test', fields)
self.assertEquals(e.exception.metric, 'test')
self.assertEquals(len(e.exception.fields), 8)
def test_set_wrong_number_of_fields(self):
m = metrics.StringMetric('foo', 'foo', [metrics.IntegerField('asdf')])
with self.assertRaises(errors.WrongFieldsError):
m.set('bar', {'asdf': 1, 'foo': 2})
def test_set_list_fields(self):
m = metrics.StringMetric('foo', 'foo', [metrics.IntegerField('asdf')])
with self.assertRaises(ValueError):
m.set('bar', [1])
def test_set_object_fields(self):
m = metrics.StringMetric('foo', 'foo', [metrics.IntegerField('asdf')])
with self.assertRaises(ValueError):
m.set('bar', object())
def test_register_unregister(self):
self.assertEquals(0, len(self.mock_state.metrics))
m = metrics.Metric('test', 'test', None)
self.assertEquals(1, len(self.mock_state.metrics))
m.unregister()
self.assertEquals(0, len(self.mock_state.metrics))
def test_reset(self):
m = metrics.StringMetric('test', 'test', None)
self.assertIsNone(m.get())
m.set('foo')
self.assertEqual('foo', m.get())
m.reset()
self.assertIsNone(m.get())
def test_populate_data_set(self):
interface.state.metric_name_prefix = '/infra/test/'
scenarios = [
(metrics.CounterMetric, 'desc', metrics_pb2.CUMULATIVE),
(metrics.GaugeMetric, 'desc', metrics_pb2.GAUGE)]
for m_ctor, desc, stream_kind in scenarios:
m = m_ctor(m_ctor.__name__, desc, None,
units=metrics.MetricsDataUnits.SECONDS)
data_set = metrics_pb2.MetricsDataSet()
m.populate_data_set(data_set)
self.assertEqual(stream_kind, data_set.stream_kind)
self.assertEqual('/infra/test/%s' % m_ctor.__name__, data_set.metric_name)
self.assertEqual(desc, data_set.description)
self.assertEqual('s', data_set.annotations.unit)
def test_populate_data(self):
m = metrics.CounterMetric('test', 'test', None)
data = metrics_pb2.MetricsData()
m.populate_data(data, 100.4, 1000.6, {}, 5)
self.assertEqual(100, data.start_timestamp.seconds)
self.assertEqual(1000, data.end_timestamp.seconds)
def test_populate_field_descriptor(self):
data_set_pb = metrics_pb2.MetricsDataSet()
m = metrics.Metric('test', 'test', [
metrics.IntegerField('a'),
metrics.BooleanField('b'),
metrics.StringField('c'),
])
m._populate_field_descriptors(data_set_pb)
field_type = metrics_pb2.MetricsDataSet.MetricFieldDescriptor
self.assertEqual(3, len(data_set_pb.field_descriptor))
self.assertEqual('a', data_set_pb.field_descriptor[0].name)
self.assertEqual(field_type.INT64,
data_set_pb.field_descriptor[0].field_type)
self.assertEqual('b', data_set_pb.field_descriptor[1].name)
self.assertEqual(field_type.BOOL,
data_set_pb.field_descriptor[1].field_type)
self.assertEqual('c', data_set_pb.field_descriptor[2].name)
self.assertEqual(field_type.STRING,
data_set_pb.field_descriptor[2].field_type)
def test_populate_fields(self):
data = metrics_pb2.MetricsData()
m = metrics.Metric('test', 'test', [
metrics.IntegerField('a'),
metrics.BooleanField('b'),
metrics.StringField('c'),
])
m._populate_fields(data, (1, True, 'test'))
self.assertEqual(3, len(data.field))
self.assertEqual('a', data.field[0].name)
self.assertEqual(1, data.field[0].int64_value)
self.assertEqual('b', data.field[1].name)
self.assertTrue(data.field[1].bool_value)
self.assertEqual('c', data.field[2].name)
self.assertEqual('test', data.field[2].string_value)
def test_bad_description(self):
with self.assertRaises(errors.MetricDefinitionError):
metrics.Metric('test', 123, None)
with self.assertRaises(errors.MetricDefinitionError):
metrics.Metric('test', '', None)
with self.assertRaises(errors.MetricDefinitionError):
metrics.Metric('test', None, None)
def test_bad_field_spec(self):
with self.assertRaises(errors.MetricDefinitionError):
metrics.Metric('test', 'desc', ['abc'])
with self.assertRaises(errors.MetricDefinitionError):
metrics.Metric('test', 'desc', ('abc',))
with self.assertRaises(errors.MetricDefinitionError):
metrics.Metric('test', 'desc', [123])
with self.assertRaises(errors.MetricDefinitionError):
metrics.Metric('test', 'desc', [None])
class FieldValidationTest(TestBase):
def test_string_field(self):
f = metrics.StringField('name')
f.validate_value('', 'string')
f.validate_value('', u'string')
with self.assertRaises(errors.MonitoringInvalidFieldTypeError):
f.validate_value('', 123)
if sys.version_info.major < 3:
with self.assertRaises(errors.MonitoringInvalidFieldTypeError):
f.validate_value('', long(123))
with self.assertRaises(errors.MonitoringInvalidFieldTypeError):
f.validate_value('', True)
with self.assertRaises(errors.MonitoringInvalidFieldTypeError):
f.validate_value('', None)
with self.assertRaises(errors.MonitoringInvalidFieldTypeError):
f.validate_value('', 12.34)
def test_integer_field(self):
f = metrics.IntegerField('name')
with self.assertRaises(errors.MonitoringInvalidFieldTypeError):
f.validate_value('', 'string')
with self.assertRaises(errors.MonitoringInvalidFieldTypeError):
f.validate_value('', u'string')
f.validate_value('', 123)
if sys.version_info.major < 3:
f.validate_value('', long(123))
f.validate_value('', True) # Python allows this *shrug*
with self.assertRaises(errors.MonitoringInvalidFieldTypeError):
f.validate_value('', None)
with self.assertRaises(errors.MonitoringInvalidFieldTypeError):
f.validate_value('', 12.34)
def test_boolean_field(self):
f = metrics.BooleanField('name')
with self.assertRaises(errors.MonitoringInvalidFieldTypeError):
f.validate_value('', 'string')
with self.assertRaises(errors.MonitoringInvalidFieldTypeError):
f.validate_value('', u'string')
with self.assertRaises(errors.MonitoringInvalidFieldTypeError):
f.validate_value('', 123)
if sys.version_info.major < 3:
with self.assertRaises(errors.MonitoringInvalidFieldTypeError):
f.validate_value('', long(123))
f.validate_value('', True)
with self.assertRaises(errors.MonitoringInvalidFieldTypeError):
f.validate_value('', None)
with self.assertRaises(errors.MonitoringInvalidFieldTypeError):
f.validate_value('', 12.34)
def test_invalid_field_name(self):
with self.assertRaises(errors.MetricDefinitionError):
metrics.StringField('foo', 'desc', [metrics.StringField(' ')])
with self.assertRaises(errors.MetricDefinitionError):
metrics.StringField('foo', 'desc', [metrics.StringField('123')])
with self.assertRaises(errors.MetricDefinitionError):
metrics.StringField('foo', 'desc', [metrics.StringField('')])
with self.assertRaises(errors.MetricDefinitionError):
metrics.StringField('foo', 'desc', [metrics.StringField(u'\U0001F4A9')])
def test_equality(self):
f = metrics.IntegerField('name')
self.assertEquals(f, f)
class StringMetricTest(TestBase):
def test_generate_proto(self):
proto = self._test_proto(
metrics.StringMetric('t', 't', None), lambda m: m.set('aaa'),
metrics_pb2.STRING, metrics_pb2.GAUGE)
self.assertEqual('aaa', proto.string_value)
def test_set(self):
m = metrics.StringMetric('test', 'test', None)
m.set('hello world')
self.assertEquals(m.get(), 'hello world')
def test_non_string_raises(self):
m = metrics.StringMetric('test', 'test', None)
with self.assertRaises(errors.MonitoringInvalidValueTypeError):
m.set(object())
def test_is_cumulative(self):
m = metrics.StringMetric('test', 'test', None)
self.assertFalse(m.is_cumulative())
class BooleanMetricTest(TestBase):
def test_generate_proto(self):
proto = self._test_proto(
metrics.BooleanMetric('test', 'test', None),
lambda m: m.set(True),
metrics_pb2.BOOL, metrics_pb2.GAUGE)
self.assertTrue(proto.bool_value)
def test_set(self):
m = metrics.BooleanMetric('test', 'test', None)
m.set(False)
self.assertEquals(m.get(), False)
def test_non_bool_raises(self):
m = metrics.BooleanMetric('test', 'test', None)
with self.assertRaises(errors.MonitoringInvalidValueTypeError):
m.set(object())
with self.assertRaises(errors.MonitoringInvalidValueTypeError):
m.set('True')
with self.assertRaises(errors.MonitoringInvalidValueTypeError):
m.set(123)
def test_is_cumulative(self):
m = metrics.BooleanMetric('test', 'test', None)
self.assertFalse(m.is_cumulative())
class CounterMetricTest(TestBase):
def test_generate_proto(self):
proto = self._test_proto(
metrics.CounterMetric('c', 'test', None),
lambda m: m.increment_by(5),
metrics_pb2.INT64, metrics_pb2.CUMULATIVE)
self.assertEqual(5, proto.int64_value)
def test_set(self):
m = metrics.CounterMetric('test', 'test', None)
m.set(10)
self.assertEquals(m.get(), 10)
def test_increment(self):
m = metrics.CounterMetric('test', 'test', None)
m.set(1)
self.assertEquals(m.get(), 1)
m.increment()
self.assertEquals(m.get(), 2)
m.increment_by(3)
self.assertAlmostEquals(m.get(), 5)
def test_decrement_raises(self):
m = metrics.CounterMetric('test', 'test', None)
m.set(1)
with self.assertRaises(errors.MonitoringDecreasingValueError):
m.set(0)
with self.assertRaises(errors.MonitoringDecreasingValueError):
m.increment_by(-1)
def test_non_int_raises(self):
m = metrics.CounterMetric('test', 'test', None)
with self.assertRaises(errors.MonitoringInvalidValueTypeError):
m.set(object())
with self.assertRaises(errors.MonitoringInvalidValueTypeError):
m.set(1.5)
with self.assertRaises(errors.MonitoringInvalidValueTypeError):
m.increment_by(1.5)
def test_multiple_field_values(self):
m = metrics.CounterMetric('test', 'test', [metrics.StringField('foo')])
m.increment({'foo': 'bar'})
m.increment({'foo': 'baz'})
m.increment({'foo': 'bar'})
with self.assertRaises(errors.WrongFieldsError):
m.get()
self.assertIsNone(m.get({'foo': ''}))
self.assertEquals(2, m.get({'foo': 'bar'}))
self.assertEquals(1, m.get({'foo': 'baz'}))
def test_is_cumulative(self):
m = metrics.CounterMetric('test', 'test', None)
self.assertTrue(m.is_cumulative())
def test_get_all(self):
m = metrics.CounterMetric('test', 'test', [metrics.StringField('foo')])
m.increment({'foo': ''})
m.increment({'foo': 'bar'})
self.assertEqual([
(('',), 1),
(('bar',), 1),
], sorted(m.get_all()))
class GaugeMetricTest(TestBase):
def test_generate_proto(self):
proto = self._test_proto(
metrics.GaugeMetric('test', 'test', None), lambda m: m.set(5),
metrics_pb2.INT64, metrics_pb2.GAUGE)
self.assertEqual(5, proto.int64_value)
def test_set(self):
m = metrics.GaugeMetric('test', 'test', None)
m.set(10)
self.assertEquals(m.get(), 10)
m.set(sys.maxint + 1)
self.assertEquals(m.get(), sys.maxint + 1)
def test_non_int_raises(self):
m = metrics.GaugeMetric('test', 'test', None)
with self.assertRaises(errors.MonitoringInvalidValueTypeError):
m.set(object())
def test_is_cumulative(self):
m = metrics.GaugeMetric('test', 'test', None)
self.assertFalse(m.is_cumulative())
class CumulativeMetricTest(TestBase):
def test_generate_proto(self):
proto = self._test_proto(
metrics.CumulativeMetric('c', 'test', None),
lambda m: m.increment_by(5.2),
metrics_pb2.DOUBLE, metrics_pb2.CUMULATIVE)
self.assertAlmostEqual(5.2, proto.double_value)
def test_set(self):
m = metrics.CumulativeMetric('test', 'test', None)
m.set(3.14)
self.assertAlmostEquals(m.get(), 3.14)
def test_decrement_raises(self):
m = metrics.CumulativeMetric('test', 'test', None)
m.set(3.14)
with self.assertRaises(errors.MonitoringDecreasingValueError):
m.set(0)
with self.assertRaises(errors.MonitoringDecreasingValueError):
m.increment_by(-1)
def test_non_number_raises(self):
m = metrics.CumulativeMetric('test', 'test', None)
with self.assertRaises(errors.MonitoringInvalidValueTypeError):
m.set(object())
def test_is_cumulative(self):
m = metrics.CumulativeMetric('test', 'test', None)
self.assertTrue(m.is_cumulative())
class FloatMetricTest(TestBase):
def test_generate_proto(self):
proto = self._test_proto(
metrics.FloatMetric('test', 'test', None), lambda m: m.set(1.23),
metrics_pb2.DOUBLE, metrics_pb2.GAUGE)
self.assertAlmostEqual(1.23, proto.double_value)
def test_set(self):
m = metrics.FloatMetric('test', 'test', None)
m.set(3.14)
self.assertEquals(m.get(), 3.14)
def test_non_number_raises(self):
m = metrics.FloatMetric('test', 'test', None)
with self.assertRaises(errors.MonitoringInvalidValueTypeError):
m.set(object())
def test_is_cumulative(self):
m = metrics.FloatMetric('test', 'test', None)
self.assertFalse(m.is_cumulative())
class DistributionMetricTest(TestBase):
def _test_distribution_proto(self, dist):
interface.register(dist)
self.time_fn.return_value = 100.3
for num in [0, 1, 5, 5.5, 9, 10, 10000]:
dist.add(num)
self.time_fn.return_value = 1000.6
proto = list(interface._generate_proto())[0]
data_set = proto.metrics_collection[0].metrics_data_set[0]
data = data_set.data[0]
self.assertAlmostEqual(1432.928571428, data.distribution_value.mean)
self.assertEqual(metrics_pb2.DISTRIBUTION, data_set.value_type)
self.assertEqual(100, data.start_timestamp.seconds)
self.assertEqual(1000, data.end_timestamp.seconds)
self.assertFalse(data_set.annotations.HasField('unit'))
return data_set, data
def test_generate_fixed_width_distribution(self):
bucketer = distribution.FixedWidthBucketer(width=1, num_finite_buckets=10)
dists = [
(metrics.NonCumulativeDistributionMetric(
'test0', 'test', None, bucketer=bucketer),
metrics_pb2.GAUGE),
(metrics.CumulativeDistributionMetric(
'test1', 'test', None, bucketer=bucketer),
metrics_pb2.CUMULATIVE)
]
for dist, stream_kind in dists:
data_set, data = self._test_distribution_proto(dist)
self.assertListEqual([0, 1, 1, 0, 0, 0, 2, 0, 0, 0, 1, 2],
list(data.distribution_value.bucket_count))
self.assertEqual(
10, data.distribution_value.linear_buckets.num_finite_buckets)
self.assertEqual(1, data.distribution_value.linear_buckets.width)
self.assertEqual(stream_kind, data_set.stream_kind)
self.assertEqual(7, data.distribution_value.count)
def test_generate_geometric_distribution(self):
bucketer = distribution.GeometricBucketer(growth_factor=10**2,
num_finite_buckets=10)
dists = [
(metrics.NonCumulativeDistributionMetric(
'test0', 'test', None, bucketer=bucketer),
metrics_pb2.GAUGE),
(metrics.CumulativeDistributionMetric(
'test1', 'test', None, bucketer=bucketer),
metrics_pb2.CUMULATIVE)
]
for dist, stream_kind in dists:
data_set, data = self._test_distribution_proto(dist)
self.assertListEqual([1, 5, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
list(data.distribution_value.bucket_count))
self.assertEqual(
10, data.distribution_value.exponential_buckets.num_finite_buckets)
self.assertEqual(
10**2, data.distribution_value.exponential_buckets.growth_factor)
self.assertEqual(stream_kind, data_set.stream_kind)
self.assertEqual(7, data.distribution_value.count)
def test_generate_geometric_distribution_with_scale(self):
bucketer = distribution.GeometricBucketer(growth_factor=10.0,
num_finite_buckets=10,
scale=0.1)
dists = [
(metrics.NonCumulativeDistributionMetric(
'test0', 'test', None, bucketer=bucketer),
metrics_pb2.GAUGE),
(metrics.CumulativeDistributionMetric(
'test1', 'test', None, bucketer=bucketer),
metrics_pb2.CUMULATIVE)
]
for dist, stream_kind in dists:
data_set, data = self._test_distribution_proto(dist)
self.assertListEqual([1, 0, 4, 1, 0, 0, 1, 0, 0, 0, 0, 0],
list(data.distribution_value.bucket_count))
self.assertEqual(
10, data.distribution_value.exponential_buckets.num_finite_buckets)
self.assertEqual(
10.0, data.distribution_value.exponential_buckets.growth_factor)
self.assertEqual(
0.1, data.distribution_value.exponential_buckets.scale)
self.assertEqual(stream_kind, data_set.stream_kind)
self.assertEqual(7, data.distribution_value.count)
def test_add(self):
m = metrics.CumulativeDistributionMetric('test', 'test', None)
m.add(1)
m.add(10)
m.add(100)
self.assertEquals({1: 1, 5: 1, 10: 1}, m.get().buckets)
self.assertEquals(111, m.get().sum)
self.assertEquals(3, m.get().count)
def test_add_custom_bucketer(self):
m = metrics.CumulativeDistributionMetric('test', 'test', None,
bucketer=distribution.FixedWidthBucketer(10))
m.add(1)
m.add(10)
m.add(100)
self.assertEquals({1: 1, 2: 1, 11: 1}, m.get().buckets)
self.assertEquals(111, m.get().sum)
self.assertEquals(3, m.get().count)
def test_set(self):
d = distribution.Distribution(
distribution.FixedWidthBucketer(10, num_finite_buckets=10))
d.add(1)
d.add(10)
d.add(100)
m = metrics.CumulativeDistributionMetric('test', 'test', None)
with self.assertRaises(TypeError):
m.set(d)
m = metrics.NonCumulativeDistributionMetric('test2', 'test', None)
m.set(d)
self.assertEquals(d, m.get())
with self.assertRaises(errors.MonitoringInvalidValueTypeError):
m.set(1)
with self.assertRaises(errors.MonitoringInvalidValueTypeError):
m.set('foo')
def test_is_cumulative(self):
cd = metrics.CumulativeDistributionMetric('test', 'test', None)
ncd = metrics.NonCumulativeDistributionMetric('test2', 'test', None)
self.assertTrue(cd.is_cumulative())
self.assertFalse(ncd.is_cumulative())