#!/usr/bin/env vpython3
# Copyright 2013 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Unit tests for owners_finder.py."""

import os
import sys
import unittest
from unittest import mock

sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))


import owners_finder
import owners_client

ben = "ben@example.com"
brett = "brett@example.com"
darin = "darin@example.com"
jochen = "jochen@example.com"
john = "john@example.com"
ken = "ken@example.com"
peter = "peter@example.com"
tom = "tom@example.com"
nonowner = "nonowner@example.com"


def owners_file(*email_addresses, **kwargs):
    s = ""
    if kwargs.get("comment"):
        s += "# %s\n" % kwargs.get("comment")
    if kwargs.get("noparent"):
        s += "set noparent\n"
    return s + "\n".join(email_addresses) + "\n"


class TestClient(owners_client.OwnersClient):
    def __init__(self):
        super(TestClient, self).__init__()
        self.owners_by_path = {
            "DEPS": [ken, peter, tom],
            "base/vlog.h": [ken, peter, tom],
            "chrome/browser/defaults.h": [brett, ben, ken, peter, tom],
            "chrome/gpu/gpu_channel.h": [ken, ben, brett, ken, peter, tom],
            "chrome/renderer/gpu/gpu_channel_host.h": [
                peter,
                ben,
                brett,
                ken,
                tom,
            ],
            "chrome/renderer/safe_browsing/scorer.h": [
                peter,
                ben,
                brett,
                ken,
                tom,
            ],
            "content/content.gyp": [john, darin],
            "content/bar/foo.cc": [john, darin],
            "content/baz/froboz.h": [brett, john, darin],
            "content/baz/ugly.cc": [brett, john, darin],
            "content/baz/ugly.h": [brett, john, darin],
            "content/common/common.cc": [jochen, john, darin],
            "content/foo/foo.cc": [jochen, john, darin],
            "content/views/pie.h": [ben, john, self.EVERYONE],
        }

    def ListOwners(self, path):
        path = path.replace(os.sep, "/")
        return self.owners_by_path[path]


class OutputInterceptedOwnersFinder(owners_finder.OwnersFinder):
    def __init__(self, files, author, reviewers, client, disable_color=False):
        super(OutputInterceptedOwnersFinder, self).__init__(
            files, author, reviewers, client, disable_color=disable_color
        )
        self.output = []
        self.indentation_stack = []

    def resetText(self):
        self.output = []
        self.indentation_stack = []

    def indent(self):
        self.indentation_stack.append(self.output)
        self.output = []

    def unindent(self):
        block = self.output
        self.output = self.indentation_stack.pop()
        self.output.append(block)

    def writeln(self, text=""):
        self.output.append(text)


class _BaseTestCase(unittest.TestCase):
    default_files = [
        "base/vlog.h",
        "chrome/browser/defaults.h",
        "chrome/gpu/gpu_channel.h",
        "chrome/renderer/gpu/gpu_channel_host.h",
        "chrome/renderer/safe_browsing/scorer.h",
        "content/content.gyp",
        "content/bar/foo.cc",
        "content/baz/ugly.cc",
        "content/baz/ugly.h",
        "content/views/pie.h",
    ]

    def ownersFinder(self, files, author=nonowner, reviewers=None):
        reviewers = reviewers or []
        return OutputInterceptedOwnersFinder(
            files, author, reviewers, TestClient(), disable_color=True
        )

    def defaultFinder(self):
        return self.ownersFinder(self.default_files)


