• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2022 - 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
15"""Tests for remote_host_cf_device_factory."""
16
17import time
18import unittest
19from unittest import mock
20
21from acloud import errors
22from acloud.internal import constants
23from acloud.internal.lib import driver_test_lib
24from acloud.public.actions import remote_host_cf_device_factory
25
26
27class RemoteHostDeviceFactoryTest(driver_test_lib.BaseDriverTest):
28    """Test RemoteHostDeviceFactory."""
29
30    def setUp(self):
31        """Set up the test."""
32        super().setUp()
33        self.Patch(remote_host_cf_device_factory.auth, "CreateCredentials")
34        mock_client = mock.Mock()
35        self.Patch(remote_host_cf_device_factory.remote_host_client,
36                   "RemoteHostClient", return_value=mock_client)
37        mock_client.RecordTime.side_effect = (
38            lambda _stage, _start_time: time.time())
39        self._mock_build_api = mock.Mock()
40        self.Patch(remote_host_cf_device_factory.android_build_client,
41                   "AndroidBuildClient", return_value=self._mock_build_api)
42
43    @staticmethod
44    def _CreateMockAvdSpec():
45        """Create a mock AvdSpec with necessary attributes."""
46        mock_cfg = mock.Mock(spec=[],
47                             ssh_private_key_path="/mock/id_rsa",
48                             extra_args_ssh_tunnel="extra args",
49                             fetch_cvd_version="123456",
50                             creds_cache_file="credential",
51                             service_account_json_private_key_path="/mock/key")
52        return mock.Mock(spec=[],
53                         remote_image={
54                             "branch": "aosp-android12-gsi",
55                             "build_id": "100000",
56                             "build_target": "aosp_cf_x86_64_phone-userdebug"},
57                         system_build_info={},
58                         kernel_build_info={},
59                         boot_build_info={},
60                         bootloader_build_info={},
61                         android_efi_loader_build_info={},
62                         ota_build_info={},
63                         host_package_build_info={},
64                         remote_host="192.0.2.100",
65                         remote_image_dir=None,
66                         host_user="user1",
67                         host_ssh_private_key_path=None,
68                         report_internal_ip=False,
69                         image_source=constants.IMAGE_SRC_REMOTE,
70                         local_image_dir=None,
71                         ins_timeout_secs=200,
72                         boot_timeout_secs=100,
73                         gpu="auto",
74                         no_pull_log=False,
75                         remote_fetch=False,
76                         fetch_cvd_wrapper=None,
77                         base_instance_num=None,
78                         num_avds_per_instance=None,
79                         fetch_cvd_version="123456",
80                         openwrt=True,
81                         cfg=mock_cfg)
82
83    @mock.patch("acloud.public.actions.remote_host_cf_device_factory.ssh")
84    @mock.patch("acloud.public.actions.remote_host_cf_device_factory."
85                "cvd_utils")
86    @mock.patch("acloud.public.actions.remote_host_cf_device_factory.pull")
87    def testCreateInstanceWithImageDir(self, mock_pull, mock_cvd_utils,
88                                       mock_ssh):
89        """Test CreateInstance with local image directory."""
90        mock_avd_spec = self._CreateMockAvdSpec()
91        mock_avd_spec.image_source = constants.IMAGE_SRC_LOCAL
92        mock_avd_spec.local_image_dir = "/mock/target_files"
93        mock_avd_spec.base_instance_num = 2
94        mock_avd_spec.num_avds_per_instance = 3
95        mock_ssh_obj = mock.Mock()
96        mock_ssh.Ssh.return_value = mock_ssh_obj
97        factory = remote_host_cf_device_factory.RemoteHostDeviceFactory(
98            mock_avd_spec, cvd_host_package_artifact="/mock/cvd.tar.gz")
99
100        log = {"path": "/log.txt"}
101        mock_cvd_utils.GetRemoteHostBaseDir.return_value = "acloud_cf_2"
102        mock_cvd_utils.FormatRemoteHostInstanceName.return_value = "inst"
103        mock_cvd_utils.AreTargetFilesRequired.return_value = True
104        mock_cvd_utils.UploadExtraImages.return_value = [("-extra", "image")]
105        mock_cvd_utils.ExecuteRemoteLaunchCvd.return_value = "failure"
106        mock_cvd_utils.FindRemoteLogs.return_value = [log]
107
108        self.assertEqual("inst", factory.CreateInstance())
109        # InitRemotehost
110        mock_cvd_utils.CleanUpRemoteCvd.assert_called_once_with(
111            mock_ssh_obj, "acloud_cf_2", raise_error=False)
112        mock_cvd_utils.GetRemoteHostBaseDir.assert_called_with(2)
113        # ProcessRemoteHostArtifacts
114        mock_ssh_obj.Run.assert_called_with("mkdir -p acloud_cf_2")
115        self._mock_build_api.GetFetchBuildArgs.assert_not_called()
116        mock_cvd_utils.UploadArtifacts.assert_called_with(
117            mock_ssh_obj, "acloud_cf_2", "/mock/target_files",
118            "/mock/cvd.tar.gz")
119        mock_cvd_utils.UploadExtraImages.assert_called_with(
120            mock_ssh_obj, "acloud_cf_2", mock_avd_spec, "/mock/target_files")
121        mock_cvd_utils.GetConfigFromRemoteAndroidInfo.assert_called_with(
122            mock_ssh_obj, "acloud_cf_2")
123        # LaunchCvd
124        mock_cvd_utils.GetRemoteLaunchCvdCmd.assert_called_with(
125            "acloud_cf_2", mock_avd_spec, mock.ANY, ["-extra", "image"])
126        mock_cvd_utils.ExecuteRemoteLaunchCvd.assert_called()
127        # FindLogFiles
128        mock_cvd_utils.FindRemoteLogs.assert_called_with(
129            mock_ssh_obj, "acloud_cf_2", 2, 3)
130        mock_pull.GetAllLogFilePaths.assert_called_once()
131        mock_pull.PullLogs.assert_called_once()
132        factory.GetAdbPorts()
133        mock_cvd_utils.GetAdbPorts.assert_called_with(2, 3)
134        factory.GetVncPorts()
135        mock_cvd_utils.GetVncPorts.assert_called_with(2, 3)
136        self.assertEqual({"inst": "failure"}, factory.GetFailures())
137        self.assertDictEqual({"inst": [log]}, factory.GetLogs())
138
139    @mock.patch("acloud.public.actions.remote_host_cf_device_factory.ssh")
140    @mock.patch("acloud.public.actions.remote_host_cf_device_factory."
141                "cvd_utils")
142    @mock.patch("acloud.public.actions.remote_host_cf_device_factory.pull")
143    def testCreateInstanceWithImageZip(self, mock_pull, mock_cvd_utils,
144                                       mock_ssh):
145        """Test CreateInstance with local image zip."""
146        mock_avd_spec = self._CreateMockAvdSpec()
147        mock_avd_spec.image_source = constants.IMAGE_SRC_LOCAL
148        mock_ssh_obj = mock.Mock()
149        mock_ssh.Ssh.return_value = mock_ssh_obj
150        factory = remote_host_cf_device_factory.RemoteHostDeviceFactory(
151            mock_avd_spec, local_image_artifact="/mock/img.zip",
152            cvd_host_package_artifact="/mock/cvd.tar.gz")
153
154        mock_cvd_utils.GetRemoteHostBaseDir.return_value = "acloud_cf_1"
155        mock_cvd_utils.FormatRemoteHostInstanceName.return_value = "inst"
156        mock_cvd_utils.AreTargetFilesRequired.return_value = False
157        mock_cvd_utils.ExecuteRemoteLaunchCvd.return_value = ""
158        mock_cvd_utils.FindRemoteLogs.return_value = []
159
160        self.assertEqual("inst", factory.CreateInstance())
161        # InitRemotehost
162        mock_cvd_utils.GetRemoteHostBaseDir.assert_called_with(None)
163        mock_cvd_utils.CleanUpRemoteCvd.assert_called_once()
164        # ProcessRemoteHostArtifacts
165        mock_ssh_obj.Run.assert_called_with("mkdir -p acloud_cf_1")
166        self._mock_build_api.GetFetchBuildArgs.assert_not_called()
167        mock_cvd_utils.UploadArtifacts.assert_called_with(
168            mock_ssh_obj, "acloud_cf_1", "/mock/img.zip", "/mock/cvd.tar.gz")
169        mock_cvd_utils.UploadExtraImages.assert_called_with(
170            mock_ssh_obj, "acloud_cf_1", mock_avd_spec, None)
171        # LaunchCvd
172        mock_cvd_utils.ExecuteRemoteLaunchCvd.assert_called()
173        # FindLogFiles
174        mock_cvd_utils.FindRemoteLogs.assert_called_with(
175            mock_ssh_obj, "acloud_cf_1", None, None)
176        mock_pull.GetAllLogFilePaths.assert_not_called()
177        mock_pull.PullLogs.assert_not_called()
178        factory.GetAdbPorts()
179        mock_cvd_utils.GetAdbPorts.assert_called_with(None, None)
180        factory.GetVncPorts()
181        mock_cvd_utils.GetVncPorts.assert_called_with(None, None)
182        self.assertFalse(factory.GetFailures())
183        self.assertDictEqual({"inst": []}, factory.GetLogs())
184
185    # pylint: disable=invalid-name
186    @mock.patch("acloud.public.actions.remote_host_cf_device_factory.ssh")
187    @mock.patch("acloud.public.actions.remote_host_cf_device_factory."
188                "cvd_utils")
189    @mock.patch("acloud.public.actions.remote_host_cf_device_factory.pull")
190    def testCreateInstanceWithTargetFilesZip(self, mock_pull, mock_cvd_utils,
191                                             mock_ssh):
192        """Test CreateInstance with local target_files zip."""
193        mock_avd_spec = self._CreateMockAvdSpec()
194        mock_avd_spec.image_source = constants.IMAGE_SRC_LOCAL
195        mock_ssh_obj = mock.Mock()
196        mock_ssh.Ssh.return_value = mock_ssh_obj
197        factory = remote_host_cf_device_factory.RemoteHostDeviceFactory(
198            mock_avd_spec, local_image_artifact="/mock/target_files.zip",
199            cvd_host_package_artifact="/mock/cvd.tar.gz")
200
201        mock_cvd_utils.GetRemoteHostBaseDir.return_value = "acloud_cf_1"
202        mock_cvd_utils.FormatRemoteHostInstanceName.return_value = "inst"
203        mock_cvd_utils.AreTargetFilesRequired.return_value = True
204        mock_cvd_utils.ExecuteRemoteLaunchCvd.return_value = ""
205        mock_cvd_utils.FindRemoteLogs.return_value = []
206
207        self.assertEqual("inst", factory.CreateInstance())
208        # InitRemotehost
209        mock_cvd_utils.GetRemoteHostBaseDir.assert_called_with(None)
210        mock_cvd_utils.CleanUpRemoteCvd.assert_called_once()
211        # ProcessRemoteHostArtifacts
212        mock_ssh_obj.Run.assert_called_with("mkdir -p acloud_cf_1")
213        mock_cvd_utils.ExtractTargetFilesZip.assert_called_with(
214            "/mock/target_files.zip", mock.ANY)
215        self._mock_build_api.GetFetchBuildArgs.assert_not_called()
216        mock_cvd_utils.UploadExtraImages.assert_called_with(
217            mock_ssh_obj, "acloud_cf_1", mock_avd_spec, mock.ANY)
218        mock_cvd_utils.UploadArtifacts.assert_called_with(
219            mock_ssh_obj, "acloud_cf_1", mock.ANY, "/mock/cvd.tar.gz")
220        self.assertIn("acloud_remote_host",  # temp dir prefix
221                      mock_cvd_utils.UploadArtifacts.call_args[0][2])
222        # LaunchCvd
223        mock_cvd_utils.ExecuteRemoteLaunchCvd.assert_called()
224        # FindLogFiles
225        mock_cvd_utils.FindRemoteLogs.assert_called_with(
226            mock_ssh_obj, "acloud_cf_1", None, None)
227        mock_pull.GetAllLogFilePaths.assert_not_called()
228        mock_pull.PullLogs.assert_not_called()
229        factory.GetAdbPorts()
230        mock_cvd_utils.GetAdbPorts.assert_called_with(None, None)
231        factory.GetVncPorts()
232        mock_cvd_utils.GetVncPorts.assert_called_with(None, None)
233        self.assertFalse(factory.GetFailures())
234        self.assertDictEqual({"inst": []}, factory.GetLogs())
235
236    @mock.patch("acloud.public.actions.remote_host_cf_device_factory.ssh")
237    @mock.patch("acloud.public.actions.remote_host_cf_device_factory."
238                "cvd_utils")
239    @mock.patch("acloud.public.actions.remote_host_cf_device_factory."
240                "subprocess.check_call")
241    @mock.patch("acloud.public.actions.remote_host_cf_device_factory.glob")
242    @mock.patch("acloud.public.actions.remote_host_cf_device_factory.pull")
243    def testCreateInstanceWithRemoteImages(self, mock_pull, mock_glob,
244                                           mock_check_call, mock_cvd_utils,
245                                           mock_ssh):
246        """Test CreateInstance with remote images."""
247        mock_avd_spec = self._CreateMockAvdSpec()
248        mock_avd_spec.image_source = constants.IMAGE_SRC_REMOTE
249        mock_ssh_obj = mock.Mock()
250        mock_ssh.Ssh.return_value = mock_ssh_obj
251        mock_ssh_obj.GetBaseCmd.return_value = "/mock/ssh"
252        mock_glob.glob.return_value = ["/mock/super.img"]
253        factory = remote_host_cf_device_factory.RemoteHostDeviceFactory(
254            mock_avd_spec)
255
256        mock_cvd_utils.GetRemoteHostBaseDir.return_value = "acloud_cf_1"
257        mock_cvd_utils.FormatRemoteHostInstanceName.return_value = "inst"
258        mock_cvd_utils.AreTargetFilesRequired.return_value = True
259        mock_cvd_utils.GetMixBuildTargetFilename.return_value = "mock.zip"
260        mock_cvd_utils.ExecuteRemoteLaunchCvd.return_value = ""
261        mock_cvd_utils.FindRemoteLogs.return_value = []
262
263        self._mock_build_api.GetFetchBuildArgs.return_value = ["-test"]
264
265        self.assertEqual("inst", factory.CreateInstance())
266        # InitRemoteHost
267        mock_cvd_utils.CleanUpRemoteCvd.assert_called_once()
268        # ProcessRemoteHostArtifacts
269        mock_ssh_obj.Run.assert_called_with("mkdir -p acloud_cf_1")
270        self._mock_build_api.DownloadArtifact.assert_called_once_with(
271            "aosp_cf_x86_64_phone-userdebug", "100000", "mock.zip", mock.ANY)
272        mock_cvd_utils.ExtractTargetFilesZip.assert_called_once()
273        self._mock_build_api.DownloadFetchcvd.assert_called_once()
274        mock_check_call.assert_called_once()
275        mock_ssh.ShellCmdWithRetry.assert_called_once()
276        self.assertRegex(mock_ssh.ShellCmdWithRetry.call_args[0][0],
277                         r"^tar -cf - --lzop -S -C \S+ super\.img \| "
278                         r"/mock/ssh -- tar -xf - --lzop -S -C acloud_cf_1$")
279        # LaunchCvd
280        mock_cvd_utils.ExecuteRemoteLaunchCvd.assert_called()
281        # FindLogFiles
282        mock_pull.GetAllLogFilePaths.assert_not_called()
283        mock_pull.PullLogs.assert_not_called()
284        self.assertFalse(factory.GetFailures())
285        self.assertDictEqual({"inst": []}, factory.GetLogs())
286
287    @mock.patch("acloud.public.actions.remote_host_cf_device_factory.ssh")
288    @mock.patch("acloud.public.actions.remote_host_cf_device_factory."
289                "cvd_utils")
290    @mock.patch("acloud.public.actions.remote_host_cf_device_factory.glob")
291    @mock.patch("acloud.public.actions.remote_host_cf_device_factory.shutil")
292    @mock.patch("acloud.public.actions.remote_host_cf_device_factory.pull")
293    def testCreateInstanceWithRemoteFetch(self, mock_pull, mock_shutil,
294                                          mock_glob, mock_cvd_utils, mock_ssh):
295        """Test CreateInstance with remotely fetched images."""
296        mock_avd_spec = self._CreateMockAvdSpec()
297        mock_avd_spec.remote_fetch = True
298        mock_ssh_obj = mock.Mock()
299        mock_ssh.Ssh.return_value = mock_ssh_obj
300        mock_ssh_obj.GetBaseCmd.return_value = "/mock/ssh"
301        mock_glob.glob.return_value = ["/mock/fetch_cvd"]
302        factory = remote_host_cf_device_factory.RemoteHostDeviceFactory(
303            mock_avd_spec)
304
305        log = {"path": "/log.txt"}
306        mock_cvd_utils.GetRemoteHostBaseDir.return_value = "acloud_cf_1"
307        mock_cvd_utils.FormatRemoteHostInstanceName.return_value = "inst"
308        mock_cvd_utils.AreTargetFilesRequired.return_value = False
309        mock_cvd_utils.ExecuteRemoteLaunchCvd.return_value = ""
310        mock_cvd_utils.FindRemoteLogs.return_value = []
311        mock_cvd_utils.GetRemoteFetcherConfigJson.return_value = log
312
313        self._mock_build_api.GetFetchBuildArgs.return_value = ["-test"]
314
315        self.assertEqual("inst", factory.CreateInstance())
316        mock_cvd_utils.CleanUpRemoteCvd.assert_called_once()
317        mock_ssh_obj.Run.assert_called_with("mkdir -p acloud_cf_1")
318        self._mock_build_api.DownloadFetchcvd.assert_called_once()
319        mock_shutil.copyfile.assert_called_with("/mock/key", mock.ANY)
320        self.assertRegex(mock_ssh.ShellCmdWithRetry.call_args_list[0][0][0],
321                         r"^tar -cf - --lzop -S -C \S+ fetch_cvd \| "
322                         r"/mock/ssh -- tar -xf - --lzop -S -C acloud_cf_1$")
323        self.assertRegex(mock_ssh.ShellCmdWithRetry.call_args_list[1][0][0],
324                         r"^/mock/ssh -- acloud_cf_1/fetch_cvd "
325                         r"-directory=acloud_cf_1 "
326                         r"-credential_source=acloud_cf_1/credential_key.json "
327                         r"-test$")
328        mock_cvd_utils.ExecuteRemoteLaunchCvd.assert_called()
329        mock_pull.GetAllLogFilePaths.assert_not_called()
330        mock_pull.PullLogs.assert_not_called()
331        self.assertFalse(factory.GetFailures())
332        self.assertDictEqual({"inst": [log]}, factory.GetLogs())
333
334    @mock.patch("acloud.public.actions.remote_host_cf_device_factory.ssh")
335    @mock.patch("acloud.public.actions.remote_host_cf_device_factory."
336                "cvd_utils")
337    @mock.patch("acloud.public.actions.remote_host_cf_device_factory.glob")
338    @mock.patch("acloud.public.actions.remote_host_cf_device_factory.shutil")
339    @mock.patch("acloud.public.actions.remote_host_cf_device_factory.pull")
340    def testCreateInstanceWithFetchCvdWrapper(self, mock_pull, mock_shutil,
341                                              mock_glob, mock_cvd_utils,
342                                              mock_ssh):
343        """Test CreateInstance with remotely fetched images."""
344        mock_avd_spec = self._CreateMockAvdSpec()
345        mock_avd_spec.remote_fetch = True
346        mock_avd_spec.fetch_cvd_wrapper = (
347            r"GOOGLE_APPLICATION_CREDENTIALS=/fake_key.json,"
348            r"CACHE_CONFIG=/home/shared/cache.properties,"
349            r"java,-jar,/home/shared/FetchCvdWrapper.jar"
350        )
351        mock_ssh_obj = mock.Mock()
352        mock_ssh.Ssh.return_value = mock_ssh_obj
353        mock_ssh_obj.GetBaseCmd.return_value = "/mock/ssh"
354        mock_glob.glob.return_value = ["/mock/fetch_cvd"]
355        factory = remote_host_cf_device_factory.RemoteHostDeviceFactory(
356            mock_avd_spec)
357
358        log = {"path": "/log.txt"}
359        mock_cvd_utils.GetRemoteHostBaseDir.return_value = "acloud_cf_1"
360        mock_cvd_utils.FormatRemoteHostInstanceName.return_value = "inst"
361        mock_cvd_utils.AreTargetFilesRequired.return_value = False
362        mock_cvd_utils.ExecuteRemoteLaunchCvd.return_value = ""
363        mock_cvd_utils.FindRemoteLogs.return_value = []
364        mock_cvd_utils.GetRemoteFetcherConfigJson.return_value = log
365
366        self._mock_build_api.GetFetchBuildArgs.return_value = ["-test"]
367
368        self.assertEqual("inst", factory.CreateInstance())
369        mock_cvd_utils.CleanUpRemoteCvd.assert_called_once()
370        mock_ssh_obj.Run.assert_called_with("mkdir -p acloud_cf_1")
371        self._mock_build_api.DownloadFetchcvd.assert_called_once()
372        mock_shutil.copyfile.assert_called_with("/mock/key", mock.ANY)
373        self.assertRegex(mock_ssh.ShellCmdWithRetry.call_args_list[0][0][0],
374                         r"^tar -cf - --lzop -S -C \S+ fetch_cvd \| "
375                         r"/mock/ssh -- tar -xf - --lzop -S -C acloud_cf_1$")
376        self.assertRegex(mock_ssh.ShellCmdWithRetry.call_args_list[1][0][0],
377                         r"^/mock/ssh -- "
378                         r"GOOGLE_APPLICATION_CREDENTIALS=/fake_key.json "
379                         r"CACHE_CONFIG=/home/shared/cache.properties "
380                         r"java -jar /home/shared/FetchCvdWrapper.jar "
381                         r"-directory=acloud_cf_1 "
382                         r"-fetch_cvd_path=acloud_cf_1/fetch_cvd "
383                         r"-credential_source=acloud_cf_1/credential_key.json "
384                         r"-test$")
385        mock_cvd_utils.ExecuteRemoteLaunchCvd.assert_called()
386        mock_pull.GetAllLogFilePaths.assert_not_called()
387        mock_pull.PullLogs.assert_not_called()
388        self.assertFalse(factory.GetFailures())
389        self.assertDictEqual({"inst": [log]}, factory.GetLogs())
390
391    @mock.patch("acloud.public.actions.remote_host_cf_device_factory.ssh")
392    @mock.patch("acloud.public.actions.remote_host_cf_device_factory."
393                "cvd_utils")
394    @mock.patch("acloud.public.actions.remote_host_cf_device_factory."
395                "subprocess.check_call")
396    @mock.patch("acloud.public.actions.remote_host_cf_device_factory.glob")
397    @mock.patch("acloud.public.actions.remote_host_cf_device_factory.pull")
398    def testCreateInstanceWithRemoteImageDir(self, _mock_pull, mock_glob,
399                                             _mock_check_call, mock_cvd_utils,
400                                             mock_ssh):
401        """Test CreateInstance with AvdSpec.remote_image_dir."""
402        mock_avd_spec = self._CreateMockAvdSpec()
403        mock_avd_spec.remote_image_dir = "mock_img_dir"
404
405        mock_ssh_obj = mock.Mock()
406        mock_ssh.Ssh.return_value = mock_ssh_obj
407        # Test initializing the remote image dir.
408        mock_glob.glob.return_value = ["/mock/super.img"]
409        factory = remote_host_cf_device_factory.RemoteHostDeviceFactory(
410            mock_avd_spec)
411
412        mock_cvd_utils.GetRemoteHostBaseDir.return_value = "acloud_cf_1"
413        mock_cvd_utils.FormatRemoteHostInstanceName.return_value = "inst"
414        mock_cvd_utils.LoadRemoteImageArgs.return_value = None
415        mock_cvd_utils.AreTargetFilesRequired.return_value = False
416        mock_cvd_utils.UploadExtraImages.return_value = [
417            ("arg", "mock_img_dir/1")]
418        mock_cvd_utils.ExecuteRemoteLaunchCvd.return_value = ""
419        mock_cvd_utils.FindRemoteLogs.return_value = []
420
421        self._mock_build_api.GetFetchBuildArgs.return_value = ["-test"]
422
423        self.assertEqual("inst", factory.CreateInstance())
424        mock_cvd_utils.PrepareRemoteImageDirLink.assert_called_once_with(
425            mock_ssh_obj, "acloud_cf_1", "mock_img_dir")
426        mock_cvd_utils.LoadRemoteImageArgs.assert_called_once_with(
427            mock_ssh_obj, "mock_img_dir/acloud_image_timestamp.txt",
428            "mock_img_dir/acloud_image_args.txt", mock.ANY)
429        mock_cvd_utils.SaveRemoteImageArgs.assert_called_once_with(
430            mock_ssh_obj, "mock_img_dir/acloud_image_args.txt",
431            [("arg", "mock_img_dir/1")])
432        mock_ssh_obj.Run.assert_called_with("cp -frT mock_img_dir acloud_cf_1")
433        self._mock_build_api.DownloadFetchcvd.assert_called_once()
434        self.assertEqual(["arg", "acloud_cf_1/1"],
435                         mock_cvd_utils.GetRemoteLaunchCvdCmd.call_args[0][3])
436
437        # Test reusing the remote image dir.
438        mock_cvd_utils.LoadRemoteImageArgs.return_value = [
439            ["arg", "mock_img_dir/2"]]
440        mock_cvd_utils.SaveRemoteImageArgs.reset_mock()
441        mock_ssh_obj.reset_mock()
442        self._mock_build_api.DownloadFetchcvd.reset_mock()
443
444        self.assertEqual("inst", factory.CreateInstance())
445        mock_cvd_utils.SaveRemoteImageArgs.assert_not_called()
446        mock_ssh_obj.Run.assert_called_with("cp -frT mock_img_dir acloud_cf_1")
447        self._mock_build_api.DownloadFetchcvd.assert_not_called()
448        self.assertEqual(["arg", "acloud_cf_1/2"],
449                         mock_cvd_utils.GetRemoteLaunchCvdCmd.call_args[0][3])
450
451    @mock.patch("acloud.public.actions.remote_host_cf_device_factory.ssh")
452    @mock.patch("acloud.public.actions.remote_host_cf_device_factory."
453                "cvd_utils")
454    @mock.patch("acloud.public.actions.remote_host_cf_device_factory."
455                "subprocess.check_call")
456    def testCreateInstanceWithCreateError(self, _mock_check_call,
457                                          mock_cvd_utils, mock_ssh):
458        """Test CreateInstance with CreateError."""
459        mock_avd_spec = self._CreateMockAvdSpec()
460        mock_avd_spec.remote_image_dir = "mock_img_dir"
461
462        mock_ssh_obj = mock.Mock()
463        mock_ssh.Ssh.return_value = mock_ssh_obj
464
465        mock_cvd_utils.GetRemoteHostBaseDir.return_value = "acloud_cf_1"
466        mock_cvd_utils.FormatRemoteHostInstanceName.return_value = "inst"
467        mock_cvd_utils.LoadRemoteImageArgs.side_effect = errors.CreateError(
468            "failure")
469        factory = remote_host_cf_device_factory.RemoteHostDeviceFactory(
470            mock_avd_spec)
471
472        self.assertEqual("inst", factory.CreateInstance())
473        self.assertEqual({"inst": "failure"}, factory.GetFailures())
474        mock_cvd_utils.ExecuteRemoteLaunchCvd.assert_not_called()
475
476
477if __name__ == "__main__":
478    unittest.main()
479