testing/mozbase/mozprofile/tests/test_addons.py
author Mozilla Releng Treescript <release+treescript@mozilla.org>
Wed, 19 Jan 2022 06:57:01 +0000
changeset 604799 184fd64dd78347ea17dbd5aef4a9d8e40fcc9f7f
parent 599832 f105477d372d5771cc0535d137bca2aedf69082c
permissions -rw-r--r--
no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD ach -> 977c0eee5774e3bd19a25900142d1dca3eb2ee29 af -> 162654a20c86192e124df9766ec5ecd54143d085 an -> d10834e1c7ba839ba9c372205f4371c4d3c19791 ar -> 3be417e9326ebc9e369fc77f1fd98481a39b07cf ast -> 41dcea23a9d890ee00cb4cc3797559fe461244bc az -> 0366cdbc61ed5620510c848b85cd593d376c75f3 be -> 62bb6ed3829ec227748a86410cf038052f673b54 bg -> 19ee7b31e0becafa054013a3c8c09844638f244c bn -> 010d5f74034cb2c18fd1100e0d0888a245d52a60 bo -> 97059fd9d1a83e254bf84da12f1612062e82d271 br -> a9e3b8ba10759c8a4f7da6b7970911ded7d04ad6 brx -> 573ec21d6a1aebf6a342533bb06b77c79b6e994b bs -> 9ebb98165b36860f938bfb97c1956090e8daf920 ca -> caf5515093962f3f92c92aea68dacd4c3e520a8d ca-valencia -> d8f8751d595bf879afe5bfa8e3854ee5b6d1b977 cak -> 0bbd8b94c9c02fa5ec6a3e5fd3bf3e69b0432829 ckb -> bd043d903599db44feb371bf29d4a3d510a48a0a cs -> ae677a6bee277f507e673c3fcd2d56088b2caafb cy -> c46474099266c675d8ddc8fbcecd23a8a11a174e da -> 4595dd6f7ed91af507076eb0a95a44f4d0646404 de -> d3fca46cd495000d26fd26d3a2776b8f4dae786c dsb -> 611248df8f629860d012cbc8f3a36acfd8744b46 el -> 22d62ecd4c214d52af254d87c2da7410bd571fb2 en-CA -> 501d80026e4fdeac79e20b41cb8087c7ec3c3ba2 en-GB -> ec99571b02068fd4bf160416b5b7e35379c899a2 eo -> 6f7723c2bfbf5e3aeb1134909464bbc099c86a4f es-AR -> e4e8f59ee9824548910f3281d02c5727caa36dd2 es-CL -> 08ee034825af181840c14c2bee5a7c20d0aa59ac es-ES -> 46729ef124e5cf1f0a3e85d50c16a069602cfed2 es-MX -> 43859d642bd044060a4d2089f4b30de8bb470e43 et -> 7cc4df8eca6d3b6c4e3073ed35770034e9be79ca eu -> 863a0a1a315b55c18694e29430c272baba355b4c fa -> 60cff0755aaf290a159cb6968c4909bf9ecae2fa ff -> ec175d67f9056e5fd2cb6568ee546336efb77d5c fi -> 6e37410128f2d675c3414ba9c6341af81b4521ff fr -> ce1d665cec91d4d29160860dba2350f22f2f4df7 fy-NL -> 5e4350e39e2a7797d6bd8e1c1bb0b15252397010 ga-IE -> 2d955174d7ab6068656b2f02e4d8c689bb2399bf gd -> 49032dc335b8b5e9ecd10435f878598bffa6541a gl -> d8761e8f84efac80d6a7482931a01cecd66dd60d gn -> aa33a53b688bed9cedeb8a3eaf0466b9451d2e18 gu-IN -> d2c670d798b925090af6321cb3270fa6f806108d he -> 3385ae23e3b569ba9c0c1aca4df25277b81a108e hi-IN -> 274838883f7f1f14d5f14d5116e7df11cc404911 hr -> a01b7ce3515d0b2f05e473625148f11a264da0a2 hsb -> f137d0e6746dc23beff4745474df87c33fae353b hu -> 39e18e316a85b5aaada8c560d51317b5e28b77d8 hy-AM -> 41e938f8ebf1581d1bc76bd088fc1ac43bb83e0b hye -> 5843bc7348ba6a533afffe08eedc48c623963542 ia -> 031f226bad1d97adf265f75fcb467a18c223c606 id -> 4d4de9751872149e1c99dcfce2b7ce4835b65589 is -> 5753814cd9664948b3ac5ace293b3d46304f0cf2 it -> 3448943cfadfc5a361bd367c7e6be58f86cc61a1 ja -> c4be75596e316966ab1dc9857a9be6b0a7bc2ef9 ja-JP-mac -> 476eca379b8b21857655d6c23908b3e2a1b09aee ka -> 2d66f0c2bd7a06ac1ad6ad02a09a26ff1e60e3cb kab -> 409893635bedbe52fa8b85784844e15756d4a8fd kk -> c68316baa4a0dd041abb4e8efd557339fb0d9cb4 km -> c04f31c98e50315177467bd4e079200d3b38b835 kn -> 0bc693a82c819a27e71f41828c040e7be4b92785 ko -> 88096d30e35f344fcb61f08900340b31fca79cc7 lij -> e46553ebd09bbabad095ccc4c8b7d147f895a475 lo -> 942d94da48979eb60bcd62d825f7f9337314999f lt -> 6a447408fe67e37ee56a85d8bea523219dac52e1 ltg -> 06e42a8ee9e5211fa8812542a6bd3c22e3c540ba lv -> 0059bd3f14ced8ec1ef0f2765b655fe4ee537e91 meh -> 54cb55f92ef9d93d0481b3806b725d08035d950f mk -> c473f0b380bd14c98754cb38cdc287c5b0df99a0 mr -> b3382076ee961b51f0e2a7369c08aaf03fb8574d ms -> ec4c315ae032575bed799d8faa57dd4441e5bd9e my -> 25d516d33a6c2f8881683c5530f468e2c25ab6eb nb-NO -> 490c2d401359fd2204ad7aa806a73107f3befc27 ne-NP -> 5676e8e0b93e18fb5eeb238953c512d4f407340c nl -> 3fbbd92a2f116150e9921f13e4c9fe37d6948464 nn-NO -> 7d8c6d43ad98b2a8a0dce7ef05234b66cb818244 oc -> 8379011f2b605f1597fc61e4585520911ca58567 pa-IN -> b28bb037d4687370eebb7a892982b31bddea32b0 pl -> 3193b4b820e7c37bdb9153ab9c525f18210a501a pt-BR -> ce1ab16cc1acd0fa7ae898feeba46c6d497fe981 pt-PT -> e0910097929ee117291ede6675c6e0add124221b rm -> cdce5683cd6621c0ca31eb6b6ffd209481fa3c85 ro -> ba7fe96d6c98e01a48f442d520c39d119c7e0727 ru -> dc6d46ab977eb359c21a0f6dfe5b22e1fa60fda0 sat -> 3ab6969e38e316491894b42527f2287dbbd7d626 sc -> 14a1b41f804a89dd00d978056ebfc966a638cbb8 scn -> 6729c4bfb82228012e51e2077cf8f2ce254e8fe6 sco -> 8aed4c4ac1dd708f213d350a46617cd3faca0a09 si -> 639820754f8563d1fccf89f48c4b155db6a3ae8d sk -> 588464d4ecd69c9a12ffd0e41ccb5e4a7f16e25e sl -> e7aa581fc059a4e59ae56eb03790d0d7e4129a42 son -> 1541de0c90d66751ae72c24d4fa8105a585cf203 sq -> 05efafdd60c5a69d6d75adf8ccb97c5b07aee239 sr -> e7c7978d2fd74f4e2098253a41b099604b88d61b sv-SE -> fbeae2bbc5837470c90793f92a1cc90b7d6fd51f szl -> 000ee773591f17bcd9fc5ed5eb504000a016ce5b ta -> 2545635e99da1c3bdfc3536ba8c6a68a7498d014 te -> 0c5e4eb71c5ffe04c651f377355ead0f976960cd tg -> 8c5d8e78f165f7aef1f2597a67944cf8c101f906 th -> b6c8c65ece954839a983d48aa92a348b84867fa7 tl -> c7615e24b3453bb59a41eb017db2e04c781c8782 tr -> 08e01536e9eab024fceabe205921d815094af748 trs -> 43debe20e58afdd09ddbcd2f7dd62039c1e8c5f8 uk -> a4d58069863410d7b878690855604f86b12c300b ur -> 354acd5f98b1a03c6960231c570fedb771588d86 uz -> f8e8cc127eaf07ab77bf51c37ec494d2721e8d2e vi -> e24285029832c0cc588e8e12aa2212dff982d623 wo -> 625cc2e007db82c90ecddd39e34f1969c4088e09 xh -> 09a4bf9ceb7fe52e7fd54b2c8a15c16c6a4c56f2 zh-CN -> 5c57a47c29f1c8105d8c268617a00c54e4a8ba8c zh-TW -> f86c570fdc59c21666014d9c81f61c9ebeac0d72

