• 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                             creds_cache_file="credential",
50                             service_account_json_private_key_path="/mock/key")
51        return mock.Mock(spec=[],
52                         remote_image={
53                             "branch": "aosp-android12-gsi",
54                             "build_id": "100000",
55                             "build_target": "aosp_cf_x86_64_phone-trunk_staging-userdebug"},
56                         system_build_info={},
57                         kernel_build_info={},
58                         boot_build_info={},
59                         bootloader_build_info={},
60                         android_efi_loader_build_info={},
61                         ota_build_info={},
62                         host_package_build_info={},
63                         remote_host="192.0.2.100",
64                         remote_image_dir=None,
65                         host_user="user1",
66                         host_ssh_private_key_path=None,
67                         report_internal_ip=False,
68                         image_source=constants.IMAGE_SRC_REMOTE,
69                         local_image_dir=None,
70                         ins_timeout_secs=200,
71                         boot_timeout_secs=100,
72                         gpu="auto",
73                         no_pull_log=False,
74                         remote_fetch=False,
75                         fetch_cvd_wrapper=None,
76                         base_instance_num=None,
77                         num_avds_per_instance=None,
78                         openwrt=True,
79                         enable_fetch_local_caching=False,
80                         cfg=mock_cfg)
81
82    @mock.patch("acloud.public.actions.remote_host_cf_device_factory.ssh")
83    @mock.patch("acloud.public.actions.remote_host_cf_device_factory."
84                "cvd_utils")
85    @mock.patch("acloud.public.actions.remote_host_cf_device_factory.pull")
86    def testCreateInstanceWithImageDir(self, mock_pull, mock_cvd_utils,
87                                       mock_ssh):
88        """Test CreateInstance with local image directory."""
89        mock_avd_spec = self._CreateMockAvdSpec()
90        mock_avd_spec.image_source = constants.IMAGE_SRC_LOCAL
91        mock_avd_spec.local_image_dir = "/mock/target_files"
92        mock_avd_spec.base_instance_num = 2
93        mock_avd_spec.num_avds_per_instance = 3
94        mock_ssh_obj = mock.Mock()
95        mock_ssh.Ssh.return_value = mock_ssh_obj
96        factory = remote_host_cf_device_factory.RemoteHostDeviceFactory(
97            mock_avd_spec, cvd_host_package_artifact="/mock/cvd.tar.gz")
98
99        mock_pull.PullLogs.side_effect = errors.DeviceConnectionError
100
101        log = {"path": "/log.txt"}
102        mock_cvd_utils.GetRemoteHostBaseDir.return_value = "acloud_cf_2"
103        mock_cvd_utils.FormatRemoteHostInstanceName.return_value = "inst"
104        mock_cvd_utils.AreTargetFilesRequired.return_value = True
105        mock_cvd_utils.UploadExtraImages.return_value = [("-extra", "image")]
106        mock_cvd_utils.ExecuteRemoteLaunchCvd.return_value = "failure"
107        mock_cvd_utils.FindRemoteLogs.return_value = [log]
108
109        self.assertEqual("inst", factory.CreateInstance())
110        # InitRemotehost
111        mock_cvd_utils.CleanUpRemoteCvd.assert_called_once_with(
112            mock_ssh_obj, "acloud_cf_2", raise_error=False)
113        mock_cvd_utils.GetRemoteHostBaseDir.assert_called_with(2)
114        # ProcessRemoteHostArtifacts
115        mock_ssh_obj.Run.assert_called_with("mkdir -p acloud_cf_2")
116        self._mock_build_api.GetFetchBuildArgs.assert_not_called()
117        mock_cvd_utils.UploadArtifacts.assert_called_with(
118            mock_ssh_obj, "acloud_cf_2", "/mock/target_files",
119            "/mock/cvd.tar.gz")
120        mock_cvd_utils.UploadExtraImages.assert_called_with(
121            mock_ssh_obj, "acloud_cf_2", mock_avd_spec, "/mock/target_files")
122        mock_cvd_utils.GetConfigFromRemoteAndroidInfo.assert_called_with(
123            mock_ssh_obj, "acloud_cf_2")
124        # LaunchCvd
125        mock_cvd_utils.GetRemoteLaunchCvdCmd.assert_called_with(
126            "acloud_cf_2", mock_avd_spec, mock.ANY, ["-extra", "image"])
127        mock_cvd_utils.ExecuteRemoteLaunchCvd.assert_called()
128        # FindLogFiles
129        mock_cvd_utils.FindRemoteLogs.assert_called_with(
130            mock_ssh_obj, "acloud_cf_2", 2, 3)
131        mock_pull.GetAllLogFilePaths.assert_called_once()
132        mock_pull.PullLogs.assert_called_once()
133        factory.GetAdbPorts()
134        mock_cvd_utils.GetAdbPorts.assert_called_with(2, 3)
135        factory.GetVncPorts()
136        mock_cvd_utils.GetVncPorts.assert_called_with(2, 3)
137        self.assertEqual({"inst": "failure"}, factory.GetFailures())
138        self.assertDictEqual({"inst": [log]}, factory.GetLogs())
139
140    @mock.patch("acloud.public.actions.remote_host_cf_device_factory.ssh")
141    @mock.patch("acloud.public.actions.remote_host_cf_device_factory."
142                "cvd_utils")
143    @mock.patch("acloud.public.actions.remote_host_cf_device_factory.pull")
144    def testCreateInstanceWithImageZip(self, mock_pull, mock_cvd_utils,
145                                       mock_ssh):
146        """Test CreateInstance with local image zip."""
147        mock_avd_spec = self._CreateMockAvdSpec()
148        mock_avd_spec.image_source = constants.IMAGE_SRC_LOCAL
149        mock_ssh_obj = mock.Mock()
150        mock_ssh.Ssh.return_value = mock_ssh_obj
151        factory = remote_host_cf_device_factory.RemoteHostDeviceFactory(
152            mock_avd_spec, local_image_artifact="/mock/img.zip",
153            cvd_host_package_artifact="/mock/cvd.tar.gz")
154
155        mock_cvd_utils.GetRemoteHostBaseDir.return_value = "acloud_cf_1"
156        mock_cvd_utils.FormatRemoteHostInstanceName.return_value = "inst"
157        mock_cvd_utils.AreTargetFilesRequired.return_value = False
158        mock_cvd_utils.ExecuteRemoteLaunchCvd.return_value = ""
159        mock_cvd_utils.FindRemoteLogs.return_value = []
160
161        self.assertEqual("inst", factory.CreateInstance())
162        # InitRemotehost
163        mock_cvd_utils.GetRemoteHostBaseDir.assert_called_with(None)
164        mock_cvd_utils.CleanUpRemoteCvd.assert_called_once()
165        # ProcessRemoteHostArtifacts
166        mock_ssh_obj.Run.assert_called_with("mkdir -p acloud_cf_1")
167        self._mock_build_api.GetFetchBuildArgs.assert_not_called()
168        mock_cvd_utils.UploadArtifacts.assert_called_with(
169            mock_ssh_obj, "acloud_cf_1", "/mock/img.zip", "/mock/cvd.tar.gz")
170        mock_cvd_utils.UploadExtraImages.assert_called_with(
171            mock_ssh_obj, "acloud_cf_1", mock_avd_spec, None)
172        # LaunchCvd
173        mock_cvd_utils.ExecuteRemoteLaunchCvd.assert_called()
174        # FindLogFiles
175        mock_cvd_utils.FindRemoteLogs.assert_called_with(
176            mock_ssh_obj, "acloud_cf_1", None, None)
177        mock_pull.GetAllLogFilePaths.assert_not_called()
178        mock_pull.PullLogs.assert_not_called()
179        factory.GetAdbPorts()
180        mock_cvd_utils.GetAdbPorts.assert_called_with(None, None)
181        factory.GetVncPorts()
182        mock_cvd_utils.GetVncPorts.assert_called_with(None, None)
183        self.assertFalse(factory.GetFailures())
184        self.assertDictEqual({"inst": []}, factory.GetLogs())
185
186    # pylint: disable=invalid-name
187    @mock.patch("acloud.public.actions.remote_host_cf_device_factory.ssh")
188    @mock.patch("acloud.public.actions.remote_host_cf_device_factory."
189                "cvd_utils")
190    @mock.patch("acloud.public.actions.remote_host_cf_device_factory.pull")
191    def testCreateInstanceWithTargetFilesZip(self, mock_pull, mock_cvd_utils,
192                                             mock_ssh):
193        """Test CreateInstance with local target_files zip."""
194        mock_avd_spec = self._CreateMockAvdSpec()
195        mock_avd_spec.image_source = constants.IMAGE_SRC_LOCAL
196        mock_ssh_obj = mock.Mock()
197        mock_ssh.Ssh.return_value = mock_ssh_obj
198        factory = remote_host_cf_device_factory.RemoteHostDeviceFactory(
199            mock_avd_spec, local_image_artifact="/mock/target_files.zip",
200            cvd_host_package_artifact="/mock/cvd.tar.gz")
201
202        mock_cvd_utils.GetRemoteHostBaseDir.return_value = "acloud_cf_1"
203        mock_cvd_utils.FormatRemoteHostInstanceName.return_value = "inst"
204        mock_cvd_utils.AreTargetFilesRequired.return_value = True
205        mock_cvd_utils.ExecuteRemoteLaunchCvd.return_value = ""
206        mock_cvd_utils.FindRemoteLogs.return_value = []
207
208        self.assertEqual("inst", factory.CreateInstance())
209        # InitRemotehost
210        mock_cvd_utils.GetRemoteHostBaseDir.assert_called_with(None)
211        mock_cvd_utils.CleanUpRemoteCvd.assert_called_once()
212        # ProcessRemoteHostArtifacts
213        mock_ssh_obj.Run.assert_called_with("mkdir -p acloud_cf_1")
214        mock_cvd_utils.ExtractTargetFilesZip.assert_called_with(
215            "/mock/target_files.zip", mock.ANY)
216        self._mock_build_api.GetFetchBuildArgs.assert_not_called()
217        mock_cvd_utils.UploadExtraImages.assert_called_with(
218            mock_ssh_obj, "acloud_cf_1", mock_avd_spec, mock.ANY)
219        mock_cvd_utils.UploadArtifacts.assert_called_with(
220            mock_ssh_obj, "acloud_cf_1", mock.ANY, "/mock/cvd.tar.gz")
221        self.assertIn("acloud_remote_host",  # temp dir prefix
222                      mock_cvd_utils.UploadArtifacts.call_args[0][2])
223        # LaunchCvd
224        mock_cvd_utils.ExecuteRemoteLaunchCvd.assert_called()
225        # FindLogFiles
226        mock_cvd_utils.FindRemoteLogs.assert_called_with(
227            mock_ssh_obj, "acloud_cf_1", None, None)
228        mock_pull.GetAllLogFilePaths.assert_not_called()
229        mock_pull.PullLogs.assert_not_called()
230        factory.GetAdbPorts()
231        mock_cvd_utils.GetAdbPorts.assert_called_with(None, None)
232        factory.GetVncPorts()
233        mock_cvd_utils.GetVncPorts.assert_called_with(None, None)
234        self.assertFalse(factory.GetFailures())
235        self.assertDictEqual({"inst": []}, factory.GetLogs())
236
237    @mock.patch("acloud.public.actions.remote_host_cf_device_factory.ssh")
238    @mock.patch("acloud.public.actions.remote_host_cf_device_factory."
239                "cvd_utils")
240    @mock.patch("acloud.public.actions.remote_host_cf_device_factory."
241                "subprocess.check_call")
242    @mock.patch("acloud.public.actions.remote_host_cf_device_factory.glob")
243    @mock.patch("acloud.public.actions.remote_host_cf_device_factory.pull")
244    def testCreateInstanceWithRemoteImages(self, mock_pull, mock_glob,
245                                           mock_check_call, mock_cvd_utils,
246                                           mock_ssh):
247        """Test CreateInstance with remote images."""
248        mock_avd_spec = self._CreateMockAvdSpec()
249        mock_avd_spec.image_source = constants.IMAGE_SRC_REMOTE
250        mock_ssh_obj = mock.Mock()
251        mock_ssh.Ssh.return_value = mock_ssh_obj
252        mock_ssh_obj.GetBaseCmd.return_value = "/mock/ssh"
253        mock_glob.glob.return_value = ["/mock/super.img"]
254        factory = remote_host_cf_device_factory.RemoteHostDeviceFactory(
255            mock_avd_spec)
256
257        mock_cvd_utils.GetRemoteHostBaseDir.return_value = "acloud_cf_1"
258        mock_cvd_utils.FormatRemoteHostInstanceName.return_value = "inst"
259        mock_cvd_utils.AreTargetFilesRequired.return_value = True
260        mock_cvd_utils.GetMixBuildTargetFilename.return_value = "mock.zip"
261        mock_cvd_utils.ExecuteRemoteLaunchCvd.return_value = ""
262        mock_cvd_utils.FindRemoteLogs.return_value = []
263
264        self._mock_build_api.GetFetchBuildArgs.return_value = ["-test"]
265
266        self.assertEqual("inst", factory.CreateInstance())
267        # InitRemoteHost
268        mock_cvd_utils.CleanUpRemoteCvd.assert_called_once()
269        # ProcessRemoteHostArtifacts
270        mock_ssh_obj.Run.assert_called_with("mkdir -p acloud_cf_1")
271        self._mock_build_api.DownloadArtifact.assert_called_once_with(
272            "aosp_cf_x86_64_phone-trunk_staging-userdebug", "100000", "mock.zip", mock.ANY)
273        mock_cvd_utils.ExtractTargetFilesZip.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        mock_shutil.copyfile.assert_called_with("/mock/key", mock.ANY)
319        self.assertRegex(mock_ssh.ShellCmdWithRetry.call_args_list[0][0][0],
320                         r"^tar -cf - --lzop -S -C \S+ fetch_cvd \| "
321                         r"/mock/ssh -- tar -xf - --lzop -S -C acloud_cf_1$")
322        self.assertRegex(mock_ssh.ShellCmdWithRetry.call_args_list[1][0][0],
323                         r"^/mock/ssh -- cvd fetch "
324                         r"-target_directory=acloud_cf_1 "
325                         r"-credential_source=acloud_cf_1/credential_key.json "
326                         r"-enable_caching=false "
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        mock_shutil.copyfile.assert_called_with("/mock/key", mock.ANY)
372        self.assertRegex(mock_ssh.ShellCmdWithRetry.call_args_list[0][0][0],
373                         r"^tar -cf - --lzop -S -C \S+ fetch_cvd \| "
374                         r"/mock/ssh -- tar -xf - --lzop -S -C acloud_cf_1$")
375        self.assertRegex(mock_ssh.ShellCmdWithRetry.call_args_list[1][0][0],
376                         r"^/mock/ssh -- "
377                         r"GOOGLE_APPLICATION_CREDENTIALS=/fake_key.json "
378                         r"CACHE_CONFIG=/home/shared/cache.properties "
379                         r"java -jar /home/shared/FetchCvdWrapper.jar "
380                         r"-fetch_cvd_path=cvd "
381                         r"fetch "
382                         r"-target_directory=acloud_cf_1 "
383                         r"-credential_source=acloud_cf_1/credential_key.json "
384                         r"-enable_caching=false "
385                         r"-test$")
386        mock_cvd_utils.ExecuteRemoteLaunchCvd.assert_called()
387        mock_pull.GetAllLogFilePaths.assert_not_called()
388        mock_pull.PullLogs.assert_not_called()
389        self.assertFalse(factory.GetFailures())
390        self.assertDictEqual({"inst": [log]}, factory.GetLogs())
391
392    @mock.patch("acloud.public.actions.remote_host_cf_device_factory.ssh")
393    @mock.patch("acloud.public.actions.remote_host_cf_device_factory."
394                "cvd_utils")
395    @mock.patch("acloud.public.actions.remote_host_cf_device_factory."
396                "subprocess.check_call")
397    @mock.patch("acloud.public.actions.remote_host_cf_device_factory.glob")
398    @mock.patch("acloud.public.actions.remote_host_cf_device_factory.pull")
399    def testCreateInstanceWithRemoteImageDir(self, _mock_pull, mock_glob,
400                                             _mock_check_call, mock_cvd_utils,
401                                             mock_ssh):
402        """Test CreateInstance with AvdSpec.remote_image_dir."""
403        mock_avd_spec = self._CreateMockAvdSpec()
404        mock_avd_spec.remote_image_dir = "mock_img_dir"
405
406        mock_ssh_obj = mock.Mock()
407        mock_ssh.Ssh.return_value = mock_ssh_obj
408        # Test initializing the remote image dir.
409        mock_glob.glob.return_value = ["/mock/super.img"]
410        factory = remote_host_cf_device_factory.RemoteHostDeviceFactory(
411            mock_avd_spec)
412
413        mock_cvd_utils.GetRemoteHostBaseDir.return_value = "acloud_cf_1"
414        mock_cvd_utils.FormatRemoteHostInstanceName.return_value = "inst"
415        mock_cvd_utils.LoadRemoteImageArgs.return_value = None
416        mock_cvd_utils.AreTargetFilesRequired.return_value = False
417        mock_cvd_utils.UploadExtraImages.return_value = [
418            ("arg", "mock_img_dir/1")]
419        mock_cvd_utils.ExecuteRemoteLaunchCvd.return_value = ""
420        mock_cvd_utils.FindRemoteLogs.return_value = []
421
422        self._mock_build_api.GetFetchBuildArgs.return_value = ["-test"]
423
424        self.assertEqual("inst", factory.CreateInstance())
425        mock_cvd_utils.PrepareRemoteImageDirLink.assert_called_once_with(
426            mock_ssh_obj, "acloud_cf_1", "mock_img_dir")
427        mock_cvd_utils.LoadRemoteImageArgs.assert_called_once_with(
428            mock_ssh_obj, "mock_img_dir/acloud_image_timestamp.txt",
429            "mock_img_dir/acloud_image_args.txt", mock.ANY)
430        mock_cvd_utils.SaveRemoteImageArgs.assert_called_once_with(
431            mock_ssh_obj, "mock_img_dir/acloud_image_args.txt",
432            [("arg", "mock_img_dir/1")])
433        mock_ssh_obj.Run.assert_called_with("cp -frT mock_img_dir acloud_cf_1")
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
443        self.assertEqual("inst", factory.CreateInstance())
444        mock_cvd_utils.SaveRemoteImageArgs.assert_not_called()
445        mock_ssh_obj.Run.assert_called_with("cp -frT mock_img_dir acloud_cf_1")
446        self.assertEqual(["arg", "acloud_cf_1/2"],
447                         mock_cvd_utils.GetRemoteLaunchCvdCmd.call_args[0][3])
448
449    @mock.patch("acloud.public.actions.remote_host_cf_device_factory.ssh")
450    @mock.patch("acloud.public.actions.remote_host_cf_device_factory."
451                "cvd_utils")
452    @mock.patch("acloud.public.actions.remote_host_cf_device_factory."
453                "subprocess.check_call")
454    def testCreateInstanceWithCreateError(self, _mock_check_call,
455                                          mock_cvd_utils, mock_ssh):
456        """Test CreateInstance with CreateError."""
457        mock_avd_spec = self._CreateMockAvdSpec()
458        mock_avd_spec.remote_image_dir = "mock_img_dir"
459
460        mock_ssh_obj = mock.Mock()
461        mock_ssh.Ssh.return_value = mock_ssh_obj
462
463        mock_cvd_utils.GetRemoteHostBaseDir.return_value = "acloud_cf_1"
464        mock_cvd_utils.FormatRemoteHostInstanceName.return_value = "inst"
465        mock_cvd_utils.LoadRemoteImageArgs.side_effect = errors.CreateError(
466            "failure")
467        factory = remote_host_cf_device_factory.RemoteHostDeviceFactory(
468            mock_avd_spec)
469
470        self.assertEqual("inst", factory.CreateInstance())
471        self.assertEqual({"inst": "failure"}, factory.GetFailures())
472        mock_cvd_utils.ExecuteRemoteLaunchCvd.assert_not_called()
473
474
475if __name__ == "__main__":
476    unittest.main()
477