blob: 4c346e5aa9b6cfdf64ab6d2a6d384cc473b1c236 [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
7import os
8from pathlib import Path
9from unittest import mock
10
11import pytest
12
13from chromite.lib import commandline
14from chromite.lib import cros_build_lib
15from chromite.scripts import build_sdk_subtools
16
17
18@pytest.fixture(name="outside_chroot")
19def outside_chroot_fixture():
20 """Mocks IsInsideChroot to be False."""
21 with mock.patch.object(
22 cros_build_lib, "IsInsideChroot", return_value=False
23 ) as outside_chroot:
24 yield outside_chroot
25
26
27@pytest.fixture(name="mock_emerge")
28def mock_emerge_fixture():
29 """Stubs the build_sdk_subtools emerge helper and sets it up to run."""
30 with mock.patch.multiple(
31 "chromite.scripts.build_sdk_subtools",
32 _run_system_emerge=mock.DEFAULT,
33 _is_inside_subtools_chroot=mock.DEFAULT,
34 ) as mocks:
35 mocks["_is_inside_subtools_chroot"].return_value = True
36 yield mocks["_run_system_emerge"]
37
38
39def test_must_run_outside_sdk(caplog) -> None:
40 """Tests build_sdk_subtools complains if run in the chroot."""
41 with pytest.raises(cros_build_lib.DieSystemExit) as error_info:
42 build_sdk_subtools.main()
43 assert error_info.value.code == 1
44 assert "build_sdk_subtools must be run outside the chroot" in caplog.text
45
46
47def test_cros_sdk(run_mock, outside_chroot) -> None:
48 """Tests the steps leading up to the cros_sdk invocation."""
49 # Fake a failure from cros_sdk to ensure it propagates.
50 run_mock.SetDefaultCmdResult(returncode=42)
51
52 assert build_sdk_subtools.main() == 42
53 assert outside_chroot.called
54 assert run_mock.call_count == 1
55 assert run_mock.call_args_list[0].args[0] == [
56 "cros_sdk",
57 "--chroot",
58 "/mnt/host/source/chroot/build/amd64-subtools-host",
59 "--create",
60 "--skip-chroot-upgrade",
61 ]
62
63
64def test_cros_sdk_clean(run_mock, outside_chroot) -> None:
65 """Tests steps leading up to the cros_sdk invocation with --clean."""
66 run_mock.SetDefaultCmdResult(returncode=42)
67
68 assert build_sdk_subtools.main(["--clean"]) == 42
69 assert outside_chroot.called
70 assert run_mock.call_count == 1
71 cros_sdk_cmd = run_mock.call_args_list[0].args[0]
72 assert cros_sdk_cmd[0] == "cros_sdk"
73 assert "--delete" in cros_sdk_cmd
74
75
76def test_cros_sdk_output_dir(run_mock, outside_chroot) -> None:
77 """Tests steps leading up to the cros_sdk invocation with --output-dir."""
78 run_mock.SetDefaultCmdResult(returncode=42)
79
80 assert build_sdk_subtools.main(["--output-dir", "/foo"]) == 42
81 assert outside_chroot.called
82 cros_sdk_cmd = run_mock.call_args_list[0].args[0]
83 chroot_arg_index = cros_sdk_cmd.index("/mnt/host/source/chroot/foo")
84 assert cros_sdk_cmd[0] == "cros_sdk"
85 assert cros_sdk_cmd[chroot_arg_index - 1] == "--chroot"
86
87
88def test_chroot_required_after_cros_sdk(run_mock, outside_chroot) -> None:
89 """Tests that build_sdk_subtools will ask for chroot when setup."""
90 with pytest.raises(commandline.ChrootRequiredError) as error_info:
91 build_sdk_subtools.main(["--no-setup-chroot"])
92
93 assert run_mock.call_count == 0
94 assert outside_chroot.called
95 assert error_info.value.cmd == ["build_sdk_subtools", "--no-setup-chroot"]
96 assert error_info.value.chroot_args == [
97 "--chroot",
98 "/mnt/host/source/chroot/build/amd64-subtools-host",
99 ]
100
101
102def test_chroots_into_output_dir(run_mock, outside_chroot) -> None:
103 """Tests that --output-dir is consumed properly after setup."""
104 with pytest.raises(commandline.ChrootRequiredError) as error_info:
105 build_sdk_subtools.main(["--no-setup-chroot", "--output-dir", "/foo"])
106
107 assert run_mock.call_count == 0
108 assert outside_chroot.called
109 assert error_info.value.chroot_args == [
110 "--chroot",
111 "/mnt/host/source/chroot/foo",
112 ]
113
114
115def test_setup_sdk_invocation(run_mock, outside_chroot) -> None:
116 """Tests the SDK setup invocation, before it becomes a subtools chroot."""
117 # Fake success from cros_sdk, failure from _setup_base_sdk().
118 run_mock.SetDefaultCmdResult(returncode=0)
119 run_mock.AddCmdResult(
120 ["sudo", "--", "build_sdk_subtools", "--relaunch-for-setup"],
121 returncode=42,
122 )
123
124 # Avoid bots passing CROS_CACHEDIR via `sudo` and messing up cmd matching.
125 with mock.patch.dict("os.environ"):
126 os.environ.pop("CROS_CACHEDIR", None)
127 assert build_sdk_subtools.main() == 42
128
129 assert run_mock.call_count == 2
130 assert outside_chroot.called
131
132 sudo_run_cmd = run_mock.call_args_list[1]
133
134 assert sudo_run_cmd.args[0] == [
135 "sudo",
136 "--",
137 "build_sdk_subtools",
138 "--relaunch-for-setup",
139 ]
140 assert sudo_run_cmd.kwargs["enter_chroot"] is True
141 assert sudo_run_cmd.kwargs["chroot_args"] == [
142 "--chroot",
143 "/mnt/host/source/chroot/build/amd64-subtools-host",
144 ]
145 # Stop here: Actually running `--relaunch-for-setup` principally wants to
146 # mutate the SDK state as root, which is too messy as a unit test.
147
148
149def test_default_package(mock_emerge) -> None:
150 """Tests a default virtual package is provided to update packages."""
151 assert build_sdk_subtools.main() == 0
152 assert mock_emerge.call_count == 1
153 emerge_cmd = mock_emerge.call_args.args[0]
154 assert Path("/mnt/host/source/chromite/bin/parallel_emerge") in emerge_cmd
155 assert emerge_cmd[-1] == "virtual/target-sdk-subtools"
156
157
158def test_provided_package(mock_emerge) -> None:
159 """Tests the default package can be passed in from command line."""
160 assert build_sdk_subtools.main(["vim"]) == 0
161 assert mock_emerge.call_args.args[0][-1] == "vim"
162
163
164def test_skip_package_update(mock_emerge) -> None:
165 """Tests --skip-package-update will not try to emerge anything."""
166 assert build_sdk_subtools.main(["--no-update-packages"]) == 0
167 assert mock_emerge.call_count == 0