#!/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 3 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 debian.watch."""

from unittest import (
    TestCase,
    )

try:
    from StringIO import StringIO  # type: ignore
except ImportError:
    from io import StringIO

try:
    # pylint: disable=unused-import
    from typing import (
        Optional,
        TypeVar,
    )
except ImportError:
    # Missing types aren't important at runtime
    TYPE_CHECKING = False

    # Fake some definitions
    if not TYPE_CHECKING:
        TypeVar = lambda t: None


from debian.watch import (
    expand,
    MissingVersion,
    Watch,
    WatchFile,
    )


T = TypeVar("T")
def not_none(obj):
    # type: (Optional[T]) -> T
    assert obj is not None
    return obj


class ParseWatchFileTests(TestCase):

    def test_parse_empty(self):
        # type: () -> None
        self.assertIs(None, WatchFile.from_lines(StringIO("")))

    def test_parse_no_version(self):
        # type: () -> None
        self.assertRaises(
            MissingVersion, WatchFile.from_lines, StringIO("foo\n"))
        self.assertRaises(
            MissingVersion, WatchFile.from_lines, StringIO("foo=bar\n"))

    def test_parse_with_spacing_around_version(self):
        # type: () -> None
        wf = WatchFile.from_lines(StringIO("""\
version = 3
https://samba.org/~jelmer/ blah-(\\d+).tar.gz
"""))
        wf = not_none(wf)
        self.assertEqual(3, wf.version)
        self.assertEqual(
            [Watch('https://samba.org/~jelmer/', 'blah-(\\d+).tar.gz')],
            wf.entries)

    def test_parse_with_script(self):
        # type: () -> None
        wf = WatchFile.from_lines(StringIO("""\
version=4
https://samba.org/~jelmer/ blah-(\\d+).tar.gz debian sh blah.sh
"""))
        wf = not_none(wf)
        self.assertEqual(4, wf.version)
        self.assertEqual(
            [Watch('https://samba.org/~jelmer/', 'blah-(\\d+).tar.gz',
                   'debian', 'sh blah.sh')],
            wf.entries)

    def test_parse_single(self):
        # type: () -> None
        wf = WatchFile.from_lines(StringIO("""\
version=4
https://samba.org/~jelmer/blah-(\\d+).tar.gz
"""))
        wf = not_none(wf)
        self.assertEqual(4, wf.version)
        self.assertEqual(
            [Watch('https://samba.org/~jelmer', 'blah-(\\d+).tar.gz')],
            wf.entries)

    def test_parse_simple(self):
        # type: () -> None
        wf = WatchFile.from_lines(StringIO("""\
version=4
https://samba.org/~jelmer/ blah-(\\d+).tar.gz
"""))
        wf = not_none(wf)
        self.assertEqual(4, wf.version)
        self.assertEqual(
            [Watch('https://samba.org/~jelmer/', 'blah-(\\d+).tar.gz')],
            wf.entries)

    def test_parse_with_opts(self):
        # type: () -> None
        wf = WatchFile.from_lines(StringIO("""\
version=4
opts=pgpmode=mangle https://samba.org/~jelmer/ blah-(\\d+).tar.gz
"""))
        wf = not_none(wf)
        self.assertEqual(4, wf.version)
        self.assertEqual([], wf.options)
        self.assertEqual(
            [Watch('https://samba.org/~jelmer/',
                   'blah-(\\d+).tar.gz', opts=['pgpmode=mangle'])],
            wf.entries)

    def test_parse_global_opts(self):
        # type: () -> None
        wf = WatchFile.from_lines(StringIO("""\
version=4
opts=pgpmode=mangle
https://samba.org/~jelmer/ blah-(\\d+).tar.gz
"""))
        wf = not_none(wf)
        self.assertEqual(4, wf.version)
        self.assertEqual(['pgpmode=mangle'], wf.options)
        self.assertEqual([
            Watch('https://samba.org/~jelmer/', 'blah-(\\d+).tar.gz')],
            wf.entries)

    def test_parse_opt_quotes(self):
        # type: () -> None
        wf = WatchFile.from_lines(StringIO("""\
version=4
opts="pgpmode=mangle" https://samba.org/~jelmer blah-(\\d+).tar.gz
"""))
        wf = not_none(wf)
        self.assertEqual(4, wf.version)
        self.assertEqual(
            wf.entries,
            [Watch('https://samba.org/~jelmer',
                   'blah-(\\d+).tar.gz', opts=['pgpmode=mangle'])])

    def test_parse_continued_leading_spaces_4(self):
        # type: () -> None
        wf = WatchFile.from_lines(StringIO("""\
version=4
opts=pgpmode=mangle,\\
    foo=bar https://samba.org/~jelmer blah-(\\d+).tar.gz
"""))
        wf = not_none(wf)
        self.assertEqual(4, wf.version)
        self.assertEqual(
            wf.entries,
            [Watch('https://samba.org/~jelmer',
                   'blah-(\\d+).tar.gz', opts=['pgpmode=mangle', 'foo=bar'])])

    def test_parse_continued_leading_spaces_3(self):
        # type: () -> None
        wf = WatchFile.from_lines(StringIO("""\
version=3
opts=pgpmode=mangle,\\
    foo=bar blah-(\\d+).tar.gz
"""))
        wf = not_none(wf)
        self.assertEqual(3, wf.version)
        self.assertEqual(
            wf.entries,
            [Watch('foo=bar',
                   'blah-(\\d+).tar.gz', opts=['pgpmode=mangle', ''])])

    def test_pattern_included(self):
        # type: () -> None
        wf = WatchFile.from_lines(StringIO("""\
version=4
https://pypi.debian.net/case/case-(.+).tar.gz debian
"""))
        wf = not_none(wf)
        self.assertEqual(4, wf.version)
        self.assertEqual(
            [Watch('https://pypi.debian.net/case', 'case-(.+).tar.gz',
                   'debian')], wf.entries)

    def test_parse_weird_quotes(self):
        # type: () -> None
        wf = WatchFile.from_lines(StringIO("""\
# please also check https://pypi.debian.net/case/watch
version=3
opts=repacksuffix=+dfsg",pgpsigurlmangle=s/$/.asc/ \\
https://pypi.debian.net/case/case-(.+)\\.(?:zip|(?:tar\\.(?:gz|bz2|xz))) \\
debian sh debian/repack.stub
"""))
        wf = not_none(wf)
        self.assertEqual(3, wf.version)
        self.assertEqual([Watch(
            'https://pypi.debian.net/case',
            'case-(.+)\\.(?:zip|(?:tar\\.(?:gz|bz2|xz)))',
            'debian', 'sh debian/repack.stub',
            opts=['repacksuffix=+dfsg"', 'pgpsigurlmangle=s/$/.asc/'])],
            wf.entries)

    def test_package_variable(self):
        # type: () -> None
        wf = WatchFile.from_lines(StringIO("""\
version = 3
https://samba.org/~jelmer/@PACKAGE@ blah-(\\d+).tar.gz
"""))
        wf = not_none(wf)
        self.assertEqual(3, wf.version)
        self.assertEqual(
            [Watch('https://samba.org/~jelmer/@PACKAGE@',
                   'blah-(\\d+).tar.gz')],
            wf.entries)
        self.assertEqual(
            'https://samba.org/~jelmer/blah',
            expand(wf.entries[0].url, 'blah'))


