blob: 0fdfaea2004b8d35d515db590539fadc09a47987 [file] [log] [blame]
Trent Apted7d2777b2023-06-29 13:35:03 +10001# Copyright 2023 The ChromiumOS Authors
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Unit tests for build_sdk_subtools."""
6
Trent Apted7d2777b2023-06-29 13:35:03 +10007from pathlib import Path
8from unittest import mock
9
10import pytest
11
12from chromite.lib import commandline
13from chromite.lib import cros_build_lib
14from chromite.scripts import build_sdk_subtools
Trent Aptedbd3cb412023-08-18 11:37:02 +100015from chromite.service import sdk_subtools
Trent Apted7d2777b2023-06-29 13:35:03 +100016
17
Trent Apted7c6b7742023-09-04 09:48:34 +100018# The argument passed to cros_sdk to ensure the correct SDK is being used.
19SDK_CHROOT_ARG = Path("/mnt/host/source/out/build/amd64-subtools-host")
20
21
Trent Apted7d2777b2023-06-29 13:35:03 +100022@pytest.fixture(name="outside_chroot")
23def outside_chroot_fixture():
24 """Mocks IsInsideChroot to be False."""
25 with mock.patch.object(
26 cros_build_lib, "IsInsideChroot", return_value=False
27 ) as outside_chroot:
28 yield outside_chroot
29
30
31@pytest.fixture(name="mock_emerge")
32def mock_emerge_fixture():
33 """Stubs the build_sdk_subtools emerge helper and sets it up to run."""
34 with mock.patch.multiple(
Trent Aptedbd3cb412023-08-18 11:37:02 +100035 "chromite.service.sdk_subtools",
Trent Apted7d2777b2023-06-29 13:35:03 +100036 _run_system_emerge=mock.DEFAULT,
Trent Aptedbd3cb412023-08-18 11:37:02 +100037 is_inside_subtools_chroot=mock.DEFAULT,
Trent Apted7d2777b2023-06-29 13:35:03 +100038 ) as mocks:
Trent Aptedbd3cb412023-08-18 11:37:02 +100039 mocks["is_inside_subtools_chroot"].return_value = True
Trent Apted7d2777b2023-06-29 13:35:03 +100040 yield mocks["_run_system_emerge"]
41
42
Trent Apted16007b82023-07-06 14:51:57 +100043@pytest.fixture(name="mock_exporter", autouse=True)
44def mock_exporter_fixture():
45 """Stubs the exporter for InstalledSubtools to avoid side-effects."""
Trent Aptedc94115a2023-09-20 16:39:18 +100046 with mock.patch(
47 "chromite.lib.subtool_lib.InstalledSubtools"
48 ) as installed, mock.patch(
49 "chromite.lib.subtool_lib.BundledSubtools"
50 ) as bundled:
51 yield {"installed": installed, "bundled": bundled}
Trent Apted16007b82023-07-06 14:51:57 +100052
53
Trent Aptedbd3cb412023-08-18 11:37:02 +100054@pytest.fixture(autouse=True)
55def build_sdk_subtools_consistency_check():
56 """Die quickly if the version file is left over in the test SDK.
57
58 This can happen if the build API entrypoint was tested on the local machine.
59 Tests in this file will fail in confusing ways if this is ever the case.
60 """
61 version_file = sdk_subtools.SUBTOOLS_CHROOT_VERSION_FILE
62 assert (
63 not version_file.exists()
64 ), f"{version_file} exists in the chroot (stray?)."
65
66
Trent Apted7d2777b2023-06-29 13:35:03 +100067def test_must_run_outside_sdk(caplog) -> None:
68 """Tests build_sdk_subtools complains if run in the chroot."""
69 with pytest.raises(cros_build_lib.DieSystemExit) as error_info:
70 build_sdk_subtools.main()
71 assert error_info.value.code == 1
72 assert "build_sdk_subtools must be run outside the chroot" in caplog.text
73
74
75def test_cros_sdk(run_mock, outside_chroot) -> None:
76 """Tests the steps leading up to the cros_sdk invocation."""
77 # Fake a failure from cros_sdk to ensure it propagates.
78 run_mock.SetDefaultCmdResult(returncode=42)
79
80 assert build_sdk_subtools.main() == 42
81 assert outside_chroot.called
82 assert run_mock.call_count == 1
83 assert run_mock.call_args_list[0].args[0] == [
84 "cros_sdk",
85 "--chroot",
Trent Apted7c6b7742023-09-04 09:48:34 +100086 SDK_CHROOT_ARG,
Trent Apted7d2777b2023-06-29 13:35:03 +100087 "--create",
88 "--skip-chroot-upgrade",
89 ]
90
91
92def test_cros_sdk_clean(run_mock, outside_chroot) -> None:
93 """Tests steps leading up to the cros_sdk invocation with --clean."""
94 run_mock.SetDefaultCmdResult(returncode=42)
95
96 assert build_sdk_subtools.main(["--clean"]) == 42
97 assert outside_chroot.called
98 assert run_mock.call_count == 1
99 cros_sdk_cmd = run_mock.call_args_list[0].args[0]
100 assert cros_sdk_cmd[0] == "cros_sdk"
101 assert "--delete" in cros_sdk_cmd
102
103
104def test_cros_sdk_output_dir(run_mock, outside_chroot) -> None:
105 """Tests steps leading up to the cros_sdk invocation with --output-dir."""
106 run_mock.SetDefaultCmdResult(returncode=42)
107
108 assert build_sdk_subtools.main(["--output-dir", "/foo"]) == 42
109 assert outside_chroot.called
110 cros_sdk_cmd = run_mock.call_args_list[0].args[0]
Trent Apted7c6b7742023-09-04 09:48:34 +1000111 chroot_arg_index = cros_sdk_cmd.index(Path("/mnt/host/source/out/foo"))
Trent Apted7d2777b2023-06-29 13:35:03 +1000112 assert cros_sdk_cmd[0] == "cros_sdk"
113 assert cros_sdk_cmd[chroot_arg_index - 1] == "--chroot"
114
115
116def test_chroot_required_after_cros_sdk(run_mock, outside_chroot) -> None:
117 """Tests that build_sdk_subtools will ask for chroot when setup."""
118 with pytest.raises(commandline.ChrootRequiredError) as error_info:
119 build_sdk_subtools.main(["--no-setup-chroot"])
120
121 assert run_mock.call_count == 0
122 assert outside_chroot.called
123 assert error_info.value.cmd == ["build_sdk_subtools", "--no-setup-chroot"]
124 assert error_info.value.chroot_args == [
125 "--chroot",
Trent Apted7c6b7742023-09-04 09:48:34 +1000126 SDK_CHROOT_ARG,
Trent Apted7d2777b2023-06-29 13:35:03 +1000127 ]
128
129
130def test_chroots_into_output_dir(run_mock, outside_chroot) -> None:
131 """Tests that --output-dir is consumed properly after setup."""
132 with pytest.raises(commandline.ChrootRequiredError) as error_info:
133 build_sdk_subtools.main(["--no-setup-chroot", "--output-dir", "/foo"])
134
135 assert run_mock.call_count == 0
136 assert outside_chroot.called
137 assert error_info.value.chroot_args == [
138 "--chroot",
Trent Apted7c6b7742023-09-04 09:48:34 +1000139 Path("/mnt/host/source/out/foo"),
Trent Apted7d2777b2023-06-29 13:35:03 +1000140 ]
141
142
143def test_setup_sdk_invocation(run_mock, outside_chroot) -> None:
144 """Tests the SDK setup invocation, before it becomes a subtools chroot."""
Trent Aptedbd3cb412023-08-18 11:37:02 +1000145 # Fake success from cros_sdk, failure from setup_base_sdk().
Trent Apted7d2777b2023-06-29 13:35:03 +1000146 run_mock.SetDefaultCmdResult(returncode=0)
147 run_mock.AddCmdResult(
148 ["sudo", "--", "build_sdk_subtools", "--relaunch-for-setup"],
149 returncode=42,
150 )
151
Trent Apted2d9111b2023-08-04 16:20:35 +1000152 assert build_sdk_subtools.main() == 42
Trent Apted7d2777b2023-06-29 13:35:03 +1000153 assert run_mock.call_count == 2
154 assert outside_chroot.called
155
156 sudo_run_cmd = run_mock.call_args_list[1]
157
158 assert sudo_run_cmd.args[0] == [
159 "sudo",
160 "--",
161 "build_sdk_subtools",
162 "--relaunch-for-setup",
163 ]
164 assert sudo_run_cmd.kwargs["enter_chroot"] is True
165 assert sudo_run_cmd.kwargs["chroot_args"] == [
166 "--chroot",
Trent Apted7c6b7742023-09-04 09:48:34 +1000167 SDK_CHROOT_ARG,
Trent Apted7d2777b2023-06-29 13:35:03 +1000168 ]
169 # Stop here: Actually running `--relaunch-for-setup` principally wants to
170 # mutate the SDK state as root, which is too messy as a unit test.
171
172
173def test_default_package(mock_emerge) -> None:
174 """Tests a default virtual package is provided to update packages."""
175 assert build_sdk_subtools.main() == 0
176 assert mock_emerge.call_count == 1
177 emerge_cmd = mock_emerge.call_args.args[0]
178 assert Path("/mnt/host/source/chromite/bin/parallel_emerge") in emerge_cmd
179 assert emerge_cmd[-1] == "virtual/target-sdk-subtools"
180
181
182def test_provided_package(mock_emerge) -> None:
183 """Tests the default package can be passed in from command line."""
184 assert build_sdk_subtools.main(["vim"]) == 0
185 assert mock_emerge.call_args.args[0][-1] == "vim"
186
187
188def test_skip_package_update(mock_emerge) -> None:
189 """Tests --skip-package-update will not try to emerge anything."""
190 assert build_sdk_subtools.main(["--no-update-packages"]) == 0
191 assert mock_emerge.call_count == 0
Trent Apted16007b82023-07-06 14:51:57 +1000192
193
Trent Aptedc94115a2023-09-20 16:39:18 +1000194def test_invokes_uploader(mock_emerge, mock_exporter) -> None:
195 """The exporter is invoked to bundle, but to upload [] by default."""
Trent Apted16007b82023-07-06 14:51:57 +1000196 assert build_sdk_subtools.main([]) == 0
197 assert mock_emerge.call_count == 1
Trent Aptedc94115a2023-09-20 16:39:18 +1000198 assert mock_exporter["installed"].called
199 installed_subtools = mock_exporter["installed"].return_value
Trent Apted16007b82023-07-06 14:51:57 +1000200 assert installed_subtools.bundle_all.called
Trent Aptedc94115a2023-09-20 16:39:18 +1000201 installed_subtools.prepare_uploads.assert_called_once_with([])
Trent Aptedde109222023-09-05 12:04:58 +1000202
203
Trent Aptedc94115a2023-09-20 16:39:18 +1000204def test_upload_option(mock_emerge, mock_exporter) -> None:
205 """Tests that the exporter is invoked with provided uploads."""
206 cmdline = ["--upload", "subtool1", "subtool2", "--", "dev-some/package"]
Trent Aptedde109222023-09-05 12:04:58 +1000207 assert build_sdk_subtools.main(cmdline) == 0
208 assert mock_emerge.call_args.args[0][-1] == "dev-some/package"
Trent Aptedc94115a2023-09-20 16:39:18 +1000209 installed_subtools = mock_exporter["installed"].return_value
210 installed_subtools.prepare_uploads.assert_called_once_with(
211 ["subtool1", "subtool2"]
Trent Aptedde109222023-09-05 12:04:58 +1000212 )
213
214
215def test_production_option(mock_emerge, mock_exporter) -> None:
Trent Aptedc94115a2023-09-20 16:39:18 +1000216 """Tests that --production is passed to the uploader."""
Trent Aptedde109222023-09-05 12:04:58 +1000217 assert build_sdk_subtools.main(["--production"]) == 0
218 assert mock_emerge.call_count == 1
Trent Aptedc94115a2023-09-20 16:39:18 +1000219 mock_exporter["bundled"].return_value.upload.assert_called_once_with(True)