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 list.""" 15 16import unittest 17 18from unittest import mock 19 20from acloud import errors 21from acloud.internal.lib import auth 22from acloud.internal.lib import cvd_runtime_config 23from acloud.internal.lib import driver_test_lib 24from acloud.internal.lib import gcompute_client 25from acloud.internal.lib import ssh 26from acloud.internal.lib import utils 27from acloud.internal.lib import adb_tools 28from acloud.list import list as list_instance 29from acloud.list import instance 30from acloud.public import config 31 32 33class InstanceObject: 34 """Mock to store data of instance.""" 35 36 def __init__(self, name): 37 self.name = name 38 39 40class ListTest(driver_test_lib.BaseDriverTest): 41 """Test list.""" 42 43 def setUp(self): 44 """Set up the test.""" 45 super().setUp() 46 self.Patch(instance, "_GetElapsedTime", return_value=0) 47 self.Patch(instance.RemoteInstance, "_GetZoneName") 48 self.Patch(instance.RemoteInstance, "_GetProjectName") 49 self.Patch(instance, "GetInstanceIP", return_value=ssh.IP()) 50 self.Patch(instance.RemoteInstance, "GetForwardedPortsFromSSHTunnel") 51 self.Patch(adb_tools, "AdbTools") 52 self.Patch(adb_tools.AdbTools, "IsAdbConnected", return_value=False) 53 self.Patch(auth, "CreateCredentials") 54 self.Patch(gcompute_client, "ComputeClient") 55 self.Patch(gcompute_client.ComputeClient, "ListInstances") 56 57 def testGetInstancesFromInstanceNames(self): 58 """test get instances from instance names.""" 59 cfg = mock.MagicMock() 60 instance_names = ["alive_instance1", "alive_local_instance"] 61 62 alive_instance1 = InstanceObject("alive_instance1") 63 alive_instance2 = InstanceObject("alive_instance2") 64 alive_local_instance = InstanceObject("alive_local_instance") 65 66 self.Patch(list_instance, "GetLocalInstancesByNames", 67 return_value=[alive_local_instance]) 68 self.Patch(list_instance, "GetRemoteInstances", 69 return_value=[alive_instance1, alive_instance2]) 70 instances_list = list_instance.GetInstancesFromInstanceNames(cfg, instance_names) 71 instances_name_in_list = [instance_object.name for instance_object in instances_list] 72 self.assertEqual(instances_name_in_list.sort(), instance_names.sort()) 73 74 instance_names = ["alive_instance1", "alive_local_instance", "alive_local_instance"] 75 instances_list = list_instance.GetInstancesFromInstanceNames(cfg, instance_names) 76 instances_name_in_list = [instance_object.name for instance_object in instances_list] 77 self.assertEqual(instances_name_in_list.sort(), instance_names.sort()) 78 79 # test get instance from instance name error with invalid input. 80 instance_names = ["miss2_local_instance", "alive_instance1"] 81 miss_instance_names = ["miss2_local_instance"] 82 self.assertRaisesRegex( 83 errors.NoInstancesFound, 84 "Did not find the following instances: %s" % ' '.join(miss_instance_names), 85 list_instance.GetInstancesFromInstanceNames, 86 cfg=cfg, 87 instance_names=instance_names) 88 89 def testChooseOneRemoteInstance(self): 90 """test choose one remote instance from instance names.""" 91 cfg = mock.MagicMock() 92 93 # Test only one instance case 94 instance_names = ["cf_instance1"] 95 self.Patch(list_instance, "GetCFRemoteInstances", return_value=instance_names) 96 expected_instance = "cf_instance1" 97 self.assertEqual(list_instance.ChooseOneRemoteInstance(cfg), expected_instance) 98 99 # Test no instance case 100 self.Patch(list_instance, "GetCFRemoteInstances", return_value=[]) 101 with self.assertRaises(errors.NoInstancesFound): 102 list_instance.ChooseOneRemoteInstance(cfg) 103 104 # Test two instances case. 105 instance_names = ["cf_instance1", "cf_instance2"] 106 choose_instance = ["cf_instance2"] 107 self.Patch(list_instance, "GetCFRemoteInstances", return_value=instance_names) 108 self.Patch(utils, "GetAnswerFromList", return_value=choose_instance) 109 expected_instance = "cf_instance2" 110 self.assertEqual(list_instance.ChooseOneRemoteInstance(cfg), expected_instance) 111 112 def testGetLocalInstancesByNames(self): 113 """test GetLocalInstancesByNames.""" 114 self.Patch( 115 instance, "GetLocalInstanceIdByName", 116 side_effect=lambda name: 1 if name == "local-instance-1" else None) 117 self.Patch(instance, "GetLocalInstanceConfig", 118 return_value="path1") 119 self.Patch(instance, "GetDefaultCuttlefishConfig", 120 return_value="path2") 121 mock_cf_ins = mock.Mock() 122 mock_cf_ins.name = "local-instance-1" 123 mock_get_cf = self.Patch(list_instance, 124 "_GetLocalCuttlefishInstances", 125 return_value=[mock_cf_ins]) 126 mock_gf_ins = mock.Mock() 127 mock_gf_ins.name = "local-goldfish-instance-1" 128 self.Patch(instance.LocalGoldfishInstance, "GetExistingInstances", 129 return_value=[mock_gf_ins]) 130 131 ins_list = list_instance.GetLocalInstancesByNames([ 132 mock_cf_ins.name, "local-instance-6", mock_gf_ins.name]) 133 self.assertEqual([mock_cf_ins, mock_gf_ins], ins_list) 134 mock_get_cf.assert_called_with([(1, "path1"), (1, "path2")]) 135 136 # pylint: disable=attribute-defined-outside-init 137 def testFilterInstancesByAdbPort(self): 138 """test FilterInstancesByAdbPort.""" 139 alive_instance1 = InstanceObject("alive_instance1") 140 alive_instance1.adb_port = 1111 141 alive_instance1.fullname = "device serial: 127.0.0.1:1111 alive_instance1" 142 expected_instance = [alive_instance1] 143 # Test to find instance by adb port number. 144 self.assertEqual( 145 expected_instance, 146 list_instance.FilterInstancesByAdbPort(expected_instance, 1111)) 147 # Test for instance can't be found by adb port number. 148 with self.assertRaises(errors.NoInstancesFound): 149 list_instance.FilterInstancesByAdbPort(expected_instance, 2222) 150 151 # pylint: disable=protected-access 152 def testGetLocalCuttlefishInstances(self): 153 """test _GetLocalCuttlefishInstances.""" 154 # Test getting two instance case 155 id_cfg_pairs = [(1, "fake_path1"), (2, "fake_path2")] 156 mock_isfile = self.Patch(list_instance.os.path, "isfile", 157 return_value=True) 158 159 mock_lock = mock.Mock() 160 mock_lock.Lock.return_value = True 161 self.Patch(instance, "GetLocalInstanceLock", return_value=mock_lock) 162 163 local_ins = mock.MagicMock() 164 local_ins.CvdStatus.return_value = True 165 self.Patch(instance, "GetCuttleFishLocalInstances", 166 return_value=[local_ins]) 167 168 ins_list = list_instance._GetLocalCuttlefishInstances(id_cfg_pairs) 169 self.assertEqual(2, len(ins_list)) 170 mock_isfile.assert_called() 171 local_ins.CvdStatus.assert_called() 172 self.assertEqual(2, mock_lock.Lock.call_count) 173 self.assertEqual(2, mock_lock.Unlock.call_count) 174 175 local_ins.CvdStatus.reset_mock() 176 mock_lock.Lock.reset_mock() 177 mock_lock.Lock.return_value = False 178 mock_lock.Unlock.reset_mock() 179 ins_list = list_instance._GetLocalCuttlefishInstances(id_cfg_pairs) 180 self.assertEqual(0, len(ins_list)) 181 local_ins.CvdStatus.assert_not_called() 182 self.assertEqual(2, mock_lock.Lock.call_count) 183 mock_lock.Unlock.assert_not_called() 184 185 mock_lock.Lock.reset_mock() 186 mock_lock.Lock.return_value = True 187 local_ins.CvdStatus.return_value = False 188 ins_list = list_instance._GetLocalCuttlefishInstances(id_cfg_pairs) 189 self.assertEqual(0, len(ins_list)) 190 self.assertEqual(2, mock_lock.Lock.call_count) 191 self.assertEqual(2, mock_lock.Unlock.call_count) 192 193 # pylint: disable=no-member 194 def testPrintInstancesDetails(self): 195 """test PrintInstancesDetails.""" 196 # Test instance Summary should be called if verbose 197 self.Patch(instance.Instance, "Summary") 198 cf_config = mock.MagicMock( 199 x_res=728, 200 y_res=728, 201 dpi=240, 202 instance_dir="fake_dir", 203 adb_ip_port="127.0.0.1:6520", 204 root_dir="root/cuttlefish_runtime" 205 ) 206 self.Patch(cvd_runtime_config, "CvdRuntimeConfig", 207 return_value=cf_config) 208 self.Patch(instance.LocalInstance, "_GetDevidInfoFromCvdStatus", 209 return_value=None) 210 211 ins = instance.LocalInstance("fake_cf_path") 212 list_instance.PrintInstancesDetails([ins], verbose=True) 213 instance.Instance.Summary.assert_called_once() 214 215 # Test Summary shouldn't be called if not verbose 216 self.Patch(instance.Instance, "Summary") 217 list_instance.PrintInstancesDetails([ins], verbose=False) 218 instance.Instance.Summary.assert_not_called() 219 220 # Test Summary shouldn't be called if no instance found. 221 list_instance.PrintInstancesDetails([], verbose=True) 222 instance.Instance.Summary.assert_not_called() 223 224 def testRun(self): 225 """test Run.""" 226 cfg = mock.MagicMock() 227 self.Patch(config, "GetAcloudConfig", return_value=cfg) 228 args = mock.MagicMock() 229 # local instance 230 args.local_only = True 231 args.verbose = False 232 self.Patch(utils, "IsSupportedPlatform", return_value=True) 233 self.Patch(list_instance, "PrintInstancesDetails") 234 self.Patch(instance, "GetAllLocalInstanceConfigs") 235 fake_local_ins1 = "local_ins1" 236 fake_local_ins2 = "local_ins2" 237 fake_local_gf_ins1 = "local_gf_ins1" 238 self.Patch(list_instance, "_GetLocalCuttlefishInstances", 239 return_value=[fake_local_ins1, fake_local_ins2]) 240 self.Patch(instance.LocalGoldfishInstance, "GetExistingInstances", 241 return_value=[fake_local_gf_ins1]) 242 list_instance.Run(args) 243 list_instance.PrintInstancesDetails.assert_called_with( 244 [fake_local_ins1, fake_local_ins2, fake_local_gf_ins1], False) 245 246 # remote instance 247 args.local_only = False 248 fake_remote_ins1= "remote_ins1" 249 fake_remote_ins2 = "remote_ins2" 250 self.Patch(list_instance, "GetRemoteInstances", 251 return_value=[fake_remote_ins1, fake_remote_ins2]) 252 list_instance.Run(args) 253 list_instance.PrintInstancesDetails.assert_called_with( 254 [fake_local_ins1, fake_local_ins2, fake_local_gf_ins1, 255 fake_remote_ins1, fake_remote_ins2], False) 256 257 def testGetRemoteInstances(self): 258 """test GetRemoteInstances.""" 259 fake_remote_ins1 = {"name": "fake_remote_ins1_name", 260 "creationTimestamp": "2021-01-14T13:00:00.000-07:00", 261 "status": "Active"} 262 fake_remote_ins2 = {"name": "fake_remote_ins2_name", 263 "creationTimestamp": "2021-01-14T13:00:00.000-07:00", 264 "status": "Active"} 265 gcompute_client.ComputeClient(None, None).ListInstances.return_value = [ 266 fake_remote_ins1, fake_remote_ins2] 267 self.assertEqual(list_instance.GetRemoteInstances(None)[0].name, 268 instance.RemoteInstance(fake_remote_ins1).name) 269 self.assertEqual(len(list_instance.GetRemoteInstances(None)), 2) 270 271 def testGetCFRemoteInstances(self): 272 """test GetCFRemoteInstances.""" 273 fake_remote_ins1 = {"name": "cf-fake_remote_ins1_name", 274 "avd_type": "cuttlefish", 275 "creationTimestamp": "2021-01-14T13:00:00.000-07:00", 276 "status": "Active"} 277 fake_remote_ins2 = {"name": "nonecf-fake_remote_ins2_name", 278 "avd_type": "goldfish", 279 "creationTimestamp": "2021-01-14T13:00:00.000-07:00", 280 "status": "Active"} 281 remote_ins_list = [instance.RemoteInstance(fake_remote_ins1), 282 instance.RemoteInstance(fake_remote_ins2)] 283 self.Patch(list_instance, "GetRemoteInstances", 284 return_value=remote_ins_list) 285 self.assertEqual(len(list_instance.GetCFRemoteInstances(None)), 1) 286 287 def testGetActiveCVD(self): 288 """test GetActiveCVD.""" 289 # get local instance config 290 self.Patch(instance, "GetLocalInstanceConfig", 291 return_value="fake_local_cfg_path") 292 fake_local_ins = mock.MagicMock() 293 self.Patch(instance, "LocalInstance", return_value=fake_local_ins) 294 fake_local_ins.CvdStatus.return_value = True 295 self.assertEqual(list_instance.GetActiveCVD(1), fake_local_ins) 296 297 fake_local_ins.CvdStatus.return_value = False 298 self.Patch(instance, "GetDefaultCuttlefishConfig", 299 return_value=None) 300 self.assertEqual(list_instance.GetActiveCVD(1), None) 301 302 # get default cf config 303 fake_local_ins.CvdStatus.return_value = True 304 self.Patch(instance, "GetLocalInstanceConfig", 305 return_value=None) 306 self.Patch(instance, "GetDefaultCuttlefishConfig", 307 return_value="fake_cf_config") 308 self.assertEqual(list_instance.GetActiveCVD(1), fake_local_ins) 309 self.assertEqual(list_instance.GetActiveCVD(2), None) 310 311 fake_local_ins.CvdStatus.return_value = False 312 self.assertEqual(list_instance.GetActiveCVD(1), None) 313 314 fake_local_ins.CvdStatus.return_value = True 315 self.Patch(instance, "GetDefaultCuttlefishConfig", 316 return_value=None) 317 self.assertEqual(list_instance.GetActiveCVD(1), None) 318 319 def testChooseInstances(self): 320 """test ChooseInstances.""" 321 self.Patch(list_instance, "GetLocalInstances", 322 return_value=["local_ins1", "local_ins2"]) 323 self.Patch(list_instance, "GetRemoteInstances", 324 return_value=["remote_ins1", "remote_ins2"]) 325 self.assertEqual(len(list_instance.ChooseInstances(None, True)), 4) 326 327 self.Patch(utils, "GetAnswerFromList", return_value=["remote_ins1"]) 328 choose_instance = ["remote_ins1"] 329 self.assertEqual(list_instance.ChooseInstances(None), choose_instance) 330 331 list_instance.GetLocalInstances.return_value = [] 332 list_instance.GetRemoteInstances.return_value = ["only_one_ins"] 333 self.assertEqual(list_instance.ChooseInstances(None), ["only_one_ins"]) 334 335 def testGetLocalInstanceLockByName(self): 336 """test GetLocalInstanceLockByName.""" 337 self.Patch(instance, "GetLocalInstanceIdByName", 338 return_value="local_ins_id") 339 self.Patch(instance, "GetLocalInstanceLock") 340 list_instance.GetLocalInstanceLockByName("query_name") 341 instance.GetLocalInstanceLock.assert_called_once() 342 343 instance.GetLocalInstanceIdByName.return_value = None 344 self.Patch(instance.LocalGoldfishInstance, 345 "GetIdByName", return_value="gf_ins_id") 346 self.Patch(instance.LocalGoldfishInstance, "GetLockById") 347 list_instance.GetLocalInstanceLockByName("query_name") 348 instance.LocalGoldfishInstance.GetLockById.assert_called_once() 349 350 self.Patch(instance.LocalGoldfishInstance, 351 "GetIdByName", return_value= None) 352 self.assertEqual( 353 list_instance.GetLocalInstanceLockByName("query_name"), None) 354 355 356if __name__ == "__main__": 357 unittest.main() 358