#!/usr/bin/python
# Copyright (C) 2019 Jelmer Vernooij
#
# 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 lintian_brush.patches."""

from io import BytesIO

from breezy.tests import (
    TestCase,
    TestCaseWithTransport,
)
from breezy.patches import (
    parse_patch,
    Patch,
)

from ..patches import (
    AppliedPatches,
    find_common_patch_suffix,
    find_patch_base,
    find_patches_branch,
    read_quilt_patches,
    read_quilt_series,
    tree_non_patches_changes,
    tree_patches_directory,
    upstream_with_applied_patches,
)


class ReadSeriesFileTests(TestCase):
    def test_comment(self):
        f = BytesIO(
            b"""\
# This file intentionally left blank.
"""
        )
        self.assertEqual(
            [("This", True, ["file", "intentionally", "left", "blank."])],
            list(read_quilt_series(f)),
        )

    def test_empty(self):
        f = BytesIO(b"\n")
        self.assertEqual([], list(read_quilt_series(f)))

    def test_empty_line(self):
        f = BytesIO(
            b"""\
patch1

patch2
"""
        )
        self.assertEqual(
            [
                ("patch1", False, []),
                ("patch2", False, []),
            ],
            list(read_quilt_series(f)),
        )

    def test_options(self):
        f = BytesIO(
            b"""\
name -p0
name2
"""
        )
        self.assertEqual(
            [
                ("name", False, ["-p0"]),
                ("name2", False, []),
            ],
            list(read_quilt_series(f)),
        )


class FindPatchBaseTests(TestCaseWithTransport):
    def setUp(self):
        super().setUp()
        self.tree = self.make_branch_and_tree(".")
        self.upstream_revid = self.tree.commit("upstream")
        self.build_tree_contents(
            [
                ("debian/",),
                (
                    "debian/changelog",
                    """\
blah (0.38) unstable; urgency=medium

  * Fix something

 -- Jelmer Vernooij <jelmer@debian.org>  Sat, 19 Oct 2019 15:21:53 +0000
 """,
                ),
            ]
        )
        self.tree.add(["debian", "debian/changelog"])

    def test_none(self):
        self.assertEqual(None, find_patch_base(self.tree))

    def test_upstream_dash(self):
        self.tree.branch.tags.set_tag("upstream-0.38", self.upstream_revid)
        self.assertEqual(self.upstream_revid, find_patch_base(self.tree))


class FindPatchBranchTests(TestCaseWithTransport):
    def make_named_branch_and_tree(self, path, name):
        dir = self.make_controldir(path)
        dir.create_repository()
        branch = dir.create_branch(name=name)
        dir.set_branch_reference(branch)
        return dir.create_workingtree()

    def test_none(self):
        tree = self.make_branch_and_tree(".")
        self.assertIs(None, find_patches_branch(tree))

    def test_patch_queue(self):
        master = self.make_named_branch_and_tree(".", name="master")
        master.branch.controldir.create_branch(name="patch-queue/master")

        self.assertEqual(
            "patch-queue/master", find_patches_branch(master).name)

    def test_patched_master(self):
        master = self.make_named_branch_and_tree(".", name="master")
        master.branch.controldir.create_branch(name="patched")
        self.assertEqual("patched", find_patches_branch(master).name)

    def test_patched_other(self):
        other = self.make_named_branch_and_tree(".", name="other")
        other.branch.controldir.create_branch(name="patched-other")
        self.assertEqual("patched-other", find_patches_branch(other).name)


class AppliedPatchesTests(TestCaseWithTransport):
    def test_apply_simple(self):
        tree = self.make_branch_and_tree(".")
        self.build_tree_contents([("a", "a\n")])
        tree.add("a")
        tree.commit("Add a")
        patch = parse_patch(
            b"""\
--- a/a
+++ b/a
@@ -1 +1 @@
-a
+b
""".splitlines(
                True
            )
        )
        with AppliedPatches(tree, [patch]) as newtree:
            self.assertEqual(b"b\n", newtree.get_file_text("a"))

    def test_apply_delete(self):
        tree = self.make_branch_and_tree(".")
        self.build_tree_contents([("a", "a\n")])
        tree.add("a")
        tree.commit("Add a")
        patch = parse_patch(
            b"""\
--- a/a
+++ /dev/null
@@ -1 +0,0 @@
-a
""".splitlines(
                True
            )
        )
        with AppliedPatches(tree, [patch]) as newtree:
            self.assertFalse(newtree.has_filename("a"))

    def test_apply_add(self):
        tree = self.make_branch_and_tree(".")
        self.build_tree_contents([("a", "a\n")])
        tree.add("a")
        tree.commit("Add a")
        patch = parse_patch(
            b"""\
--- /dev/null
+++ b/b
@@ -0,0 +1 @@
+b
""".splitlines(
                True
            )
        )
        with AppliedPatches(tree, [patch]) as newtree:
            self.assertEqual(b"b\n", newtree.get_file_text("b"))