class DumpWatchFileTests(TestCase):

    def test_empty(self):
        # type: () -> None
        wf = WatchFile()
        f = StringIO()
        wf.dump(f)
        self.assertEqual(f.getvalue(), "version=4\n")

    def test_simple(self):
        # type: () -> None
        wf = WatchFile()
        wf.entries = [
            Watch('https://pypi.debian.net/case', 'case-(.+).tar.gz')]
        f = StringIO()
        wf.dump(f)
        self.assertEqual(f.getvalue(), """\
version=4
https://pypi.debian.net/case case-(.+).tar.gz
""")

    def test_opts(self):
        # type: () -> None
        wf = WatchFile()
        wf.entries = [
            Watch('https://samba.org/~jelmer',
                  'blah-(\\d+).tar.gz', opts=['pgpmode=mangle'])]
        wf.options = ['useragent=lynx']
        f = StringIO()
        wf.dump(f)
        self.assertEqual(f.getvalue(), """\
version=4
opts=useragent=lynx
opts=pgpmode=mangle https://samba.org/~jelmer blah-(\\d+).tar.gz
""")

    def test_multiple_lines(self):
        # type: () -> None
        wf = WatchFile()
        wf.entries = [
            Watch('https://samba.org/~jelmer',
                  'blah-(\\d+).tar.gz', opts=['pgpmode=mangle']),
            Watch('https://salsa.debian.org/python-team/blah-(.*).tar.gz')]
        f = StringIO()
        wf.dump(f)
        self.assertEqual(f.getvalue(), """\
version=4
opts=pgpmode=mangle https://samba.org/~jelmer blah-(\\d+).tar.gz
https://salsa.debian.org/python-team/blah-(.*).tar.gz
""")


class ExpandTests(TestCase):

    def test_expand_package(self):
        # type: () -> None
        self.assertEqual(
            'foo-1.2.3.tar.gz',
            expand('@PACKAGE@-1.2.3.tar.gz', 'foo'))

    def test_static(self):
        # type: () -> None
        self.assertEqual(
            r'foo-[-_]?(\d[\-+\.:\~\da-zA-Z]*)',
            expand('foo-@ANY_VERSION@', 'foo'))