#!/usr/bin/env python

# 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/.

from __future__ import absolute_import, unicode_literals

import os
import zipfile

import mozunit
import pytest

import mozprofile
from addon_stubs import generate_addon

here = os.path.dirname(os.path.abspath(__file__))


@pytest.fixture
def profile():
    return mozprofile.Profile()


@pytest.fixture
def am(profile):
    return profile.addons


def test_install_multiple_same_source(tmpdir, am):
    path = tmpdir.strpath

    # Generate installer stubs for all possible types of addons
    addon_xpi = generate_addon("test-addon-1@mozilla.org", path=path)
    addon_folder = generate_addon("test-addon-1@mozilla.org", path=path, xpi=False)

    # The same folder should not be installed twice
    am.install([addon_folder, addon_folder])
    assert am.installed_addons == [addon_folder]
    am.clean()

    # The same XPI file should not be installed twice
    am.install([addon_xpi, addon_xpi])
    assert am.installed_addons == [addon_xpi]
    am.clean()

    # Even if it is the same id the add-on should be installed twice, if
    # specified via XPI and folder
    am.install([addon_folder, addon_xpi])
    assert len(am.installed_addons) == 2
    assert addon_folder in am.installed_addons
    assert addon_xpi in am.installed_addons
    am.clean()


def test_install_webextension_from_dir(tmpdir, am):
    tmpdir = tmpdir.strpath

    addon = os.path.join(here, "addons", "apply-css.xpi")
    zipped = zipfile.ZipFile(addon)
    try:
        zipped.extractall(tmpdir)
    finally:
        zipped.close()
    am.install(tmpdir)
    assert len(am.installed_addons) == 1
    assert os.path.isdir(am.installed_addons[0])


