# Copyright 2015 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import unittest

from google.appengine.ext import ndb

from dashboard.common import testing_common
from dashboard.common import utils
from dashboard.models import alert_group
from dashboard.models import anomaly
from dashboard.models import sheriff


class AnomalyGroupingTest(testing_common.TestCase):
  """Test case for the behavior of updating anomaly groups."""

  def _AddAnomalies(self):
    """Adds a set of sample data used in the tests below."""
    testing_common.AddTests(
        ['ChromiumGPU'], ['linux-release'],
        {'scrolling_benchmark': {'first_paint': {}}})
    first_paint_key = utils.TestKey(
        'ChromiumGPU/linux-release/scrolling_benchmark/first_paint')
    first_paint_test = first_paint_key.get()
    first_paint_test.improvement_direction = anomaly.DOWN
    first_paint_test.put()

    group_keys = [
        alert_group.AlertGroup(
            start_revision=3000,
            end_revision=4000,
            alert_kind='Anomaly',
            test_suites=['scrolling_benchmark']).put(),
        alert_group.AlertGroup(
            start_revision=6000,
            end_revision=8000,
            alert_kind='Anomaly',
            test_suites=['scrolling_benchmark']).put(),
    ]

    anomaly_keys = [
        anomaly.Anomaly(
            start_revision=2000,
            end_revision=4000,
            bug_id=12345,
            test=first_paint_key).put(),
        anomaly.Anomaly(
            start_revision=3000,
            end_revision=5000,
            bug_id=12345,
            test=first_paint_key).put(),
        anomaly.Anomaly(
            start_revision=6000,
            end_revision=8000,
            bug_id=None,
            test=first_paint_key).put(),
    ]

    anomalies = ndb.get_multi(anomaly_keys)

    # Add these anomalies to groups and put them again.
    anomalies[0].group = group_keys[0]
    anomalies[0].put()
    anomalies[1].group = group_keys[0]
    anomalies[1].put()
    anomalies[2].group = group_keys[1]
    anomalies[2].put()

    # Note that after these anomalies are added, the state of the two groups
    # is updated. Also, the first two anomalies are in the same group.
    self.assertEqual(anomalies[0].group, anomalies[1].group)
    self.assertNotEqual(anomalies[0].group, anomalies[2].group)
    return anomalies

  def testUpdateAnomalyBugId_UpdatesGroupOfAnomaly(self):
    anomalies = self._AddAnomalies()

    # At first, two anomalies are in separate groups, and the second anomaly
    # has not been assigned a bug ID.
    self.assertNotEqual(anomalies[1].group, anomalies[2].group)
    self.assertEqual(12345, anomalies[1].bug_id)
    self.assertIsNone(anomalies[2].bug_id)

    # Test setting bug_id. Anomaly should be moved to new group.
    anomalies[2].bug_id = 12345
    anomalies[2].put()
    self.assertEqual(anomalies[1].bug_id, anomalies[2].bug_id)

  def testMarkAnomalyInvalid_AnomalyIsRemovedFromGroup(self):
    anomalies = self._AddAnomalies()

    # At first, two anomalies are in the same group.
    self.assertEqual(anomalies[0].group, anomalies[1].group)

    # Mark one of the alerts as invalid.
    self.assertEqual(12345, anomalies[1].bug_id)

    alert_group.ModifyAlertsAndAssociatedGroups([anomalies[1]], bug_id=-1)

    # Now, the alert marked as invalid has no group.
    # Also, the group's revision range has been updated accordingly.
    self.assertNotEqual(anomalies[0].group, anomalies[1].group)
    self.assertIsNone(anomalies[1].group)
    group = anomalies[0].group.get()
    self.assertEqual(2000, group.start_revision)
    self.assertEqual(4000, group.end_revision)

  def testUpdateAnomalyRevisionRange_UpdatesGroupRevisionRange(self):
    anomalies = self._AddAnomalies()

    # Add another anomaly to the same group as the first two anomalies,
    # but with a non-overlapping revision range.
    new_anomaly = anomaly.Anomaly(
        start_revision=3000,
        end_revision=4000,
        group=anomalies[0].group,
        test=utils.TestKey('master/bot/benchmark/metric'))
    new_anomaly.put()

    # Associate it with a group; alert_group.ModifyAlertsAndAssociatedGroups
    # will update the group's revision range here.
    alert_group.ModifyAlertsAndAssociatedGroups(
        [new_anomaly], start_revision=3010, end_revision=3020)