#!/usr/bin/python3

import pathlib
import unittest
import sys

from qmldependstestcase import QmlDependsTestCase

from pythonlib import qmldeps

class TestQmlDepends(QmlDependsTestCase):
    def test_var_missing_in_control(self):
        pkg = self.dc.packages["testpackage"]
        for field in ("qml6:depends", "qml6:Recommends", "qml6:Suggests"):
            with self.subTest(field=field):
                self.assertEqual(qmldeps.is_var_missing_in_control(pkg, field), True)

        pkg["Suggests"] = "${qml6:Depends}, ${qml6:Recommends}, ${qml6:Suggests}"
        for field in ("qml6:Depends", "qml6:Recommends", "qml6:Suggests"):
            with self.subTest(suggests=True, field=field):
                self.assertEqual(qmldeps.is_var_missing_in_control(pkg, field), False)

        pkg["Depends"] = pkg["Suggests"]
        del pkg["Suggests"]
        for field in ("qml6:Depends", "qml6:Recommends", "qml6:Suggests"):
            with self.subTest(depends=True, field=field):
                self.assertEqual(qmldeps.is_var_missing_in_control(pkg, field), False)

    def test_var_complete_demoted(self):
        pkg = self.dc.packages["testpackage"]
        for f in ("Depends", "Recommends", "Suggests"):
            for var_name in ("qml6:depends", "qml6:Recommends", "qml6:Suggests"):
                with self.subTest(field=f, var_name=var_name):
                    self.assertEqual(qmldeps.is_var_complete_demoted(pkg, var_name, f), False)

        pkg["Suggests"] = "${qml6:Depends}, ${qml6:Recommends}, ${qml6:Suggests}"
        for f in ("Depends", "Recommends", "Suggests"):
            for var_name in ("qml6:Depends", "qml6:Recommends", "qml6:Suggests"):
                with self.subTest(suggests=True, field=f, var_name=var_name):
                    self.assertEqual(qmldeps.is_var_complete_demoted(pkg, var_name, f), f!="Suggests")

        pkg["Depends"] = pkg["Suggests"]
        del pkg["Suggests"]
        for f in ("Depends", "Recommends", "Suggests"):
            for var_name in ("qml6:Depends", "qml6:Recommends", "qml6:Suggests"):
                with self.subTest(depends=True, field=f, var_name=var_name):
                    self.assertEqual(qmldeps.is_var_complete_demoted(pkg, var_name, f), False)

    def test_partial_demoted(self):
        pkg = self.dc.packages["testpackage"]
        for f in ("Depends", "Recommends", "Suggests"):
            dependencies = set(("qml6-module-blub",))
            with self.subTest(field=f):
                self.assertEqual(qmldeps.partial_demotion(pkg, dependencies ,f), False)
                self.assertEqual(dependencies, set(("qml6-module-blub",)))

        pkg["Suggests"] = "qml6-module-blub, ignored-dep"
        for f in ("Depends", "Recommends", "Suggests"):
            dependencies = set(("qml6-module-blub", "unaffected-dep"))
            with self.subTest(suggests=True,field=f):
                if f == "Suggests":
                    self.assertEqual(qmldeps.partial_demotion(pkg, dependencies ,f), False)
                    self.assertEqual(dependencies, set(("qml6-module-blub","unaffected-dep")))
                else:
                    with self.assertLogs('dh_qmldeps', level='WARNING') as cm:
                        self.assertEqual(qmldeps.partial_demotion(pkg, dependencies ,f), True)
                    self.assertEqual(cm.output,[f"WARNING:dh_qmldeps:Detected dependencies ({{'qml6-module-blub'}}) are demoted from {f} to Suggests. To be future proof with debhelper level 15, add -d Suggests with the corresponding qml files."])
                    self.assertEqual(dependencies, set(("unaffected-dep",)))  # Demotion happend

    def test_partial_promition(self):
        pkg = self.dc.packages["testpackage"]
        pkg["Depends"] = "qml6-module-blub, ignored-dep"
        for f in ("Depends", "Recommends", "Suggests"):
            dependencies = set(("qml6-module-blub",))
            with self.subTest(depends=True, field=f):
                self.assertEqual(qmldeps.partial_demotion(pkg, dependencies ,f), False)
                self.assertEqual(dependencies, set(("qml6-module-blub",)))


