# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

import os
import unittest

import mozunit

from mozpack.chrome.manifest import (
    MANIFESTS_TYPES,
    Manifest,
    ManifestBinaryComponent,
    ManifestCategory,
    ManifestComponent,
    ManifestContent,
    ManifestContract,
    ManifestInterfaces,
    ManifestLocale,
    ManifestOverlay,
    ManifestOverride,
    ManifestResource,
    ManifestSkin,
    ManifestStyle,
    parse_manifest,
    parse_manifest_line,
)
from mozpack.errors import AccumulatedErrors, errors
from test_errors import TestErrors


class TestManifest(unittest.TestCase):
    def test_parse_manifest(self):
        manifest = [
            "content global content/global/",
            "content global content/global/ application=foo application=bar"
            + " platform",
            "locale global en-US content/en-US/",
            "locale global en-US content/en-US/ application=foo",
            "skin global classic/1.0 content/skin/classic/",
            "skin global classic/1.0 content/skin/classic/ application=foo"
            + " os=WINNT",
            "",
            "manifest pdfjs/chrome.manifest",
            "resource gre-resources toolkit/res/",
            "override chrome://global/locale/netError.dtd"
            + " chrome://browser/locale/netError.dtd",
            "# Comment",
            "component {b2bba4df-057d-41ea-b6b1-94a10a8ede68} foo.js",
            "contract @mozilla.org/foo;1" + " {b2bba4df-057d-41ea-b6b1-94a10a8ede68}",
            "interfaces foo.xpt",
            "binary-component bar.so",
            "category command-line-handler m-browser"
            + " @mozilla.org/browser/clh;1"
            + " application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}",
            "style chrome://global/content/viewSource.xul" + " chrome://browser/skin/",
            "overlay chrome://global/content/viewSource.xul"
            + " chrome://browser/content/viewSourceOverlay.xul",
        ]
        other_manifest = ["content global content/global/"]
        expected_result = [
            ManifestContent("", "global", "content/global/"),
            ManifestContent(
                "",
                "global",
                "content/global/",
                "application=foo",
                "application=bar",
                "platform",
            ),
            ManifestLocale("", "global", "en-US", "content/en-US/"),
            ManifestLocale("", "global", "en-US", "content/en-US/", "application=foo"),
            ManifestSkin("", "global", "classic/1.0", "content/skin/classic/"),
            ManifestSkin(
                "",
                "global",
                "classic/1.0",
                "content/skin/classic/",
                "application=foo",
                "os=WINNT",
            ),
            Manifest("", "pdfjs/chrome.manifest"),
            ManifestResource("", "gre-resources", "toolkit/res/"),
            ManifestOverride(
                "",
                "chrome://global/locale/netError.dtd",
                "chrome://browser/locale/netError.dtd",
            ),
            ManifestComponent("", "{b2bba4df-057d-41ea-b6b1-94a10a8ede68}", "foo.js"),
            ManifestContract(
                "", "@mozilla.org/foo;1", "{b2bba4df-057d-41ea-b6b1-94a10a8ede68}"
            ),
            ManifestInterfaces("", "foo.xpt"),
            ManifestBinaryComponent("", "bar.so"),
            ManifestCategory(
                "",
                "command-line-handler",
                "m-browser",
                "@mozilla.org/browser/clh;1",
                "application=" + "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}",
            ),
            ManifestStyle(
                "", "chrome://global/content/viewSource.xul", "chrome://browser/skin/"
            ),
            ManifestOverlay(
                "",
                "chrome://global/content/viewSource.xul",
                "chrome://browser/content/viewSourceOverlay.xul",
            ),
        ]
        with mozunit.MockedOpen(
            {
                "manifest": "\n".join(manifest),
                "other/manifest": "\n".join(other_manifest),
            }
        ):
            # Ensure we have tests for all types of manifests.
            self.assertEqual(
                set(type(e) for e in expected_result), set(MANIFESTS_TYPES.values())
            )
            self.assertEqual(
                list(parse_manifest(os.curdir, "manifest")), expected_result
            )
            self.assertEqual(
                list(parse_manifest(os.curdir, "other/manifest")),
                [ManifestContent("other", "global", "content/global/")],
            )

    def test_manifest_rebase(self):
        m = parse_manifest_line("chrome", "content global content/global/")
        m = m.rebase("")
        self.assertEqual(str(m), "content global chrome/content/global/")
        m = m.rebase("chrome")
        self.assertEqual(str(m), "content global content/global/")

        m = parse_manifest_line("chrome/foo", "content global content/global/")
        m = m.rebase("chrome")
        self.assertEqual(str(m), "content global foo/content/global/")
        m = m.rebase("chrome/foo")
        self.assertEqual(str(m), "content global content/global/")

        m = parse_manifest_line("modules/foo", "resource foo ./")
        m = m.rebase("modules")
        self.assertEqual(str(m), "resource foo foo/")
        m = m.rebase("modules/foo")
        self.assertEqual(str(m), "resource foo ./")

        m = parse_manifest_line("chrome", "content browser browser/content/")
        m = m.rebase("chrome/browser").move("jar:browser.jar!").rebase("")
        self.assertEqual(str(m), "content browser jar:browser.jar!/content/")


class TestManifestErrors(TestErrors, unittest.TestCase):
    def test_parse_manifest_errors(self):
        manifest = [
            "skin global classic/1.0 content/skin/classic/ platform",
            "",
            "binary-component bar.so",
            "unsupported foo",
        ]
        with mozunit.MockedOpen({"manifest": "\n".join(manifest)}):
            with self.assertRaises(AccumulatedErrors):
                with errors.accumulate():
                    list(parse_manifest(os.curdir, "manifest"))
            out = self.get_output()
            # Expecting 2 errors
            self.assertEqual(len(out), 2)
            path = os.path.abspath("manifest")
            # First on line 1
            self.assertTrue(out[0].startswith("error: %s:1: " % path))
            # Second on line 4
            self.assertTrue(out[1].startswith("error: %s:4: " % path))


if __name__ == "__main__":
    mozunit.main()
