github.com/apache/beam/sdks/v2@v2.48.2/python/apache_beam/metrics/metric_test.py (about) 1 # 2 # Licensed to the Apache Software Foundation (ASF) under one or more 3 # contributor license agreements. See the NOTICE file distributed with 4 # this work for additional information regarding copyright ownership. 5 # The ASF licenses this file to You under the Apache License, Version 2.0 6 # (the "License"); you may not use this file except in compliance with 7 # the License. You may obtain a copy of the License at 8 # 9 # http://www.apache.org/licenses/LICENSE-2.0 10 # 11 # Unless required by applicable law or agreed to in writing, software 12 # distributed under the License is distributed on an "AS IS" BASIS, 13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 # See the License for the specific language governing permissions and 15 # limitations under the License. 16 # 17 18 # pytype: skip-file 19 20 # beam-playground: 21 # name: MetricTest 22 # description: Unit-test for the Metric example. 23 # multifile: false 24 # context_line: 52 25 # categories: 26 # - Metrics 27 # complexity: MEDIUM 28 # tags: 29 # - metrics 30 # - test 31 32 import unittest 33 34 import hamcrest as hc 35 import pytest 36 37 import apache_beam as beam 38 from apache_beam import metrics 39 from apache_beam.metrics.cells import DistributionData 40 from apache_beam.metrics.execution import MetricKey 41 from apache_beam.metrics.execution import MetricsContainer 42 from apache_beam.metrics.execution import MetricsEnvironment 43 from apache_beam.metrics.metric import MetricResults 44 from apache_beam.metrics.metric import Metrics 45 from apache_beam.metrics.metric import MetricsFilter 46 from apache_beam.metrics.metricbase import MetricName 47 from apache_beam.runners.worker import statesampler 48 from apache_beam.testing.metric_result_matchers import DistributionMatcher 49 from apache_beam.testing.metric_result_matchers import MetricResultMatcher 50 from apache_beam.testing.test_pipeline import TestPipeline 51 from apache_beam.testing.util import assert_that 52 from apache_beam.testing.util import equal_to 53 from apache_beam.utils import counters 54 55 56 class NameTest(unittest.TestCase): 57 def test_basic_metric_name(self): 58 name = MetricName('namespace1', 'name1') 59 self.assertEqual(name.namespace, 'namespace1') 60 self.assertEqual(name.name, 'name1') 61 self.assertEqual(name, MetricName('namespace1', 'name1')) 62 63 key = MetricKey('step1', name) 64 self.assertEqual(key.step, 'step1') 65 self.assertEqual(key.metric.namespace, 'namespace1') 66 self.assertEqual(key.metric.name, 'name1') 67 self.assertEqual(key, MetricKey('step1', MetricName('namespace1', 'name1'))) 68 69 70 class MetricResultsTest(unittest.TestCase): 71 def test_metric_filter_namespace_matching(self): 72 filter = MetricsFilter().with_namespace('ns1') 73 name = MetricName('ns1', 'name1') 74 key = MetricKey('step1', name) 75 self.assertTrue(MetricResults.matches(filter, key)) 76 77 def test_metric_filter_name_matching(self): 78 filter = MetricsFilter().with_name('name1').with_namespace('ns1') 79 name = MetricName('ns1', 'name1') 80 key = MetricKey('step1', name) 81 self.assertTrue(MetricResults.matches(filter, key)) 82 83 filter = MetricsFilter().with_name('name1') 84 name = MetricName('ns1', 'name1') 85 key = MetricKey('step1', name) 86 self.assertTrue(MetricResults.matches(filter, key)) 87 88 def test_metric_filter_step_matching(self): 89 name = MetricName('ns1', 'name1') 90 filter = MetricsFilter().with_step('Step1') 91 92 key = MetricKey('Step1', name) 93 self.assertTrue(MetricResults.matches(filter, key)) 94 95 key = MetricKey('Step10', name) 96 self.assertFalse(MetricResults.matches(filter, key)) 97 98 key = MetricKey('Step10/Step1', name) 99 self.assertTrue(MetricResults.matches(filter, key)) 100 101 key = MetricKey('Top1/Outer1/Inner1', name) 102 103 filter = MetricsFilter().with_step('Top1/Outer1/Inner1') 104 self.assertTrue(MetricResults.matches(filter, key)) 105 106 filter = MetricsFilter().with_step('Top1/Outer1') 107 self.assertTrue(MetricResults.matches(filter, key)) 108 109 filter = MetricsFilter().with_step('Outer1/Inner1') 110 self.assertTrue(MetricResults.matches(filter, key)) 111 112 filter = MetricsFilter().with_step('Top1/Inner1') 113 self.assertFalse(MetricResults.matches(filter, key)) 114 115 116 class MetricsTest(unittest.TestCase): 117 def test_get_namespace_class(self): 118 class MyClass(object): 119 pass 120 121 self.assertEqual( 122 '{}.{}'.format(MyClass.__module__, MyClass.__name__), 123 Metrics.get_namespace(MyClass)) 124 125 def test_get_namespace_string(self): 126 namespace = 'MyNamespace' 127 self.assertEqual(namespace, Metrics.get_namespace(namespace)) 128 129 def test_get_namespace_error(self): 130 with self.assertRaises(ValueError): 131 Metrics.get_namespace(object()) 132 133 def test_counter_empty_name(self): 134 with self.assertRaises(ValueError): 135 Metrics.counter("namespace", "") 136 137 def test_counter_empty_namespace(self): 138 with self.assertRaises(ValueError): 139 Metrics.counter("", "names") 140 141 def test_distribution_empty_name(self): 142 with self.assertRaises(ValueError): 143 Metrics.distribution("namespace", "") 144 145 def test_distribution_empty_namespace(self): 146 with self.assertRaises(ValueError): 147 Metrics.distribution("", "names") 148 149 # Do not change the behaviour of str(), do tno update/delete this test case 150 # if the behaviour of str() is changed. Doing so will 151 # break end user beam code which depends on the str() behaviour. 152 def test_user_metric_name_str(self): 153 mn = MetricName("my_namespace", "my_name") 154 expected_str = 'MetricName(namespace=my_namespace, name=my_name)' 155 self.assertEqual(str(mn), expected_str) 156 157 def test_general_urn_metric_name_str(self): 158 mn = MetricName( 159 "my_namespace", "my_name", urn='my_urn', labels={'key': 'value'}) 160 expected_str = ( 161 "MetricName(namespace=my_namespace, name=my_name, " 162 "urn=my_urn, labels={'key': 'value'})") 163 self.assertEqual(str(mn), expected_str) 164 165 @pytest.mark.it_validatesrunner 166 def test_user_counter_using_pardo(self): 167 class SomeDoFn(beam.DoFn): 168 """A custom dummy DoFn using yield.""" 169 static_counter_elements = metrics.Metrics.counter( 170 "SomeDoFn", 'metrics_static_counter_element') 171 172 def __init__(self): 173 self.user_counter_elements = metrics.Metrics.counter( 174 self.__class__, 'metrics_user_counter_element') 175 176 def process(self, element): 177 self.static_counter_elements.inc(2) 178 self.user_counter_elements.inc() 179 distro = Metrics.distribution(self.__class__, 'element_dist') 180 distro.update(element) 181 yield element 182 183 pipeline = TestPipeline() 184 nums = pipeline | 'Input' >> beam.Create([1, 2, 3, 4]) 185 results = nums | 'ApplyPardo' >> beam.ParDo(SomeDoFn()) 186 assert_that(results, equal_to([1, 2, 3, 4])) 187 188 res = pipeline.run() 189 res.wait_until_finish() 190 191 # Verify static counter. 192 metric_results = ( 193 res.metrics().query( 194 MetricsFilter().with_metric(SomeDoFn.static_counter_elements))) 195 outputs_static_counter = metric_results['counters'][0] 196 197 self.assertEqual( 198 outputs_static_counter.key.metric.name, 199 'metrics_static_counter_element') 200 self.assertEqual(outputs_static_counter.committed, 8) 201 202 # Verify user counter. 203 metric_results = ( 204 res.metrics().query( 205 MetricsFilter().with_name('metrics_user_counter_element'))) 206 outputs_user_counter = metric_results['counters'][0] 207 208 self.assertEqual( 209 outputs_user_counter.key.metric.name, 'metrics_user_counter_element') 210 self.assertEqual(outputs_user_counter.committed, 4) 211 212 # Verify user distribution counter. 213 metric_results = res.metrics().query() 214 matcher = MetricResultMatcher( 215 step='ApplyPardo', 216 namespace=hc.contains_string('SomeDoFn'), 217 name='element_dist', 218 committed=DistributionMatcher( 219 sum_value=hc.greater_than_or_equal_to(0), 220 count_value=hc.greater_than_or_equal_to(0), 221 min_value=hc.greater_than_or_equal_to(0), 222 max_value=hc.greater_than_or_equal_to(0))) 223 hc.assert_that( 224 metric_results['distributions'], hc.contains_inanyorder(matcher)) 225 226 def test_create_counter_distribution(self): 227 sampler = statesampler.StateSampler('', counters.CounterFactory()) 228 statesampler.set_current_tracker(sampler) 229 state1 = sampler.scoped_state( 230 'mystep', 'myState', metrics_container=MetricsContainer('mystep')) 231 232 try: 233 sampler.start() 234 with state1: 235 counter_ns = 'aCounterNamespace' 236 distro_ns = 'aDistributionNamespace' 237 name = 'a_name' 238 counter = Metrics.counter(counter_ns, name) 239 distro = Metrics.distribution(distro_ns, name) 240 counter.inc(10) 241 counter.dec(3) 242 distro.update(10) 243 distro.update(2) 244 self.assertTrue(isinstance(counter, Metrics.DelegatingCounter)) 245 self.assertTrue(isinstance(distro, Metrics.DelegatingDistribution)) 246 247 del distro 248 del counter 249 250 container = MetricsEnvironment.current_container() 251 self.assertEqual( 252 container.get_counter(MetricName(counter_ns, 253 name)).get_cumulative(), 254 7) 255 self.assertEqual( 256 container.get_distribution(MetricName(distro_ns, 257 name)).get_cumulative(), 258 DistributionData(12, 2, 2, 10)) 259 finally: 260 sampler.stop() 261 262 263 if __name__ == '__main__': 264 unittest.main()