# Copyright (C) 2020 Jelmer Vernooij <jelmer@jelmer.uk>
#
# 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 breezy.git.tests."""

from __future__ import absolute_import

import stat
from unittest import TestCase

from dulwich.objects import Tree, Blob

from breezy.delta import TreeDelta
from breezy.bzr.inventorytree import InventoryTreeChange as TreeChange
from breezy.git.tree import (
    changes_from_git_changes,
    tree_delta_from_git_changes,
    )

from breezy.git.mapping import default_mapping


class ChangesFromGitChangesTests(TestCase):

    def setUp(self):
        super(ChangesFromGitChangesTests, self).setUp()
        self.maxDiff = None
        self.mapping = default_mapping

    def transform(
            self, changes, specific_files=None, include_unchanged=False,
            source_extras=None, target_extras=None):
        return list(changes_from_git_changes(
            changes, self.mapping, specific_files=specific_files,
            include_unchanged=include_unchanged, source_extras=source_extras,
            target_extras=target_extras))

    def test_empty(self):
        self.assertEqual([], self.transform([]))

    def test_modified(self):
        a = Blob.from_string(b'a')
        b = Blob.from_string(b'b')
        self.assertEqual([
            TreeChange(
                b'git:a', ('a', 'a'), True, (True, True),
                (b'TREE_ROOT', b'TREE_ROOT'), ('a', 'a'), ('file', 'file'),
                (False, False), False)
            ], self.transform([
            ('modify',
            (b'a', stat.S_IFREG|0o644, a), (b'a', stat.S_IFREG|0o644, b))]))

    def test_kind_changed(self):
        a = Blob.from_string(b'a')
        b = Blob.from_string(b'target')
        self.assertEqual([
            TreeChange(
                b'git:a', ('a', 'a'), True, (True, True),
                (b'TREE_ROOT', b'TREE_ROOT'), ('a', 'a'), ('file', 'symlink'),
                (False, False), False)
            ], self.transform([
            ('modify',
            (b'a', stat.S_IFREG|0o644, a), (b'a', stat.S_IFLNK, b))]))

    def test_rename_no_changes(self):
        a = Blob.from_string(b'a')
        self.assertEqual([
            TreeChange(
                b'git:old', ('old', 'a'), False, (True, True),
                (b'TREE_ROOT', b'TREE_ROOT'), ('old', 'a'),
                ('file', 'file'), (False, False), False)
            ], self.transform([
            ('rename',
            (b'old', stat.S_IFREG|0o644, a), (b'a', stat.S_IFREG|0o644, a))]))

    def test_rename_and_modify(self):
        a = Blob.from_string(b'a')
        b = Blob.from_string(b'b')
        self.assertEqual([
            TreeChange(
                b'git:a', ('a', 'b'), True, (True, True),
                (b'TREE_ROOT', b'TREE_ROOT'), ('a', 'b'),
                ('file', 'file'), (False, False), False)
            ], self.transform([
            ('rename',
            (b'a', stat.S_IFREG|0o644, a), (b'b', stat.S_IFREG|0o644, b))]))

    def test_copy_no_changes(self):
        a = Blob.from_string(b'a')
        self.assertEqual([
            TreeChange(
                b'git:a', ('old', 'a'), False, (True, True),
                (b'TREE_ROOT', b'TREE_ROOT'), ('old', 'a'),
                ('file', 'file'), (False, False), True)
            ], self.transform([
            ('copy',
            (b'old', stat.S_IFREG|0o644, a), (b'a', stat.S_IFREG|0o644, a))]))

    def test_copy_and_modify(self):
        a = Blob.from_string(b'a')
        b = Blob.from_string(b'b')
        self.assertEqual([
            TreeChange(
                b'git:b', ('a', 'b'), True, (True, True),
                (b'TREE_ROOT', b'TREE_ROOT'), ('a', 'b'),
                ('file', 'file'), (False, False), True)
            ], self.transform([
            ('copy',
            (b'a', stat.S_IFREG|0o644, a), (b'b', stat.S_IFREG|0o644, b))]))

    def test_add(self):
        b = Blob.from_string(b'b')
        self.assertEqual([
            TreeChange(
                b'git:a', (None, 'a'), True, (False, True), (None, b'TREE_ROOT'),
                (None, 'a'), (None, 'file'), (None, False), False)
            ], self.transform([
            ('add',
            (None, None, None), (b'a', stat.S_IFREG|0o644, b))]))

    def test_delete(self):
        b = Blob.from_string(b'b')
        self.assertEqual([
            TreeChange(
                b'git:a', ('a', None), True, (True, False), (b'TREE_ROOT', None),
                ('a', None), ('file', None), (False, None), False)
            ], self.transform([
            ('remove',
            (b'a', stat.S_IFREG|0o644, b), (None, None, None))]))

    def test_unchanged(self):
        b = Blob.from_string(b'b')
        self.assertEqual([
            TreeChange(
                b'git:a', ('a', 'a'), False, (True, True),
                (b'TREE_ROOT', b'TREE_ROOT'), ('a', 'a'), ('file', 'file'),
                (False, False), False)
            ], self.transform([
            ('unchanged',
            (b'a', stat.S_IFREG|0o644, b), (b'a', stat.S_IFREG|0o644, b))],
            include_unchanged=True))
        self.assertEqual([], self.transform([
            ('unchanged',
            (b'a', stat.S_IFREG|0o644, b), (b'a', stat.S_IFREG|0o644, b))],
            include_unchanged=False))

    def test_unversioned(self):
        b = Blob.from_string(b'b')
        self.assertEqual([
            TreeChange(
                None, (None, 'a'), True, (False, False),
                (None, b'TREE_ROOT'), (None, 'a'), (None, 'file'),
                (None, False), False)
            ], self.transform([
            ('add',
            (None, None, None), (b'a', stat.S_IFREG|0o644, b))],
            target_extras=set([b'a'])))
        self.assertEqual([
            TreeChange(
                None, ('a', 'a'), False, (False, False),
                (b'TREE_ROOT', b'TREE_ROOT'), ('a', 'a'), ('file', 'file'),
                (False, False), False)
            ], self.transform([
            ('add',
            (b'a', stat.S_IFREG|0o644, b), (b'a', stat.S_IFREG|0o644, b))],
            source_extras=set([b'a']),
            target_extras=set([b'a'])))


