Bug 1868144 - Introduce a new condprof-addons linter and condprof-addons-verify taskcluster task. r=sparky,jmaher,linter-reviewers,ahal

Differential Revision: https://phabricator.services.mozilla.com/D195434
This commit is contained in:
Luca Greco 2024-01-17 14:07:53 +00:00
Родитель 40e1d9883e
Коммит 398b822d42
17 изменённых файлов: 649 добавлений и 8 удалений

Просмотреть файл

@ -0,0 +1,85 @@
CondProf Addons
===============
CondProf Addons is a linter for condprof customization JSON files (see :searchfox:`testing/condprofile/condprof/customization`),
it reports linting errors if:
- any of the addons required by the customization files (e.g. see :searchfox:`testing/condprofile/condprof/customization/webext.json`)
is not found in the tar file fetched through the `firefox-addons` fetch task (see :searchfox:`taskcluster/ci/fetch/browsertime.yml`)
- or the expected `firefox-addons` fetch task has not been found
Run Locally
-----------
The mozlint integration of condprof-addons can be run using mach:
.. parsed-literal::
$ mach lint --linter condprof-addons <file paths>
Alternatively, if the ``--linter condprof-addons`` is omitted, the ``condprof-addons`` will still be selected automatically if
any of the files paths passed explicitly is detected to be part of the condprof customization directory.
The ``condprof-addons`` will also be running automatically on ``mach lint --outgoing`` if there are customization files changes
detected in the outgoing patches.
Run on Taskcluster
------------------
The condprof-addons job shows up as ``misc(condprof-addons)`` in the linting job. It should run automatically if changes are made
to condprof customization JSON files.
Fix reported errors
-------------------
XPI file is missing from the firefox-addons.tar archive
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This linting errors is expected to be reported if the linter detected that a confprof customization file
requires an addon but the related xpi filename is not included in the firefox-addons.tar file fetched
through the `firefox-addons` fetch task (see :searchfox:`taskcluster/ci/fetch/browsertime.yml`).
If the patch or phabricator revision is not meant to be landed, but only used as a temporary patch
pushed on try or only applied locally (e.g. to run the tp6/tp6m webextensions perftests with a given
third party extension installed to gather some metrics and/or GeckoProfiler data), then it can be
safely ignored.
On the contrary, if the patch or phabricator revision is meant to be landed on mozilla-central,
the linting error have to be fixed before or along landing the change, either by:
- removing the addition to the customization file if it wasn't intended to include that addon to all runs
of the tp6/tp6m webextensions perftests
- updating the `firefox-addons` fetch task as defined in :searchfox:`taskcluster/ci/fetch/browsertime.yml`
by creating a pull request in the github repository where the asset is stored, and ask a review from
a peer of the `#webextensions-reviewer` and `#perftests-reviewers` review groups.
firefox-addons taskcluster config 'add-prefix' attribute should be set to 'firefox-addons/'
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If this linting error is hit, then the `firefox-addons` task defined in :searchfox:`taskcluster/ci/fetch/browsertime.yml`
is missing the `add-prefix` attribute or its value is not set to the expected 'firefox-addons/' subdir name.
This is enforced as a linting rule because when the condprof utility is going to build a conditioned profile
for which some add-ons xpi files are expected to be sideloaded (see :searchfox:`testing/condprofile/condprof/customization.webext.json`),
to avoid re-downloading the same xpi from a remote urls every time the conditioned profile is built on the build infrastructure
(which for tp6/tp6m perftests will happen once per job being executed) condprof is going to look first if the expected xpi file
names are already available in `$MOZ_FETCHES_DIR/firefox-addons`.
firefox-addons taskcluser fetch config section not found
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This linting error is hit if the linter does not find the expected `firefox-addons` task defined in :searchfox:`taskcluster/ci/fetch/browsertime.yml`
or it is missing the expected `fetch` attribute.
Configuration
-------------
ConfProf Addons does not currently provide any configuration files.
Sources
-------
* :searchfox:`Configuration (YAML) <tools/lint/condprof-addons/yml>`
* :searchfox:`Source <tools/lint/condprof-addons/__init__.py>`
* :searchfox:`Test <tools/lint/test/test_condprof_addons.py>`

