#!/usr/bin/env python3
# encoding: utf-8

# Unit tests for decopy
# Copyright (C) 2016 Margarita Manterola <marga@debian.org>
#
# License: ISC
#  Permission to use, copy, modify, and/or distribute this software for any
#  purpose with or without fee is hereby granted, provided that the above
#  copyright notice and this permission notice appear in all copies.
#  .
#  THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
#  REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
#  AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
#  INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
#  LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
#  OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
#  PERFORMANCE OF THIS SOFTWARE.

import unittest

from decopy import cmdoptions
from decopy import tree
from decopy import dep5
from decopy import datatypes


class TestCopyright(unittest.TestCase):

    def _processTree(self, path):
        '''Process the received tree and parse the copyright file.'''
        options = cmdoptions.process_options(['--no-progress', '--root', path])
        filetree = tree.RootInfo.build(options)
        copyright_ = dep5.Copyright.build(filetree, options)
        copyright_.process(filetree)
        return copyright_, options

    def testProcessEmpty(self):
        '''Parses a tree with no debian/copyright file.'''

        copyright_, _ = self._processTree('tests/testdata/two')

        # An empty copyright file should yield no groups after processing
        self.assertEqual(copyright_.groups, [])

    def testProcessMalformed(self):
        '''Parses a tree with a malformed debian/copyright file.'''

        copyright_, _ = self._processTree('tests/testdata/malformed')

        # An empty copyright file should yield no groups after processing
        self.assertEqual(copyright_.groups, [])

    def testProcessIncomplete(self):
        '''Parses a tree with an incomplete debian/copyright file.'''

        copyright_, options = self._processTree('tests/testdata/incomplete')

        # An incomplete copyright file should yield the incomplete groups after
        # processing
        expected_groups = [
            {
                'license': 'Unknown',
                'holders': [],
                'files':   ['debian/copyright'],
                'comment': '',
            },
            {
                'license': 'Unknown',
                'holders': [],
                'files':   ['test'],
                'comment': '',
            },
        ]
        self._checkSortedGroups(expected_groups, copyright_.groups, options)

    def testProcessSimple(self):
        '''Parses a debian/copyright with two entries (testdata/four).'''

        copyright_, options = self._processTree('tests/testdata/four')

        expected_groups = [
            {
                'license': 'LGPL-2+',
                'holders': ['2014-2016, Maximiliano Curia <maxy@debian.org>'],
                'files':   ['debian/compat', 'debian/copyright'],
                'comment': '',
            },
            {
                'license': 'GPL-2+',
                'holders': ['2016, Jane Doe <janedoe@example.com>'],
                'files':   ['test'],
                'comment': '',
            },
        ]

        self._checkSortedGroups(expected_groups, copyright_.groups, options)

    def testProcessComplex(self):
        '''Parses a debian/copyright with many entries (testdata/three).'''

        copyright_, options = self._processTree('tests/testdata/three')

        expected_groups = [
            {
                'license': 'LGPL-2+',
                'holders': ['2014-2016, Maximiliano Curia <maxy@debian.org>'],
                'files':   ['debian/compat', 'debian/copyright',
                            'debian/rules'],
                'comment': 'No real packaging, just empty files.',
            },
            {
                'license': 'GPL-2+',
                'holders': ['1989-2003, Free Software Foundation, Inc'],
                'files':   ['acinclude.m4'],
                'comment': '',
            },
            {
                'license': 'MIT/X11',
                'holders': ['1993-2004, Clara Oswald <clara@example.com>'],
                'files':   ['src/do-something.m', 'src/do-something.py'],
                'comment': '',
            },
            {
                'license': 'GPL-2+',
                'holders': ['2016, Jane Doe <janedoe@example.com>'],
                'files':   ['COPYING', 'data/file.txt', 'src/main.py', 'src/module.py'],
                'comment': '',
            },
        ]

        self._checkSortedGroups(expected_groups, copyright_.groups, options)

    def _checkSortedGroups(self, expected_groups, groups, options):
        for i, group in enumerate(groups):
            key = group.get_key(options)
            expected = expected_groups[i]
            with self.subTest(msg='Checking "{}"'.format(key)):
                self.assertEqual(
                    list(group.copyrights.sorted_members()),
                    expected['holders'])
                self.assertEqual(group.license, expected['license'])
                self.assertEqual(
                    group.files.sorted_members(), expected['files'])
                self.assertEqual(group.get_comments(), expected['comment'])

    def _processAndRemove(self, path, opts):
        '''Lists the tree, parses the copyright, reads all files, removes
        misplaced.'''
        options = cmdoptions.process_options(['--no-progress', '--root', path] + opts)
        filetree = tree.RootInfo.build(options)
        copyright_ = dep5.Copyright.build(filetree, options)
        copyright_.process(filetree)
        filetree.process(options)
        # This function removes files from the groups when they should no longer
        # be there.  It does not add them to new or existing groups.  So the
        # effect that we will see is that some files or groups go "missing".
        copyright_.remove_misplaced_files(options)
        return copyright_, options

    def _checkExpectedGroups(self, expected_groups, groups):
        '''Checks that the received and expected groups match.'''
        # Check that we got all expected groups
        self.longMessage = False
        for key in expected_groups:
            with self.subTest(msg='Checking "{}"'.format(key)):
                self.assertIn(key, groups,
                                msg='missing expected group: {}'.format(key))
        for key in groups:
            with self.subTest(msg='Checking "{}"'.format(key)):
                self.assertIn(key, expected_groups,
                                msg='unexpected group: {}'.format(key))
        self.longMessage = True

        # Check that the groups are what we expected
        for key, group in groups.items():
            with self.subTest(msg='Checking "{}"'.format(key)):
                expected = expected_groups[key]
                self.assertEqual(
                    list(group.copyrights.sorted_members()),
                    expected['holders'])
                self.assertEqual(group.license, expected['license'])
                self.assertEqual(
                    group.files.sorted_members(), expected['files'])
                self.assertEqual(group.get_comments(), expected['comment'])

    def testRemoveMisplaced(self):
        '''Removes files from copyright if they were wrong (testdata/three).'''

        copyright_, options = self._processAndRemove('tests/testdata/three', [])

        expected_groups = {
            ('LGPL-2+', None, 'debian'): {
                'license': 'LGPL-2+',
                'holders': ['2014-2016, Maximiliano Curia <maxy@debian.org>'],
                'files':   ['debian/compat', 'debian/copyright',
                            'debian/rules'],
                'comment': 'No real packaging, just empty files.',
            },
            ('GPL-2+', None, ''): {
                'license': 'GPL-2+',
                'holders': ['1989-2003, Free Software Foundation, Inc',
                            '2016, Jane Doe <janedoe@example.com>'],
                'files':   ['COPYING', 'acinclude.m4', 'src/main.py', 'src/module.py'],
                'comment': '',
            },
            ('MIT/X11', None, ''): {
                'license': 'MIT/X11',
                'holders': ['1993-2004, Clara Oswald <clara@example.com>'],
                'files':   ['src/do-something.m', 'src/do-something.py'],
                'comment': '',
            },
        }
        groups = copyright_.get_group_dict(options)
        self._checkExpectedGroups(expected_groups, groups)

    def testRemoveMisplacedCopyright(self):
        '''Removes files if they were wrong, grouping by copyright.'''

        copyright_, options = self._processAndRemove(
            'tests/testdata/three', ['--group-by', 'copyright'])

        expected_groups = {
            ('LGPL-2+', ('maxy@debian.org',), 'debian'): {
                'license': 'LGPL-2+',
                'holders': ['2014-2016, Maximiliano Curia <maxy@debian.org>'],
                'files':   ['debian/compat', 'debian/copyright',
                            'debian/rules'],
                'comment': 'No real packaging, just empty files.',
            },
            ('GPL-2+', ('janedoe@example.com',), ''): {
                'license': 'GPL-2+',
                'holders': ['2016, Jane Doe <janedoe@example.com>'],
                'files':   ['COPYING', 'src/main.py', 'src/module.py'],
                'comment': '',
            },
            ('MIT/X11', ('clara@example.com',), ''): {
                'license': 'MIT/X11',
                'holders': ['1993-2004, Clara Oswald <clara@example.com>'],
                'files':   ['src/do-something.m', 'src/do-something.py'],
                'comment': '',
            },
            ('GPL-2+', ('Free Software Foundation, Inc',), ''): {
                'license': 'GPL-2+',
                'holders': ['1989-2003, Free Software Foundation, Inc'],
                'files':   ['acinclude.m4'],
                'comment': '',
            },
        }
        groups = copyright_.get_group_dict(options)
        self._checkExpectedGroups(expected_groups, groups)

    def testRemoveMisplacedPartial(self):
        '''Removes files if they were wrong, grouping by copyright.'''

        copyright_, options = self._processAndRemove(
            'tests/testdata/three',
            ['--mode', 'partial', 'tests/testdata/three/debian'])

        expected_groups = {
            ('LGPL-2+', None, 'debian'): {
                'license': 'LGPL-2+',
                'holders': ['2014-2016, Maximiliano Curia <maxy@debian.org>'],
                'files':   ['debian/compat', 'debian/copyright',
                            'debian/rules'],
                'comment': 'No real packaging, just empty files.',
            },
            ('GPL-2+', None, ''): {
                'license': 'GPL-2+',
                'holders': ['1989-2003, Free Software Foundation, Inc'],
                'files':   [],
                'comment': '',
            },
            ('MIT/X11', None, ''): {
                'license': 'MIT/X11',
                'holders': ['1993-2004, Clara Oswald <clara@example.com>'],
                'files':   [],
                'comment': '',
            },
        }

        groups = copyright_.get_group_dict(options)
        self._checkExpectedGroups(expected_groups, groups)


class TestGroup(unittest.TestCase):

    def testUnknownLicense(self):
        g = dep5.Group()
        self.assertEqual(g.license, datatypes.UNKNOWN)
        cg = tree.CopyrightGroup()
        cg.add(datatypes.CopyrightHolder('', 'foo@example.com', ''))
        g.add_copyrights(cg)
        self.assertEqual(g.license, datatypes.UNKNOWN_COPYRIGHTED)


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