#!/usr/bin/env vpython3
"""Tests for split_cl."""

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 split_cl


class SplitClTest(unittest.TestCase):
    def testAddUploadedByGitClSplitToDescription(self):
        description = """Convert use of X to Y in $directory

<add some background about this conversion for the reviewers>

"""
        footers = 'Bug: 12345'

        added_line = 'This CL was uploaded by git cl split.'

        # Description without footers
        self.assertEqual(
            split_cl.AddUploadedByGitClSplitToDescription(description),
            description + added_line)
        # Description with footers
        self.assertEqual(
            split_cl.AddUploadedByGitClSplitToDescription(description +
                                                          footers),
            description + added_line + '\n\n' + footers)

    def testFormatDescriptionOrComment(self):
        description = "Converted use of X to Y in $directory."

        # One directory
        self.assertEqual(
            split_cl.FormatDescriptionOrComment(description, ["foo"]),
            "Converted use of X to Y in /foo.")

        # Many directories
        self.assertEqual(
            split_cl.FormatDescriptionOrComment(description, ["foo", "bar"]),
            "Converted use of X to Y in ['/foo', '/bar'].")

    def GetDirectoryBaseName(self, file_path):
        return os.path.basename(os.path.dirname(file_path))

    def MockSuggestOwners(self, paths, exclude=None):
        if not paths:
            return ["superowner"]
        return self.GetDirectoryBaseName(paths[0]).split(",")

    def MockIsFile(self, file_path):
        if os.path.basename(file_path) == "OWNERS":
            return "owner" in self.GetDirectoryBaseName(file_path)

        return True

    @mock.patch("os.path.isfile")
    def testSelectReviewersForFiles(self, mock_is_file):
        mock_is_file.side_effect = self.MockIsFile

        owners_client = mock.Mock(SuggestOwners=self.MockSuggestOwners,
                                  EVERYONE="*")
        cl = mock.Mock(owners_client=owners_client)

        files = [("M", os.path.join("foo", "owner1,owner2", "a.txt")),
                 ("M", os.path.join("foo", "owner1,owner2", "b.txt")),
                 ("M", os.path.join("bar", "owner1,owner2", "c.txt")),
                 ("M", os.path.join("bax", "owner2", "d.txt")),
                 ("M", os.path.join("baz", "owner3", "e.txt"))]

        files_split_by_reviewers = split_cl.SelectReviewersForFiles(
            cl, "author", files, 0)

        self.assertEqual(3, len(files_split_by_reviewers.keys()))
        info1 = files_split_by_reviewers[tuple(["owner1", "owner2"])]
        self.assertEqual(info1.files,
                         [("M", os.path.join("foo", "owner1,owner2", "a.txt")),
                          ("M", os.path.join("foo", "owner1,owner2", "b.txt")),
                          ("M", os.path.join("bar", "owner1,owner2", "c.txt"))])
        self.assertEqual(info1.owners_directories,
                         ["foo/owner1,owner2", "bar/owner1,owner2"])
        info2 = files_split_by_reviewers[tuple(["owner2"])]
        self.assertEqual(info2.files,
                         [("M", os.path.join("bax", "owner2", "d.txt"))])
        self.assertEqual(info2.owners_directories, ["bax/owner2"])
        info3 = files_split_by_reviewers[tuple(["owner3"])]
        self.assertEqual(info3.files,
                         [("M", os.path.join("baz", "owner3", "e.txt"))])
        self.assertEqual(info3.owners_directories, ["baz/owner3"])

    class UploadClTester:
        """Sets up test environment for testing split_cl.UploadCl()"""
        def __init__(self, test):
            self.mock_git_branches = self.StartPatcher("git_common.branches",
                                                       test)
            self.mock_git_branches.return_value = []
            self.mock_git_current_branch = self.StartPatcher(
                "git_common.current_branch", test)
            self.mock_git_current_branch.return_value = "branch_to_upload"
            self.mock_git_run = self.StartPatcher("git_common.run", test)
            self.mock_temporary_file = self.StartPatcher(
                "gclient_utils.temporary_file", test)
            self.mock_temporary_file(
            ).__enter__.return_value = "temporary_file0"
            self.mock_file_writer = self.StartPatcher("gclient_utils.FileWrite",
                                                      test)

        def StartPatcher(self, target, test):
            patcher = mock.patch(target)
            test.addCleanup(patcher.stop)
            return patcher.start()

        def DoUploadCl(self, directories, files, reviewers, cmd_upload):
            split_cl.UploadCl("branch_to_upload", "upstream_branch",
                              directories, files, "description", None,
                              reviewers, mock.Mock(), cmd_upload, True, True,
                              "topic", os.path.sep)

    def testUploadCl(self):
        """Tests commands run by UploadCl."""

        upload_cl_tester = self.UploadClTester(self)

        directories = ["dir0"]
        files = [("M", os.path.join("bar", "a.cc")),
                 ("D", os.path.join("foo", "b.cc"))]
        reviewers = {"reviewer1@gmail.com", "reviewer2@gmail.com"}
        mock_cmd_upload = mock.Mock()
        upload_cl_tester.DoUploadCl(directories, files, reviewers,
                                    mock_cmd_upload)

        abs_repository_path = os.path.abspath(os.path.sep)
        mock_git_run = upload_cl_tester.mock_git_run
        self.assertEqual(mock_git_run.call_count, 4)
        mock_git_run.assert_has_calls([
            mock.call("checkout", "-t", "upstream_branch", "-b",
                      "branch_to_upload_dir0_split"),
            mock.call("rm", os.path.join(abs_repository_path, "foo", "b.cc")),
            mock.call("checkout", "branch_to_upload", "--",
                      os.path.join(abs_repository_path, "bar", "a.cc")),
            mock.call("commit", "-F", "temporary_file0")
        ])

        expected_upload_args = [
            "-f", "-r", "reviewer1@gmail.com,reviewer2@gmail.com",
            "--cq-dry-run", "--send-mail", "--enable-auto-submit",
            "--topic=topic"
        ]
        mock_cmd_upload.assert_called_once_with(expected_upload_args)

    def testDontUploadClIfBranchAlreadyExists(self):
        """Tests that a CL is not uploaded if split branch already exists"""

        upload_cl_tester = self.UploadClTester(self)
        upload_cl_tester.mock_git_branches.return_value = [
            "branch0", "branch_to_upload_dir0_split"
        ]

        directories = ["dir0"]
        files = [("M", os.path.join("bar", "a.cc")),
                 ("D", os.path.join("foo", "b.cc"))]
        reviewers = {"reviewer1@gmail.com"}
        mock_cmd_upload = mock.Mock()
        upload_cl_tester.DoUploadCl(directories, files, reviewers,
                                    mock_cmd_upload)

        upload_cl_tester.mock_git_run.assert_not_called()
        mock_cmd_upload.assert_not_called()

    @mock.patch("gclient_utils.AskForData")
    def testCheckDescriptionBugLink(self, mock_ask_for_data):
        # Description contains bug link.
        self.assertTrue(split_cl.CheckDescriptionBugLink("Bug:1234"))
        self.assertEqual(mock_ask_for_data.call_count, 0)

        # Description does not contain bug link. User does not enter 'y' when
        # prompted.
        mock_ask_for_data.reset_mock()
        mock_ask_for_data.return_value = "m"
        self.assertFalse(split_cl.CheckDescriptionBugLink("Description"))
        self.assertEqual(mock_ask_for_data.call_count, 1)

        # Description does not contain bug link. User enters 'y' when prompted.
        mock_ask_for_data.reset_mock()
        mock_ask_for_data.return_value = "y"
        self.assertTrue(split_cl.CheckDescriptionBugLink("Description"))
        self.assertEqual(mock_ask_for_data.call_count, 1)


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