class OwnersFinderTests(_BaseTestCase):
    def test_constructor(self):
        self.assertNotEqual(self.defaultFinder(), None)

    def test_skip_files_owned_by_reviewers(self):
        files = [
            "chrome/browser/defaults.h",  # owned by brett
            "content/bar/foo.cc",  # not owned by brett
        ]
        finder = self.ownersFinder(files, reviewers=[brett])
        self.assertEqual(finder.unreviewed_files, {"content/bar/foo.cc"})

    def test_skip_files_owned_by_author(self):
        files = [
            "chrome/browser/defaults.h",  # owned by brett
            "content/bar/foo.cc",  # not owned by brett
        ]
        finder = self.ownersFinder(files, author=brett)
        self.assertEqual(finder.unreviewed_files, {"content/bar/foo.cc"})

    def test_native_path_sep(self):
        # Create a path with backslashes on Windows to make sure these are
        # handled. This test is a harmless duplicate on other platforms.
        native_slashes_path = "chrome/browser/defaults.h".replace("/", os.sep)
        files = [
            native_slashes_path,  # owned by brett
            "content/bar/foo.cc",  # not owned by brett
        ]
        finder = self.ownersFinder(files, reviewers=[brett])
        self.assertEqual(finder.unreviewed_files, {"content/bar/foo.cc"})

    @mock.patch("owners_client.OwnersClient.ScoreOwners")
    def test_reset(self, mockScoreOwners):
        mockScoreOwners.return_value = [
            brett,
            darin,
            john,
            peter,
            ken,
            ben,
            tom,
        ]
        finder = self.defaultFinder()
        for _ in range(2):
            expected = [brett, darin, john, peter, ken, ben, tom]
            self.assertEqual(finder.owners_queue, expected)
            self.assertEqual(
                finder.unreviewed_files,
                {
                    "base/vlog.h",
                    "chrome/browser/defaults.h",
                    "chrome/gpu/gpu_channel.h",
                    "chrome/renderer/gpu/gpu_channel_host.h",
                    "chrome/renderer/safe_browsing/scorer.h",
                    "content/content.gyp",
                    "content/bar/foo.cc",
                    "content/baz/ugly.cc",
                    "content/baz/ugly.h",
                },
            )
            self.assertEqual(finder.selected_owners, set())
            self.assertEqual(finder.deselected_owners, set())
            self.assertEqual(finder.reviewed_by, {})
            self.assertEqual(finder.output, [])

            finder.select_owner(john)
            finder.reset()
            finder.resetText()

    @mock.patch("owners_client.OwnersClient.ScoreOwners")
    def test_select(self, mockScoreOwners):
        mockScoreOwners.return_value = [
            brett,
            darin,
            john,
            peter,
            ken,
            ben,
            tom,
        ]
        finder = self.defaultFinder()
        finder.select_owner(john)
        self.assertEqual(finder.owners_queue, [brett, peter, ken, ben, tom])
        self.assertEqual(finder.selected_owners, {john})
        self.assertEqual(finder.deselected_owners, {darin})
        self.assertEqual(
            finder.reviewed_by,
            {
                "content/bar/foo.cc": john,
                "content/baz/ugly.cc": john,
                "content/baz/ugly.h": john,
                "content/content.gyp": john,
            },
        )
        self.assertEqual(
            finder.output, ["Selected: " + john, "Deselected: " + darin]
        )

        finder = self.defaultFinder()
        finder.select_owner(darin)
        self.assertEqual(finder.owners_queue, [brett, peter, ken, ben, tom])
        self.assertEqual(finder.selected_owners, {darin})
        self.assertEqual(finder.deselected_owners, {john})
        self.assertEqual(
            finder.reviewed_by,
            {
                "content/bar/foo.cc": darin,
                "content/baz/ugly.cc": darin,
                "content/baz/ugly.h": darin,
                "content/content.gyp": darin,
            },
        )
        self.assertEqual(
            finder.output, ["Selected: " + darin, "Deselected: " + john]
        )

        finder = self.defaultFinder()
        finder.select_owner(brett)
        expected = [darin, john, peter, ken, tom]
        self.assertEqual(finder.owners_queue, expected)
        self.assertEqual(finder.selected_owners, {brett})
        self.assertEqual(finder.deselected_owners, {ben})
        self.assertEqual(
            finder.reviewed_by,
            {
                "chrome/browser/defaults.h": brett,
                "chrome/gpu/gpu_channel.h": brett,
                "chrome/renderer/gpu/gpu_channel_host.h": brett,
                "chrome/renderer/safe_browsing/scorer.h": brett,
                "content/baz/ugly.cc": brett,
                "content/baz/ugly.h": brett,
            },
        )
        self.assertEqual(
            finder.output, ["Selected: " + brett, "Deselected: " + ben]
        )

    @mock.patch("owners_client.OwnersClient.ScoreOwners")
    def test_deselect(self, mockScoreOwners):
        mockScoreOwners.return_value = [
            brett,
            darin,
            john,
            peter,
            ken,
            ben,
            tom,
        ]
        finder = self.defaultFinder()
        finder.deselect_owner(john)
        self.assertEqual(finder.owners_queue, [brett, peter, ken, ben, tom])
        self.assertEqual(finder.selected_owners, {darin})
        self.assertEqual(finder.deselected_owners, {john})
        self.assertEqual(
            finder.reviewed_by,
            {
                "content/bar/foo.cc": darin,
                "content/baz/ugly.cc": darin,
                "content/baz/ugly.h": darin,
                "content/content.gyp": darin,
            },
        )
        self.assertEqual(
            finder.output, ["Deselected: " + john, "Selected: " + darin]
        )

    def test_print_file_info(self):
        finder = self.defaultFinder()
        finder.print_file_info("chrome/browser/defaults.h")
        self.assertEqual(finder.output, ["chrome/browser/defaults.h [5]"])
        finder.resetText()

        finder.print_file_info("chrome/renderer/gpu/gpu_channel_host.h")
        self.assertEqual(
            finder.output, ["chrome/renderer/gpu/gpu_channel_host.h [5]"]
        )

    def test_print_file_info_detailed(self):
        finder = self.defaultFinder()
        finder.print_file_info_detailed("chrome/browser/defaults.h")
        self.assertEqual(
            finder.output,
            ["chrome/browser/defaults.h", [ben, brett, ken, peter, tom]],
        )
        finder.resetText()

        finder.print_file_info_detailed(
            "chrome/renderer/gpu/gpu_channel_host.h"
        )
        self.assertEqual(
            finder.output,
            [
                "chrome/renderer/gpu/gpu_channel_host.h",
                [ben, brett, ken, peter, tom],
            ],
        )


if __name__ == "__main__":
    unittest.main()