def test_install_webextension(am):
    addon = os.path.join(here, "addons", "apply-css.xpi")

    am.install(addon)
    assert len(am.installed_addons) == 1
    assert os.path.isfile(am.installed_addons[0])
    assert "apply-css.xpi" == os.path.basename(am.installed_addons[0])

    details = am.addon_details(am.installed_addons[0])
    assert "test-webext@quality.mozilla.org" == details["id"]


def test_install_webextension_id_via_browser_specific_settings(am):
    # See Bug 1572404
    addon = os.path.join(
        here, "addons", "apply-css-id-via-browser-specific-settings.xpi"
    )
    am.install(addon)
    assert len(am.installed_addons) == 1
    assert os.path.isfile(am.installed_addons[0])
    assert "apply-css-id-via-browser-specific-settings.xpi" == os.path.basename(
        am.installed_addons[0]
    )

    details = am.addon_details(am.installed_addons[0])
    assert "test-webext@quality.mozilla.org" == details["id"]


def test_install_webextension_sans_id(am):
    addon = os.path.join(here, "addons", "apply-css-sans-id.xpi")
    am.install(addon)

    assert len(am.installed_addons) == 1
    assert os.path.isfile(am.installed_addons[0])
    assert "apply-css-sans-id.xpi" == os.path.basename(am.installed_addons[0])

    details = am.addon_details(am.installed_addons[0])
    assert "@temporary-addon" in details["id"]