Просмотреть файл

@ -174,6 +174,7 @@ firefox-addons:
fetch:
type: static-url
artifact-name: firefox-addons.tar.zst
add-prefix: firefox-addons/
url: https://github.com/mozilla/perf-automation/raw/c9e497eaa86f45538e3509120947215c6f97c95b/test_files/firefox-addons.tar
sha256: 20372ff1d58fc33d1568f8922fe66e2e2e01c77663820344d2a364a8ddd68281
size: 3584000

Просмотреть файл

@ -561,6 +561,22 @@ perfdocs-verify:
- 'testing/perfdocs/**'
- 'tools/lint/perfdocs.yml'
condprof-addons-verify:
description: |
Verifies all xpi files needed by condprof customization sideloading addons are
included in the firefox-addons fetch task.
treeherder:
symbol: misc(condprof-addons)
run:
mach: |
lint -v -l condprof-addons -f treeherder -f json:/builds/worker/mozlint.json .
fetches:
fetch:
- firefox-addons
when:
files-changed:
- 'testing/condprofile/condprof/customization/*.json'
- 'taskcluster/ci/fetch/browsertime.yml'
fluent-lint:
description: Check for problems with Fluent files.

Просмотреть файл

@ -21,7 +21,7 @@ from requests.packages.urllib3.util.retry import Retry
from condprof import progress
TASK_CLUSTER = "TASK_ID" in os.environ.keys()
TASK_CLUSTER = "MOZ_AUTOMATION" in os.environ.keys()
DOWNLOAD_TIMEOUT = 30
@ -122,7 +122,12 @@ def fresh_profile(profile, customization_data):
extensions = []
for name, url in customization_data["addons"].items():
logger.info("Downloading addon %s" % name)
extension = download_file(url, check_mozfetches=True)
# When running on the CI, we expect the xpi files to have been
# fetched by the firefox-addons fetch task dependency (see
# taskcluster/ci/fetch/browsertime.yml) and the condprof-addons
# linter enforces the content of the archive to be unpacked into
# a subdirectory named "firefox-addons".
extension = download_file(url, mozfetches_subdir="firefox-addons")
extensions.append(extension)
logger.info("Installing addons")
new_profile.addons.install(extensions)
@ -184,25 +189,26 @@ def check_exists(archive, server=None, all_types=False):
return exists, resp.headers
def check_mozfetches_dir(target):
def check_mozfetches_dir(target, mozfetches_subdir):
logger.info("Checking for existence of: %s in MOZ_FETCHES_DIR" % target)
fetches = os.environ.get("MOZ_FETCHES_DIR")
if fetches is None:
return None
fetches_target = os.path.join(fetches, target)
fetches_target = os.path.join(fetches, mozfetches_subdir, target)
if not os.path.exists(fetches_target):
return None
logger.info("Already fetched and available in MOZ_FETCHES_DIR: %s" % fetches_target)
return fetches_target
def download_file(url, target=None, check_mozfetches=False):
def download_file(url, target=None, mozfetches_subdir=None):
if target is None:
target = url.split("/")[-1]
# check if the assets has been fetched through a taskgraph fetch task dependency.
if check_mozfetches:
filepath = check_mozfetches_dir(target)
# check if the assets has been fetched through a taskgraph fetch task dependency
# and already available in the MOZ_FETCHES_DIR passed as an additional parameter.
if mozfetches_subdir is not None:
filepath = check_mozfetches_dir(target, mozfetches_subdir)
if filepath is not None:
return filepath

Просмотреть файл

@ -0,0 +1,10 @@
---
condprof-addons:
description: Lint condprof customizations json files sideloading addons
include:
- 'testing/condprofile/condprof/customization'
exclude: []
extensions: ['json']
support-files: ['taskcluster/ci/fetch/browsertime.yml']
type: structured_log
payload: condprof-addons:lint

