3 from pathlib
import Path
4 from unittest
import mock
9 LINTER
= "condprof-addons"
12 def linter_module_mocks(
13 customizations_path
=".", browsertime_fetches_path
="browsertime.yml", **othermocks
15 return mock
.patch
.multiple(
17 CUSTOMIZATIONS_PATH
=Path(customizations_path
),
18 BROWSERTIME_FETCHES_PATH
=Path(browsertime_fetches_path
),
23 def linter_class_mocks(**mocks
):
24 return mock
.patch
.multiple(
25 f
"{LINTER}.CondprofAddonsLinter",
30 # Sanity check (make sure linter message includes the xpi filename).
31 def test_get_missing_xpi_msg(lint
, paths
):
32 condprof_addons
= importlib
.import_module("condprof-addons")
33 with
linter_class_mocks(
34 get_firefox_addons_tar_names
=mock
.Mock(return_value
=list()),
36 instance
= condprof_addons
.CondprofAddonsLinter(
37 topsrcdir
=paths()[0], logger
=mock
.Mock()
39 assert instance
.get_missing_xpi_msg("test.xpi").startswith(
44 def test_xpi_missing_from_firefox_addons_tar(lint
, paths
):
45 fixture_customizations
= paths("with-missing-xpi.json")
46 with
linter_module_mocks(), linter_class_mocks(
47 get_firefox_addons_tar_names
=mock
.Mock(return_value
=list()),
49 logger_mock
= mock
.Mock()
50 lint(fixture_customizations
, logger
=logger_mock
)
51 assert logger_mock
.lint_error
.call_count
== 1
52 assert Path(fixture_customizations
[0]).samefile(
53 logger_mock
.lint_error
.call_args
.kwargs
["path"]
55 importlib
.import_module("condprof-addons")
56 assert "non-existing.xpi" in logger_mock
.lint_error
.call_args
.args
[0]
59 def test_xpi_all_found_in_firefox_addons_tar(lint
, paths
):
60 get_tarnames_mock
= mock
.Mock(
61 return_value
=["an-extension.xpi", "another-extension.xpi"]
63 read_json_mock
= mock
.Mock(
66 "an-extension": "http://localhost/ext/an-extension.xpi",
67 "another-extension": "http://localhost/ext/another-extension.xpi",
72 with
linter_module_mocks(), linter_class_mocks(
73 get_firefox_addons_tar_names
=get_tarnames_mock
, read_json
=read_json_mock
75 logger_mock
= mock
.Mock()
76 # Compute a fake condprof customization path, the content is
77 # going to be the read_json_mock.return_value and so the
78 # fixture file does not actually exists.
79 fixture_customizations
= paths("fake-condprof-config.json")
81 fixture_customizations
,
83 config
={"include": paths(), "extensions": ["json", "yml"]},
85 assert read_json_mock
.call_count
== 1
86 assert get_tarnames_mock
.call_count
== 1
87 assert logger_mock
.lint_error
.call_count
== 0
90 def test_lint_error_on_missing_or_invalid_firefoxaddons_fetch_task(
94 read_json_mock
= mock
.Mock(return_value
=dict())
95 read_yaml_mock
= mock
.Mock(return_value
=dict())
96 # Verify that an explicit linter error is reported if the fetch task is not found.
97 with
linter_module_mocks(), linter_class_mocks(
98 read_json
=read_json_mock
, read_yaml
=read_yaml_mock
100 logger_mock
= mock
.Mock()
101 fixture_customizations
= paths("fake-condprof-config.json")
102 condprof_addons
= importlib
.import_module("condprof-addons")
104 def assert_linter_error(yaml_mock_value
, expected_msg
):
105 logger_mock
.reset_mock()
106 read_yaml_mock
.return_value
= yaml_mock_value
107 lint(fixture_customizations
, logger
=logger_mock
)
108 assert logger_mock
.lint_error
.call_count
== 1
109 expected_path
= condprof_addons
.BROWSERTIME_FETCHES_PATH
110 assert logger_mock
.lint_error
.call_args
.kwargs
["path"] == expected_path
111 assert logger_mock
.lint_error
.call_args
.args
[0] == expected_msg
113 # Mock a yaml file that is not including the expected firefox-addons fetch task.
115 yaml_mock_value
=dict(), expected_msg
=condprof_addons
.ERR_FETCH_TASK_MISSING
117 # Mock a yaml file where firefox-addons is missing the fetch attribute.
119 yaml_mock_value
={"firefox-addons": {}},
120 expected_msg
=condprof_addons
.ERR_FETCH_TASK_MISSING
,
122 # Mock a yaml file where firefox-addons add-prefix is missing.
124 yaml_mock_value
={"firefox-addons": {"fetch": {}}},
125 expected_msg
=condprof_addons
.ERR_FETCH_TASK_ADDPREFIX
,
127 # Mock a yaml file where firefox-addons add-prefix is invalid.
130 "firefox-addons": {"fetch": {"add-prefix": "invalid-subdir-name/"}}
132 expected_msg
=condprof_addons
.ERR_FETCH_TASK_ADDPREFIX
,
136 def test_get_xpi_list_from_fetch_dir(lint
, paths
):
137 # Verify that when executed on the CI, the helper method looks for the xpi files
138 # in the MOZ_FETCHES_DIR subdir where they are expected to be unpacked by the
140 with
linter_module_mocks(
141 MOZ_AUTOMATION
=1, MOZ_FETCHES_DIR
=paths("fake-fetches-dir")[0]
143 condprof_addons
= importlib
.import_module("condprof-addons")
144 logger_mock
= mock
.Mock()
145 Path(paths("browsertime.yml")[0])
147 linter
= condprof_addons
.CondprofAddonsLinter(
148 topsrcdir
=paths()[0], logger
=logger_mock
150 results
= linter
.tar_xpi_filenames
153 assert results
== ["fake-ext-01.xpi", "fake-ext-02.xpi"]
156 def test_get_xpi_list_from_downloaded_tar(lint
, paths
):
157 def mocked_download_tar(firefox_addons_tar_url
, tar_tmp_path
):
158 tar_tmp_path
.write_bytes(Path(paths("firefox-addons-fake.tar")[0]).read_bytes())
160 download_firefox_addons_tar_mock
= mock
.Mock()
161 download_firefox_addons_tar_mock
.side_effect
= mocked_download_tar
163 # Verify that when executed locally on a developer machine, the tar archive is downloaded
164 # and the list of xpi files included in it returned by the helper method.
165 with tempfile
.TemporaryDirectory() as tempdir
, linter_module_mocks(
168 ), linter_class_mocks(
169 download_firefox_addons_tar
=download_firefox_addons_tar_mock
,
171 condprof_addons
= importlib
.import_module("condprof-addons")
172 logger_mock
= mock
.Mock()
173 Path(paths("browsertime.yml")[0])
175 linter
= condprof_addons
.CondprofAddonsLinter(
176 topsrcdir
=paths()[0], logger
=logger_mock
178 results
= linter
.tar_xpi_filenames
179 assert len(results
) > 0
180 print("List of addons found in the downloaded file archive:", results
)
181 assert all(filename
.endswith(".xpi") for filename
in results
)
182 assert download_firefox_addons_tar_mock
.call_count
== 1
185 @mock.patch("requests.get")
186 def test_error_on_downloading_tar(requests_get_mock
, lint
, paths
):
187 # Verify that when executed locally and the tar archive fails to download
188 # the linter does report an explicit linting error with the http error included.
189 with tempfile
.TemporaryDirectory() as tempdir
, linter_module_mocks(
190 MOZ_AUTOMATION
=0, tempdir
=tempdir
192 condprof_addons
= importlib
.import_module("condprof-addons")
193 logger_mock
= mock
.Mock()
194 response_mock
= mock
.Mock()
195 response_mock
.raise_for_status
.side_effect
= requests
.exceptions
.HTTPError(
198 requests_get_mock
.return_value
= response_mock
199 Path(paths("browsertime.yml")[0])
201 linter
= condprof_addons
.CondprofAddonsLinter(
202 topsrcdir
=paths()[0], logger
=logger_mock
206 logger_mock
.lint_error
.call_args
.kwargs
["path"]
207 == condprof_addons
.BROWSERTIME_FETCHES_PATH
210 logger_mock
.lint_error
.call_args
.args
[0]
211 == f
"{condprof_addons.ERR_FETCH_TASK_ARCHIVE}, MOCK_ERROR"
213 assert requests_get_mock
.call_count
== 1
214 assert len(linter
.tar_xpi_filenames
) == 0
217 @mock.patch("requests.get")
218 def test_error_on_opening_tar(requests_get_mock
, lint
, paths
):
219 # Verify that when executed locally and the tar archive fails to open
220 # the linter does report an explicit linting error with the tarfile error included.
221 with tempfile
.TemporaryDirectory() as tempdir
, linter_module_mocks(
222 MOZ_AUTOMATION
=0, tempdir
=tempdir
224 condprof_addons
= importlib
.import_module("condprof-addons")
225 logger_mock
= mock
.Mock()
226 response_mock
= mock
.Mock()
227 response_mock
.raise_for_status
.return_value
= None
229 def mock_iter_content(chunk_size
):
230 yield b
"fake tar content"
231 yield b
"expected to trigger tarfile.ReadError"
233 response_mock
.iter_content
.side_effect
= mock_iter_content
234 requests_get_mock
.return_value
= response_mock
235 Path(paths("browsertime.yml")[0])
237 linter
= condprof_addons
.CondprofAddonsLinter(
238 topsrcdir
=paths()[0], logger
=logger_mock
242 logger_mock
.lint_error
.call_args
.kwargs
["path"]
243 == condprof_addons
.BROWSERTIME_FETCHES_PATH
245 actual_msg
= logger_mock
.lint_error
.call_args
.args
[0]
246 print("Got linter error message:", actual_msg
)
247 assert actual_msg
.startswith(
248 f
"{condprof_addons.ERR_FETCH_TASK_ARCHIVE}, file could not be opened successfully"
250 assert requests_get_mock
.call_count
== 1
251 assert len(linter
.tar_xpi_filenames
) == 0
254 def test_lint_all_customization_files_when_linting_browsertime_yml(
258 get_tarnames_mock
= mock
.Mock(return_value
=["an-extension.xpi"])
259 read_json_mock
= mock
.Mock(
261 "addons": {"an-extension": "http://localhost/ext/an-extension.xpi"}
264 with
linter_module_mocks(
265 customizations_path
="fake-customizations-dir",
266 ), linter_class_mocks(
267 get_firefox_addons_tar_names
=get_tarnames_mock
,
268 read_json
=read_json_mock
,
270 logger_mock
= mock
.Mock()
271 importlib
.import_module("condprof-addons")
272 # When mozlint detects a change to the ci fetch browser.yml support file,
273 # condprof-addons linter is called for the entire customizations dir path
274 # and we expect that to be expanded to the list of the json customizations
275 # files from that directory path.
276 lint(paths("fake-customizations-dir"), logger
=logger_mock
)
277 # Expect read_json_mock to be called once per each of the json files
278 # found in the fixture dir.
279 assert read_json_mock
.call_count
== 3
280 assert get_tarnames_mock
.call_count
== 1
281 assert logger_mock
.lint_error
.call_count
== 0
284 if __name__
== "__main__":