class ReadQuiltPatchesTests(TestCaseWithTransport):
    def test_read_patches(self):
        patch = """\
--- a/a
+++ b/a
@@ -1,5 +1,5 @@
 line 1
 line 2
-line 3
+new line 3
 line 4
 line 5
"""
        tree = self.make_branch_and_tree(".")
        self.build_tree_contents(
            [
                ("debian/",),
                ("debian/patches/",),
                ("debian/patches/series", "foo\n"),
                ("debian/patches/foo", patch),
            ]
        )
        tree.add(
            ["debian", "debian/patches", "debian/patches/series",
             "debian/patches/foo"]
        )
        tree.commit("add patch")
        patches = list(read_quilt_patches(tree))
        self.assertEqual(1, len(patches))
        self.assertIsInstance(patches[0], Patch)
        self.assertEqual(patch.encode("utf-8"), patches[0].as_bytes())

    def test_no_series_file(self):
        t = self.make_branch_and_tree(".")
        self.assertEqual([], list(read_quilt_patches(t)))

    def test_comments(self):
        t = self.make_branch_and_tree(".")
        self.build_tree_contents(
            [
                ("debian/",),
                ("debian/patches/",),
                ("debian/patches/series",
                 "# This file intentionally left blank.\n"),
            ]
        )
        self.assertEqual([], list(read_quilt_patches(t)))


class UpstreamWithAppliedPatchesTests(TestCaseWithTransport):
    def setUp(self):
        super().setUp()
        self.tree = self.make_branch_and_tree(".")
        self.build_tree_contents([("afile", "some line\n")])
        self.tree.add("afile")
        self.upstream_revid = self.tree.commit("upstream")
        self.build_tree_contents(
            [
                ("debian/",),
                (
                    "debian/changelog",
                    """\
blah (0.38) unstable; urgency=medium

  * Fix something

 -- Jelmer Vernooij <jelmer@debian.org>  Sat, 19 Oct 2019 15:21:53 +0000
 """,
                ),
                ("debian/patches/",),
                ("debian/patches/series", "1.patch\n"),
                (
                    "debian/patches/1.patch",
                    """\
--- a/afile
+++ b/afile
@@ -1 +1 @@
-some line
+another line
--- /dev/null
+++ b/newfile
@@ -0,0 +1 @@
+new line
""",
                ),
                ("unchangedfile", "unchanged\n"),
            ]
        )
        self.tree.add(["debian", "debian/changelog", "unchangedfile"])

    def test_upstream_branch(self):
        self.tree.branch.tags.set_tag("upstream/0.38", self.upstream_revid)
        patches = list(read_quilt_patches(self.tree))
        with upstream_with_applied_patches(self.tree, patches) as t:
            self.assertEqual(b"another line\n", t.get_file_text("afile"))
            self.assertEqual(b"new line\n", t.get_file_text("newfile"))
            # TODO(jelmer): PreviewTree appears to be broken
            # self.assertEqual(b'unchanged\n',
            #                  t.get_file_text('unchangedfile'))


class TreePatchesNonPatchesTests(TestCaseWithTransport):
    def setUp(self):
        super().setUp()
        self.tree = self.make_branch_and_tree(".")
        self.build_tree_contents([("afile", "some line\n")])
        self.tree.add("afile")
        self.upstream_revid = self.tree.commit("upstream")
        self.build_tree_contents(
            [
                ("debian/",),
                (
                    "debian/changelog",
                    """\
blah (0.38) unstable; urgency=medium

  * Fix something

 -- Jelmer Vernooij <jelmer@debian.org>  Sat, 19 Oct 2019 15:21:53 +0000
 """,
                ),
                ("debian/patches/",),
                ("debian/patches/series", "1.patch\n"),
                (
                    "debian/patches/1.patch",
                    """\
--- a/afile
+++ b/afile
@@ -1 +1 @@
-some line
+another line
""",
                ),
            ]
        )
        self.tree.add(["debian", "debian/changelog"])

    def test_no_delta(self):
        self.tree.branch.tags.set_tag("upstream/0.38", self.upstream_revid)
        self.assertEqual(
            [], list(tree_non_patches_changes(self.tree, "debian/patches"))
        )

    def test_delta(self):
        self.tree.branch.tags.set_tag("upstream/0.38", self.upstream_revid)
        self.build_tree_contents([("anotherfile", "blah")])
        self.tree.add("anotherfile")
        self.assertEqual(
            1, len(list(tree_non_patches_changes(self.tree, "debian/patches")))
        )


class FindCommonPatchSuffixTests(TestCase):
    def test_simple(self):
        self.assertEqual(
            ".blah", find_common_patch_suffix(["foo.blah", "series"]))
        self.assertEqual(
            ".patch", find_common_patch_suffix(["foo.patch", "series"]))
        self.assertEqual(
            ".patch", find_common_patch_suffix(["series"]))
        self.assertEqual(
            ".blah", find_common_patch_suffix(["series"], ".blah"))
        self.assertEqual(
            "", find_common_patch_suffix(["series", "foo", "bar"]))
        self.assertEqual(
            ".patch",
            find_common_patch_suffix(["series", "foo.patch", "bar.patch"])
        )


class TreePatchesDirectoryTests(TestCaseWithTransport):
    def test_none(self):
        tree = self.make_branch_and_tree(".")
        self.assertEqual("debian/patches", tree_patches_directory(tree))

    def test_default(self):
        tree = self.make_branch_and_tree(".")
        tree.mkdir("debian")
        tree.mkdir("debian/patches")
        self.assertEqual("debian/patches", tree_patches_directory(tree))

    def test_custom(self):
        tree = self.make_branch_and_tree(".")
        tree.mkdir("debian")
        tree.mkdir("debian/patches-applied")
        self.build_tree_contents(
            [
                (
                    "debian/rules",
                    """\

export QUILT_PATCH_DIR := debian/patches-applied

all:

blah: bloe
\tfoo
""",
                )
            ]
        )
        tree.add("debian/rules")
        self.assertEqual(
            "debian/patches-applied", tree_patches_directory(tree))