Просмотреть файл

@ -0,0 +1,217 @@
# 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 hashlib
import json
import os
import tarfile
import tempfile
from pathlib import Path
import requests
import yaml
from mozlint.pathutils import expand_exclusions
BROWSERTIME_FETCHES_PATH = Path("taskcluster/ci/fetch/browsertime.yml")
CUSTOMIZATIONS_PATH = Path("testing/condprofile/condprof/customization/")
DOWNLOAD_TIMEOUT = 30
ERR_FETCH_TASK_MISSING = "firefox-addons taskcluster fetch config section not found"
ERR_FETCH_TASK_ADDPREFIX = "firefox-addons taskcluster config 'add-prefix' attribute should be set to 'firefox-addons/'"
ERR_FETCH_TASK_ARCHIVE = (
"Error downloading or opening archive from firefox-addons taskcluster fetch url"
)
LINTER_NAME = "condprof-addons"
MOZ_FETCHES_DIR = os.environ.get("MOZ_FETCHES_DIR")
RULE_DESC = "condprof addons all listed in firefox-addons.tar fetched archive"
MOZ_AUTOMATION = "MOZ_AUTOMATION" in os.environ
tempdir = tempfile.gettempdir()
def lint(paths, config, logger, fix=None, **lintargs):
filepaths = [Path(p) for p in expand_exclusions(paths, config, lintargs["root"])]
if len(filepaths) == 0:
return
linter = CondprofAddonsLinter(topsrcdir=lintargs["root"], logger=logger)
for filepath in filepaths:
linter.lint(filepath)
class CondprofAddonsLinter:
def __init__(self, topsrcdir, logger):
self.topsrcdir = topsrcdir
self.logger = logger
self.BROWSERTIME_FETCHES_FULLPATH = Path(
self.topsrcdir, BROWSERTIME_FETCHES_PATH
)
self.CUSTOMIZATIONS_FULLPATH = Path(self.topsrcdir, CUSTOMIZATIONS_PATH)
self.tar_xpi_filenames = self.get_firefox_addons_tar_names()
def lint(self, filepath):
data = self.read_json(filepath)
if "addons" not in data:
return
for addon_key in data["addons"]:
xpi_url = data["addons"][addon_key]
xpi_filename = xpi_url.split("/")[-1]
self.logger.info(f"Found addon {xpi_filename}")
if xpi_filename not in self.tar_xpi_filenames:
self.logger.lint_error(
self.get_missing_xpi_msg(xpi_filename),
lineno=0,
column=None,
path=str(filepath),
linter=LINTER_NAME,
rule=RULE_DESC,
)
def get_missing_xpi_msg(self, xpi_filename):
return f"{xpi_filename} is missing from the firefox-addons.tar archive"
def read_json(self, filepath):
with filepath.open("r") as f:
return json.load(f)
def read_yaml(self, filepath):
with filepath.open("r") as f:
return yaml.safe_load(f)
def download_firefox_addons_tar(self, firefox_addons_tar_url, tar_tmp_path):
self.logger.info(f"Downloading {firefox_addons_tar_url} to {tar_tmp_path}")
res = requests.get(
firefox_addons_tar_url, stream=True, timeout=DOWNLOAD_TIMEOUT
)
res.raise_for_status()
with tar_tmp_path.open("wb") as f:
for chunk in res.iter_content(chunk_size=1024):
if chunk is not None:
f.write(chunk)
f.flush()
def get_firefox_addons_tar_names(self):
# Get firefox-addons fetch task config.
browsertime_fetches = self.read_yaml(self.BROWSERTIME_FETCHES_FULLPATH)
if not (
"firefox-addons" in browsertime_fetches
and "fetch" in browsertime_fetches["firefox-addons"]
):
self.logger.lint_error(
ERR_FETCH_TASK_MISSING,
lineno=0,
column=None,
path=BROWSERTIME_FETCHES_PATH,
linter=LINTER_NAME,
rule=RULE_DESC,
)
return []
fetch_config = browsertime_fetches["firefox-addons"]["fetch"]
if not (
"add-prefix" in fetch_config
and fetch_config["add-prefix"] == "firefox-addons/"
):
self.logger.lint_error(
ERR_FETCH_TASK_ADDPREFIX,
lineno=0,
column=None,
path=BROWSERTIME_FETCHES_PATH,
linter=LINTER_NAME,
rule=RULE_DESC,
)
return []
firefox_addons_tar_url = fetch_config["url"]
firefox_addons_tar_sha256 = fetch_config["sha256"]
tar_xpi_files = list()
# When running on the CI, try to retrieve the list of xpi files from the target MOZ_FETCHES_DIR
# subdirectory instead of downloading the archive from the fetch url.
if MOZ_AUTOMATION:
fetches_path = (
Path(MOZ_FETCHES_DIR) if MOZ_FETCHES_DIR is not None else None
)
if fetches_path is not None and fetches_path.exists():
self.logger.info(
"Detected MOZ_FETCHES_DIR, look for pre-downloaded firefox-addons fetch results"
)
# add-prefix presence and value has been enforced at the start of this method.
fetches_addons = Path(fetches_path, "firefox-addons/")
if fetches_addons.exists():
self.logger.info(
f"Retrieve list of xpi files from firefox-addons fetch result at {str(fetches_addons)}"
)
for xpi_path in fetches_addons.iterdir():
if xpi_path.suffix == ".xpi":
tar_xpi_files.append(xpi_path.name)
return tar_xpi_files
else:
self.logger.warning(
"No 'firefox-addons/' subdir found in MOZ_FETCHES_DIR"
)
# Fallback to download the tar archive and retrieve the list of xpi file from it
# (e.g. when linting the local changes on the developers environment).
tar_tmp_path = Path(tempdir, "firefox-addons.tar")
tar_tmp_ready = False
# If the firefox-addons.tar file is found in the tempdir, check if the
# file hash matches, if it does then don't download it again.
if tar_tmp_path.exists():
tar_tmp_hash = hashlib.sha256()
with tar_tmp_path.open("rb") as f:
while chunk := f.read(1024):
tar_tmp_hash.update(chunk)
if tar_tmp_hash.hexdigest() == firefox_addons_tar_sha256:
self.logger.info(
f"Pre-downloaded file for {tar_tmp_path} found and sha256 matching"
)
tar_tmp_ready = True
else:
self.logger.info(
f"{tar_tmp_path} sha256 does not match the fetch config"
)
# If the file is not found or the hash doesn't match, download it from the fetch task url.
if not tar_tmp_ready:
try:
self.download_firefox_addons_tar(firefox_addons_tar_url, tar_tmp_path)
except requests.exceptions.HTTPError as http_err:
self.logger.lint_error(
f"{ERR_FETCH_TASK_ARCHIVE}, {str(http_err)}",
lineno=0,
column=None,
path=BROWSERTIME_FETCHES_PATH,
linter=LINTER_NAME,
rule=RULE_DESC,
)
return []
# Retrieve and return the list of xpi file names.
try:
with tarfile.open(tar_tmp_path, "r") as tf:
names = tf.getnames()
for name in names:
file_path = Path(name)
if file_path.suffix == ".xpi":
tar_xpi_files.append(file_path.name)
except tarfile.ReadError as read_err:
self.logger.lint_error(
f"{ERR_FETCH_TASK_ARCHIVE}, {str(read_err)}",
lineno=0,
column=None,
path=BROWSERTIME_FETCHES_PATH,
linter=LINTER_NAME,
rule=RULE_DESC,
)
return []
return tar_xpi_files

