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