github.com/apache/beam/sdks/v2@v2.48.2/python/apache_beam/testing/analyzers/perf_analysis_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 # pytype: skip-file 18 19 import logging 20 import os 21 import unittest 22 23 import mock 24 import pandas as pd 25 26 # pylint: disable=ungrouped-imports 27 try: 28 import apache_beam.testing.analyzers.perf_analysis as analysis 29 from apache_beam.testing.analyzers import constants 30 from apache_beam.testing.analyzers.perf_analysis_utils import is_change_point_in_valid_window 31 from apache_beam.testing.analyzers.perf_analysis_utils import is_perf_alert 32 from apache_beam.testing.analyzers.perf_analysis_utils import e_divisive 33 from apache_beam.testing.analyzers.perf_analysis_utils import validate_config 34 except ImportError as e: 35 analysis = None # type: ignore 36 37 38 # mock methods. 39 def get_fake_data_with_no_change_point(**kwargs): 40 num_samples = 20 41 metric_values = [1] * num_samples 42 timestamps = list(range(num_samples)) 43 return metric_values, timestamps 44 45 46 def get_fake_data_with_change_point(**kwargs): 47 num_samples = 20 48 metric_values = [0] * (num_samples // 2) + [1] * (num_samples // 2) 49 timestamps = [i for i in range(num_samples)] 50 return metric_values, timestamps 51 52 53 def get_existing_issue_data(**kwargs): 54 # change point found at index 10. So passing 10 in the 55 # existing issue data in mock method. 56 return pd.DataFrame([{ 57 constants._CHANGE_POINT_TIMESTAMP_LABEL: 10, constants._ISSUE_NUMBER: 0 58 }]) 59 60 61 @unittest.skipIf( 62 analysis is None, 63 'Missing dependencies. ' 64 'Test dependencies are missing for the Analyzer.') 65 class TestChangePointAnalysis(unittest.TestCase): 66 def setUp(self) -> None: 67 self.single_change_point_series = [0] * 10 + [1] * 10 68 self.multiple_change_point_series = self.single_change_point_series + [ 69 2 70 ] * 20 71 self.timestamps = list(range(5)) 72 self.params = { 73 'test_name': 'fake_test', 74 'metrics_dataset': 'fake_dataset', 75 'metrics_table': 'fake_table', 76 'project': 'fake_project', 77 'metric_name': 'fake_metric_name' 78 } 79 self.test_id = 'fake_id' 80 81 def test_edivisive_means(self): 82 change_point_indexes = e_divisive(self.single_change_point_series) 83 self.assertEqual(change_point_indexes, [10]) 84 change_point_indexes = e_divisive(self.multiple_change_point_series) 85 self.assertEqual(sorted(change_point_indexes), [10, 20]) 86 87 def test_is_changepoint_in_valid_window(self): 88 changepoint_to_recent_run_window = 19 89 change_point_index = 14 90 91 is_valid = is_change_point_in_valid_window( 92 changepoint_to_recent_run_window, change_point_index) 93 self.assertEqual(is_valid, True) 94 95 def test_change_point_outside_inspection_window_is_not_a_valid_alert(self): 96 changepoint_to_recent_run_window = 12 97 change_point_index = 14 98 99 is_valid = is_change_point_in_valid_window( 100 changepoint_to_recent_run_window, change_point_index) 101 self.assertEqual(is_valid, False) 102 103 def test_validate_config(self): 104 test_keys = { 105 'test_name', 106 'metrics_dataset', 107 'metrics_table', 108 'project', 109 'metric_name' 110 } 111 self.assertEqual(test_keys, constants._PERF_TEST_KEYS) 112 self.assertTrue(validate_config(test_keys)) 113 114 def test_duplicate_change_point(self): 115 change_point_index = 2 116 min_runs_between_change_points = 1 117 is_alert = is_perf_alert( 118 previous_change_point_timestamps=[self.timestamps[0]], 119 timestamps=self.timestamps, 120 change_point_index=change_point_index, 121 min_runs_between_change_points=min_runs_between_change_points) 122 self.assertTrue(is_alert) 123 124 def test_duplicate_change_points_are_not_valid_alerts(self): 125 change_point_index = 2 126 min_runs_between_change_points = 1 127 is_alert = is_perf_alert( 128 previous_change_point_timestamps=[self.timestamps[3]], 129 timestamps=self.timestamps, 130 change_point_index=change_point_index, 131 min_runs_between_change_points=min_runs_between_change_points) 132 self.assertFalse(is_alert) 133 134 is_alert = is_perf_alert( 135 previous_change_point_timestamps=[ 136 self.timestamps[0], self.timestamps[3] 137 ], 138 timestamps=self.timestamps, 139 change_point_index=change_point_index, 140 min_runs_between_change_points=min_runs_between_change_points) 141 self.assertFalse(is_alert) 142 143 @mock.patch( 144 'apache_beam.testing.analyzers.perf_analysis.fetch_metric_data', 145 get_fake_data_with_no_change_point) 146 def test_no_alerts_when_no_change_points(self): 147 is_alert = analysis.run_change_point_analysis( 148 params=self.params, 149 test_id=self.test_id, 150 big_query_metrics_fetcher=None) 151 self.assertFalse(is_alert) 152 153 @mock.patch( 154 'apache_beam.testing.analyzers.perf_analysis.fetch_metric_data', 155 get_fake_data_with_change_point) 156 @mock.patch( 157 'apache_beam.testing.analyzers.perf_analysis.get_existing_issues_data', 158 return_value=None) 159 @mock.patch( 160 'apache_beam.testing.analyzers.perf_analysis.' 161 'publish_issue_metadata_to_big_query', 162 return_value=None) 163 @mock.patch( 164 'apache_beam.testing.analyzers.perf_analysis' 165 '.create_performance_alert', 166 return_value=(0, '')) 167 def test_alert_on_data_with_change_point(self, *args): 168 is_alert = analysis.run_change_point_analysis( 169 params=self.params, 170 test_id=self.test_id, 171 big_query_metrics_fetcher=None) 172 self.assertTrue(is_alert) 173 174 @mock.patch( 175 'apache_beam.testing.analyzers.perf_analysis.fetch_metric_data', 176 get_fake_data_with_change_point) 177 @mock.patch( 178 'apache_beam.testing.analyzers.perf_analysis.get_existing_issues_data', 179 get_existing_issue_data) 180 @mock.patch( 181 'apache_beam.testing.analyzers.perf_analysis.' 182 'publish_issue_metadata_to_big_query', 183 return_value=None) 184 @mock.patch( 185 'apache_beam.testing.analyzers.perf_analysis.create_performance_alert', 186 return_value=(0, '')) 187 def test_alert_on_data_with_reported_change_point(self, *args): 188 is_alert = analysis.run_change_point_analysis( 189 params=self.params, 190 test_id=self.test_id, 191 big_query_metrics_fetcher=None) 192 self.assertFalse(is_alert) 193 194 195 if __name__ == '__main__': 196 logging.getLogger().setLevel(logging.DEBUG) 197 os.environ['GITHUB_TOKEN'] = 'fake_token' 198 unittest.main()