blob: 85306749ee990996132e6b1a704c38b091d7744a [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."""
46 with mock.patch("chromite.lib.subtool_lib.InstalledSubtools") as mock_lib:
47 yield mock_lib
48
49
Trent Aptedbd3cb412023-08-18 11:37:02 +100050@pytest.fixture(autouse=True)
51def build_sdk_subtools_consistency_check():
52 """Die quickly if the version file is left over in the test SDK.
53
54 This can happen if the build API entrypoint was tested on the local machine.
55 Tests in this file will fail in confusing ways if this is ever the case.
56 """
57 version_file = sdk_subtools.SUBTOOLS_CHROOT_VERSION_FILE
58 assert (
59 not version_file.exists()
60 ), f"{version_file} exists in the chroot (stray?)."
61
62
Trent Apted7d2777b2023-06-29 13:35:03 +100063def test_must_run_outside_sdk(caplog) -> None:
64 """Tests build_sdk_subtools complains if run in the chroot."""
65 with pytest.raises(cros_build_lib.DieSystemExit) as error_info:
66 build_sdk_subtools.main()
67 assert error_info.value.code == 1
68 assert "build_sdk_subtools must be run outside the chroot" in caplog.text
69
70
71def test_cros_sdk(run_mock, outside_chroot) -> None:
72 """Tests the steps leading up to the cros_sdk invocation."""
73 # Fake a failure from cros_sdk to ensure it propagates.
74 run_mock.SetDefaultCmdResult(returncode=42)
75
76 assert build_sdk_subtools.main() == 42
77 assert outside_chroot.called
78 assert run_mock.call_count == 1
79 assert run_mock.call_args_list[0].args[0] == [
80 "cros_sdk",
81 "--chroot",
Trent Apted7c6b7742023-09-04 09:48:34 +100082 SDK_CHROOT_ARG,
Trent Apted7d2777b2023-06-29 13:35:03 +100083 "--create",
84 "--skip-chroot-upgrade",
85 ]
86
87
88def test_cros_sdk_clean(run_mock, outside_chroot) -> None:
89 """Tests steps leading up to the cros_sdk invocation with --clean."""
90 run_mock.SetDefaultCmdResult(returncode=42)
91
92 assert build_sdk_subtools.main(["--clean"]) == 42
93 assert outside_chroot.called
94 assert run_mock.call_count == 1
95 cros_sdk_cmd = run_mock.call_args_list[0].args[0]
96 assert cros_sdk_cmd[0] == "cros_sdk"
97 assert "--delete" in cros_sdk_cmd
98
99
100def test_cros_sdk_output_dir(run_mock, outside_chroot) -> None:
101 """Tests steps leading up to the cros_sdk invocation with --output-dir."""
102 run_mock.SetDefaultCmdResult(returncode=42)
103
104 assert build_sdk_subtools.main(["--output-dir", "/foo"]) == 42
105 assert outside_chroot.called
106 cros_sdk_cmd = run_mock.call_args_list[0].args[0]
Trent Apted7c6b7742023-09-04 09:48:34 +1000107 chroot_arg_index = cros_sdk_cmd.index(Path("/mnt/host/source/out/foo"))
Trent Apted7d2777b2023-06-29 13:35:03 +1000108 assert cros_sdk_cmd[0] == "cros_sdk"
109 assert cros_sdk_cmd[chroot_arg_index - 1] == "--chroot"
110
111
112def test_chroot_required_after_cros_sdk(run_mock, outside_chroot) -> None:
113 """Tests that build_sdk_subtools will ask for chroot when setup."""
114 with pytest.raises(commandline.ChrootRequiredError) as error_info:
115 build_sdk_subtools.main(["--no-setup-chroot"])
116
117 assert run_mock.call_count == 0
118 assert outside_chroot.called
119 assert error_info.value.cmd == ["build_sdk_subtools", "--no-setup-chroot"]
120 assert error_info.value.chroot_args == [
121 "--chroot",
Trent Apted7c6b7742023-09-04 09:48:34 +1000122 SDK_CHROOT_ARG,
Trent Apted7d2777b2023-06-29 13:35:03 +1000123 ]
124
125
126def test_chroots_into_output_dir(run_mock, outside_chroot) -> None:
127 """Tests that --output-dir is consumed properly after setup."""
128 with pytest.raises(commandline.ChrootRequiredError) as error_info:
129 build_sdk_subtools.main(["--no-setup-chroot", "--output-dir", "/foo"])
130
131 assert run_mock.call_count == 0
132 assert outside_chroot.called
133 assert error_info.value.chroot_args == [
134 "--chroot",
Trent Apted7c6b7742023-09-04 09:48:34 +1000135 Path("/mnt/host/source/out/foo"),
Trent Apted7d2777b2023-06-29 13:35:03 +1000136 ]
137
138
139def test_setup_sdk_invocation(run_mock, outside_chroot) -> None:
140 """Tests the SDK setup invocation, before it becomes a subtools chroot."""
Trent Aptedbd3cb412023-08-18 11:37:02 +1000141 # Fake success from cros_sdk, failure from setup_base_sdk().
Trent Apted7d2777b2023-06-29 13:35:03 +1000142 run_mock.SetDefaultCmdResult(returncode=0)
143 run_mock.AddCmdResult(
144 ["sudo", "--", "build_sdk_subtools", "--relaunch-for-setup"],
145 returncode=42,
146 )
147
Trent Apted2d9111b2023-08-04 16:20:35 +1000148 assert build_sdk_subtools.main() == 42
Trent Apted7d2777b2023-06-29 13:35:03 +1000149 assert run_mock.call_count == 2
150 assert outside_chroot.called
151
152 sudo_run_cmd = run_mock.call_args_list[1]
153
154 assert sudo_run_cmd.args[0] == [
155 "sudo",
156 "--",
157 "build_sdk_subtools",
158 "--relaunch-for-setup",
159 ]
160 assert sudo_run_cmd.kwargs["enter_chroot"] is True
161 assert sudo_run_cmd.kwargs["chroot_args"] == [
162 "--chroot",
Trent Apted7c6b7742023-09-04 09:48:34 +1000163 SDK_CHROOT_ARG,
Trent Apted7d2777b2023-06-29 13:35:03 +1000164 ]
165 # Stop here: Actually running `--relaunch-for-setup` principally wants to
166 # mutate the SDK state as root, which is too messy as a unit test.
167
168
169def test_default_package(mock_emerge) -> None:
170 """Tests a default virtual package is provided to update packages."""
171 assert build_sdk_subtools.main() == 0
172 assert mock_emerge.call_count == 1
173 emerge_cmd = mock_emerge.call_args.args[0]
174 assert Path("/mnt/host/source/chromite/bin/parallel_emerge") in emerge_cmd
175 assert emerge_cmd[-1] == "virtual/target-sdk-subtools"
176
177
178def test_provided_package(mock_emerge) -> None:
179 """Tests the default package can be passed in from command line."""
180 assert build_sdk_subtools.main(["vim"]) == 0
181 assert mock_emerge.call_args.args[0][-1] == "vim"
182
183
184def test_skip_package_update(mock_emerge) -> None:
185 """Tests --skip-package-update will not try to emerge anything."""
186 assert build_sdk_subtools.main(["--no-update-packages"]) == 0
187 assert mock_emerge.call_count == 0
Trent Apted16007b82023-07-06 14:51:57 +1000188
189
190def test_invokes_exporter(mock_emerge, mock_exporter) -> None:
Trent Aptedde109222023-09-05 12:04:58 +1000191 """The exporter is invoked to bundle, but to export [] by default."""
Trent Apted16007b82023-07-06 14:51:57 +1000192 assert build_sdk_subtools.main([]) == 0
193 assert mock_emerge.call_count == 1
194 assert mock_exporter.called
195 installed_subtools = mock_exporter.return_value
196 assert installed_subtools.bundle_all.called
Trent Aptedde109222023-09-05 12:04:58 +1000197 installed_subtools.export.assert_called_once_with(False, [])
198
199
200def test_export_option(mock_emerge, mock_exporter) -> None:
201 """Tests that the exporter is invoked with provided exports."""
202 cmdline = ["--export", "subtool1", "subtool2", "--", "dev-some/package"]
203 assert build_sdk_subtools.main(cmdline) == 0
204 assert mock_emerge.call_args.args[0][-1] == "dev-some/package"
205 installed_subtools = mock_exporter.return_value
206 installed_subtools.export.assert_called_once_with(
207 False, ["subtool1", "subtool2"]
208 )
209
210
211def test_production_option(mock_emerge, mock_exporter) -> None:
212 """Tests that --production is passed to the exporter."""
213 assert build_sdk_subtools.main(["--production"]) == 0
214 assert mock_emerge.call_count == 1
215 mock_exporter.return_value.export.assert_called_once_with(True, [])