xref: /aosp_15_r20/tools/acloud/create/remote_image_local_instance_test.py (revision 800a58d989c669b8eb8a71d8df53b1ba3d411444)
1# Copyright 2018 - The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14"""Tests for remote_image_local_instance."""
15
16import builtins
17from collections import namedtuple
18import os
19import shutil
20import subprocess
21import unittest
22
23from unittest import mock
24
25from acloud import errors
26from acloud.create import create_common
27from acloud.create import remote_image_local_instance
28from acloud.internal import constants
29from acloud.internal.lib import android_build_client
30from acloud.internal.lib import auth
31from acloud.internal.lib import cvd_utils
32from acloud.internal.lib import driver_test_lib
33from acloud.internal.lib import ota_tools
34from acloud.internal.lib import utils
35from acloud.setup import setup_common
36
37
38# pylint: disable=invalid-name, protected-access, no-member
39class RemoteImageLocalInstanceTest(driver_test_lib.BaseDriverTest):
40    """Test remote_image_local_instance methods."""
41
42    def setUp(self):
43        """Initialize remote_image_local_instance."""
44        super().setUp()
45        self.build_client = mock.MagicMock()
46        self.Patch(
47            android_build_client,
48            "AndroidBuildClient",
49            return_value=self.build_client)
50        self.Patch(auth, "CreateCredentials", return_value=mock.MagicMock())
51        self.RemoteImageLocalInstance = remote_image_local_instance.RemoteImageLocalInstance()
52        self._fake_remote_image = {"build_target" : "aosp_cf_x86_64_phone-userdebug",
53                                   "build_id": "1234",
54                                   "branch": "aosp_master"}
55        self._extract_path = "/tmp/acloud_image_artifacts/1234"
56
57    @mock.patch.object(remote_image_local_instance, "DownloadAndProcessImageFiles")
58    def testGetImageArtifactsPath(self, mock_proc):
59        """Test get image artifacts path."""
60        mock_proc.return_value = "/unit/test"
61        avd_spec = mock.MagicMock()
62        avd_spec.local_system_image = None
63        avd_spec.local_kernel_image = None
64        avd_spec.local_vendor_image = None
65        avd_spec.local_vendor_boot_image = None
66        # raise errors.NoCuttlefishCommonInstalled
67        self.Patch(setup_common, "PackageInstalled", return_value=False)
68        self.assertRaises(errors.NoCuttlefishCommonInstalled,
69                          self.RemoteImageLocalInstance.GetImageArtifactsPath,
70                          avd_spec)
71
72        # Valid _DownloadAndProcessImageFiles run.
73        mock_launch_cvd = os.path.join("/unit/test", "bin", "launch_cvd")
74        self.Patch(setup_common, "PackageInstalled", return_value=True)
75        self.Patch(remote_image_local_instance,
76                   "ConfirmDownloadRemoteImageDir", return_value="/tmp")
77        mock_exists = self.Patch(
78            os.path, "exists", side_effect=lambda p: p == mock_launch_cvd)
79        paths = self.RemoteImageLocalInstance.GetImageArtifactsPath(avd_spec)
80        mock_proc.assert_called_once_with(avd_spec)
81        self.assertEqual(paths.image_dir, "/unit/test")
82        self.assertEqual(paths.host_bins, "/unit/test")
83
84        # GSI
85        avd_spec.local_system_image = "/test_local_system_image_dir"
86        avd_spec.local_tool_dirs = "/test_local_tool_dirs"
87        avd_spec.cfg = None
88        avd_spec.remote_image = self._fake_remote_image
89        self.Patch(os, "makedirs")
90        self.Patch(create_common, "DownloadRemoteArtifact")
91        self.Patch(create_common, "GetNonEmptyEnvVars")
92        self.Patch(cvd_utils, "FindMiscInfo",
93                   return_value="/mix_image_1234/MISC")
94        self.Patch(cvd_utils, "FindImageDir",
95                   return_value="/mix_image_1234/IMAGES")
96        self.Patch(ota_tools, "FindOtaToolsDir", return_value="/ota_tools_dir")
97        self.Patch(create_common, "FindSystemImages",
98                   return_value=("/system_image_path",
99                                 "/system_ext_image_path",
100                                 "/product_image_path"))
101        self.Patch(self.RemoteImageLocalInstance, "_FindCvdHostBinaries",
102                   side_effect=errors.GetCvdLocalHostPackageError("not found"))
103        paths = self.RemoteImageLocalInstance.GetImageArtifactsPath(avd_spec)
104        create_common.DownloadRemoteArtifact.assert_called_with(
105            avd_spec.cfg, "aosp_cf_x86_64_phone-userdebug", "1234",
106            "aosp_cf_x86_64_phone-target_files-1234.zip", "/unit/test/mix_image_1234",
107            decompress=True)
108        self.assertEqual(paths.image_dir, "/mix_image_1234/IMAGES")
109        self.assertEqual(paths.misc_info, "/mix_image_1234/MISC")
110        self.assertEqual(paths.host_bins, "/unit/test")
111        self.assertEqual(paths.ota_tools_dir, "/ota_tools_dir")
112        self.assertEqual(paths.system_image, "/system_image_path")
113        self.assertEqual(paths.system_ext_image, "/system_ext_image_path")
114        self.assertEqual(paths.product_image, "/product_image_path")
115        self.RemoteImageLocalInstance._FindCvdHostBinaries.assert_not_called()
116
117        # Boot and kernel images
118        avd_spec.local_kernel_image = "/test_local_kernel_image_dir"
119        self.Patch(self.RemoteImageLocalInstance, "FindBootOrKernelImages",
120                   return_value=("/boot_image_path", "/vendor_boot_image_path",
121                                 None, None))
122        paths = self.RemoteImageLocalInstance.GetImageArtifactsPath(avd_spec)
123        self.RemoteImageLocalInstance.FindBootOrKernelImages.assert_called()
124        self.assertEqual(paths.boot_image, "/boot_image_path")
125        self.assertEqual(paths.vendor_boot_image, "/vendor_boot_image_path")
126        self.assertIsNone(paths.kernel_image)
127        self.assertIsNone(paths.initramfs_image)
128
129        # local vendor image, local tool including host bins
130        avd_spec.local_vendor_image = "/test_local_vendor_image_dir"
131        vendor_image_paths = cvd_utils.VendorImagePaths(
132            "vendor.img", "vendor_dlkm.img", "odm.img", "odm_dlkm.img")
133        self.Patch(cvd_utils, "FindVendorImages",
134                   return_value=vendor_image_paths)
135        self.Patch(self.RemoteImageLocalInstance, "_FindCvdHostBinaries",
136                   return_value="/test_local_tool_dirs")
137        paths = self.RemoteImageLocalInstance.GetImageArtifactsPath(avd_spec)
138        self.assertEqual(paths.host_bins, "/test_local_tool_dirs")
139
140        # local vendor image, local tool without host bins
141        avd_spec.local_vendor_image = "/test_local_vendor_image_dir"
142        self.Patch(self.RemoteImageLocalInstance, "_FindCvdHostBinaries",
143                   side_effect=errors.GetCvdLocalHostPackageError("not found"))
144        paths = self.RemoteImageLocalInstance.GetImageArtifactsPath(avd_spec)
145        self.assertEqual(paths.host_bins, "/unit/test")
146
147        create_common.DownloadRemoteArtifact.reset_mock()
148
149        mock_exists.side_effect = lambda p: p in (
150            mock_launch_cvd, os.path.join("/unit/test", "mix_image_1234"))
151        self.RemoteImageLocalInstance.GetImageArtifactsPath(avd_spec)
152        create_common.DownloadRemoteArtifact.assert_not_called()
153
154    @mock.patch.object(shutil, "rmtree")
155    def testDownloadAndProcessImageFiles(self, mock_rmtree):
156        """Test process remote cuttlefish image."""
157        avd_spec = mock.MagicMock()
158        avd_spec.cfg = mock.MagicMock()
159        avd_spec.cfg.creds_cache_file = "cache.file"
160        avd_spec.remote_image = self._fake_remote_image
161        avd_spec.image_download_dir = "/tmp"
162        avd_spec.force_sync = True
163        self.Patch(os.path, "exists", side_effect=[True, False])
164        self.Patch(os, "makedirs")
165        self.Patch(subprocess, "check_call")
166        mock_open = self.Patch(builtins, "open")
167        fetch_dir = remote_image_local_instance.DownloadAndProcessImageFiles(
168            avd_spec)
169        self.assertEqual(mock_rmtree.call_count, 1)
170        self.assertEqual(self.build_client.GetFetchBuildArgs.call_count, 1)
171        self.assertEqual(self.build_client.GetFetchCertArg.call_count, 1)
172        cvd_config_filename = os.path.join(fetch_dir,
173                                           constants.FETCH_CVD_ARGS_FILE)
174        mock_open.assert_called_once_with(cvd_config_filename, "w")
175
176    def testConfirmDownloadRemoteImageDir(self):
177        """Test confirm download remote image dir"""
178        self.Patch(os.path, "exists", return_value=True)
179        self.Patch(os, "makedirs")
180        # Default minimum avail space should be more than 10G
181        # then return download_dir directly.
182        self.Patch(os, "statvfs", return_value=namedtuple(
183            "statvfs", "f_bavail, f_bsize")(11, 1073741824))
184        download_dir = "/tmp"
185        self.assertEqual(
186            remote_image_local_instance.ConfirmDownloadRemoteImageDir(
187                download_dir), "/tmp")
188
189        # Test when insuficient disk space and input 'q' to exit.
190        self.Patch(os, "statvfs", return_value=namedtuple(
191            "statvfs", "f_bavail, f_bsize")(9, 1073741824))
192        self.Patch(utils, "InteractWithQuestion", return_value="q")
193        self.assertRaises(SystemExit,
194                          remote_image_local_instance.ConfirmDownloadRemoteImageDir,
195                          download_dir)
196
197        # If avail space detect as 9GB, and 2nd input 7GB both less than 10GB
198        # 3rd input over 10GB, so return path should be "/tmp3".
199        self.Patch(os, "statvfs", side_effect=[
200            namedtuple("statvfs", "f_bavail, f_bsize")(9, 1073741824),
201            namedtuple("statvfs", "f_bavail, f_bsize")(7, 1073741824),
202            namedtuple("statvfs", "f_bavail, f_bsize")(11, 1073741824)])
203        self.Patch(utils, "InteractWithQuestion", side_effect=["/tmp2",
204                                                               "/tmp3"])
205        self.assertEqual(
206            remote_image_local_instance.ConfirmDownloadRemoteImageDir(
207                download_dir), "/tmp3")
208
209        # Test when path not exist, define --image-download-dir
210        # enter anything else to exit out.
211        download_dir = "/image_download_dir1"
212        self.Patch(os.path, "exists", return_value=False)
213        self.Patch(utils, "InteractWithQuestion", return_value="")
214        self.assertRaises(SystemExit,
215                          remote_image_local_instance.ConfirmDownloadRemoteImageDir,
216                          download_dir)
217
218        # Test using --image-dowload-dir and makedirs.
219        # enter 'y' to create it.
220        self.Patch(utils, "InteractWithQuestion", return_value="y")
221        self.Patch(os, "statvfs", return_value=namedtuple(
222            "statvfs", "f_bavail, f_bsize")(10, 1073741824))
223        self.assertEqual(
224            remote_image_local_instance.ConfirmDownloadRemoteImageDir(
225                download_dir), "/image_download_dir1")
226
227        # Test when 1st check fails for insufficient disk space, user inputs an
228        # alternate dir but it doesn't exist and the user choose to exit.
229        self.Patch(os, "statvfs", side_effect=[
230            namedtuple("statvfs", "f_bavail, f_bsize")(9, 1073741824),
231            namedtuple("statvfs", "f_bavail, f_bsize")(11, 1073741824)])
232        self.Patch(os.path, "exists", side_effect=[True, False])
233        self.Patch(utils, "InteractWithQuestion",
234                   side_effect=["~/nopath", "not_y"])
235        self.assertRaises(
236            SystemExit,
237            remote_image_local_instance.ConfirmDownloadRemoteImageDir,
238            download_dir)
239
240        # Test when 1st check fails for insufficient disk space, user inputs an
241        # alternate dir but it doesn't exist and they request to create it.
242        self.Patch(os, "statvfs", side_effect=[
243            namedtuple("statvfs", "f_bavail, f_bsize")(9, 1073741824),
244            namedtuple("statvfs", "f_bavail, f_bsize")(10, 1073741824)])
245        self.Patch(os.path, "exists", side_effect=[True, False])
246        self.Patch(utils, "InteractWithQuestion", side_effect=["~/nopath", "y"])
247        self.assertEqual(
248            remote_image_local_instance.ConfirmDownloadRemoteImageDir(
249                download_dir), os.path.expanduser("~/nopath"))
250
251
252if __name__ == "__main__":
253    unittest.main()
254