# Copyright (C) 2009, 2010, 2016 Canonical Ltd
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA

"""Tests for controldir implementations - push."""

from breezy.tests.per_controldir import TestCaseWithControlDir

from ...controldir import NoColocatedBranchSupport
from ...errors import LossyPushToSameVCS, NoSuchRevision, TagsNotSupported
from ...revision import NULL_REVISION
from .. import TestNotApplicable


class TestPush(TestCaseWithControlDir):
    def create_simple_tree(self):
        tree = self.make_branch_and_tree("tree")
        self.build_tree(["tree/a"])
        tree.add(["a"])
        rev_1 = tree.commit("one")
        return tree, rev_1

    def test_push_new_branch(self):
        tree, rev_1 = self.create_simple_tree()
        dir = self.make_repository("dir").controldir
        result = dir.push_branch(tree.branch)
        self.assertEqual(tree.branch, result.source_branch)
        self.assertEqual(dir.open_branch().base, result.target_branch.base)
        self.assertEqual(dir.open_branch().base, tree.branch.get_push_location())

    def test_push_to_colocated_active(self):
        tree, rev_1 = self.create_simple_tree()
        dir = self.make_repository("dir").controldir
        try:
            result = dir.push_branch(tree.branch, name="colo")
        except NoColocatedBranchSupport:
            raise TestNotApplicable("no colocated branch support")
        self.assertEqual(tree.branch, result.source_branch)
        self.assertEqual(dir.open_branch(name="colo").base, result.target_branch.base)
        self.assertEqual(
            dir.open_branch(name="colo").base, tree.branch.get_push_location()
        )

    def test_push_to_colocated_new_inactive(self):
        tree, rev_1 = self.create_simple_tree()
        target_tree = self.make_branch_and_tree("dir")
        rev_o = target_tree.commit("another")
        try:
            result = target_tree.branch.controldir.push_branch(tree.branch, name="colo")
        except NoColocatedBranchSupport:
            raise TestNotApplicable("no colocated branch support")
        target_branch = target_tree.branch.controldir.open_branch(name="colo")
        self.assertEqual(tree.branch, result.source_branch)
        self.assertEqual(target_tree.last_revision(), rev_o)
        self.assertEqual(target_tree.branch.last_revision(), rev_o)
        self.assertEqual(target_branch.base, result.target_branch.base)
        self.assertEqual(target_branch.base, tree.branch.get_push_location())
        self.assertNotEqual(target_branch.controldir.open_branch(name="").name, "colo")

    def test_push_to_colocated_existing_inactive(self):
        tree, rev_1 = self.create_simple_tree()
        target_tree = self.make_branch_and_tree("dir")
        rev_o = target_tree.commit("another")
        try:
            target_tree.branch.controldir.create_branch(name="colo")
        except NoColocatedBranchSupport:
            raise TestNotApplicable("no colocated branch support")

        try:
            result = target_tree.branch.controldir.push_branch(tree.branch, name="colo")
        except NoColocatedBranchSupport:
            raise TestNotApplicable("no colocated branch support")
        target_branch = target_tree.branch.controldir.open_branch(name="colo")
        self.assertEqual(tree.branch, result.source_branch)
        self.assertEqual(target_tree.last_revision(), rev_o)
        self.assertEqual(target_tree.branch.last_revision(), rev_o)
        self.assertEqual(target_branch.base, result.target_branch.base)
        self.assertEqual(target_branch.base, tree.branch.get_push_location())

    def test_push_no_such_revision(self):
        tree, rev_1 = self.create_simple_tree()
        dir = self.make_repository("dir").controldir
        self.assertRaises(
            NoSuchRevision, dir.push_branch, tree.branch, revision_id=b"idonotexist"
        )

    def test_push_new_branch_fetch_tags(self):
        builder = self.make_branch_builder("from")
        builder.start_series()
        rev_1 = builder.build_snapshot(
            None,
            [
                ("add", ("", None, "directory", "")),
                ("add", ("filename", None, "file", b"content")),
            ],
        )
        rev_2 = builder.build_snapshot(
            [rev_1], [("modify", ("filename", b"new-content\n"))]
        )
        rev_3 = builder.build_snapshot(
            [rev_1], [("modify", ("filename", b"new-new-content\n"))]
        )
        builder.finish_series()
        branch = builder.get_branch()
        try:
            branch.tags.set_tag("atag", rev_2)
        except TagsNotSupported:
            raise TestNotApplicable("source format does not support tags")

        dir = self.make_repository("target").controldir
        branch.get_config().set_user_option("branch.fetch_tags", True)
        result = dir.push_branch(branch)
        self.assertEqual(
            {rev_1, rev_2, rev_3},
            set(result.source_branch.repository.all_revision_ids()),
        )
        self.assertEqual({"atag": rev_2}, result.source_branch.tags.get_tag_dict())

    def test_push_new_branch_lossy(self):
        tree, rev_1 = self.create_simple_tree()
        dir = self.make_repository("dir").controldir
        self.assertRaises(LossyPushToSameVCS, dir.push_branch, tree.branch, lossy=True)

    def test_push_new_empty(self):
        tree = self.make_branch_and_tree("tree")
        dir = self.make_repository("dir").controldir
        result = dir.push_branch(tree.branch)
        self.assertEqual(tree.branch.base, result.source_branch.base)
        self.assertEqual(dir.open_branch().base, result.target_branch.base)

    def test_push_incremental(self):
        tree, rev1 = self.create_simple_tree()
        dir = self.make_repository("dir").controldir
        dir.push_branch(tree.branch)
        self.build_tree(["tree/b"])
        tree.add(["b"])
        rev_2 = tree.commit("two")
        result = dir.push_branch(tree.branch)
        self.assertEqual(tree.last_revision(), result.branch_push_result.new_revid)
        self.assertEqual(2, result.branch_push_result.new_revno)
        self.assertEqual(tree.branch.base, result.source_branch.base)
        self.assertEqual(dir.open_branch().base, result.target_branch.base)

    def test_push_tag_selector(self):
        tree, rev1 = self.create_simple_tree()
        try:
            tree.branch.tags.set_tag("tag1", rev1)
        except TagsNotSupported:
            raise TestNotApplicable("tags not supported")
        tree.branch.tags.set_tag("tag2", rev1)
        dir = self.make_repository("dir").controldir
        dir.push_branch(tree.branch, tag_selector=lambda x: x == "tag1")
        self.assertEqual({"tag1": rev1}, dir.open_branch().tags.get_tag_dict())