def test_install_xpi(tmpdir, am):
    tmpdir = tmpdir.strpath

    addons_to_install = []
    addons_installed = []

    # Generate installer stubs and install them
    for ext in ["test-addon-1@mozilla.org", "test-addon-2@mozilla.org"]:
        temp_addon = generate_addon(ext, path=tmpdir)
        addons_to_install.append(am.addon_details(temp_addon)["id"])
        am.install(temp_addon)

    # Generate a list of addons installed in the profile
    addons_installed = [
        str(x[: -len(".xpi")])
        for x in os.listdir(os.path.join(am.profile, "extensions"))
    ]
    assert addons_to_install.sort() == addons_installed.sort()


def test_install_folder(tmpdir, am):
    tmpdir = tmpdir.strpath

    # Generate installer stubs for all possible types of addons
    addons = []
    addons.append(generate_addon("test-addon-1@mozilla.org", path=tmpdir))
    addons.append(generate_addon("test-addon-2@mozilla.org", path=tmpdir, xpi=False))
    addons.append(
        generate_addon("test-addon-3@mozilla.org", path=tmpdir, name="addon-3")
    )
    addons.append(
        generate_addon(
            "test-addon-4@mozilla.org", path=tmpdir, name="addon-4", xpi=False
        )
    )
    addons.sort()

    am.install(tmpdir)

    assert am.installed_addons == addons


def test_install_unpack(tmpdir, am):
    tmpdir = tmpdir.strpath

    # Generate installer stubs for all possible types of addons
    addon_xpi = generate_addon("test-addon-unpack@mozilla.org", path=tmpdir)
    addon_folder = generate_addon(
        "test-addon-unpack@mozilla.org", path=tmpdir, xpi=False
    )
    addon_no_unpack = generate_addon("test-addon-1@mozilla.org", path=tmpdir)

    # Test unpack flag for add-on as XPI
    am.install(addon_xpi)
    assert am.installed_addons == [addon_xpi]
    am.clean()

    # Test unpack flag for add-on as folder
    am.install(addon_folder)
    assert am.installed_addons == [addon_folder]
    am.clean()

    # Test forcing unpack an add-on
    am.install(addon_no_unpack, unpack=True)
    assert am.installed_addons == [addon_no_unpack]
    am.clean()


def test_install_after_reset(tmpdir, profile):
    tmpdir = tmpdir.strpath
    am = profile.addons

    # Installing the same add-on after a reset should not cause a failure
    addon = generate_addon("test-addon-1@mozilla.org", path=tmpdir, xpi=False)

    # We cannot use am because profile.reset() creates a new instance
    am.install(addon)
    profile.reset()

    am.install(addon)
    assert am.installed_addons == [addon]


def test_install_backup(tmpdir, am):
    tmpdir = tmpdir.strpath

    staged_path = os.path.join(am.profile, "extensions")

    # Generate installer stubs for all possible types of addons
    addon_xpi = generate_addon("test-addon-1@mozilla.org", path=tmpdir)
    addon_folder = generate_addon("test-addon-1@mozilla.org", path=tmpdir, xpi=False)
    addon_name = generate_addon(
        "test-addon-1@mozilla.org", path=tmpdir, name="test-addon-1-dupe@mozilla.org"
    )

    # Test backup of xpi files
    am.install(addon_xpi)
    assert am.backup_dir is None

    am.install(addon_xpi)
    assert am.backup_dir is not None
    assert os.listdir(am.backup_dir) == ["test-addon-1@mozilla.org.xpi"]

    am.clean()
    assert os.listdir(staged_path) == ["test-addon-1@mozilla.org.xpi"]
    am.clean()

    # Test backup of folders
    am.install(addon_folder)
    assert am.backup_dir is None

    am.install(addon_folder)
    assert am.backup_dir is not None
    assert os.listdir(am.backup_dir) == ["test-addon-1@mozilla.org"]

    am.clean()
    assert os.listdir(staged_path) == ["test-addon-1@mozilla.org"]
    am.clean()

    # Test backup of xpi files with another file name
    am.install(addon_name)
    assert am.backup_dir is None

    am.install(addon_xpi)
    assert am.backup_dir is not None
    assert os.listdir(am.backup_dir) == ["test-addon-1@mozilla.org.xpi"]

    am.clean()
    assert os.listdir(staged_path) == ["test-addon-1@mozilla.org.xpi"]
    am.clean()


