xref: /aosp_15_r20/tools/acloud/delete/delete_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 delete."""
15
16import subprocess
17import unittest
18
19from unittest import mock
20
21from acloud import errors
22from acloud.delete import delete
23from acloud.internal.lib import driver_test_lib
24from acloud.internal.lib import oxygen_client
25from acloud.internal.lib import utils
26from acloud.list import list as list_instances
27from acloud.public import config
28from acloud.public import device_driver
29from acloud.public import report
30
31
32# pylint: disable=invalid-name,protected-access,unused-argument,no-member
33class DeleteTest(driver_test_lib.BaseDriverTest):
34    """Test delete functions."""
35
36    def testDeleteLocalCuttlefishInstanceSuccess(self):
37        """Test DeleteLocalCuttlefishInstance."""
38        instance_object = mock.MagicMock()
39        instance_object.name = "local-instance"
40        mock_lock = mock.Mock()
41        mock_lock.Lock.return_value = True
42        instance_object.GetLock.return_value = mock_lock
43
44        delete_report = report.Report(command="delete")
45        delete.DeleteLocalCuttlefishInstance(instance_object, delete_report)
46        self.assertEqual(delete_report.data, {
47            "deleted": [
48                {
49                    "type": "instance",
50                    "name": "local-instance",
51                },
52            ],
53        })
54        self.assertEqual(delete_report.status, "SUCCESS")
55        mock_lock.SetInUse.assert_called_once_with(False)
56        mock_lock.Unlock.assert_called_once()
57
58        mock_lock.Lock.return_value = False
59        delete.DeleteLocalCuttlefishInstance(instance_object, delete_report)
60        self.assertEqual(delete_report.status, "FAIL")
61
62    def testDeleteLocalCuttlefishInstanceFailure(self):
63        """Test DeleteLocalCuttlefishInstance with command failure."""
64        instance_object = mock.MagicMock()
65        instance_object.name = "local-instance"
66        instance_object.Delete.side_effect = subprocess.CalledProcessError(
67            1, "cmd")
68        mock_lock = mock.Mock()
69        mock_lock.Lock.return_value = True
70        instance_object.GetLock.return_value = mock_lock
71
72        delete_report = report.Report(command="delete")
73        delete.DeleteLocalCuttlefishInstance(instance_object, delete_report)
74
75        self.assertEqual(delete_report.status, "FAIL")
76        mock_lock.SetInUse.assert_called_once_with(False)
77        mock_lock.Unlock.assert_called_once()
78
79    def testDeleteLocalGoldfishInstanceSuccess(self):
80        """Test DeleteLocalGoldfishInstance."""
81        mock_adb_tools = mock.Mock()
82        mock_adb_tools.EmuCommand.return_value = 0
83        mock_instance = mock.Mock(adb=mock_adb_tools,
84                                  adb_port=5555,
85                                  device_serial="serial",
86                                  instance_dir="/unit/test")
87        # name is a positional argument of Mock().
88        mock_instance.name = "unittest"
89        mock_lock = mock.Mock()
90        mock_lock.Lock.return_value = True
91        mock_instance.GetLock.return_value = mock_lock
92
93        delete_report = report.Report(command="delete")
94        delete.DeleteLocalGoldfishInstance(mock_instance, delete_report)
95
96        mock_adb_tools.EmuCommand.assert_called_with("kill")
97        self.assertEqual(delete_report.data, {
98            "deleted": [
99                {
100                    "type": "instance",
101                    "name": "unittest",
102                },
103            ],
104        })
105        self.assertEqual(delete_report.status, "SUCCESS")
106        mock_lock.SetInUse.assert_called_once_with(False)
107        mock_lock.Unlock.assert_called_once()
108
109        mock_lock.Lock.return_value = False
110        delete.DeleteLocalGoldfishInstance(mock_instance, delete_report)
111        self.assertEqual(delete_report.status, "FAIL")
112
113    def testDeleteLocalGoldfishInstanceFailure(self):
114        """Test DeleteLocalGoldfishInstance with adb command failure."""
115        mock_adb_tools = mock.Mock()
116        mock_adb_tools.EmuCommand.return_value = 1
117        mock_instance = mock.Mock(adb=mock_adb_tools,
118                                  adb_port=5555,
119                                  device_serial="serial",
120                                  instance_dir="/unit/test")
121        # name is a positional argument of Mock().
122        mock_instance.name = "unittest"
123        mock_lock = mock.Mock()
124        mock_lock.Lock.return_value = True
125        mock_instance.GetLock.return_value = mock_lock
126
127        delete_report = report.Report(command="delete")
128        delete.DeleteLocalGoldfishInstance(mock_instance, delete_report)
129
130        mock_adb_tools.EmuCommand.assert_called_with("kill")
131        self.assertTrue(len(delete_report.errors) > 0)
132        self.assertEqual(delete_report.status, "FAIL")
133        mock_lock.SetInUse.assert_called_once_with(False)
134        mock_lock.Unlock.assert_called_once()
135
136    def testResetLocalInstanceLockByName(self):
137        """test ResetLocalInstanceLockByName."""
138        mock_lock = mock.Mock()
139        mock_lock.Lock.return_value = True
140        self.Patch(list_instances, "GetLocalInstanceLockByName",
141                   return_value=mock_lock)
142        delete_report = report.Report(command="delete")
143        delete.ResetLocalInstanceLockByName("unittest", delete_report)
144
145        self.assertEqual(delete_report.data, {
146            "deleted": [
147                {
148                    "type": "instance",
149                    "name": "unittest",
150                },
151            ],
152        })
153        mock_lock.Lock.assert_called_once()
154        mock_lock.SetInUse.assert_called_once_with(False)
155        mock_lock.Unlock.assert_called_once()
156
157        mock_lock.Lock.return_value = False
158        delete.ResetLocalInstanceLockByName("unittest", delete_report)
159        self.assertEqual(delete_report.status, "FAIL")
160
161    def testResetLocalInstanceLockByNameFailure(self):
162        """test ResetLocalInstanceLockByName with an invalid name."""
163        self.Patch(list_instances, "GetLocalInstanceLockByName",
164                   return_value=None)
165        delete_report = report.Report(command="delete")
166        delete.ResetLocalInstanceLockByName("unittest", delete_report)
167
168        self.assertTrue(len(delete_report.errors) > 0)
169        self.assertEqual(delete_report.status, "FAIL")
170
171    @mock.patch("acloud.delete.delete.emulator_console.RemoteEmulatorConsole")
172    def testDeleteHostGoldfishInstance(self, mock_console):
173        """test DeleteHostGoldfishInstance."""
174        mock_console_obj = mock.MagicMock()
175        mock_console.return_value = mock_console_obj
176        mock_console_obj.__enter__.return_value = mock_console_obj
177
178        cfg_attrs = {"ssh_private_key_path": "cfg_key_path",
179                     "extra_args_ssh_tunnel": "extra args"}
180        mock_cfg = mock.Mock(spec_set=list(cfg_attrs.keys()), **cfg_attrs)
181        instance_name = "host-goldfish-192.0.2.1-5554-123456-sdk_x86_64-sdk"
182        delete_report = report.Report(command="delete")
183
184        delete.DeleteHostGoldfishInstance(mock_cfg, instance_name,
185                                          None, None, delete_report)
186        mock_console.assert_called_with("192.0.2.1", 5554, "vsoc-01",
187                                        "cfg_key_path", "extra args")
188        mock_console_obj.Kill.assert_called()
189        self.assertEqual(delete_report.status, "SUCCESS")
190        self.assertEqual(delete_report.data, {
191            "deleted": [
192                {
193                    "type": "instance",
194                    "name": instance_name,
195                },
196            ],
197        })
198
199        mock_console_obj.reset_mock()
200        mock_console_obj.Kill.side_effect = errors.DeviceConnectionError
201        delete_report = report.Report(command="delete")
202
203        delete.DeleteHostGoldfishInstance(mock_cfg, instance_name,
204                                          "user", "key_path", delete_report)
205        mock_console.assert_called_with("192.0.2.1", 5554, "user",
206                                        "key_path", "extra args")
207        self.assertEqual(delete_report.status, "FAIL")
208        self.assertEqual(len(delete_report.errors), 1)
209
210    @mock.patch.object(delete, "ssh")
211    @mock.patch.object(delete, "cvd_utils")
212    def testCleanUpRemoteHost(self, mock_cvd_utils, mock_ssh):
213        """Test CleanUpRemoteHost."""
214        mock_ssh_ip = mock.Mock()
215        mock_ssh.IP.return_value = mock_ssh_ip
216        mock_ssh_obj = mock.Mock()
217        mock_ssh.Ssh.return_value = mock_ssh_obj
218        cfg_attrs = {"ssh_private_key_path": "cfg_key_path"}
219        mock_cfg = mock.Mock(spec_set=list(cfg_attrs.keys()), **cfg_attrs)
220        delete_report = report.Report(command="delete")
221        delete.CleanUpRemoteHost(mock_cfg, "192.0.2.1", "vsoc-01", None, ".",
222                                 delete_report)
223
224        mock_ssh.IP.assert_called_with(ip="192.0.2.1")
225        mock_ssh.Ssh.assert_called_with(
226            ip=mock_ssh_ip,
227            user="vsoc-01",
228            ssh_private_key_path="cfg_key_path")
229        mock_cvd_utils.CleanUpRemoteCvd.assert_called_with(
230            mock_ssh_obj, ".", raise_error=True)
231        self.assertEqual(delete_report.status, "SUCCESS")
232        self.assertEqual(delete_report.data, {
233            "deleted": [
234                {
235                    "type": "remote host",
236                    "name": "192.0.2.1",
237                },
238            ],
239        })
240
241        mock_ssh_ip.reset_mock()
242        mock_ssh_obj.reset_mock()
243        mock_cvd_utils.reset_mock()
244        mock_cvd_utils.CleanUpRemoteCvd.side_effect = (
245            subprocess.CalledProcessError(cmd="test", returncode=1))
246        delete_report = report.Report(command="delete")
247
248        delete.CleanUpRemoteHost(mock_cfg, "192.0.2.2", "user", "key_path",
249                                 "acloud_cf_1", delete_report)
250        mock_ssh.IP.assert_called_with(ip="192.0.2.2")
251        mock_ssh.Ssh.assert_called_with(
252            ip=mock_ssh_ip,
253            user="user",
254            ssh_private_key_path="key_path")
255        mock_cvd_utils.CleanUpRemoteCvd.assert_called_with(
256            mock_ssh_obj, "acloud_cf_1", raise_error=True)
257        self.assertEqual(delete_report.status, "FAIL")
258        self.assertEqual(len(delete_report.errors), 1)
259
260    @mock.patch.object(delete, "DeleteInstances", return_value="")
261    @mock.patch.object(delete, "ResetLocalInstanceLockByName")
262    @mock.patch.object(delete, "CleanUpRemoteHost")
263    @mock.patch.object(delete, "DeleteHostGoldfishInstance")
264    @mock.patch.object(delete, "DeleteRemoteInstances", return_value="")
265    def testDeleteInstanceByNames(self, mock_delete_remote_ins,
266                                  mock_delete_host_gf_ins,
267                                  mock_clean_up_remote_host, mock_reset_lock,
268                                  mock_delete_local_ins):
269        """test DeleteInstanceByNames."""
270        cfg = mock.Mock()
271        # Test delete local instances.
272        instances = ["local-instance-1", "local-instance-2"]
273        mock_local_ins = mock.Mock()
274        mock_local_ins.name = "local-instance-1"
275        self.Patch(list_instances, "GetLocalInstancesByNames",
276                   return_value=[mock_local_ins])
277        delete.DeleteInstanceByNames(cfg, instances, None, None)
278        mock_delete_local_ins.assert_called_with(cfg, [mock_local_ins])
279        mock_reset_lock.assert_called_with("local-instance-2", mock.ANY)
280
281        # Test delete remote host instances.
282        instances = ["host-goldfish-192.0.2.1-5554-123456-sdk_x86_64-sdk",
283                     "host-192.0.2.2-3-123456-aosp_cf_x86_64_phone"]
284        delete.DeleteInstanceByNames(cfg, instances, "user", "key")
285        mock_delete_host_gf_ins.assert_called_with(
286            cfg, instances[0], "user", "key", mock.ANY)
287        mock_clean_up_remote_host.assert_called_with(
288            cfg, "192.0.2.2", "user", "key", "acloud_cf_3", mock.ANY)
289
290        # Test delete remote instances.
291        instances = ["ins-id1-cf-x86-phone-userdebug",
292                     "ins-id2-cf-x86-phone-userdebug"]
293        delete.DeleteInstanceByNames(cfg, instances, None, None)
294        mock_delete_remote_ins.assert_called()
295
296    @mock.patch.object(oxygen_client.OxygenClient, "ReleaseDevice")
297    def testReleaseOxygenDevice(self, mock_release):
298        """test ReleaseOxygenDevice"""
299        cfg = mock.Mock()
300        cfg.oxygen_client = "oxygen_client"
301        ip = "0.0.0.0"
302        # Raise exception for multiple instances
303        instances = ["local-instance-1", "local-instance-2"]
304        self.assertRaises(errors.CommandArgError, delete._ReleaseOxygenDevice, cfg, instances, ip)
305
306        # Test release device with oxygen client
307        instances = ["local-instance-1"]
308        delete._ReleaseOxygenDevice(cfg, instances, ip)
309        mock_release.assert_called_once()
310
311        mock_release.side_effect = subprocess.CalledProcessError(
312            0, "fake_cmd",
313            "Error received while trying to release device: error_msg")
314        delete_report = delete._ReleaseOxygenDevice(cfg, instances, ip)
315        self.assertEqual(delete_report.errors, ["error_msg"])
316
317        mock_release.side_effect = subprocess.CalledProcessError(
318            0, "fake_cmd",
319            "error")
320        delete_report = delete._ReleaseOxygenDevice(cfg, instances, ip)
321        self.assertEqual(delete_report.status, "FAIL")
322
323    def testDeleteInstances(self):
324        """test DeleteInstances."""
325        fake_ins = mock.MagicMock()
326        fake_ins.islocal = False
327        fake_ins.avd_type = "cuttlefish"
328        fake_ins.vnc_port = None
329
330        fake_ins2 = mock.MagicMock()
331        fake_ins2.islocal = True
332        fake_ins2.avd_type = "cuttlefish"
333        fake_ins2.vnc_port = None
334
335        fake_ins3 = mock.MagicMock()
336        fake_ins3.islocal = True
337        fake_ins3.avd_type = "goldfish"
338        fake_ins3.vnc_port = None
339
340        fake_ins4 = mock.MagicMock()
341        fake_ins4.islocal = True
342        fake_ins4.avd_type = "unknown"
343        fake_ins4.vnc_port = 12345
344
345        self.Patch(delete, "DeleteLocalGoldfishInstance")
346        self.Patch(delete, "DeleteLocalCuttlefishInstance")
347        self.Patch(delete, "DeleteRemoteInstances")
348        self.Patch(utils, "CleanupSSVncviewer")
349
350        fake_instances_to_delete = []
351        delete.DeleteInstances(None, fake_instances_to_delete)
352        delete.DeleteRemoteInstances.assert_not_called()
353
354        fake_instances_to_delete = [
355            fake_ins, fake_ins2, fake_ins3, fake_ins4]
356        delete.DeleteInstances(None, fake_instances_to_delete)
357        delete.DeleteRemoteInstances.assert_called_once()
358        delete.DeleteLocalGoldfishInstance.assert_called_once()
359        delete.DeleteLocalCuttlefishInstance.assert_called_once()
360        utils.CleanupSSVncviewer.assert_called_once()
361
362    def testDeleteRemoteInstances(self):
363        """test DeleteRemoteInstances."""
364        fake_cfg = mock.MagicMock()
365        fake_cfg.SupportRemoteInstance = mock.MagicMock()
366        fake_cfg.SupportRemoteInstance.return_value = True
367        fake_instances_to_delete = ["fake_ins"]
368        delete_report = report.Report(command="delete")
369        self.Patch(device_driver, "DeleteAndroidVirtualDevices",
370                   return_value=delete_report)
371        delete.DeleteRemoteInstances(fake_cfg, fake_instances_to_delete)
372        device_driver.DeleteAndroidVirtualDevices.assert_called_once()
373
374        fake_cfg.SupportRemoteInstance.return_value = False
375        self.assertRaises(errors.ConfigError,
376                          delete.DeleteRemoteInstances,
377                          fake_cfg, fake_instances_to_delete)
378
379    def testRun(self):
380        """test Run."""
381        args = mock.MagicMock()
382        args.oxygen = False
383        args.instance_names = None
384        args.remote_host = None
385        args.local_only = True
386        args.adb_port = None
387        args.all = True
388
389        self.Patch(delete, "_ReleaseOxygenDevice")
390        self.Patch(delete, "DeleteInstanceByNames")
391        self.Patch(delete, "CleanUpRemoteHost")
392        fake_cfg = mock.MagicMock()
393        fake_cfg.SupportRemoteInstance = mock.MagicMock()
394        self.Patch(config, "GetAcloudConfig", return_value=fake_cfg)
395        self.Patch(list_instances, "GetLocalInstances",
396                   return_value=[])
397        self.Patch(list_instances, "GetRemoteInstances",
398                   return_value=["remote_instances"])
399        self.Patch(list_instances, "FilterInstancesByAdbPort",
400                   return_value=["filter_by_port_instance"])
401        self.Patch(list_instances, "ChooseInstancesFromList",
402                   return_value=["choice_instance"])
403        self.Patch(delete, "DeleteInstances")
404
405        delete.Run(args)
406        delete.DeleteInstances.assert_called_with(fake_cfg, [])
407
408        list_instances.GetLocalInstances.return_value = ["local_instances"]
409        delete.Run(args)
410        delete.DeleteInstances.assert_called_with(fake_cfg, ["local_instances"])
411
412        args.all = False
413        delete.Run(args)
414        delete.DeleteInstances.assert_called_with(fake_cfg, ["choice_instance"])
415
416        args.adb_port = "12345"
417        delete.Run(args)
418        delete.DeleteInstances.assert_called_with(fake_cfg, ["filter_by_port_instance"])
419
420        args.local_only = False
421        args.all = True
422        args.adb_port = None
423        delete.Run(args)
424        delete.DeleteInstances.assert_called_with(
425            fake_cfg, ["local_instances", "remote_instances"])
426
427        args.remote_host = True
428        delete.Run(args)
429        delete.CleanUpRemoteHost.assert_called_once()
430
431        args.instance_names = ["fake_ins_name"]
432        delete.Run(args)
433        delete.DeleteInstanceByNames.assert_called_once()
434
435        args.oxygen = True
436        delete.Run(args)
437        delete._ReleaseOxygenDevice.assert_called_once()
438
439
440if __name__ == "__main__":
441    unittest.main()
442