Просмотреть файл

@ -0,0 +1,10 @@
---
firefox-addons:
description: "fixture for the expected firefox-addons.tar ci fetch config"
fetch:
type: static-url
artifact-name: firefox-addons.tar.zst
add-prefix: firefox-addons/
url: https://localhost/fake-firefox-addons.tar
sha256: 20372ff1d58fc33d1568f8922fe66e2e2e01c77663820344d2a364a8ddd68281
size: 3584000

Просмотреть файл

@ -0,0 +1 @@
{}

Просмотреть файл

@ -0,0 +1 @@
{}

Просмотреть файл

@ -0,0 +1 @@
{}

Просмотреть файл

@ -0,0 +1 @@
{}

Просмотреть файл

Просмотреть файл

Двоичные данные
tools/lint/test/files/condprof-addons/firefox-addons-fake.tar Normal file

Двоичный файл не отображается.

Просмотреть файл

@ -0,0 +1,5 @@
{
"addons": {
"non-existing": "http://localhost/non-existing.xpi"
}
}

Просмотреть файл

@ -10,6 +10,8 @@ requirements = "tools/lint/python/black_requirements.txt"
["test_codespell.py"]
["test_condprof_addons.py"]
["test_eslint.py"]
skip-if = ["os == 'win'"] # busts the tree for subsequent tasks on the same worker (bug 1708591)
# Setup conflicts with stylelint setup so this should run sequentially.

