test_addons.py (11227B)
1 #!/usr/bin/env python 2 3 # This Source Code Form is subject to the terms of the Mozilla Public 4 # License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 # You can obtain one at http://mozilla.org/MPL/2.0/. 6 7 import os 8 import zipfile 9 10 import mozprofile 11 import mozunit 12 import pytest 13 from addon_stubs import generate_addon 14 15 here = os.path.dirname(os.path.abspath(__file__)) 16 17 18 @pytest.fixture 19 def profile(): 20 return mozprofile.Profile() 21 22 23 @pytest.fixture 24 def am(profile): 25 return profile.addons 26 27 28 def test_install_multiple_same_source(tmpdir, am): 29 path = tmpdir.strpath 30 31 # Generate installer stubs for all possible types of addons 32 addon_xpi = generate_addon("test-addon-1@mozilla.org", path=path) 33 addon_folder = generate_addon("test-addon-1@mozilla.org", path=path, xpi=False) 34 35 # The same folder should not be installed twice 36 am.install([addon_folder, addon_folder]) 37 assert am.installed_addons == [addon_folder] 38 am.clean() 39 40 # The same XPI file should not be installed twice 41 am.install([addon_xpi, addon_xpi]) 42 assert am.installed_addons == [addon_xpi] 43 am.clean() 44 45 # Even if it is the same id the add-on should be installed twice, if 46 # specified via XPI and folder 47 am.install([addon_folder, addon_xpi]) 48 assert len(am.installed_addons) == 2 49 assert addon_folder in am.installed_addons 50 assert addon_xpi in am.installed_addons 51 am.clean() 52 53 54 def test_install_webextension_from_dir(tmpdir, am): 55 tmpdir = tmpdir.strpath 56 57 addon = os.path.join(here, "addons", "apply-css.xpi") 58 zipped = zipfile.ZipFile(addon) 59 try: 60 zipped.extractall(tmpdir) 61 finally: 62 zipped.close() 63 am.install(tmpdir) 64 assert len(am.installed_addons) == 1 65 assert os.path.isdir(am.installed_addons[0]) 66 67 68 def test_install_webextension(am): 69 addon = os.path.join(here, "addons", "apply-css.xpi") 70 71 am.install(addon) 72 assert len(am.installed_addons) == 1 73 assert os.path.isfile(am.installed_addons[0]) 74 assert "apply-css.xpi" == os.path.basename(am.installed_addons[0]) 75 76 details = am.addon_details(am.installed_addons[0]) 77 assert "test-webext@quality.mozilla.org" == details["id"] 78 79 80 def test_install_webextension_id_via_browser_specific_settings(am): 81 # See Bug 1572404 82 addon = os.path.join( 83 here, "addons", "apply-css-id-via-browser-specific-settings.xpi" 84 ) 85 am.install(addon) 86 assert len(am.installed_addons) == 1 87 assert os.path.isfile(am.installed_addons[0]) 88 assert "apply-css-id-via-browser-specific-settings.xpi" == os.path.basename( 89 am.installed_addons[0] 90 ) 91 92 details = am.addon_details(am.installed_addons[0]) 93 assert "test-webext@quality.mozilla.org" == details["id"] 94 95 96 def test_install_webextension_sans_id(am): 97 addon = os.path.join(here, "addons", "apply-css-sans-id.xpi") 98 am.install(addon) 99 100 assert len(am.installed_addons) == 1 101 assert os.path.isfile(am.installed_addons[0]) 102 assert "apply-css-sans-id.xpi" == os.path.basename(am.installed_addons[0]) 103 104 details = am.addon_details(am.installed_addons[0]) 105 assert "@temporary-addon" in details["id"] 106 107 108 def test_install_xpi(tmpdir, am): 109 tmpdir = tmpdir.strpath 110 111 addons_to_install = [] 112 addons_installed = [] 113 114 # Generate installer stubs and install them 115 for ext in ["test-addon-1@mozilla.org", "test-addon-2@mozilla.org"]: 116 temp_addon = generate_addon(ext, path=tmpdir) 117 addons_to_install.append(am.addon_details(temp_addon)["id"]) 118 am.install(temp_addon) 119 120 # Generate a list of addons installed in the profile 121 addons_installed = [ 122 str(x[: -len(".xpi")]) 123 for x in os.listdir(os.path.join(am.profile, "extensions")) 124 ] 125 assert addons_to_install.sort() == addons_installed.sort() 126 127 128 def test_install_folder(tmpdir, am): 129 tmpdir = tmpdir.strpath 130 131 # Generate installer stubs for all possible types of addons 132 addons = [] 133 addons.append(generate_addon("test-addon-1@mozilla.org", path=tmpdir)) 134 addons.append(generate_addon("test-addon-2@mozilla.org", path=tmpdir, xpi=False)) 135 addons.append( 136 generate_addon("test-addon-3@mozilla.org", path=tmpdir, name="addon-3") 137 ) 138 addons.append( 139 generate_addon( 140 "test-addon-4@mozilla.org", path=tmpdir, name="addon-4", xpi=False 141 ) 142 ) 143 addons.sort() 144 145 am.install(tmpdir) 146 147 assert am.installed_addons == addons 148 149 150 def test_install_unpack(tmpdir, am): 151 tmpdir = tmpdir.strpath 152 153 # Generate installer stubs for all possible types of addons 154 addon_xpi = generate_addon("test-addon-unpack@mozilla.org", path=tmpdir) 155 addon_folder = generate_addon( 156 "test-addon-unpack@mozilla.org", path=tmpdir, xpi=False 157 ) 158 addon_no_unpack = generate_addon("test-addon-1@mozilla.org", path=tmpdir) 159 160 # Test unpack flag for add-on as XPI 161 am.install(addon_xpi) 162 assert am.installed_addons == [addon_xpi] 163 am.clean() 164 165 # Test unpack flag for add-on as folder 166 am.install(addon_folder) 167 assert am.installed_addons == [addon_folder] 168 am.clean() 169 170 # Test forcing unpack an add-on 171 am.install(addon_no_unpack, unpack=True) 172 assert am.installed_addons == [addon_no_unpack] 173 am.clean() 174 175 176 def test_install_after_reset(tmpdir, profile): 177 tmpdir = tmpdir.strpath 178 am = profile.addons 179 180 # Installing the same add-on after a reset should not cause a failure 181 addon = generate_addon("test-addon-1@mozilla.org", path=tmpdir, xpi=False) 182 183 # We cannot use am because profile.reset() creates a new instance 184 am.install(addon) 185 profile.reset() 186 187 am.install(addon) 188 assert am.installed_addons == [addon] 189 190 191 def test_install_backup(tmpdir, am): 192 tmpdir = tmpdir.strpath 193 194 staged_path = os.path.join(am.profile, "extensions") 195 196 # Generate installer stubs for all possible types of addons 197 addon_xpi = generate_addon("test-addon-1@mozilla.org", path=tmpdir) 198 addon_folder = generate_addon("test-addon-1@mozilla.org", path=tmpdir, xpi=False) 199 addon_name = generate_addon( 200 "test-addon-1@mozilla.org", path=tmpdir, name="test-addon-1-dupe@mozilla.org" 201 ) 202 203 # Test backup of xpi files 204 am.install(addon_xpi) 205 assert am.backup_dir is None 206 207 am.install(addon_xpi) 208 assert am.backup_dir is not None 209 assert os.listdir(am.backup_dir) == ["test-addon-1@mozilla.org.xpi"] 210 211 am.clean() 212 assert os.listdir(staged_path) == ["test-addon-1@mozilla.org.xpi"] 213 am.clean() 214 215 # Test backup of folders 216 am.install(addon_folder) 217 assert am.backup_dir is None 218 219 am.install(addon_folder) 220 assert am.backup_dir is not None 221 assert os.listdir(am.backup_dir) == ["test-addon-1@mozilla.org"] 222 223 am.clean() 224 assert os.listdir(staged_path) == ["test-addon-1@mozilla.org"] 225 am.clean() 226 227 # Test backup of xpi files with another file name 228 am.install(addon_name) 229 assert am.backup_dir is None 230 231 am.install(addon_xpi) 232 assert am.backup_dir is not None 233 assert os.listdir(am.backup_dir) == ["test-addon-1@mozilla.org.xpi"] 234 235 am.clean() 236 assert os.listdir(staged_path) == ["test-addon-1@mozilla.org.xpi"] 237 am.clean() 238 239 240 def test_install_invalid_addons(tmpdir, am): 241 tmpdir = tmpdir.strpath 242 243 # Generate installer stubs for all possible types of addons 244 addons = [] 245 addons.append( 246 generate_addon( 247 "test-addon-invalid-no-manifest@mozilla.org", path=tmpdir, xpi=False 248 ) 249 ) 250 addons.append(generate_addon("test-addon-invalid-no-id@mozilla.org", path=tmpdir)) 251 252 am.install(tmpdir) 253 254 assert am.installed_addons == [] 255 256 257 @pytest.mark.xfail(reason="feature not implemented as part of AddonManger") 258 def test_install_error(am): 259 """Check install raises an error with an invalid addon""" 260 temp_addon = generate_addon("test-addon-invalid-version@mozilla.org") 261 # This should raise an error here 262 with pytest.raises(Exception): 263 am.install(temp_addon) 264 265 266 def test_addon_details(tmpdir, am): 267 tmpdir = tmpdir.strpath 268 269 # Generate installer stubs for a valid and invalid add-on manifest 270 valid_addon = generate_addon("test-addon-1@mozilla.org", path=tmpdir) 271 invalid_addon = generate_addon( 272 "test-addon-invalid-not-wellformed@mozilla.org", path=tmpdir 273 ) 274 275 # Check valid add-on 276 details = am.addon_details(valid_addon) 277 assert details["id"] == "test-addon-1@mozilla.org" 278 assert details["name"] == "Test Add-on 1" 279 assert not details["unpack"] 280 assert details["version"] == "0.1" 281 282 # Check invalid add-on 283 with pytest.raises(mozprofile.addons.AddonFormatError): 284 am.addon_details(invalid_addon) 285 286 # Check invalid path 287 with pytest.raises(IOError): 288 am.addon_details("") 289 290 # Check invalid add-on format 291 addon_path = os.path.join(os.path.join(here, "files"), "not_an_addon.txt") 292 with pytest.raises(mozprofile.addons.AddonFormatError): 293 am.addon_details(addon_path) 294 295 296 def test_clean_addons(am): 297 addon_one = generate_addon("test-addon-1@mozilla.org") 298 addon_two = generate_addon("test-addon-2@mozilla.org") 299 300 am.install(addon_one) 301 installed_addons = [ 302 str(x[: -len(".xpi")]) 303 for x in os.listdir(os.path.join(am.profile, "extensions")) 304 ] 305 306 # Create a new profile based on an existing profile 307 # Install an extra addon in the new profile 308 # Cleanup addons 309 duplicate_profile = mozprofile.profile.Profile(profile=am.profile, addons=addon_two) 310 duplicate_profile.addons.clean() 311 312 addons_after_cleanup = [ 313 str(x[: -len(".xpi")]) 314 for x in os.listdir(os.path.join(duplicate_profile.profile, "extensions")) 315 ] 316 # New addons installed should be removed by clean_addons() 317 assert installed_addons == addons_after_cleanup 318 319 320 def test_noclean(tmpdir): 321 """test `restore=True/False` functionality""" 322 profile = tmpdir.mkdtemp().strpath 323 tmpdir = tmpdir.mkdtemp().strpath 324 325 # empty initially 326 assert not bool(os.listdir(profile)) 327 328 # make an addon 329 addons = [ 330 generate_addon("test-addon-1@mozilla.org", path=tmpdir), 331 os.path.join(here, "addons", "empty.xpi"), 332 ] 333 334 # install it with a restore=True AddonManager 335 am = mozprofile.addons.AddonManager(profile, restore=True) 336 337 for addon in addons: 338 am.install(addon) 339 340 # now its there 341 assert os.listdir(profile) == ["extensions"] 342 staging_folder = os.path.join(profile, "extensions") 343 assert os.path.exists(staging_folder) 344 assert len(os.listdir(staging_folder)) == 2 345 346 del am 347 348 assert os.listdir(profile) == ["extensions"] 349 assert os.path.exists(staging_folder) 350 assert os.listdir(staging_folder) == [] 351 352 353 def test_remove_addon(tmpdir, am): 354 tmpdir = tmpdir.strpath 355 356 addons = [] 357 addons.append(generate_addon("test-addon-1@mozilla.org", path=tmpdir)) 358 addons.append(generate_addon("test-addon-2@mozilla.org", path=tmpdir)) 359 360 am.install(tmpdir) 361 362 extensions_path = os.path.join(am.profile, "extensions") 363 staging_path = os.path.join(extensions_path) 364 365 for addon in am._addons: 366 am.remove_addon(addon) 367 368 assert os.listdir(staging_path) == [] 369 assert os.listdir(extensions_path) == [] 370 371 372 if __name__ == "__main__": 373 mozunit.main()