From e465401f75d12527a29b0cd07f68d0a9e1a6d152 Mon Sep 17 00:00:00 2001 From: Chris Kleinknecht Date: Wed, 24 Oct 2018 19:25:40 -0700 Subject: [PATCH 01/17] Fix MetricDescriptor docstring --- opencensus/metrics/export/metric_descriptor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/opencensus/metrics/export/metric_descriptor.py b/opencensus/metrics/export/metric_descriptor.py index c0c06bc27..8e2e731a9 100644 --- a/opencensus/metrics/export/metric_descriptor.py +++ b/opencensus/metrics/export/metric_descriptor.py @@ -119,8 +119,8 @@ class MetricDescriptor(object): format described by http://unitsofmeasure.org/ucum.html. :type type_: int - :param unit: The unit in which the metric value is reported. The - MetricDescriptorType class enumerates valid options. + :param type_: The type of metric. MetricDescriptorType enumerates the valid + options. :type label_keys: list(:class: '~opencensus.metrics.label_key.LabelKey') :param label_keys: The label keys associated with the metric descriptor. From 56e074ad88035c876bc27ea581d0634a0dcbee2e Mon Sep 17 00:00:00 2001 From: Chris Kleinknecht Date: Thu, 25 Oct 2018 12:28:52 -0700 Subject: [PATCH 02/17] Change TS type check to check Value types instead of underlying primitives. --- opencensus/metrics/export/metric.py | 22 +-------- .../metrics/export/metric_descriptor.py | 17 +++---- opencensus/metrics/export/time_series.py | 20 ++++---- .../metrics/export/test_metric_descriptor.py | 49 ++++++++++--------- tests/unit/metrics/export/test_time_series.py | 13 ++--- 5 files changed, 51 insertions(+), 70 deletions(-) diff --git a/opencensus/metrics/export/metric.py b/opencensus/metrics/export/metric.py index beb805c54..6f23d470b 100644 --- a/opencensus/metrics/export/metric.py +++ b/opencensus/metrics/export/metric.py @@ -13,25 +13,6 @@ # limitations under the License. from opencensus.metrics.export import metric_descriptor -from opencensus.metrics.export import value - - -DESCRIPTOR_VALUE = { - metric_descriptor.MetricDescriptorType.GAUGE_INT64: - value.ValueLong, - metric_descriptor.MetricDescriptorType.CUMULATIVE_INT64: - value.ValueLong, - metric_descriptor.MetricDescriptorType.GAUGE_DOUBLE: - value.ValueDouble, - metric_descriptor.MetricDescriptorType.CUMULATIVE_DOUBLE: - value.ValueDouble, - metric_descriptor.MetricDescriptorType.GAUGE_DISTRIBUTION: - value.ValueDistribution, - metric_descriptor.MetricDescriptorType.CUMULATIVE_DISTRIBUTION: - value.ValueDistribution, - metric_descriptor.MetricDescriptorType.SUMMARY: - value.ValueSummary, -} class Metric(object): @@ -71,7 +52,8 @@ def descriptor(self): def _check_type(self): """Check that point value types match the descriptor type.""" - check_type = DESCRIPTOR_VALUE.get(self.descriptor.type) + check_type = metric_descriptor.MetricDescriptorType.to_type_class( + self.descriptor.type) if check_type is None: raise ValueError("Unknown metric descriptor type") for ts in self.time_series: diff --git a/opencensus/metrics/export/metric_descriptor.py b/opencensus/metrics/export/metric_descriptor.py index 8e2e731a9..8ee63dad6 100644 --- a/opencensus/metrics/export/metric_descriptor.py +++ b/opencensus/metrics/export/metric_descriptor.py @@ -14,8 +14,7 @@ import six -from opencensus.metrics.export.value import ValueDistribution -from opencensus.metrics.export.value import ValueSummary +from opencensus.metrics.export import value class _MetricDescriptorTypeMeta(type): @@ -84,13 +83,13 @@ class MetricDescriptorType(object): @classmethod def to_type_class(cls, metric_descriptor_type): type_map = { - cls.GAUGE_INT64: int, - cls.GAUGE_DOUBLE: float, - cls.GAUGE_DISTRIBUTION: ValueDistribution, - cls.CUMULATIVE_INT64: int, - cls.CUMULATIVE_DOUBLE: float, - cls.CUMULATIVE_DISTRIBUTION: ValueDistribution, - cls.SUMMARY: ValueSummary + cls.GAUGE_INT64: value.ValueLong, + cls.GAUGE_DOUBLE: value.ValueDouble, + cls.GAUGE_DISTRIBUTION: value.ValueDistribution, + cls.CUMULATIVE_INT64: value.ValueLong, + cls.CUMULATIVE_DOUBLE: value.ValueDouble, + cls.CUMULATIVE_DISTRIBUTION: value.ValueDistribution, + cls.SUMMARY: value.ValueSummary } try: return type_map[metric_descriptor_type] diff --git a/opencensus/metrics/export/time_series.py b/opencensus/metrics/export/time_series.py index b4dc6fbee..6d1718d47 100644 --- a/opencensus/metrics/export/time_series.py +++ b/opencensus/metrics/export/time_series.py @@ -12,8 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -from opencensus.metrics.export import metric_descriptor - class TimeSeries(object): """Time series data for a given metric and time interval. @@ -60,15 +58,19 @@ def label_values(self): def points(self): return self._points - def check_points_type(self, type_): - """Check that each point's value is an instance `type_`. + def check_points_type(self, type_class): + """Check that each point's value is an instance of `type_class`. + + `type_class` should typically be a Value type, i.e. one that extends + :class: `opencensus.metrics.export.value.Value`. + + :type type_class: type + :param type_class: Type to check against. - :type type_: type - :param type_: Type to check against. + :rtype: bool + :return: Whether all points are instances of `type_class`. """ - type_class = ( - metric_descriptor.MetricDescriptorType.to_type_class(type_)) for point in self.points: - if not isinstance(point.value.value, type_class): + if not isinstance(point.value, type_class): return False return True diff --git a/tests/unit/metrics/export/test_metric_descriptor.py b/tests/unit/metrics/export/test_metric_descriptor.py index 2e9b90950..30eef9e2b 100644 --- a/tests/unit/metrics/export/test_metric_descriptor.py +++ b/tests/unit/metrics/export/test_metric_descriptor.py @@ -16,48 +16,53 @@ import unittest -from opencensus.metrics.export.metric_descriptor import MetricDescriptor -from opencensus.metrics.export.metric_descriptor import MetricDescriptorType -from opencensus.metrics.label_key import LabelKey +from opencensus.metrics import label_key +from opencensus.metrics.export import metric_descriptor +from opencensus.metrics.export import value NAME = 'metric' DESCRIPTION = 'Metric description' UNIT = '0.738.[ft_i].[lbf_av]/s' -LABEL_KEY1 = LabelKey('key1', 'key description one') -LABEL_KEY2 = LabelKey('值', '测试用键') +LABEL_KEY1 = label_key.LabelKey('key1', 'key description one') +LABEL_KEY2 = label_key.LabelKey('值', '测试用键') LABEL_KEYS = (LABEL_KEY1, LABEL_KEY2) class TestMetricDescriptor(unittest.TestCase): def test_init(self): - metric_descriptor = MetricDescriptor(NAME, DESCRIPTION, UNIT, - MetricDescriptorType.GAUGE_DOUBLE, - (LABEL_KEY1, LABEL_KEY2)) + md = metric_descriptor.MetricDescriptor( + NAME, DESCRIPTION, UNIT, + metric_descriptor.MetricDescriptorType.GAUGE_DOUBLE, + (LABEL_KEY1, LABEL_KEY2)) - self.assertEqual(metric_descriptor.name, NAME) - self.assertEqual(metric_descriptor.description, DESCRIPTION) - self.assertEqual(metric_descriptor.unit, UNIT) - self.assertEqual(metric_descriptor.type, - MetricDescriptorType.GAUGE_DOUBLE) - self.assertEqual(metric_descriptor.label_keys, LABEL_KEYS) + self.assertEqual(md.name, NAME) + self.assertEqual(md.description, DESCRIPTION) + self.assertEqual(md.unit, UNIT) + self.assertEqual(md.type, + metric_descriptor.MetricDescriptorType.GAUGE_DOUBLE) + self.assertEqual(md.label_keys, LABEL_KEYS) def test_bogus_type(self): with self.assertRaises(ValueError): - MetricDescriptor(NAME, DESCRIPTION, UNIT, 0, (LABEL_KEY1, )) + metric_descriptor.MetricDescriptor(NAME, DESCRIPTION, UNIT, 0, + (LABEL_KEY1, )) def test_null_label_keys(self): with self.assertRaises(ValueError): - MetricDescriptor(NAME, DESCRIPTION, UNIT, - MetricDescriptorType.GAUGE_DOUBLE, None) + metric_descriptor.MetricDescriptor( + NAME, DESCRIPTION, UNIT, + metric_descriptor.MetricDescriptorType.GAUGE_DOUBLE, None) def test_null_label_key_values(self): with self.assertRaises(ValueError): - MetricDescriptor(NAME, DESCRIPTION, UNIT, - MetricDescriptorType.GAUGE_DOUBLE, (None, )) + metric_descriptor.MetricDescriptor( + NAME, DESCRIPTION, UNIT, + metric_descriptor.MetricDescriptorType.GAUGE_DOUBLE, (None, )) def test_to_type_class(self): self.assertEqual( - MetricDescriptorType.to_type_class( - MetricDescriptorType.GAUGE_INT64), int) + metric_descriptor.MetricDescriptorType.to_type_class( + metric_descriptor.MetricDescriptorType.GAUGE_INT64), + value.ValueLong) with self.assertRaises(ValueError): - MetricDescriptorType.to_type_class(10) + metric_descriptor.MetricDescriptorType.to_type_class(10) diff --git a/tests/unit/metrics/export/test_time_series.py b/tests/unit/metrics/export/test_time_series.py index 76b475d35..0c54debad 100644 --- a/tests/unit/metrics/export/test_time_series.py +++ b/tests/unit/metrics/export/test_time_series.py @@ -17,7 +17,6 @@ import unittest from opencensus.metrics import label_value -from opencensus.metrics.export import metric_descriptor from opencensus.metrics.export import point from opencensus.metrics.export import time_series from opencensus.metrics.export import value @@ -59,18 +58,12 @@ def test_init_invalid(self): def test_check_points_type(self): ts = time_series.TimeSeries(LABEL_VALUES, POINTS, START_TIMESTAMP) - self.assertTrue( - ts.check_points_type( - metric_descriptor.MetricDescriptorType.GAUGE_INT64)) + self.assertTrue(ts.check_points_type(value.ValueLong)) bad_points = POINTS + (point.Point( value.Value.double_value(6.0), "2018-10-10T04:33:44.012345Z"), ) bad_time_series = time_series.TimeSeries(LABEL_VALUES, bad_points, START_TIMESTAMP) - self.assertFalse( - bad_time_series.check_points_type( - metric_descriptor.MetricDescriptorType.GAUGE_INT64)) - self.assertFalse( - bad_time_series.check_points_type( - metric_descriptor.MetricDescriptorType.GAUGE_DOUBLE)) + self.assertFalse(bad_time_series.check_points_type(value.ValueLong)) + self.assertFalse(bad_time_series.check_points_type(value.ValueLong)) From 77d4413f9bfe0991b11b8f13a8b51030dc2df0f5 Mon Sep 17 00:00:00 2001 From: Chris Kleinknecht Date: Tue, 30 Oct 2018 17:30:09 -0700 Subject: [PATCH 03/17] Fix view data test --- tests/unit/stats/test_view_data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/stats/test_view_data.py b/tests/unit/stats/test_view_data.py index b472f4e77..31b046772 100644 --- a/tests/unit/stats/test_view_data.py +++ b/tests/unit/stats/test_view_data.py @@ -150,7 +150,7 @@ def test_record_with_attachment(self): self.assertTrue(tuple_vals in view_data.tag_value_aggregation_data_map) self.assertIsNotNone(view_data.tag_value_aggregation_data_map[tuple_vals]) self.assertEqual(attachments, view_data. - tag_value_aggregation_data_map[tuple_vals].exemplars[3].attachments) + tag_value_aggregation_data_map[tuple_vals].exemplars[1].attachments) def test_record_with_attachment_no_histogram(self): boundaries = None From 1530c5135fa9bf1305a35ce797bb721b0120cdaf Mon Sep 17 00:00:00 2001 From: Chris Kleinknecht Date: Tue, 30 Oct 2018 17:30:42 -0700 Subject: [PATCH 04/17] Fix old view test lint errors --- tests/unit/stats/test_view_data.py | 159 +++++++++++++++++------------ 1 file changed, 95 insertions(+), 64 deletions(-) diff --git a/tests/unit/stats/test_view_data.py b/tests/unit/stats/test_view_data.py index 31b046772..c442d1e8d 100644 --- a/tests/unit/stats/test_view_data.py +++ b/tests/unit/stats/test_view_data.py @@ -22,12 +22,12 @@ class TestViewData(unittest.TestCase): - def test_constructor(self): view = mock.Mock() start_time = datetime.utcnow() end_time = datetime.utcnow() - view_data = view_data_module.ViewData(view=view, start_time=start_time, end_time=end_time) + view_data = view_data_module.ViewData( + view=view, start_time=start_time, end_time=end_time) self.assertEqual(view, view_data.view) self.assertEqual(start_time, view_data.start_time) @@ -38,9 +38,8 @@ def test_start(self): view = mock.Mock() start_time = mock.Mock() end_time = datetime.utcnow() - view_data = view_data_module.ViewData(view=view, - start_time=start_time, - end_time=end_time) + view_data = view_data_module.ViewData( + view=view, start_time=start_time, end_time=end_time) view_data.start() self.assertIsNotNone(view_data.start_time) @@ -49,9 +48,8 @@ def test_end(self): view = mock.Mock() start_time = datetime.utcnow() end_time = mock.Mock() - view_data = view_data_module.ViewData(view=view, - start_time=start_time, - end_time=end_time) + view_data = view_data_module.ViewData( + view=view, start_time=start_time, end_time=end_time) view_data.end() self.assertIsNotNone(view_data.end_time) @@ -60,9 +58,8 @@ def test_get_tag_values(self): view = mock.Mock() start_time = datetime.utcnow() end_time = datetime.utcnow() - view_data = view_data_module.ViewData(view=view, - start_time=start_time, - end_time=end_time) + view_data = view_data_module.ViewData( + view=view, start_time=start_time, end_time=end_time) tags = {'testTag1': 'testVal1'} columns = ['testTag1'] @@ -80,9 +77,8 @@ def test_record(self): view.aggregation = mock.Mock() start_time = datetime.utcnow() end_time = datetime.utcnow() - view_data = view_data_module.ViewData(view=view, - start_time=start_time, - end_time=end_time) + view_data = view_data_module.ViewData( + view=view, start_time=start_time, end_time=end_time) context = mock.Mock() context.map = {'key1': 'val1', 'key2': 'val2'} @@ -98,49 +94,63 @@ def test_record(self): self.assertIsNotNone(view_data.tag_value_aggregation_data_map) self.assertTrue(tuple_vals in view_data.tag_value_aggregation_data_map) - self.assertIsNotNone(view_data.tag_value_aggregation_data_map[tuple_vals]) - self.assertIsNotNone(view_data.tag_value_aggregation_data_map.get( - tuple_vals).add(value)) + self.assertIsNotNone( + view_data.tag_value_aggregation_data_map[tuple_vals]) + self.assertIsNotNone( + view_data.tag_value_aggregation_data_map.get(tuple_vals).add( + value)) view_data.record(context=context, value=value, timestamp=time) tag_values.append('val2') tuple_vals_2 = tuple(['val2']) - self.assertFalse(tuple_vals_2 in view_data.tag_value_aggregation_data_map) - view_data.tag_value_aggregation_data_map[tuple_vals_2] = view.aggregation - self.assertEqual(view_data.tag_value_aggregation_data_map.get(tuple_vals_2), - view_data.view.aggregation) - self.assertIsNotNone(view_data.tag_value_aggregation_data_map.get( - tuple_vals_2).add(value)) + self.assertFalse( + tuple_vals_2 in view_data.tag_value_aggregation_data_map) + view_data.tag_value_aggregation_data_map[ + tuple_vals_2] = view.aggregation + self.assertEqual( + view_data.tag_value_aggregation_data_map.get(tuple_vals_2), + view_data.view.aggregation) + self.assertIsNotNone( + view_data.tag_value_aggregation_data_map.get(tuple_vals_2).add( + value)) def test_record_with_attachment(self): boundaries = [1, 2, 3] distribution = {1: "test"} - distribution_aggregation = aggregation_module.DistributionAggregation(boundaries=boundaries, - distribution=distribution) + distribution_aggregation = aggregation_module.DistributionAggregation( + boundaries=boundaries, distribution=distribution) name = "testName" description = "testMeasure" unit = "testUnit" - measure = measure_module.MeasureInt(name=name, description=description, unit=unit) + measure = measure_module.MeasureInt( + name=name, description=description, unit=unit) description = "testMeasure" columns = ["key1", "key2"] - view = view_module.View(name=name, description=description, columns=columns, measure=measure, - aggregation=distribution_aggregation) + view = view_module.View( + name=name, + description=description, + columns=columns, + measure=measure, + aggregation=distribution_aggregation) start_time = datetime.utcnow() attachments = {"One": "one", "Two": "two"} end_time = datetime.utcnow() - view_data = view_data_module.ViewData(view=view, - start_time=start_time, - end_time=end_time) + view_data = view_data_module.ViewData( + view=view, start_time=start_time, end_time=end_time) context = mock.Mock context.map = {'key1': 'val1', 'key2': 'val2'} time = datetime.utcnow().isoformat() + 'Z' value = 1 - view_data.record(context=context, value=value, timestamp=time, attachments=attachments) + view_data.record( + context=context, + value=value, + timestamp=time, + attachments=attachments) tag_values = view_data.get_tag_values( tags=context.map, columns=view.columns) tuple_vals = tuple(tag_values) @@ -148,38 +158,48 @@ def test_record_with_attachment(self): self.assertEqual(['val1', 'val2'], tag_values) self.assertIsNotNone(view_data.tag_value_aggregation_data_map) self.assertTrue(tuple_vals in view_data.tag_value_aggregation_data_map) - self.assertIsNotNone(view_data.tag_value_aggregation_data_map[tuple_vals]) - self.assertEqual(attachments, view_data. - tag_value_aggregation_data_map[tuple_vals].exemplars[1].attachments) + self.assertIsNotNone( + view_data.tag_value_aggregation_data_map[tuple_vals]) + self.assertEqual( + attachments, view_data.tag_value_aggregation_data_map[tuple_vals]. + exemplars[1].attachments) def test_record_with_attachment_no_histogram(self): boundaries = None distribution = {1: "test"} - distribution_aggregation = aggregation_module.DistributionAggregation(boundaries=boundaries, - distribution=distribution) + distribution_aggregation = aggregation_module.DistributionAggregation( + boundaries=boundaries, distribution=distribution) name = "testName" description = "testMeasure" unit = "testUnit" - measure = measure_module.MeasureInt(name=name, description=description, unit=unit) + measure = measure_module.MeasureInt( + name=name, description=description, unit=unit) description = "testMeasure" columns = ["key1", "key2"] - view = view_module.View(name=name, description=description, columns=columns, measure=measure, - aggregation=distribution_aggregation) + view = view_module.View( + name=name, + description=description, + columns=columns, + measure=measure, + aggregation=distribution_aggregation) start_time = datetime.utcnow() attachments = {"One": "one", "Two": "two"} end_time = datetime.utcnow() - view_data = view_data_module.ViewData(view=view, - start_time=start_time, - end_time=end_time) + view_data = view_data_module.ViewData( + view=view, start_time=start_time, end_time=end_time) context = mock.Mock context.map = {'key1': 'val1', 'key2': 'val2'} time = datetime.utcnow().isoformat() + 'Z' value = 1 - view_data.record(context=context, value=value, timestamp=time, attachments=attachments) + view_data.record( + context=context, + value=value, + timestamp=time, + attachments=attachments) tag_values = view_data.get_tag_values( tags=context.map, columns=view.columns) tuple_vals = tuple(tag_values) @@ -187,34 +207,36 @@ def test_record_with_attachment_no_histogram(self): self.assertEqual(['val1', 'val2'], tag_values) self.assertIsNotNone(view_data.tag_value_aggregation_data_map) self.assertTrue(tuple_vals in view_data.tag_value_aggregation_data_map) - self.assertIsNotNone(view_data.tag_value_aggregation_data_map[tuple_vals]) - self.assertIsNone(view_data. - tag_value_aggregation_data_map[tuple_vals].exemplars) + self.assertIsNotNone( + view_data.tag_value_aggregation_data_map[tuple_vals]) + self.assertIsNone( + view_data.tag_value_aggregation_data_map[tuple_vals].exemplars) def test_record_with_multi_keys(self): measure = mock.Mock() sum_aggregation = aggregation_module.SumAggregation() - view = view_module.View( - "test_view", "description", ['key1', 'key2'], measure, sum_aggregation) + view = view_module.View("test_view", "description", ['key1', 'key2'], + measure, sum_aggregation) start_time = datetime.utcnow() end_time = datetime.utcnow() - view_data = view_data_module.ViewData(view=view, - start_time=start_time, - end_time=end_time) + view_data = view_data_module.ViewData( + view=view, start_time=start_time, end_time=end_time) context = mock.Mock() context.map = {'key1': 'val1', 'key2': 'val2'} time = datetime.utcnow().isoformat() + 'Z' value = 1 self.assertEqual({}, view_data.tag_value_aggregation_data_map) - view_data.record(context=context, value=value, timestamp=time, attachments=None) + view_data.record( + context=context, value=value, timestamp=time, attachments=None) tag_values = view_data.get_tag_values( tags=context.map, columns=view.columns) tuple_vals = tuple(tag_values) self.assertEqual(['val1', 'val2'], tag_values) self.assertIsNotNone(view_data.tag_value_aggregation_data_map) self.assertTrue(tuple_vals in view_data.tag_value_aggregation_data_map) - self.assertIsNotNone(view_data.tag_value_aggregation_data_map[tuple_vals]) + self.assertIsNotNone( + view_data.tag_value_aggregation_data_map[tuple_vals]) sum_data = view_data.tag_value_aggregation_data_map.get(tuple_vals) self.assertEqual(1, sum_data.sum_data) @@ -222,12 +244,17 @@ def test_record_with_multi_keys(self): context_2.map = {'key1': 'val3', 'key2': 'val2'} time_2 = datetime.utcnow().isoformat() + 'Z' value_2 = 2 - view_data.record(context=context_2, value=value_2, timestamp=time_2, attachments=None) + view_data.record( + context=context_2, + value=value_2, + timestamp=time_2, + attachments=None) tag_values_2 = view_data.get_tag_values( tags=context_2.map, columns=view.columns) tuple_vals_2 = tuple(tag_values_2) self.assertEqual(['val3', 'val2'], tag_values_2) - self.assertTrue(tuple_vals_2 in view_data.tag_value_aggregation_data_map) + self.assertTrue( + tuple_vals_2 in view_data.tag_value_aggregation_data_map) sum_data_2 = view_data.tag_value_aggregation_data_map.get(tuple_vals_2) self.assertEqual(2, sum_data_2.sum_data) @@ -235,7 +262,8 @@ def test_record_with_multi_keys(self): value_3 = 3 # Use the same context {'key1': 'val1', 'key2': 'val2'}. # Record to entry [(val1, val2), sum=1]. - view_data.record(context=context, value=value_3, timestamp=time_3, attachments=None) + view_data.record( + context=context, value=value_3, timestamp=time_3, attachments=None) self.assertEqual(4, sum_data.sum_data) # The other entry should remain unchanged. self.assertEqual(2, sum_data_2.sum_data) @@ -243,18 +271,21 @@ def test_record_with_multi_keys(self): def test_record_with_missing_key_in_context(self): measure = mock.Mock() sum_aggregation = aggregation_module.SumAggregation() - view = view_module.View( - "test_view", "description", ['key1', 'key2'], measure, sum_aggregation) + view = view_module.View("test_view", "description", ['key1', 'key2'], + measure, sum_aggregation) start_time = datetime.utcnow() end_time = datetime.utcnow() - view_data = view_data_module.ViewData(view=view, - start_time=start_time, - end_time=end_time) + view_data = view_data_module.ViewData( + view=view, start_time=start_time, end_time=end_time) context = mock.Mock() - context.map = {'key1': 'val1', 'key3': 'val3'} # key2 is not in the context. + context.map = { + 'key1': 'val1', + 'key3': 'val3' + } # key2 is not in the context. time = datetime.utcnow().isoformat() + 'Z' value = 4 - view_data.record(context=context, value=value, timestamp=time, attachments=None) + view_data.record( + context=context, value=value, timestamp=time, attachments=None) tag_values = view_data.get_tag_values( tags=context.map, columns=view.columns) tuple_vals = tuple(tag_values) From ad79b6477c8d1228796e9bd6e3080c197abe1d11 Mon Sep 17 00:00:00 2001 From: Chris Kleinknecht Date: Tue, 30 Oct 2018 17:33:39 -0700 Subject: [PATCH 05/17] Remove old prints from measure to view map test --- tests/unit/stats/test_measure_to_view_map.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/unit/stats/test_measure_to_view_map.py b/tests/unit/stats/test_measure_to_view_map.py index d96d77ab2..02304e6a2 100644 --- a/tests/unit/stats/test_measure_to_view_map.py +++ b/tests/unit/stats/test_measure_to_view_map.py @@ -98,7 +98,6 @@ def test_filter_exported_views(self): columns=columns, measure=measure, aggregation=aggregation) - print("test view 1", test_view_1) test_view_2_name = "testView2" test_view_2 = View( @@ -107,13 +106,10 @@ def test_filter_exported_views(self): columns=columns, measure=measure, aggregation=aggregation) - print("test view 2", test_view_2) all_the_views = {test_view_1, test_view_2} - print("all the views", all_the_views) measure_to_view_map = measure_to_view_map_module.MeasureToViewMap() views = measure_to_view_map.filter_exported_views( all_views=all_the_views) - print("filtered views", views) self.assertEqual(views, all_the_views) def test_register_view(self): From 0aa90ed54b82311c30aeb0bae883a7afc86946cc Mon Sep 17 00:00:00 2001 From: Chris Kleinknecht Date: Tue, 30 Oct 2018 17:35:19 -0700 Subject: [PATCH 06/17] Fix old measure to view map test lint errors --- tests/unit/stats/test_measure_to_view_map.py | 123 +++++++------------ 1 file changed, 47 insertions(+), 76 deletions(-) diff --git a/tests/unit/stats/test_measure_to_view_map.py b/tests/unit/stats/test_measure_to_view_map.py index 02304e6a2..738bf9133 100644 --- a/tests/unit/stats/test_measure_to_view_map.py +++ b/tests/unit/stats/test_measure_to_view_map.py @@ -13,15 +13,14 @@ # limitations under the License. import unittest + import mock -import logging -from datetime import datetime -from opencensus.stats.view import View -from opencensus.stats.view_data import ViewData -from opencensus.stats.measurement import Measurement + +from opencensus.stats import measure_to_view_map as measure_to_view_map_module from opencensus.stats.measure import BaseMeasure from opencensus.stats.measure import MeasureInt -from opencensus.stats import measure_to_view_map as measure_to_view_map_module +from opencensus.stats.view import View +from opencensus.stats.view_data import ViewData class TestMeasureToViewMap(unittest.TestCase): @@ -135,9 +134,8 @@ def test_register_view(self): view) self.assertEqual( measure_to_view_map._registered_measures[measure.name], measure) - self.assertIsNotNone( - measure_to_view_map._measure_to_view_data_list_map[ - view.measure.name]) + self.assertIsNotNone(measure_to_view_map. + _measure_to_view_data_list_map[view.measure.name]) # Registers a view with an existing measure. view2 = View( @@ -150,10 +148,11 @@ def test_register_view(self): view=view2, timestamp=timestamp) self.assertIsNone(test_with_registered_measures) self.assertEqual( - measure_to_view_map._registered_measures[measure.name], measure) + measure_to_view_map._registered_measures[measure.name], measure) - # Registers a view with a measure that has the same name as an existing measure, - # but with different schema. measure2 and view3 should be ignored. + # Registers a view with a measure that has the same name as an existing + # measure, but with different schema. measure2 and view3 should be + # ignored. measure2 = MeasureInt("measure", "another measure", "ms") view3 = View( name="testView3", @@ -165,7 +164,7 @@ def test_register_view(self): view=view3, timestamp=timestamp) self.assertIsNone(test_with_registered_measures) self.assertEqual( - measure_to_view_map._registered_measures[measure2.name], measure) + measure_to_view_map._registered_measures[measure2.name], measure) measure_to_view_map._registered_measures = {measure.name: None} self.assertIsNone( @@ -174,17 +173,15 @@ def test_register_view(self): # view is already registered, measure will not be registered again. self.assertIsNone( measure_to_view_map._registered_measures.get(measure.name)) - self.assertIsNotNone( - measure_to_view_map._measure_to_view_data_list_map[ - view.measure.name]) + self.assertIsNotNone(measure_to_view_map. + _measure_to_view_data_list_map[view.measure.name]) measure_to_view_map._registered_views = {name: view} test_result_1 = measure_to_view_map.register_view( view=view, timestamp=timestamp) self.assertIsNone(test_result_1) - self.assertIsNotNone( - measure_to_view_map._measure_to_view_data_list_map[ - view.measure.name]) + self.assertIsNotNone(measure_to_view_map. + _measure_to_view_data_list_map[view.measure.name]) def test_register_view_with_exporter(self): exporter = mock.Mock() @@ -210,9 +207,8 @@ def test_register_view_with_exporter(self): view) self.assertEqual( measure_to_view_map._registered_measures[measure.name], measure) - self.assertIsNotNone( - measure_to_view_map._measure_to_view_data_list_map[ - view.measure.name]) + self.assertIsNotNone(measure_to_view_map. + _measure_to_view_data_list_map[view.measure.name]) def test_record(self): measure_name = "test_measure" @@ -225,12 +221,11 @@ def test_record(self): view_columns = ["testTag1", "testColumn2"] view_measure = measure view_aggregation = mock.Mock() - view = View( - name=view_name, - description=view_description, - columns=view_columns, - measure=view_measure, - aggregation=view_aggregation) + View(name=view_name, + description=view_description, + columns=view_columns, + measure=view_measure, + aggregation=view_aggregation) measure_value = 5 tags = {"testTag1": "testTag1Value"} @@ -240,7 +235,10 @@ def test_record(self): measure_to_view_map = measure_to_view_map_module.MeasureToViewMap() measure_to_view_map._registered_measures = {} record = measure_to_view_map.record( - tags=tags, measurement_map=measurement_map, timestamp=timestamp, attachments=None) + tags=tags, + measurement_map=measurement_map, + timestamp=timestamp, + attachments=None) self.assertNotEqual( measure, measure_to_view_map._registered_measures.get(measure.name)) @@ -249,7 +247,10 @@ def test_record(self): measure_to_view_map._registered_measures = {measure.name: measure} measure_to_view_map._measure_to_view_data_list_map = {} record = measure_to_view_map.record( - tags=tags, measurement_map=measurement_map, timestamp=timestamp, attachments=None) + tags=tags, + measurement_map=measurement_map, + timestamp=timestamp, + attachments=None) self.assertEqual( measure, measure_to_view_map._registered_measures.get(measure.name)) @@ -259,7 +260,10 @@ def test_record(self): measure.name: [mock.Mock()] } measure_to_view_map.record( - tags=tags, measurement_map=measurement_map, timestamp=timestamp, attachments=None) + tags=tags, + measurement_map=measurement_map, + timestamp=timestamp, + attachments=None) self.assertEqual( measure, measure_to_view_map._registered_measures.get(measure.name)) @@ -270,9 +274,12 @@ def test_record(self): "testing": [mock.Mock()] } measure_to_view_map.record( - tags=tags, measurement_map=measurement_map, timestamp=timestamp, attachments=None) - self.assertTrue(measure.name not in - measure_to_view_map._measure_to_view_data_list_map) + tags=tags, + measurement_map=measurement_map, + timestamp=timestamp, + attachments=None) + self.assertTrue(measure.name not in measure_to_view_map. + _measure_to_view_data_list_map) measure_to_view_map_mock = mock.Mock() measure_to_view_map = measure_to_view_map_mock @@ -290,41 +297,10 @@ def test_record(self): measurement_map = {} measure_to_view_map = measure_to_view_map_module.MeasureToViewMap() record = measure_to_view_map.record( - tags=tags, measurement_map=measurement_map, timestamp=timestamp, attachments=None) - self.assertIsNone(record) - - def test_record_with_exporter(self): - exporter = mock.Mock() - measure_name = "test_measure" - measure_description = "test_description" - measure = BaseMeasure( - name=measure_name, description=measure_description) - - view_name = "test_view" - view_description = "test_description" - view_columns = ["testTag1", "testColumn2"] - view_measure = measure - view_aggregation = mock.Mock() - view = View( - name=view_name, - description=view_description, - columns=view_columns, - measure=view_measure, - aggregation=view_aggregation) - - measure_value = 5 - tags = {"testTag1": "testTag1Value"} - measurement_map = {measure: measure_value} - timestamp = mock.Mock() - - measure_to_view_map = measure_to_view_map_module.MeasureToViewMap() - measure_to_view_map.exporters.append(exporter) - measure_to_view_map._registered_measures = {} - record = measure_to_view_map.record( - tags=tags, measurement_map=measurement_map, timestamp=timestamp) - self.assertNotEqual( - measure, - measure_to_view_map._registered_measures.get(measure.name)) + tags=tags, + measurement_map=measurement_map, + timestamp=timestamp, + attachments=None) self.assertIsNone(record) def test_record_with_exporter(self): @@ -339,7 +315,7 @@ def test_record_with_exporter(self): view_columns = ["testTag1", "testColumn2"] view_measure = measure view_aggregation = mock.Mock() - view = View( + View( name=view_name, description=view_description, columns=view_columns, @@ -374,18 +350,13 @@ def test_export(self): view_columns = ["testTag1", "testColumn2"] view_measure = measure view_aggregation = mock.Mock() - view = View( + View( name=view_name, description=view_description, columns=view_columns, measure=view_measure, aggregation=view_aggregation) - measure_value = 5 - tags = {"testTag1": "testTag1Value"} - measurement_map = {measure: measure_value} - timestamp = mock.Mock() - measure_to_view_map = measure_to_view_map_module.MeasureToViewMap() measure_to_view_map.exporters.append(exporter) measure_to_view_map._registered_measures = {} From 472392dffc2f8d7e3b6137e368af473483da6306 Mon Sep 17 00:00:00 2001 From: Chris Kleinknecht Date: Tue, 30 Oct 2018 17:38:59 -0700 Subject: [PATCH 07/17] Fix, lint aggregation data tests --- tests/unit/stats/test_aggregation_data.py | 181 +++++++++++----------- 1 file changed, 90 insertions(+), 91 deletions(-) diff --git a/tests/unit/stats/test_aggregation_data.py b/tests/unit/stats/test_aggregation_data.py index 2149357d5..0b1bb8415 100644 --- a/tests/unit/stats/test_aggregation_data.py +++ b/tests/unit/stats/test_aggregation_data.py @@ -12,14 +12,15 @@ # See the License for the specific language governing permissions and # limitations under the License. -import unittest import time +import unittest + import mock + from opencensus.stats import aggregation_data as aggregation_data_module class TestBaseAggregationData(unittest.TestCase): - def test_constructor(self): aggregation_data = 0 base_aggregation_data = aggregation_data_module.BaseAggregationData( @@ -29,7 +30,6 @@ def test_constructor(self): class TestSumAggregationData(unittest.TestCase): - def test_constructor(self): sum_data = 1 sum_aggregation_data = aggregation_data_module.SumAggregationDataFloat( @@ -48,7 +48,6 @@ def test_add_sample(self): class TestCountAggregationData(unittest.TestCase): - def test_constructor(self): count_data = 0 count_aggregation_data = aggregation_data_module.CountAggregationData( @@ -64,44 +63,42 @@ def test_add_sample(self): self.assertEqual(1, count_aggregation_data.count_data) + class TestLastValueAggregationData(unittest.TestCase): - def test_constructor(self): value_data = 0 - last_value_aggregation_data = aggregation_data_module.LastValueAggregationData( - value=value_data) + last_value_aggregation_data =\ + aggregation_data_module.LastValueAggregationData(value=value_data) self.assertEqual(0, last_value_aggregation_data.value) def test_overwrite_sample(self): first_data = 0 - last_value_aggregation_data = aggregation_data_module.LastValueAggregationData( - value=first_data) + last_value_aggregation_data =\ + aggregation_data_module.LastValueAggregationData(value=first_data) self.assertEqual(0, last_value_aggregation_data.value) last_value_aggregation_data.add_sample(1, None, None) self.assertEqual(1, last_value_aggregation_data.value) class TestDistributionAggregationData(unittest.TestCase): - def test_constructor(self): mean_data = 1 count_data = 0 _min = 0 _max = 1 sum_of_sqd_deviations = mock.Mock() - counts_per_bucket = [1, 1, 1] - bounds = [0, 1/2, 1] + counts_per_bucket = [1, 1, 1, 1] + bounds = [0, 1.0 / 2.0, 1] dist_agg_data = aggregation_data_module.DistributionAggregationData( - mean_data=mean_data, - count_data=count_data, - min_= _min, - max_ = _max, - sum_of_sqd_deviations=sum_of_sqd_deviations, - counts_per_bucket=counts_per_bucket, - bounds=bounds - ) + mean_data=mean_data, + count_data=count_data, + min_=_min, + max_=_max, + sum_of_sqd_deviations=sum_of_sqd_deviations, + counts_per_bucket=counts_per_bucket, + bounds=bounds) self.assertEqual(1, dist_agg_data.mean_data) self.assertEqual(0, dist_agg_data.count_data) @@ -109,8 +106,8 @@ def test_constructor(self): self.assertEqual(1, dist_agg_data.max) self.assertEqual(sum_of_sqd_deviations, dist_agg_data.sum_of_sqd_deviations) - self.assertEqual([1, 1, 1], dist_agg_data.counts_per_bucket) - self.assertEqual([0, 1/2, 1], dist_agg_data.bounds) + self.assertEqual([1, 1, 1, 1], dist_agg_data.counts_per_bucket) + self.assertEqual([0, 1.0 / 2.0, 1], dist_agg_data.bounds) self.assertIsNotNone(dist_agg_data.sum) self.assertEqual(0, dist_agg_data.variance) @@ -118,27 +115,28 @@ def test_constructor(self): def test_constructor_with_exemplar(self): timestamp = time.time() attachments = {"One": "one", "Two": "two"} - exemplar_1 = aggregation_data_module.Exemplar(4, timestamp, attachments) - exemplar_2 = aggregation_data_module.Exemplar(5, timestamp, attachments) + exemplar_1 = aggregation_data_module.Exemplar(4, timestamp, + attachments) + exemplar_2 = aggregation_data_module.Exemplar(5, timestamp, + attachments) mean_data = 1 count_data = 0 _min = 0 _max = 1 sum_of_sqd_deviations = mock.Mock() - counts_per_bucket = [1, 1, 1] - bounds = [0, 1/2, 1] + counts_per_bucket = [1, 1, 1, 1] + bounds = [0, 1.0 / 2.0, 1] exemplars = [exemplar_1, exemplar_2] dist_agg_data = aggregation_data_module.DistributionAggregationData( - mean_data=mean_data, - count_data=count_data, - min_= _min, - max_ = _max, - sum_of_sqd_deviations=sum_of_sqd_deviations, - exemplars=exemplars, - counts_per_bucket=counts_per_bucket, - bounds=bounds - ) + mean_data=mean_data, + count_data=count_data, + min_=_min, + max_=_max, + sum_of_sqd_deviations=sum_of_sqd_deviations, + exemplars=exemplars, + counts_per_bucket=counts_per_bucket, + bounds=bounds) self.assertEqual(1, dist_agg_data.mean_data) self.assertEqual(0, dist_agg_data.count_data) @@ -146,9 +144,9 @@ def test_constructor_with_exemplar(self): self.assertEqual(1, dist_agg_data.max) self.assertEqual(sum_of_sqd_deviations, dist_agg_data.sum_of_sqd_deviations) - self.assertEqual([1, 1, 1], dist_agg_data.counts_per_bucket) + self.assertEqual([1, 1, 1, 1], dist_agg_data.counts_per_bucket) self.assertEqual([exemplar_1, exemplar_2], dist_agg_data.exemplars[3]) - self.assertEqual([0, 1/2, 1], dist_agg_data.bounds) + self.assertEqual([0, 1.0 / 2.0, 1], dist_agg_data.bounds) self.assertIsNotNone(dist_agg_data.sum) self.assertEqual(0, dist_agg_data.variance) @@ -165,35 +163,44 @@ def test_exemplar(self): def test_exemplar_null_attachments(self): timestamp = time.time() - with self.assertRaisesRegexp(TypeError, 'attachments should not be empty'): + with self.assertRaisesRegexp(TypeError, + 'attachments should not be empty'): aggregation_data_module.Exemplar(6, timestamp, None) def test_exemplar_null_attachment_key(self): timestamp = time.time() attachment = {None: "one", "Two": "two"} - with self.assertRaisesRegexp(TypeError, 'attachment key should not be empty and should be a string'): + with self.assertRaisesRegexp( + TypeError, + 'attachment key should not be empty and should be a string'): aggregation_data_module.Exemplar(6, timestamp, attachment) def test_exemplar_null_attachment_value(self): timestamp = time.time() attachment = {"One": "one", "Two": None} - with self.assertRaisesRegexp(TypeError, 'attachment value should not be empty and should be a string'): + with self.assertRaisesRegexp( + TypeError, + 'attachment value should not be empty and should be a string'): aggregation_data_module.Exemplar(6, timestamp, attachment) def test_exemplar_int_attachment_key(self): timestamp = time.time() attachment = {1: "one", "Two": "two"} - with self.assertRaisesRegexp(TypeError, 'attachment key should not be empty and should be a string'): + with self.assertRaisesRegexp( + TypeError, + 'attachment key should not be empty and should be a string'): aggregation_data_module.Exemplar(6, timestamp, attachment) def test_exemplar_int_attachment_value(self): timestamp = time.time() attachment = {"One": "one", "Two": 2} - with self.assertRaisesRegexp(TypeError, 'attachment value should not be empty and should be a string'): + with self.assertRaisesRegexp( + TypeError, + 'attachment value should not be empty and should be a string'): aggregation_data_module.Exemplar(6, timestamp, attachment) def test_variance(self): @@ -202,17 +209,16 @@ def test_variance(self): _min = mock.Mock() _max = mock.Mock() sum_of_sqd_deviations = mock.Mock() - counts_per_bucket = [1, 1, 1] - bounds = [0, 1/2, 1] + counts_per_bucket = [1, 1, 1, 1] + bounds = [0, 1.0 / 2.0, 1] dist_agg_data = aggregation_data_module.DistributionAggregationData( mean_data=mean_data, count_data=count_data, - min_= _min, - max_ = _max, + min_=_min, + max_=_max, sum_of_sqd_deviations=sum_of_sqd_deviations, counts_per_bucket=counts_per_bucket, - bounds=bounds - ) + bounds=bounds) self.assertEqual(0, dist_agg_data.variance) count_data = 2 @@ -220,12 +226,11 @@ def test_variance(self): dist_agg_data = aggregation_data_module.DistributionAggregationData( mean_data=mean_data, count_data=count_data, - min_= _min, - max_ = _max, + min_=_min, + max_=_max, sum_of_sqd_deviations=sum_of_sqd_deviations, counts_per_bucket=counts_per_bucket, - bounds=bounds - ) + bounds=bounds) self.assertEqual(2.0, dist_agg_data.variance) def test_add_sample(self): @@ -234,7 +239,7 @@ def test_add_sample(self): _min = 0 _max = 1 sum_of_sqd_deviations = 2 - counts_per_bucket = [1, 1, 1, 1] + counts_per_bucket = [1, 1, 1, 1, 1] bounds = [0, 0.5, 1, 1.5] value = 3 @@ -242,12 +247,11 @@ def test_add_sample(self): dist_agg_data = aggregation_data_module.DistributionAggregationData( mean_data=mean_data, count_data=count_data, - min_= _min, - max_ = _max, + min_=_min, + max_=_max, sum_of_sqd_deviations=sum_of_sqd_deviations, counts_per_bucket=counts_per_bucket, - bounds=bounds - ) + bounds=bounds) dist_agg_data.add_sample(value, None, None) self.assertEqual(0, dist_agg_data.min) @@ -259,12 +263,11 @@ def test_add_sample(self): dist_agg_data = aggregation_data_module.DistributionAggregationData( mean_data=mean_data, count_data=count_data, - min_= _min, - max_ = _max, + min_=_min, + max_=_max, sum_of_sqd_deviations=sum_of_sqd_deviations, counts_per_bucket=counts_per_bucket, - bounds=bounds - ) + bounds=bounds) dist_agg_data.add_sample(value, None, None) self.assertEqual(2, dist_agg_data.count_data) @@ -282,26 +285,26 @@ def test_add_sample_attachment(self): _min = 0 _max = 1 sum_of_sqd_deviations = 2 - counts_per_bucket = [1, 1, 1, 1] + counts_per_bucket = [1, 1, 1, 1, 1] bounds = [0, 0.5, 1, 1.5] value = 3 timestamp = time.time() attachments = {"One": "one", "Two": "two"} - exemplar_1 = aggregation_data_module.Exemplar(4, timestamp, attachments) + exemplar_1 = aggregation_data_module.Exemplar(4, timestamp, + attachments) dist_agg_data = aggregation_data_module.DistributionAggregationData( mean_data=mean_data, count_data=count_data, - min_= _min, - max_ = _max, + min_=_min, + max_=_max, sum_of_sqd_deviations=sum_of_sqd_deviations, counts_per_bucket=counts_per_bucket, bounds=bounds, - exemplars=exemplar_1 - ) + exemplars=exemplar_1) - self.assertEqual({4:exemplar_1}, dist_agg_data.exemplars) + self.assertEqual({4: exemplar_1}, dist_agg_data.exemplars) dist_agg_data.add_sample(value, timestamp, attachments) self.assertEqual(0, dist_agg_data.min) @@ -314,19 +317,18 @@ def test_add_sample_attachment(self): dist_agg_data = aggregation_data_module.DistributionAggregationData( mean_data=mean_data, count_data=count_data, - min_= _min, - max_ = _max, + min_=_min, + max_=_max, sum_of_sqd_deviations=sum_of_sqd_deviations, - counts_per_bucket=[2, 1, 2, 1, 1], - bounds=[1, 2, 3, 4, 5] - ) + counts_per_bucket=[2, 1, 2, 1, 1, 1], + bounds=[1, 2, 3, 4, 5]) dist_agg_data.add_sample(value, timestamp, attachments) self.assertEqual(5, dist_agg_data.count_data) self.assertEqual(1.4, dist_agg_data.mean_data) self.assertEqual(5.2, dist_agg_data.sum_of_sqd_deviations) self.assertIsNot(0, dist_agg_data.count_data) - self.assertEqual(3, dist_agg_data.exemplars[5].value) + self.assertEqual(3, dist_agg_data.exemplars[3].value) def test_increment_bucket_count(self): mean_data = mock.Mock() @@ -342,43 +344,40 @@ def test_increment_bucket_count(self): dist_agg_data = aggregation_data_module.DistributionAggregationData( mean_data=mean_data, count_data=count_data, - min_= _min, - max_ = _max, + min_=_min, + max_=_max, sum_of_sqd_deviations=sum_of_sqd_deviations, counts_per_bucket=counts_per_bucket, - bounds=bounds - ) + bounds=bounds) dist_agg_data.increment_bucket_count(value=value) self.assertEqual([1], dist_agg_data.counts_per_bucket) - counts_per_bucket = [1, 1] - bounds = [1/4, 3/2] + counts_per_bucket = [1, 1, 1] + bounds = [1.0 / 4.0, 3.0 / 2.0] dist_agg_data = aggregation_data_module.DistributionAggregationData( mean_data=mean_data, count_data=count_data, - min_= _min, - max_ = _max, + min_=_min, + max_=_max, sum_of_sqd_deviations=sum_of_sqd_deviations, counts_per_bucket=counts_per_bucket, - bounds=bounds - ) + bounds=bounds) dist_agg_data.increment_bucket_count(value=value) - self.assertEqual([1, 2], dist_agg_data.counts_per_bucket) + self.assertEqual([1, 2, 1], dist_agg_data.counts_per_bucket) - bounds = [1/4, 1/2] + bounds = [1.0 / 4.0, 1.0 / 2.0] dist_agg_data = aggregation_data_module.DistributionAggregationData( mean_data=mean_data, count_data=count_data, - min_= _min, - max_ = _max, + min_=_min, + max_=_max, sum_of_sqd_deviations=sum_of_sqd_deviations, counts_per_bucket=counts_per_bucket, - bounds=bounds - ) + bounds=bounds) dist_agg_data.increment_bucket_count(value=value) - self.assertEqual([1, 3], dist_agg_data.counts_per_bucket) + self.assertEqual([1, 2, 2], dist_agg_data.counts_per_bucket) From b05274860d124c3958c3a5584ed76104dd1945cd Mon Sep 17 00:00:00 2001 From: Chris Kleinknecht Date: Tue, 30 Oct 2018 17:44:45 -0700 Subject: [PATCH 08/17] Fix increment_bucket_count --- opencensus/stats/aggregation_data.py | 51 ++++++++++++++-------------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/opencensus/stats/aggregation_data.py b/opencensus/stats/aggregation_data.py index 74c61ab4d..15ab1b95d 100644 --- a/opencensus/stats/aggregation_data.py +++ b/opencensus/stats/aggregation_data.py @@ -11,6 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. + from opencensus.stats import bucket_boundaries @@ -21,6 +22,7 @@ class BaseAggregationData(object): :param aggregation_data: represents the aggregated value from a collection """ + def __init__(self, aggregation_data): self._aggregation_data = aggregation_data @@ -37,6 +39,7 @@ class SumAggregationDataFloat(BaseAggregationData): :param sum_data: represents the aggregated sum """ + def __init__(self, sum_data): super(SumAggregationDataFloat, self).__init__(sum_data) self._sum_data = sum_data @@ -60,6 +63,7 @@ class CountAggregationData(BaseAggregationData): :param count_data: represents the aggregated count """ + def __init__(self, count_data): super(CountAggregationData, self).__init__(count_data) self._count_data = count_data @@ -104,6 +108,7 @@ class DistributionAggregationData(BaseAggregationData): :param bounds: the histogram distribution of the values """ + def __init__(self, mean_data, count_data, @@ -123,13 +128,14 @@ def __init__(self, bounds = [] if counts_per_bucket is None: - counts_per_bucket = [] - bucket_size = len(bounds) + 1 - for i in range(bucket_size): - counts_per_bucket.append(0) + counts_per_bucket = [0 for ii in range(len(bounds) + 1)] + elif len(counts_per_bucket) != len(bounds) + 1: + raise ValueError("counts_per_bucket length does not match bounds " + "length") + self._counts_per_bucket = counts_per_bucket self._bounds = bucket_boundaries.BucketBoundaries( - boundaries=bounds).boundaries + boundaries=bounds).boundaries bucket = 0 for _ in self.bounds: bucket = bucket + 1 @@ -207,30 +213,25 @@ def add_sample(self, value, timestamp, attachments): old_mean = self._mean_data self._mean_data = self._mean_data + ( - (value - self._mean_data) / self._count_data) + (value - self._mean_data) / self._count_data) self._sum_of_sqd_deviations = self._sum_of_sqd_deviations + ( - (value - old_mean) * - (value - self._mean_data)) + (value - old_mean) * (value - self._mean_data)) + def increment_bucket_count(self, value): """Increment the bucket count based on a given value from the user""" - i = 0 - incremented = False - for b in self._bounds: - if value < b and not incremented: - self._counts_per_bucket[i] += 1 - incremented = True - i += 1 - - if incremented: - return i - if len(self._bounds) == 0: self._counts_per_bucket[0] += 1 - return i + return 0 - self._counts_per_bucket[(len(self._bounds))-1] += 1 - return i + for ii, bb in enumerate(self._bounds): + if value < bb: + self._counts_per_bucket[ii] += 1 + return ii + else: + last_bucket_index = len(self._bounds) + self._counts_per_bucket[last_bucket_index] += 1 + return last_bucket_index class LastValueAggregationData(BaseAggregationData): @@ -241,6 +242,7 @@ class LastValueAggregationData(BaseAggregationData): :param value: represents the current value """ + def __init__(self, value): super(LastValueAggregationData, self).__init__(value) self._value = value @@ -271,10 +273,7 @@ class Exemplar(object): :param attachments: the contextual information about the example value. """ - def __init__(self, - value, - timestamp, - attachments): + def __init__(self, value, timestamp, attachments): self._value = value self._timestamp = timestamp From 22494f43803c88ca52c1efca65555e52fefcd64a Mon Sep 17 00:00:00 2001 From: Chris Kleinknecht Date: Tue, 30 Oct 2018 18:20:49 -0700 Subject: [PATCH 09/17] Add test for new agg data precondition --- tests/unit/stats/test_aggregation_data.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/unit/stats/test_aggregation_data.py b/tests/unit/stats/test_aggregation_data.py index 0b1bb8415..30766e4c2 100644 --- a/tests/unit/stats/test_aggregation_data.py +++ b/tests/unit/stats/test_aggregation_data.py @@ -112,6 +112,28 @@ def test_constructor(self): self.assertIsNotNone(dist_agg_data.sum) self.assertEqual(0, dist_agg_data.variance) + def test_init_bad_bucket_counts(self): + # Check that len(counts_per_bucket) == len(bounds) + 1 + with self.assertRaises(ValueError): + aggregation_data_module.DistributionAggregationData( + mean_data=mock.Mock(), + count_data=mock.Mock(), + min_=mock.Mock(), + max_=mock.Mock(), + sum_of_sqd_deviations=mock.Mock(), + counts_per_bucket=[0, 0, 0], + bounds=[0, 1, 2]) + + # And check that we don't throw given the right args + aggregation_data_module.DistributionAggregationData( + mean_data=mock.Mock(), + count_data=mock.Mock(), + min_=mock.Mock(), + max_=mock.Mock(), + sum_of_sqd_deviations=mock.Mock(), + counts_per_bucket=[0, 0, 0, 0], + bounds=[0, 1, 2]) + def test_constructor_with_exemplar(self): timestamp = time.time() attachments = {"One": "one", "Two": "two"} From cf130aba5611fb18c9e014c77c8d0ad85110ed41 Mon Sep 17 00:00:00 2001 From: Chris Kleinknecht Date: Tue, 30 Oct 2018 18:34:03 -0700 Subject: [PATCH 10/17] Change DistributionAggregation defaults, add test --- opencensus/stats/aggregation.py | 2 +- tests/unit/stats/test_aggregation.py | 59 +++++++++++++++++++--------- 2 files changed, 41 insertions(+), 20 deletions(-) diff --git a/opencensus/stats/aggregation.py b/opencensus/stats/aggregation.py index bebe6ca66..f2bdc10ec 100644 --- a/opencensus/stats/aggregation.py +++ b/opencensus/stats/aggregation.py @@ -133,7 +133,7 @@ def __init__( self._boundaries = bucket_boundaries.BucketBoundaries(boundaries) self._distribution = distribution or {} self.aggregation_data = aggregation_data.DistributionAggregationData( - 0, 0, 0, 0, 0, None, boundaries) + 0, 0, float('inf'), float('-inf'), 0, None, boundaries) @property def boundaries(self): diff --git a/tests/unit/stats/test_aggregation.py b/tests/unit/stats/test_aggregation.py index cc7e6bff3..71b2d546d 100644 --- a/tests/unit/stats/test_aggregation.py +++ b/tests/unit/stats/test_aggregation.py @@ -12,17 +12,18 @@ # See the License for the specific language governing permissions and # limitations under the License. +from datetime import datetime import unittest -import mock + from opencensus.stats import aggregation as aggregation_module class TestBaseAggregation(unittest.TestCase): - def test_constructor_defaults(self): base_aggregation = aggregation_module.BaseAggregation() - self.assertEqual(aggregation_module.Type.NONE, base_aggregation.aggregation_type) + self.assertEqual(aggregation_module.Type.NONE, + base_aggregation.aggregation_type) self.assertEqual([], base_aggregation.buckets) def test_constructor_explicit(self): @@ -30,17 +31,18 @@ def test_constructor_explicit(self): buckets = ["test"] base_aggregation = aggregation_module.BaseAggregation(buckets=buckets) - self.assertEqual(aggregation_module.Type.NONE, base_aggregation.aggregation_type) + self.assertEqual(aggregation_module.Type.NONE, + base_aggregation.aggregation_type) self.assertEqual(["test"], base_aggregation.buckets) class TestSumAggregation(unittest.TestCase): - def test_constructor_defaults(self): sum_aggregation = aggregation_module.SumAggregation() self.assertEqual(0, sum_aggregation.sum.sum_data) - self.assertEqual(aggregation_module.Type.SUM, sum_aggregation.aggregation_type) + self.assertEqual(aggregation_module.Type.SUM, + sum_aggregation.aggregation_type) def test_constructor_explicit(self): sum = 1 @@ -48,16 +50,17 @@ def test_constructor_explicit(self): sum_aggregation = aggregation_module.SumAggregation(sum=sum) self.assertEqual(1, sum_aggregation.sum.sum_data) - self.assertEqual(aggregation_module.Type.SUM, sum_aggregation.aggregation_type) + self.assertEqual(aggregation_module.Type.SUM, + sum_aggregation.aggregation_type) class TestCountAggregation(unittest.TestCase): - def test_constructor_defaults(self): count_aggregation = aggregation_module.CountAggregation() self.assertEqual(0, count_aggregation.count.count_data) - self.assertEqual(aggregation_module.Type.COUNT, count_aggregation.aggregation_type) + self.assertEqual(aggregation_module.Type.COUNT, + count_aggregation.aggregation_type) def test_constructor_explicit(self): count = 4 @@ -65,39 +68,57 @@ def test_constructor_explicit(self): count_aggregation = aggregation_module.CountAggregation(count=count) self.assertEqual(4, count_aggregation.count.count_data) - self.assertEqual(aggregation_module.Type.COUNT, count_aggregation.aggregation_type) + self.assertEqual(aggregation_module.Type.COUNT, + count_aggregation.aggregation_type) class TestLastValueAggregation(unittest.TestCase): - def test_constructor_defaults(self): last_value_aggregation = aggregation_module.LastValueAggregation() self.assertEqual(0, last_value_aggregation.value) - self.assertEqual(aggregation_module.Type.LASTVALUE, last_value_aggregation.aggregation_type) + self.assertEqual(aggregation_module.Type.LASTVALUE, + last_value_aggregation.aggregation_type) def test_constructor_explicit(self): val = 16 - last_value_aggregation = aggregation_module.LastValueAggregation(value=val) + last_value_aggregation = aggregation_module.LastValueAggregation( + value=val) self.assertEqual(16, last_value_aggregation.value) - self.assertEqual(aggregation_module.Type.LASTVALUE, last_value_aggregation.aggregation_type) + self.assertEqual(aggregation_module.Type.LASTVALUE, + last_value_aggregation.aggregation_type) class TestDistributionAggregation(unittest.TestCase): - def test_constructor_defaults(self): distribution_aggregation = aggregation_module.DistributionAggregation() self.assertEqual([], distribution_aggregation.boundaries.boundaries) self.assertEqual({}, distribution_aggregation.distribution) - self.assertEqual(aggregation_module.Type.DISTRIBUTION, distribution_aggregation.aggregation_type) + self.assertEqual(aggregation_module.Type.DISTRIBUTION, + distribution_aggregation.aggregation_type) def test_constructor_explicit(self): boundaries = ["test"] distribution = {1: "test"} - distribution_aggregation = aggregation_module.DistributionAggregation(boundaries=boundaries, distribution=distribution) + distribution_aggregation = aggregation_module.DistributionAggregation( + boundaries=boundaries, distribution=distribution) - self.assertEqual(["test"], distribution_aggregation.boundaries.boundaries) + self.assertEqual(["test"], + distribution_aggregation.boundaries.boundaries) self.assertEqual({1: "test"}, distribution_aggregation.distribution) - self.assertEqual(aggregation_module.Type.DISTRIBUTION, distribution_aggregation.aggregation_type) + self.assertEqual(aggregation_module.Type.DISTRIBUTION, + distribution_aggregation.aggregation_type) + + def test_min_max(self): + da = aggregation_module.DistributionAggregation([]) + + self.assertEqual(da.aggregation_data.min, float('inf')) + self.assertEqual(da.aggregation_data.max, float('-inf')) + + for dp in range(-10, 11): + da.aggregation_data.add_sample(dp, datetime(1999, 12, 31), {}) + + self.assertEqual(da.aggregation_data.min, -10) + self.assertEqual(da.aggregation_data.max, 10) From b2cc95fd4cd9abd00284f7c8f31c7918bffa0eb5 Mon Sep 17 00:00:00 2001 From: Chris Kleinknecht Date: Wed, 31 Oct 2018 12:41:32 -0700 Subject: [PATCH 11/17] Lint rolling --- opencensus/stats/aggregation_data.py | 1 - 1 file changed, 1 deletion(-) diff --git a/opencensus/stats/aggregation_data.py b/opencensus/stats/aggregation_data.py index 15ab1b95d..5e79ee9fe 100644 --- a/opencensus/stats/aggregation_data.py +++ b/opencensus/stats/aggregation_data.py @@ -217,7 +217,6 @@ def add_sample(self, value, timestamp, attachments): self._sum_of_sqd_deviations = self._sum_of_sqd_deviations + ( (value - old_mean) * (value - self._mean_data)) - def increment_bucket_count(self, value): """Increment the bucket count based on a given value from the user""" if len(self._bounds) == 0: From 7cb29eb4979bf1c258903c0de93dc9ceb48e0434 Mon Sep 17 00:00:00 2001 From: Chris Kleinknecht Date: Thu, 24 Jan 2019 11:13:19 -0800 Subject: [PATCH 12/17] Add point conversions for stats data agg classes --- opencensus/stats/aggregation_data.py | 111 ++++++++++++++-- tests/unit/stats/test_aggregation_data.py | 153 ++++++++++++++++++---- 2 files changed, 230 insertions(+), 34 deletions(-) diff --git a/opencensus/stats/aggregation_data.py b/opencensus/stats/aggregation_data.py index 338b04bdb..a9083d38f 100644 --- a/opencensus/stats/aggregation_data.py +++ b/opencensus/stats/aggregation_data.py @@ -12,8 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. +import copy import logging +from opencensus.metrics.export import point +from opencensus.metrics.export import value from opencensus.stats import bucket_boundaries @@ -36,6 +39,18 @@ def aggregation_data(self): """The current aggregation data""" return self._aggregation_data + def to_point(self, timestamp): + """Get a Point conversion of this aggregation. + + :type timestamp: :class: `datetime.datetime` + :param timestamp: The time to report the point as having been recorded. + + :rtype: :class: `opencensus.metrics.export.point.Point` + :return: a Point with with this aggregation's value and appropriate + value type. + """ + raise NotImplementedError # pragma: NO COVER + class SumAggregationDataFloat(BaseAggregationData): """Sum Aggregation Data is the aggregated data for the Sum aggregation @@ -60,6 +75,18 @@ def sum_data(self): """The current sum data""" return self._sum_data + def to_point(self, timestamp): + """Get a Point conversion of this aggregation. + + :type timestamp: :class: `datetime.datetime` + :param timestamp: The time to report the point as having been recorded. + + :rtype: :class: `opencensus.metrics.export.point.Point` + :return: a :class: `opencensus.metrics.export.value.ValueLong`-valued + Point with value equal to `sum_data`. + """ + return point.Point(value.ValueDouble(self.sum_data), timestamp) + class CountAggregationData(BaseAggregationData): """Count Aggregation Data is the count value of aggregated data @@ -83,6 +110,18 @@ def count_data(self): """The current count data""" return self._count_data + def to_point(self, timestamp): + """Get a Point conversion of this aggregation. + + :type timestamp: :class: `datetime.datetime` + :param timestamp: The time to report the point as having been recorded. + + :rtype: :class: `opencensus.metrics.export.point.Point` + :return: a :class: `opencensus.metrics.export.value.ValueLong`-valued + Point with value equal to `count_data`. + """ + return point.Point(value.ValueLong(self.count_data), timestamp) + class DistributionAggregationData(BaseAggregationData): """Distribution Aggregation Data refers to the distribution stats of @@ -123,34 +162,37 @@ def __init__(self, counts_per_bucket=None, bounds=None, exemplars=None): + if bounds is None and exemplars is not None: + raise ValueError + if exemplars is not None and len(exemplars) != len(bounds) + 1: + raise ValueError + super(DistributionAggregationData, self).__init__(mean_data) self._mean_data = mean_data self._count_data = count_data self._min = min_ self._max = max_ self._sum_of_sqd_deviations = sum_of_sqd_deviations + if bounds is None: bounds = [] + self._exemplars = None else: assert bounds == list(sorted(set(bounds))) assert all(bb > 0 for bb in bounds) + if exemplars is None: + self._exemplars = {ii: None for ii in range(len(bounds) + 1)} + else: + self._exemplars = {ii: ex for ii, ex in enumerate(exemplars)} + self._bounds = (bucket_boundaries.BucketBoundaries(boundaries=bounds) + .boundaries) if counts_per_bucket is None: counts_per_bucket = [0 for ii in range(len(bounds) + 1)] else: assert all(cc >= 0 for cc in counts_per_bucket) assert len(counts_per_bucket) == len(bounds) + 1 - self._counts_per_bucket = counts_per_bucket - self._bounds = bucket_boundaries.BucketBoundaries( - boundaries=bounds).boundaries - bucket = 0 - for _ in self.bounds: - bucket = bucket + 1 - - # If there is no histogram, do not record an exemplar - self._exemplars = \ - {bucket: exemplars} if len(self._bounds) > 0 else None @property def mean_data(self): @@ -240,6 +282,43 @@ def increment_bucket_count(self, value): self._counts_per_bucket[last_bucket_index] += 1 return last_bucket_index + def to_point(self, timestamp): + """Get a Point conversion of this aggregation. + + This method creates a :class: `opencensus.metrics.export.point.Point` + with a :class: `opencensus.metrics.export.value.ValueDistribution` + value, and creates buckets and exemplars for that distribution from the + appropriate classes in the `metrics` package. + + :type timestamp: :class: `datetime.datetime` + :param timestamp: The time to report the point as having been recorded. + + :rtype: :class: `opencensus.metrics.export.point.Point` + :return: a :class: `opencensus.metrics.export.value.ValueDistribution` + -valued Point. + """ + buckets = [None] * len(self.counts_per_bucket) + for ii, count in enumerate(self.counts_per_bucket): + stat_ex = self.exemplars.get(ii, None) + if stat_ex is not None: + metric_ex = value.Exemplar(stat_ex.value, stat_ex.timestamp, + copy.copy(stat_ex.attachments)) + buckets[ii] = value.Bucket(count, metric_ex) + else: + buckets[ii] = value.Bucket(count) + + bucket_options = value.BucketOptions(value.Explicit(self.bounds)) + return point.Point( + value.ValueDistribution( + count=self.count_data, + sum_=self.sum, + sum_of_squared_deviation=self.sum_of_sqd_deviations, + bucket_options=bucket_options, + buckets=buckets + ), + timestamp + ) + class LastValueAggregationData(BaseAggregationData): """ @@ -265,6 +344,18 @@ def value(self): """The current value recorded""" return self._value + def to_point(self, timestamp): + """Get a Point conversion of this aggregation. + + :type timestamp: :class: `datetime.datetime` + :param timestamp: The time to report the point as having been recorded. + + :rtype: :class: `opencensus.metrics.export.point.Point` + :return: a :class: `opencensus.metrics.export.value.ValueDouble`-valued + Point. + """ + return point.Point(value.ValueDouble(self.value), timestamp) + class Exemplar(object): """ Exemplar represents an example point that may be used to annotate diff --git a/tests/unit/stats/test_aggregation_data.py b/tests/unit/stats/test_aggregation_data.py index 4c86e5240..74bee71ad 100644 --- a/tests/unit/stats/test_aggregation_data.py +++ b/tests/unit/stats/test_aggregation_data.py @@ -12,11 +12,14 @@ # See the License for the specific language governing permissions and # limitations under the License. +from datetime import datetime import time import unittest import mock +from opencensus.metrics.export import point +from opencensus.metrics.export import value from opencensus.stats import aggregation_data as aggregation_data_module @@ -46,6 +49,16 @@ def test_add_sample(self): self.assertEqual(4, sum_aggregation_data.sum_data) + def test_to_point(self): + sum_data = 12.345 + timestamp = datetime(1970, 1, 1) + agg = aggregation_data_module.SumAggregationDataFloat(sum_data) + converted_point = agg.to_point(timestamp) + self.assertTrue(isinstance(converted_point, point.Point)) + self.assertTrue(isinstance(converted_point.value, value.ValueDouble)) + self.assertEqual(converted_point.value.value, sum_data) + self.assertEqual(converted_point.timestamp, timestamp) + class TestCountAggregationData(unittest.TestCase): def test_constructor(self): @@ -63,6 +76,16 @@ def test_add_sample(self): self.assertEqual(1, count_aggregation_data.count_data) + def test_to_point(self): + count_data = 123 + timestamp = datetime(1970, 1, 1) + agg = aggregation_data_module.CountAggregationData(count_data) + converted_point = agg.to_point(timestamp) + self.assertTrue(isinstance(converted_point, point.Point)) + self.assertTrue(isinstance(converted_point.value, value.ValueLong)) + self.assertEqual(converted_point.value.value, count_data) + self.assertEqual(converted_point.timestamp, timestamp) + class TestLastValueAggregationData(unittest.TestCase): def test_constructor(self): @@ -80,6 +103,25 @@ def test_overwrite_sample(self): last_value_aggregation_data.add_sample(1, None, None) self.assertEqual(1, last_value_aggregation_data.value) + def test_to_point(self): + val = 1.2 + timestamp = datetime(1970, 1, 1) + agg = aggregation_data_module.LastValueAggregationData(val) + converted_point = agg.to_point(timestamp) + self.assertTrue(isinstance(converted_point, point.Point)) + self.assertTrue(isinstance(converted_point.value, value.ValueDouble)) + self.assertEqual(converted_point.value.value, val) + self.assertEqual(converted_point.timestamp, timestamp) + + +def exemplars_equal(stats_ex, metrics_ex): + """Compare a stats exemplar to a metrics exemplar.""" + assert isinstance(stats_ex, aggregation_data_module.Exemplar) + assert isinstance(metrics_ex, value.Exemplar) + return (stats_ex.value == metrics_ex.value and + stats_ex.timestamp == metrics_ex.timestamp and + stats_ex.attachments == metrics_ex.attachments) + class TestDistributionAggregationData(unittest.TestCase): def test_constructor(self): @@ -179,21 +221,46 @@ def test_init_bad_bounds(self): counts_per_bucket=[0, 0, 0, 0], bounds=[-1, 1, 2]) + def test_init_bad_exemplars(self): + # Check that we don't allow exemplars without bounds + with self.assertRaises(ValueError): + aggregation_data_module.DistributionAggregationData( + mean_data=mock.Mock(), + count_data=mock.Mock(), + min_=mock.Mock(), + max_=mock.Mock(), + sum_of_sqd_deviations=mock.Mock(), + counts_per_bucket=mock.Mock(), + bounds=None, + exemplars=[mock.Mock()]) + + # Check that the exemplar count matches the bucket count + with self.assertRaises(ValueError): + aggregation_data_module.DistributionAggregationData( + mean_data=mock.Mock(), + count_data=mock.Mock(), + min_=mock.Mock(), + max_=mock.Mock(), + sum_of_sqd_deviations=mock.Mock(), + counts_per_bucket=mock.Mock(), + bounds=[0, 1], + exemplars=[mock.Mock(), mock.Mock()]) + def test_constructor_with_exemplar(self): timestamp = time.time() attachments = {"One": "one", "Two": "two"} - exemplar_1 = aggregation_data_module.Exemplar(4, timestamp, - attachments) - exemplar_2 = aggregation_data_module.Exemplar(5, timestamp, - attachments) - mean_data = 1 - count_data = 0 - _min = 0 - _max = 1 + exemplars = [ + aggregation_data_module.Exemplar(.07, timestamp, attachments), + aggregation_data_module.Exemplar(.7, timestamp, attachments), + aggregation_data_module.Exemplar(7, timestamp, attachments) + ] + mean_data = 2.59 + count_data = 3 + _min = .07 + _max = 7 sum_of_sqd_deviations = mock.Mock() counts_per_bucket = [1, 1, 1] bounds = [1.0 / 2.0, 1] - exemplars = [exemplar_1, exemplar_2] dist_agg_data = aggregation_data_module.DistributionAggregationData( mean_data=mean_data, @@ -205,18 +272,17 @@ def test_constructor_with_exemplar(self): counts_per_bucket=counts_per_bucket, bounds=bounds) - self.assertEqual(1, dist_agg_data.mean_data) - self.assertEqual(0, dist_agg_data.count_data) - self.assertEqual(0, dist_agg_data.min) - self.assertEqual(1, dist_agg_data.max) - self.assertEqual(sum_of_sqd_deviations, - dist_agg_data.sum_of_sqd_deviations) - self.assertEqual([1, 1, 1], dist_agg_data.counts_per_bucket) - self.assertEqual([exemplar_1, exemplar_2], dist_agg_data.exemplars[2]) - self.assertEqual([1.0 / 2.0, 1], dist_agg_data.bounds) - - self.assertIsNotNone(dist_agg_data.sum) - self.assertEqual(0, dist_agg_data.variance) + self.assertEqual(dist_agg_data.mean_data, mean_data) + self.assertEqual(dist_agg_data.count_data, count_data) + self.assertEqual(dist_agg_data.min, _min) + self.assertEqual(dist_agg_data.max, _max) + self.assertEqual(dist_agg_data.sum_of_sqd_deviations, + sum_of_sqd_deviations) + self.assertEqual(dist_agg_data.counts_per_bucket, counts_per_bucket) + self.assertEqual(dist_agg_data.bounds, bounds) + self.assertEqual(dist_agg_data.sum, mean_data * count_data) + for ii, ex in enumerate(exemplars): + self.assertEqual(dist_agg_data.exemplars[ii], ex) def test_exemplar(self): timestamp = time.time() @@ -369,16 +435,19 @@ def test_add_sample_attachment(self): sum_of_sqd_deviations=sum_of_sqd_deviations, counts_per_bucket=counts_per_bucket, bounds=bounds, - exemplars=exemplar_1) + exemplars=[None, None, None, exemplar_1]) - self.assertEqual({3: exemplar_1}, dist_agg_data.exemplars) + self.assertEqual(dist_agg_data.exemplars[3], exemplar_1) dist_agg_data.add_sample(value, timestamp, attachments) self.assertEqual(0, dist_agg_data.min) self.assertEqual(3, dist_agg_data.max) self.assertEqual(2, dist_agg_data.count_data) self.assertEqual(2.0, dist_agg_data.mean_data) - self.assertEqual(3, dist_agg_data.exemplars[3].value) + # Check that adding a sample overwrites the bucket's exemplar + self.assertNotEqual(dist_agg_data.exemplars[3], exemplar_1) + self.assertEqual(dist_agg_data.exemplars[3].value, 3) + self.assertEqual(dist_agg_data.exemplars[3].attachments, attachments) count_data = 4 dist_agg_data = aggregation_data_module.DistributionAggregationData( @@ -448,3 +517,39 @@ def test_increment_bucket_count(self): dist_agg_data.increment_bucket_count(value=value) self.assertEqual([1, 2, 2], dist_agg_data.counts_per_bucket) + + def test_to_point(self): + timestamp = datetime(1970, 1, 1) + ex_9 = aggregation_data_module.Exemplar( + 9, timestamp, {'trace_id': 'dead', 'span_id': 'beef'} + ) + ex_99 = aggregation_data_module.Exemplar( + 99, timestamp, {'trace_id': 'dead', 'span_id': 'bef0'} + ) + dist_agg_data = aggregation_data_module.DistributionAggregationData( + mean_data=50, + count_data=99, + min_=1, + max_=99, + sum_of_sqd_deviations=80850.0, + counts_per_bucket=[0, 9, 90, 0], + bounds=[1, 10, 100], + exemplars=[None, ex_9, ex_99, None], + ) + converted_point = dist_agg_data.to_point(timestamp) + self.assertTrue(isinstance(converted_point.value, + value.ValueDistribution)) + self.assertEqual(converted_point.value.count, 99) + self.assertEqual(converted_point.value.sum, 4950) + self.assertEqual(converted_point.value.sum_of_squared_deviation, + 80850.0) + self.assertEqual([bb.count for bb in converted_point.value.buckets], + [0, 9, 90, 0]) + self.assertTrue( + exemplars_equal( + ex_9, + converted_point.value.buckets[1].exemplar)) + self.assertTrue( + exemplars_equal( + ex_99, + converted_point.value.buckets[2].exemplar)) From 56521e17dd135ef741cdb7276dca9f8338ff5538 Mon Sep 17 00:00:00 2001 From: Chris Kleinknecht Date: Thu, 24 Jan 2019 11:32:16 -0800 Subject: [PATCH 13/17] Make bucket histograms optional --- opencensus/metrics/export/value.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opencensus/metrics/export/value.py b/opencensus/metrics/export/value.py index 065d30180..82c3fba53 100644 --- a/opencensus/metrics/export/value.py +++ b/opencensus/metrics/export/value.py @@ -149,7 +149,7 @@ class Bucket(object): distribution does not have a histogram. """ - def __init__(self, count, exemplar): + def __init__(self, count, exemplar=None): self._count = count self._exemplar = exemplar From fbdb1c5e1d6f1cd6be4f75b250fa9f4dfd78fe5f Mon Sep 17 00:00:00 2001 From: Chris Kleinknecht Date: Thu, 24 Jan 2019 13:32:14 -0800 Subject: [PATCH 14/17] Lose redundant descriptor type check --- opencensus/metrics/export/metric.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/opencensus/metrics/export/metric.py b/opencensus/metrics/export/metric.py index 6f23d470b..c41e2dd4a 100644 --- a/opencensus/metrics/export/metric.py +++ b/opencensus/metrics/export/metric.py @@ -54,8 +54,6 @@ def _check_type(self): """Check that point value types match the descriptor type.""" check_type = metric_descriptor.MetricDescriptorType.to_type_class( self.descriptor.type) - if check_type is None: - raise ValueError("Unknown metric descriptor type") for ts in self.time_series: if not ts.check_points_type(check_type): raise ValueError("Invalid point value type") From 7a43a50bf4cea81b1c58d46c942a78ccde949896 Mon Sep 17 00:00:00 2001 From: Chris Kleinknecht Date: Thu, 24 Jan 2019 16:22:56 -0800 Subject: [PATCH 15/17] Only initialize _type_map once --- .../metrics/export/metric_descriptor.py | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/opencensus/metrics/export/metric_descriptor.py b/opencensus/metrics/export/metric_descriptor.py index 8ee63dad6..99e3b6944 100644 --- a/opencensus/metrics/export/metric_descriptor.py +++ b/opencensus/metrics/export/metric_descriptor.py @@ -80,19 +80,20 @@ class MetricDescriptorType(object): # is not recommended, since it cannot be aggregated. SUMMARY = 7 + _type_map = { + GAUGE_INT64: value.ValueLong, + GAUGE_DOUBLE: value.ValueDouble, + GAUGE_DISTRIBUTION: value.ValueDistribution, + CUMULATIVE_INT64: value.ValueLong, + CUMULATIVE_DOUBLE: value.ValueDouble, + CUMULATIVE_DISTRIBUTION: value.ValueDistribution, + SUMMARY: value.ValueSummary + } + @classmethod def to_type_class(cls, metric_descriptor_type): - type_map = { - cls.GAUGE_INT64: value.ValueLong, - cls.GAUGE_DOUBLE: value.ValueDouble, - cls.GAUGE_DISTRIBUTION: value.ValueDistribution, - cls.CUMULATIVE_INT64: value.ValueLong, - cls.CUMULATIVE_DOUBLE: value.ValueDouble, - cls.CUMULATIVE_DISTRIBUTION: value.ValueDistribution, - cls.SUMMARY: value.ValueSummary - } try: - return type_map[metric_descriptor_type] + return cls._type_map[metric_descriptor_type] except KeyError: raise ValueError("Unknown MetricDescriptorType value") From 9bbc9f4dfffff79b4e542b2bde89fd0b9dc422ba Mon Sep 17 00:00:00 2001 From: Chris Kleinknecht Date: Fri, 25 Jan 2019 08:49:26 -0800 Subject: [PATCH 16/17] Fix docstring typo --- opencensus/stats/aggregation_data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opencensus/stats/aggregation_data.py b/opencensus/stats/aggregation_data.py index a9083d38f..c0d2cf7a2 100644 --- a/opencensus/stats/aggregation_data.py +++ b/opencensus/stats/aggregation_data.py @@ -82,7 +82,7 @@ def to_point(self, timestamp): :param timestamp: The time to report the point as having been recorded. :rtype: :class: `opencensus.metrics.export.point.Point` - :return: a :class: `opencensus.metrics.export.value.ValueLong`-valued + :return: a :class: `opencensus.metrics.export.value.ValueDouble`-valued Point with value equal to `sum_data`. """ return point.Point(value.ValueDouble(self.sum_data), timestamp) From f9d64fb668b7e3097a7a0bdba6ca1725a54382cf Mon Sep 17 00:00:00 2001 From: Chris Kleinknecht Date: Fri, 25 Jan 2019 08:55:54 -0800 Subject: [PATCH 17/17] Add check for bucket options --- tests/unit/stats/test_aggregation_data.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/unit/stats/test_aggregation_data.py b/tests/unit/stats/test_aggregation_data.py index 74bee71ad..477a62d06 100644 --- a/tests/unit/stats/test_aggregation_data.py +++ b/tests/unit/stats/test_aggregation_data.py @@ -545,6 +545,8 @@ def test_to_point(self): 80850.0) self.assertEqual([bb.count for bb in converted_point.value.buckets], [0, 9, 90, 0]) + self.assertEqual(converted_point.value.bucket_options.type_.bounds, + [1, 10, 100]) self.assertTrue( exemplars_equal( ex_9,