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()