def test_install_invalid_addons(tmpdir, am):
    tmpdir = tmpdir.strpath

    # Generate installer stubs for all possible types of addons
    addons = []
    addons.append(
        generate_addon(
            "test-addon-invalid-no-manifest@mozilla.org", path=tmpdir, xpi=False
        )
    )
    addons.append(generate_addon("test-addon-invalid-no-id@mozilla.org", path=tmpdir))

    am.install(tmpdir)

    assert am.installed_addons == []


@pytest.mark.xfail(reason="feature not implemented as part of AddonManger")
def test_install_error(am):
    """Check install raises an error with an invalid addon"""
    temp_addon = generate_addon("test-addon-invalid-version@mozilla.org")
    # This should raise an error here
    with pytest.raises(Exception):
        am.install(temp_addon)


def test_addon_details(tmpdir, am):
    tmpdir = tmpdir.strpath

    # Generate installer stubs for a valid and invalid add-on manifest
    valid_addon = generate_addon("test-addon-1@mozilla.org", path=tmpdir)
    invalid_addon = generate_addon(
        "test-addon-invalid-not-wellformed@mozilla.org", path=tmpdir
    )

    # Check valid add-on
    details = am.addon_details(valid_addon)
    assert details["id"] == "test-addon-1@mozilla.org"
    assert details["name"] == "Test Add-on 1"
    assert not details["unpack"]
    assert details["version"] == "0.1"

    # Check invalid add-on
    with pytest.raises(mozprofile.addons.AddonFormatError):
        am.addon_details(invalid_addon)

    # Check invalid path
    with pytest.raises(IOError):
        am.addon_details("")

    # Check invalid add-on format
    addon_path = os.path.join(os.path.join(here, "files"), "not_an_addon.txt")
    with pytest.raises(mozprofile.addons.AddonFormatError):
        am.addon_details(addon_path)


def test_clean_addons(am):
    addon_one = generate_addon("test-addon-1@mozilla.org")
    addon_two = generate_addon("test-addon-2@mozilla.org")

    am.install(addon_one)
    installed_addons = [
        str(x[: -len(".xpi")])
        for x in os.listdir(os.path.join(am.profile, "extensions"))
    ]

    # Create a new profile based on an existing profile
    # Install an extra addon in the new profile
    # Cleanup addons
    duplicate_profile = mozprofile.profile.Profile(profile=am.profile, addons=addon_two)
    duplicate_profile.addons.clean()

    addons_after_cleanup = [
        str(x[: -len(".xpi")])
        for x in os.listdir(os.path.join(duplicate_profile.profile, "extensions"))
    ]
    # New addons installed should be removed by clean_addons()
    assert installed_addons == addons_after_cleanup


def test_noclean(tmpdir):
    """test `restore=True/False` functionality"""
    profile = tmpdir.mkdtemp().strpath
    tmpdir = tmpdir.mkdtemp().strpath

    # empty initially
    assert not bool(os.listdir(profile))

    # make an addon
    addons = [
        generate_addon("test-addon-1@mozilla.org", path=tmpdir),
        os.path.join(here, "addons", "empty.xpi"),
    ]

    # install it with a restore=True AddonManager
    am = mozprofile.addons.AddonManager(profile, restore=True)

    for addon in addons:
        am.install(addon)

    # now its there
    assert os.listdir(profile) == ["extensions"]
    staging_folder = os.path.join(profile, "extensions")
    assert os.path.exists(staging_folder)
    assert len(os.listdir(staging_folder)) == 2

    del am

    assert os.listdir(profile) == ["extensions"]
    assert os.path.exists(staging_folder)
    assert os.listdir(staging_folder) == []


def test_remove_addon(tmpdir, am):
    tmpdir = tmpdir.strpath

    addons = []
    addons.append(generate_addon("test-addon-1@mozilla.org", path=tmpdir))
    addons.append(generate_addon("test-addon-2@mozilla.org", path=tmpdir))

    am.install(tmpdir)

    extensions_path = os.path.join(am.profile, "extensions")
    staging_path = os.path.join(extensions_path)

    for addon in am._addons:
        am.remove_addon(addon)

    assert os.listdir(staging_path) == []
    assert os.listdir(extensions_path) == []


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