class TestQmlDependsDH15(QmlDependsTestCase):
    dh_compat = "15"
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.create_no_context = False
        self.verbose = "-v" in sys.argv

    def setUp(self):
        super().setUp()
        self.create_no_context = False
        if self.dc.source["X-DH-Compat"] != self.dh_compat:
            self.dc.source["X-DH-Compat"] = self.dh_compat
            self.writeControlFile()

    def _call_main(self, args):
        if self.verbose:
            args = ["-v",] + args
        return qmldeps.main(args)

    def _test_depends(self):
        self._call_main(["-d", "Depends", "-p", "testpackage", "--", "-qmlFiles", "quick.qml"])
        self.check_substvar("qml6:Depends", "qml6-module-qtquick")

    test_depends = _test_depends
    _test_partial_depends = _test_depends

    def test_recommends(self):
        self._call_main(["-d", "Recommends", "-p", "testpackage", "--", "-qmlFiles", "quick.qml"])
        self.check_substvar("qml6:Recommends", "qml6-module-qtquick")

    _test_recommends = test_recommends

    def test_suggests(self):
        self._call_main(["-d", "Suggests", "-p", "testpackage", "--", "-qmlFiles", "quick.qml"])
        self.check_substvar("qml6:Suggests", "qml6-module-qtquick")

    def test_multiplecalls(self):
        self._call_main(["-d", "Depends", "-p", "testpackage", "--", "-qmlFiles", "quick.qml"])
        self._call_main(["-d", "Suggests", "-p", "testpackage", "--", "-qmlFiles", "test.qml"])
        self._call_main(["-d", "Recommends", "-p", "testpackage", "--", "-qmlFiles", "both.qml"])
        self.check_substvar("qml6:Depends", "qml6-module-qtquick")
        self.check_substvar("qml6:Suggests", "qml6-module-qttest")
        self.check_substvar("qml6:Recommends", "qml6-module-qtquick, qml6-module-qttest")

    def test_full_demotion(self):
        """ On Debhelper > 13 full demotion is ignored"""
        pkg = self.dc.packages["testpackage"]
        pkg["Suggests"] = "${qml6:Depends}"
        self.writeControlFile()
        self._test_depends()

    def test_only_depends_var_usage_preor_0_22(self):
        # testcase prior 0.22 version only qml6:Depends exists and users used
        # it in other fields. Make sure that the build does not break, but only
        # list an error for dh 13.
        pkg = self.dc.packages["testpackage"]
        pkg["Suggests"] = "${qml6:Depends}"
        self.writeControlFile()
        self._test_recommends()

    def test_partial_demotion(self):
        """ On Debhelper > 14 partial demotion is ignored"""
        pkg = self.dc.packages["testpackage"]
        pkg["Suggests"] = "qml6-module-qtquick"
        self.writeControlFile()
        self._test_partial_depends()

    def test_override(self):
        (pathlib.Path(self.tempdir.name)/"debian/qmldeps.overrides").write_text("QtQuick foo\n")
        self._call_main(["-d", "Depends", "-p", "testpackage", "--", "-qmlFiles", "quick.qml"])
        self.check_substvar("qml6:Depends", "foo")

    def test_optional_no_change(self):
        (pathlib.Path(self.tempdir.name)/"debian/qmldeps.optional").write_text("QtQuick foo\n")
        self._call_main(["-d", "Depends", "-p", "testpackage", "--", "-qmlFiles", "quick.qml"])
        self.check_substvar("qml6:Depends", "qml6-module-qtquick")

    def test_optional_missing(self):
        args = ["-d", "Depends", "-p", "testpackage", "--", "-qmlFiles", "missing.qml"]
        with self.assertLogs('dh_qmldeps', level='ERROR') as cm:
            with self.assertRaises(SystemExit):
                self._call_main(args)
        self.assertEqual(cm.output,["ERROR:dh_qmldeps:Missing QML module IAmMissing",])

        (pathlib.Path(self.tempdir.name)/"debian/qmldeps.optional").write_text("IAmMissing foo\n")
        self._call_main(args)
        self.check_substvar("qml6:Depends", "foo")