class DeltaFromGitChangesTests(TestCase):

    def setUp(self):
        super(DeltaFromGitChangesTests, self).setUp()
        self.maxDiff = None
        self.mapping = default_mapping

    def transform(
            self, changes, specific_files=None,
            require_versioned=False, include_root=False,
            source_extras=None, target_extras=None):
        return tree_delta_from_git_changes(
            changes, (self.mapping, self.mapping),
            specific_files=specific_files,
            require_versioned=require_versioned, include_root=include_root,
            source_extras=source_extras, target_extras=target_extras)

    def test_empty(self):
        self.assertEqual(TreeDelta(), self.transform([]))

    def test_modified(self):
        a = Blob.from_string(b'a')
        b = Blob.from_string(b'b')
        delta = self.transform([
            ('modify',
            (b'a', stat.S_IFREG|0o644, a), (b'a', stat.S_IFREG|0o644, b))])
        expected_delta = TreeDelta()
        expected_delta.modified.append(TreeChange(
            b'git:a', ('a', 'a'), True, (True, True),
            (b'TREE_ROOT', b'TREE_ROOT'), ('a', 'a'), ('file', 'file'),
            (False, False), False))
        self.assertEqual(expected_delta, delta)

    def test_rename_no_changes(self):
        a = Blob.from_string(b'a')
        delta = self.transform([
            ('rename',
            (b'old', stat.S_IFREG|0o644, a), (b'a', stat.S_IFREG|0o644, a))])
        expected_delta = TreeDelta()
        expected_delta.renamed.append(
            TreeChange(
                b'git:old', ('old', 'a'), False, (True, True),
                (b'TREE_ROOT', b'TREE_ROOT'), ('old', 'a'),
                ('file', 'file'), (False, False), False))
        self.assertEqual(expected_delta, delta)

    def test_rename_and_modify(self):
        a = Blob.from_string(b'a')
        b = Blob.from_string(b'b')
        delta = self.transform([
            ('rename',
            (b'a', stat.S_IFREG|0o644, a), (b'b', stat.S_IFREG|0o644, b))])
        expected_delta = TreeDelta()
        expected_delta.renamed.append(
            TreeChange(
                b'git:a', ('a', 'b'), True, (True, True),
                (b'TREE_ROOT', b'TREE_ROOT'), ('a', 'b'),
                ('file', 'file'), (False, False), False))
        self.assertEqual(delta, expected_delta)

    def test_copy_no_changes(self):
        a = Blob.from_string(b'a')
        delta = self.transform([
            ('copy',
            (b'old', stat.S_IFREG|0o644, a), (b'a', stat.S_IFREG|0o644, a))])
        expected_delta = TreeDelta()
        expected_delta.copied.append(TreeChange(
                b'git:a', ('old', 'a'), False, (True, True),
                (b'TREE_ROOT', b'TREE_ROOT'), ('old', 'a'),
                ('file', 'file'), (False, False), True))
        self.assertEqual(expected_delta, delta)

    def test_copy_and_modify(self):
        a = Blob.from_string(b'a')
        b = Blob.from_string(b'b')
        delta = self.transform([
            ('copy',
            (b'a', stat.S_IFREG|0o644, a), (b'b', stat.S_IFREG|0o644, b))])
        expected_delta = TreeDelta()
        expected_delta.copied.append(TreeChange(
                b'git:b', ('a', 'b'), True, (True, True),
                (b'TREE_ROOT', b'TREE_ROOT'), ('a', 'b'),
                ('file', 'file'), (False, False), True))
        self.assertEqual(expected_delta, delta)

    def test_add(self):
        b = Blob.from_string(b'b')
        delta = self.transform([
            ('add',
            (None, None, None), (b'a', stat.S_IFREG|0o644, b))])
        expected_delta = TreeDelta()
        expected_delta.added.append(TreeChange(
                b'git:a', (None, 'a'), True, (False, True), (None, b'TREE_ROOT'),
                (None, 'a'), (None, 'file'), (None, False), False))
        self.assertEqual(delta, expected_delta)

    def test_delete(self):
        b = Blob.from_string(b'b')
        delta = self.transform([
            ('remove',
            (b'a', stat.S_IFREG|0o644, b), (None, None, None))])
        expected_delta = TreeDelta()
        expected_delta.removed.append(TreeChange(
                b'git:a', ('a', None), True, (True, False), (b'TREE_ROOT', None),
                ('a', None), ('file', None), (False, None), False))
        self.assertEqual(delta, expected_delta)

    def test_unchanged(self):
        b = Blob.from_string(b'b')
        delta = self.transform([
            ('unchanged',
            (b'a', stat.S_IFREG|0o644, b), (b'a', stat.S_IFREG|0o644, b))])
        expected_delta = TreeDelta()
        expected_delta.unchanged.append(TreeChange(
                b'git:a', ('a', 'a'), False, (True, True),
                (b'TREE_ROOT', b'TREE_ROOT'), ('a', 'a'), ('file', 'file'),
                (False, False), False))

    def test_unversioned(self):
        b = Blob.from_string(b'b')
        delta = self.transform([
            ('add',
            (None, None, None), (b'a', stat.S_IFREG|0o644, b))],
            target_extras=set([b'a']))
        expected_delta = TreeDelta()
        expected_delta.unversioned.append(
            TreeChange(
                None, (None, 'a'), True, (False, False),
                (None, b'TREE_ROOT'), (None, 'a'), (None, 'file'),
                (None, False), False))
        self.assertEqual(delta, expected_delta)
        delta = self.transform([
            ('add',
            (b'a', stat.S_IFREG|0o644, b), (b'a', stat.S_IFREG|0o644, b))],
            source_extras=set([b'a']),
            target_extras=set([b'a']))
        expected_delta = TreeDelta()
        expected_delta.unversioned.append(TreeChange(
            None, ('a', 'a'), False, (False, False),
            (b'TREE_ROOT', b'TREE_ROOT'), ('a', 'a'), ('file', 'file'),
            (False, False), False))
        self.assertEqual(delta, expected_delta)

    def test_kind_change(self):
        a = Blob.from_string(b'a')
        b = Blob.from_string(b'target')
        delta = self.transform([
            ('modify',
             (b'a', stat.S_IFREG|0o644, a), (b'a', stat.S_IFLNK, b))])
        expected_delta = TreeDelta()
        expected_delta.kind_changed.append(TreeChange(
            b'git:a', ('a', 'a'), True, (True, True),
            (b'TREE_ROOT', b'TREE_ROOT'), ('a', 'a'), ('file', 'symlink'),
            (False, False), False))
        self.assertEqual(expected_delta, delta)
