chromium/tools/perf/core/results_dashboard_unittest.py

# Copyright 2018 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import json
import logging
import unittest
from unittest import mock
from unittest.mock import call

from core import results_dashboard


class ResultsDashboardTest(unittest.TestCase):

  def setUp(self):
    self.dummy_token_generator = lambda: 'Arthur-Merlin'
    self.perf_data = {'foo': 1, 'bar': 2}
    self.dashboard_url = 'https://chromeperf.appspot.com'
    self.dummy_token = {'token': 'dummy1234', 'file': 'path/to/file'}

  def testRetryForSendResultRetryException(self):

    def raise_retry_exception(url, histogramset_json, token_generator_callback):
      del url, histogramset_json  # unused
      del token_generator_callback  # unused
      raise results_dashboard.SendResultsRetryException('Should retry')

    with mock.patch('core.results_dashboard.time.sleep') as sleep_mock:
      with mock.patch(
          'core.results_dashboard._SendHistogramJson',
          side_effect=raise_retry_exception) as m:
        upload_result = results_dashboard.SendResults(
            self.perf_data,
            'dummy_benchmark',
            self.dashboard_url,
            send_as_histograms=True,
            token_generator_callback=self.dummy_token_generator,
            num_retries=5)
        self.assertFalse(upload_result)
        self.assertEqual(m.call_count, 5)
        self.assertEqual(
            sleep_mock.mock_calls,
            [call(15), call(30),
             call(60), call(120),
             call(240)])

  def testNoRetryForSendResultFatalException(self):

    def raise_retry_exception(url, histogramset_json, token_generator_callback):
      del url, histogramset_json  # unused
      del token_generator_callback  # unused
      raise results_dashboard.SendResultsFatalException('Do not retry')

    with mock.patch('core.results_dashboard.time.sleep') as sleep_mock:
      with mock.patch(
          'core.results_dashboard._SendHistogramJson',
          side_effect=raise_retry_exception) as m:
        upload_result = results_dashboard.SendResults(
            self.perf_data,
            'dummy_benchmark',
            self.dashboard_url,
            send_as_histograms=True,
            token_generator_callback=self.dummy_token_generator,
            num_retries=5)
        self.assertFalse(upload_result)
        self.assertEqual(m.call_count, 1)
        self.assertFalse(sleep_mock.mock_calls)

  def testNoRetryForSuccessfulSendResult(self):
    with mock.patch('core.results_dashboard.time.sleep') as sleep_mock:
      with mock.patch('core.results_dashboard._SendHistogramJson') as m:
        upload_result = results_dashboard.SendResults(
            self.perf_data,
            'dummy_benchmark',
            self.dashboard_url,
            send_as_histograms=True,
            token_generator_callback=self.dummy_token_generator,
            num_retries=5)
        self.assertTrue(upload_result)
        self.assertEqual(m.call_count, 1)
        self.assertFalse(sleep_mock.mock_calls)

  def testNoRetryAfterSucessfulSendResult(self):
    counter = [0]

    def raise_retry_exception_first_two_times(url, histogramset_json,
                                              token_generator_callback):
      del url, histogramset_json  # unused
      del token_generator_callback  # unused
      counter[0] += 1
      if counter[0] <= 2:
        raise results_dashboard.SendResultsRetryException('Please retry')

    with mock.patch('core.results_dashboard.time.sleep') as sleep_mock:
      with mock.patch(
          'core.results_dashboard._SendHistogramJson',
          side_effect=raise_retry_exception_first_two_times) as m:
        upload_result = results_dashboard.SendResults(
            self.perf_data,
            'dummy_benchmark',
            self.dashboard_url,
            send_as_histograms=True,
            token_generator_callback=self.dummy_token_generator,
            num_retries=5)
        self.assertTrue(upload_result)
        self.assertEqual(m.call_count, 3)
        self.assertEqual(sleep_mock.mock_calls, [call(15), call(30)])

  def testSendHistogramJsonRetryException(self):

    def request(*args, **kwargs):
      del args, kwargs
      content = json.dumps(self.dummy_token)

      class Response(object):
        status = 403
        reason = 'dummy auth error'

      return Response(), content

    with mock.patch('core.results_dashboard.time.sleep') as sleep_mock:
      with mock.patch.object(
          results_dashboard.httplib2.Http, 'request', new=request):
        upload_result = results_dashboard.SendResults(
            self.perf_data,
            'dummy_benchmark',
            self.dashboard_url,
            send_as_histograms=True,
            token_generator_callback=self.dummy_token_generator,
            num_retries=5)
        self.assertFalse(upload_result)
        self.assertEqual(
            sleep_mock.mock_calls,
            [call(15), call(30),
             call(60), call(120),
             call(240)])

  def testSendHistogramJsonTypeWarning(self):

    def request(*args, **kwargs):
      del args, kwargs
      content = 'non json response'

      class Response(object):
        status = 200

      return Response(), content

    with mock.patch('core.results_dashboard.time.sleep') as sleep_mock:
      with mock.patch.object(
          results_dashboard.httplib2.Http, 'request', new=request):

        upload_result = results_dashboard.SendResults(
            self.perf_data,
            'dummy_benchmark',
            self.dashboard_url,
            send_as_histograms=True,
            token_generator_callback=self.dummy_token_generator,
            num_retries=5)

        # upload should still be successful, despite failing to fetch token.
        self.assertTrue(upload_result)
        self.assertFalse(sleep_mock.mock_calls)

  def testSendHistogramJsonValueWarning(self):

    def request(*args, **kwargs):
      del args, kwargs
      content = json.dumps(None)

      class Response(object):
        status = 200

      return Response(), content

    with mock.patch('core.results_dashboard.time.sleep') as sleep_mock:
      with mock.patch.object(
          results_dashboard.httplib2.Http, 'request', new=request):

        upload_result = results_dashboard.SendResults(
            self.perf_data,
            'dummy_benchmark',
            self.dashboard_url,
            send_as_histograms=True,
            token_generator_callback=self.dummy_token_generator,
            num_retries=5)

        # upload should still be successful, despite failing to fetch token.
        self.assertTrue(upload_result)
        self.assertFalse(sleep_mock.mock_calls)

  def testSuccessfulSendHistogramJson(self):

    def request(*args, **kwargs):
      del args, kwargs
      content = json.dumps(self.dummy_token)

      class Response(object):
        status = 200

      return Response(), content

    with mock.patch('core.results_dashboard.time.sleep') as sleep_mock:
      with mock.patch.object(
          results_dashboard.httplib2.Http, 'request', new=request):
        with mock.patch.object(logging, 'info') as log_mock:

          upload_result = results_dashboard.SendResults(
              self.perf_data,
              'dummy_benchmark',
              self.dashboard_url,
              send_as_histograms=True,
              token_generator_callback=self.dummy_token_generator,
              num_retries=5)

          self.assertTrue(upload_result)
          self.assertFalse(sleep_mock.mock_calls)

          # check token id is logged to info.
          found_token = False
          for log_call in log_mock.call_args_list:
            log = log_call[0][0] % tuple(log_call[0][1:])
            if 'dummy1234' in log:
              found_token = True
              break
          self.assertTrue(
              found_token, msg='Upload completion token not found in logs.')