class TestQmlDependsDH14(TestQmlDependsDH15):
    dh_compat = "14"

    def partial_demotion_msg(self, deps, _from, _to):
        return f"WARNING:dh_qmldeps:Detected dependencies ({deps}) are demoted from {_from} to {_to}. To be future proof with debhelper level 15, add -d {_to} with the corresponding qml files."

    def _test_partial_depends(self):
        """qml6-module-qtquick is demoted to suggests"""
        args = ["-d", "Depends", "-p", "testpackage", "--", "-qmlFiles", "quick.qml"]
        if not self.create_no_context:
            with self.assertLogs('dh_qmldeps', level='WARNING') as cm:
                self._call_main(args)
            self.assertEqual(cm.output,[self.partial_demotion_msg(set(("qml6-module-qtquick",)),"Depends", "Suggests")])
        else:
            self._call_main(args)
        self.check_substvar("qml6:Depends", "")


class TestQmlDependsDH13(TestQmlDependsDH14):
    dh_compat = "13"

    def missing_field(self, fieldname):
        return f"ERROR:dh_qmldeps:cannot insert qml dependency (missing qml6:{fieldname} in Depends/Recommends/Suggests)"

    def check_error_call(self, func, fieldname):
        with self.assertLogs('dh_qmldeps', level='ERROR') as cm:
            with self.assertRaises(SystemExit):
                self.create_no_context = True
                func()
        self.assertEqual(cm.output,[self.missing_field(fieldname),])
        self.create_no_context = False

    def check_call(self, func, fieldname):
        self.check_error_call(func, fieldname)

        pkg = self.dc.packages["testpackage"]
        pkg[fieldname] = f"${{qml6:{fieldname}}}"
        self.writeControlFile()

        func()

    def test_depends(self):
        self.check_call(super().test_depends, "Depends")

    def test_recommends(self):
        self.check_call(super().test_recommends, "Recommends")

    def test_suggests(self):
        self.check_call(super().test_suggests, "Suggests")

    def test_multiplecalls(self):
        self.check_error_call(super().test_multiplecalls, "Depends")

        pkg = self.dc.packages["testpackage"]
        pkg["Depends"] = "${qml6:Depends}"
        pkg["Recommends"] = "${qml6:Recommends}"
        pkg["Suggests"] = "${qml6:Suggests}"
        self.writeControlFile()
        super().test_multiplecalls()

    def test_full_demotion(self):
        with self.assertLogs('dh_qmldeps', level='WARNING') as cm:
            super().test_full_demotion()
        self.assertEqual(cm.output, ['WARNING:dh_qmldeps:The full demotion will not work anymore with Debhelper 14. Please use -d parameter to tell dh_qmldeps the correct dependency fieldname.'])

    def test_partial_demotion(self):
        self.check_call(super().test_partial_demotion, "Depends")

    def test_override(self):
        self.check_call(super().test_override, "Depends")

    def test_optional_no_change(self):
        self.check_call(super().test_optional_no_change, "Depends")

    def test_optional_missing(self):
        fieldname = "Depends"
        self.check_error_call(super().test_optional_missing, fieldname)

        pkg = self.dc.packages["testpackage"]
        pkg[fieldname] = f"${{qml6:{fieldname}}}"
        self.writeControlFile()

        (pathlib.Path(self.tempdir.name)/"debian/qmldeps.optional").unlink()

        super().test_optional_missing()

    def test_only_depends_var_usage_preor_0_22(self):
        with self.assertLogs('dh_qmldeps', level='WARNING') as cm:
            super().test_only_depends_var_usage_preor_0_22()
        self.assertEqual(cm.output,["WARNING:dh_qmldeps:qml6:Recommends is missing in Depends/Recommends/Suggests. pkg-kde-tools >= 0.22.0 supports mutliple fields. In order to not break builds, the detected dependencies are added to qml6:Depends, too."])
        self.check_substvar("qml6:Depends", "qml6-module-qtquick")
if __name__ == "__main__":
    unittest.main(verbosity=2)