Просмотреть файл

@ -0,0 +1,285 @@
import importlib
import tempfile
from pathlib import Path
from unittest import mock
import mozunit
import requests
LINTER = "condprof-addons"
def linter_module_mocks(
customizations_path=".", browsertime_fetches_path="browsertime.yml", **othermocks
):
return mock.patch.multiple(
LINTER,
CUSTOMIZATIONS_PATH=Path(customizations_path),
BROWSERTIME_FETCHES_PATH=Path(browsertime_fetches_path),
**othermocks,
)
def linter_class_mocks(**mocks):
return mock.patch.multiple(
f"{LINTER}.CondprofAddonsLinter",
**mocks,
)
# Sanity check (make sure linter message includes the xpi filename).
def test_get_missing_xpi_msg(lint, paths):
condprof_addons = importlib.import_module("condprof-addons")
with linter_class_mocks(
get_firefox_addons_tar_names=mock.Mock(return_value=list()),
):
instance = condprof_addons.CondprofAddonsLinter(
topsrcdir=paths()[0], logger=mock.Mock()
)
assert instance.get_missing_xpi_msg("test.xpi").startswith(
"test.xpi is missing"
)
def test_xpi_missing_from_firefox_addons_tar(lint, paths):
fixture_customizations = paths("with-missing-xpi.json")
with linter_module_mocks(), linter_class_mocks(
get_firefox_addons_tar_names=mock.Mock(return_value=list()),
):
logger_mock = mock.Mock()
lint(fixture_customizations, logger=logger_mock)
assert logger_mock.lint_error.call_count == 1
assert Path(fixture_customizations[0]).samefile(
logger_mock.lint_error.call_args.kwargs["path"]
)
importlib.import_module("condprof-addons")
assert "non-existing.xpi" in logger_mock.lint_error.call_args.args[0]
def test_xpi_all_found_in_firefox_addons_tar(lint, paths):
get_tarnames_mock = mock.Mock(
return_value=["an-extension.xpi", "another-extension.xpi"]
)
read_json_mock = mock.Mock(
return_value={
"addons": {
"an-extension": "http://localhost/ext/an-extension.xpi",
"another-extension": "http://localhost/ext/another-extension.xpi",
}
}
)
with linter_module_mocks(), linter_class_mocks(
get_firefox_addons_tar_names=get_tarnames_mock, read_json=read_json_mock
):
logger_mock = mock.Mock()
# Compute a fake condprof customization path, the content is
# going to be the read_json_mock.return_value and so the
# fixture file does not actually exists.
fixture_customizations = paths("fake-condprof-config.json")
lint(
fixture_customizations,
logger=logger_mock,
config={"include": paths(), "extensions": ["json", "yml"]},
)
assert read_json_mock.call_count == 1
assert get_tarnames_mock.call_count == 1
assert logger_mock.lint_error.call_count == 0
def test_lint_error_on_missing_or_invalid_firefoxaddons_fetch_task(
lint,
paths,
):
read_json_mock = mock.Mock(return_value=dict())
read_yaml_mock = mock.Mock(return_value=dict())
# Verify that an explicit linter error is reported if the fetch task is not found.
with linter_module_mocks(), linter_class_mocks(
read_json=read_json_mock, read_yaml=read_yaml_mock
):
logger_mock = mock.Mock()
fixture_customizations = paths("fake-condprof-config.json")
condprof_addons = importlib.import_module("condprof-addons")
def assert_linter_error(yaml_mock_value, expected_msg):
logger_mock.reset_mock()
read_yaml_mock.return_value = yaml_mock_value
lint(fixture_customizations, logger=logger_mock)
assert logger_mock.lint_error.call_count == 1
expected_path = condprof_addons.BROWSERTIME_FETCHES_PATH
assert logger_mock.lint_error.call_args.kwargs["path"] == expected_path
assert logger_mock.lint_error.call_args.args[0] == expected_msg
# Mock a yaml file that is not including the expected firefox-addons fetch task.
assert_linter_error(
yaml_mock_value=dict(), expected_msg=condprof_addons.ERR_FETCH_TASK_MISSING
)
# Mock a yaml file where firefox-addons is missing the fetch attribute.
assert_linter_error(
yaml_mock_value={"firefox-addons": {}},
expected_msg=condprof_addons.ERR_FETCH_TASK_MISSING,
)
# Mock a yaml file where firefox-addons add-prefix is missing.
assert_linter_error(
yaml_mock_value={"firefox-addons": {"fetch": {}}},
expected_msg=condprof_addons.ERR_FETCH_TASK_ADDPREFIX,
)
# Mock a yaml file where firefox-addons add-prefix is invalid.
assert_linter_error(
yaml_mock_value={
"firefox-addons": {"fetch": {"add-prefix": "invalid-subdir-name/"}}
},
expected_msg=condprof_addons.ERR_FETCH_TASK_ADDPREFIX,
)
def test_get_xpi_list_from_fetch_dir(lint, paths):
# Verify that when executed on the CI, the helper method looks for the xpi files
# in the MOZ_FETCHES_DIR subdir where they are expected to be unpacked by the
# fetch task.
with linter_module_mocks(
MOZ_AUTOMATION=1, MOZ_FETCHES_DIR=paths("fake-fetches-dir")[0]
):
condprof_addons = importlib.import_module("condprof-addons")
logger_mock = mock.Mock()
Path(paths("browsertime.yml")[0])
linter = condprof_addons.CondprofAddonsLinter(
topsrcdir=paths()[0], logger=logger_mock
)
results = linter.tar_xpi_filenames
results.sort()
assert results == ["fake-ext-01.xpi", "fake-ext-02.xpi"]
def test_get_xpi_list_from_downloaded_tar(lint, paths):
def mocked_download_tar(firefox_addons_tar_url, tar_tmp_path):
tar_tmp_path.write_bytes(Path(paths("firefox-addons-fake.tar")[0]).read_bytes())
download_firefox_addons_tar_mock = mock.Mock()
download_firefox_addons_tar_mock.side_effect = mocked_download_tar
# Verify that when executed locally on a developer machine, the tar archive is downloaded
# and the list of xpi files included in it returned by the helper method.
with tempfile.TemporaryDirectory() as tempdir, linter_module_mocks(
MOZ_AUTOMATION=0,
tempdir=tempdir,
), linter_class_mocks(
download_firefox_addons_tar=download_firefox_addons_tar_mock,
):
condprof_addons = importlib.import_module("condprof-addons")
logger_mock = mock.Mock()
Path(paths("browsertime.yml")[0])
linter = condprof_addons.CondprofAddonsLinter(
topsrcdir=paths()[0], logger=logger_mock
)
results = linter.tar_xpi_filenames
assert len(results) > 0
print("List of addons found in the downloaded file archive:", results)
assert all(filename.endswith(".xpi") for filename in results)
assert download_firefox_addons_tar_mock.call_count == 1
@mock.patch("requests.get")
def test_error_on_downloading_tar(requests_get_mock, lint, paths):
# Verify that when executed locally and the tar archive fails to download
# the linter does report an explicit linting error with the http error included.
with tempfile.TemporaryDirectory() as tempdir, linter_module_mocks(
MOZ_AUTOMATION=0, tempdir=tempdir
):
condprof_addons = importlib.import_module("condprof-addons")
logger_mock = mock.Mock()
response_mock = mock.Mock()
response_mock.raise_for_status.side_effect = requests.exceptions.HTTPError(
"MOCK_ERROR"
)
requests_get_mock.return_value = response_mock
Path(paths("browsertime.yml")[0])
linter = condprof_addons.CondprofAddonsLinter(
topsrcdir=paths()[0], logger=logger_mock
)
assert (
logger_mock.lint_error.call_args.kwargs["path"]
== condprof_addons.BROWSERTIME_FETCHES_PATH
)
assert (
logger_mock.lint_error.call_args.args[0]
== f"{condprof_addons.ERR_FETCH_TASK_ARCHIVE}, MOCK_ERROR"
)
assert requests_get_mock.call_count == 1
assert len(linter.tar_xpi_filenames) == 0
@mock.patch("requests.get")
def test_error_on_opening_tar(requests_get_mock, lint, paths):
# Verify that when executed locally and the tar archive fails to open
# the linter does report an explicit linting error with the tarfile error included.
with tempfile.TemporaryDirectory() as tempdir, linter_module_mocks(
MOZ_AUTOMATION=0, tempdir=tempdir
):
condprof_addons = importlib.import_module("condprof-addons")
logger_mock = mock.Mock()
response_mock = mock.Mock()
response_mock.raise_for_status.return_value = None
def mock_iter_content(chunk_size):
yield b"fake tar content"
yield b"expected to trigger tarfile.ReadError"
response_mock.iter_content.side_effect = mock_iter_content
requests_get_mock.return_value = response_mock
Path(paths("browsertime.yml")[0])
linter = condprof_addons.CondprofAddonsLinter(
topsrcdir=paths()[0], logger=logger_mock
)
assert (
logger_mock.lint_error.call_args.kwargs["path"]
== condprof_addons.BROWSERTIME_FETCHES_PATH
)
actual_msg = logger_mock.lint_error.call_args.args[0]
print("Got linter error message:", actual_msg)
assert actual_msg.startswith(
f"{condprof_addons.ERR_FETCH_TASK_ARCHIVE}, file could not be opened successfully"
)
assert requests_get_mock.call_count == 1
assert len(linter.tar_xpi_filenames) == 0
def test_lint_all_customization_files_when_linting_browsertime_yml(
lint,
paths,
):
get_tarnames_mock = mock.Mock(return_value=["an-extension.xpi"])
read_json_mock = mock.Mock(
return_value={
"addons": {"an-extension": "http://localhost/ext/an-extension.xpi"}
}
)
with linter_module_mocks(
customizations_path="fake-customizations-dir",
), linter_class_mocks(
get_firefox_addons_tar_names=get_tarnames_mock,
read_json=read_json_mock,
):
logger_mock = mock.Mock()
importlib.import_module("condprof-addons")
# When mozlint detects a change to the ci fetch browser.yml support file,
# condprof-addons linter is called for the entire customizations dir path
# and we expect that to be expanded to the list of the json customizations
# files from that directory path.
lint(paths("fake-customizations-dir"), logger=logger_mock)
# Expect read_json_mock to be called once per each of the json files
# found in the fixture dir.
assert read_json_mock.call_count == 3
assert get_tarnames_mock.call_count == 1
assert logger_mock.lint_error.call_count == 0
if __name__ == "__main__":
mozunit.main()