blob: 71e2d69e1a6a86216d1459d3a6b061f5efaa2e52 [file] [log] [blame]
Fang Deng69498c32017-03-02 14:29:30 -08001#!/usr/bin/env python
2#
3# Copyright 2016 - The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
Fang Deng69498c32017-03-02 14:29:30 -080016"""Tests for acloud.internal.lib.utils."""
17
Fang Deng26e4dc12018-03-04 19:01:59 -080018import errno
Fang Deng69498c32017-03-02 14:29:30 -080019import getpass
herbertxue07293a32018-11-05 20:40:11 +080020import grp
Fang Deng69498c32017-03-02 14:29:30 -080021import os
Fang Deng26e4dc12018-03-04 19:01:59 -080022import shutil
Fang Deng69498c32017-03-02 14:29:30 -080023import subprocess
Fang Deng26e4dc12018-03-04 19:01:59 -080024import tempfile
Fang Dengf24be082018-02-10 10:09:55 -080025import time
Sam Chiu7a477f52018-10-22 11:20:36 +080026import Tkinter
Fang Deng69498c32017-03-02 14:29:30 -080027
cylan0d77ae12018-05-18 08:36:48 +000028import unittest
Fang Deng69498c32017-03-02 14:29:30 -080029import mock
30
Fang Deng69498c32017-03-02 14:29:30 -080031from acloud.internal.lib import driver_test_lib
32from acloud.internal.lib import utils
33
Kevin Cheng358fb3e2018-11-13 14:05:54 -080034class FakeTkinter(object):
35 """Fake implementation of Tkinter.Tk()"""
36
37 def __init__(self, width=None, height=None):
38 self.width = width
39 self.height = height
40
41 # pylint: disable=invalid-name
42 def winfo_screenheight(self):
43 """Return the screen height."""
44 return self.height
45
46 # pylint: disable=invalid-name
47 def winfo_screenwidth(self):
48 """Return the screen width."""
49 return self.width
50
Fang Deng69498c32017-03-02 14:29:30 -080051
52class UtilsTest(driver_test_lib.BaseDriverTest):
cylan0d77ae12018-05-18 08:36:48 +000053 """Test Utils."""
Fang Deng69498c32017-03-02 14:29:30 -080054
cylan0d77ae12018-05-18 08:36:48 +000055 def TestTempDirSuccess(self):
56 """Test create a temp dir."""
57 self.Patch(os, "chmod")
58 self.Patch(tempfile, "mkdtemp", return_value="/tmp/tempdir")
59 self.Patch(shutil, "rmtree")
60 with utils.TempDir():
61 pass
62 # Verify.
63 tempfile.mkdtemp.assert_called_once() # pylint: disable=no-member
64 shutil.rmtree.assert_called_with("/tmp/tempdir") # pylint: disable=no-member
Fang Deng26e4dc12018-03-04 19:01:59 -080065
cylan0d77ae12018-05-18 08:36:48 +000066 def TestTempDirExceptionRaised(self):
67 """Test create a temp dir and exception is raised within with-clause."""
68 self.Patch(os, "chmod")
69 self.Patch(tempfile, "mkdtemp", return_value="/tmp/tempdir")
70 self.Patch(shutil, "rmtree")
Fang Deng26e4dc12018-03-04 19:01:59 -080071
cylan0d77ae12018-05-18 08:36:48 +000072 class ExpectedException(Exception):
73 """Expected exception."""
74 pass
Fang Deng26e4dc12018-03-04 19:01:59 -080075
cylan0d77ae12018-05-18 08:36:48 +000076 def _Call():
77 with utils.TempDir():
78 raise ExpectedException("Expected exception.")
Fang Deng26e4dc12018-03-04 19:01:59 -080079
cylan0d77ae12018-05-18 08:36:48 +000080 # Verify. ExpectedException should be raised.
81 self.assertRaises(ExpectedException, _Call)
82 tempfile.mkdtemp.assert_called_once() # pylint: disable=no-member
83 shutil.rmtree.assert_called_with("/tmp/tempdir") #pylint: disable=no-member
Fang Deng26e4dc12018-03-04 19:01:59 -080084
cylan0d77ae12018-05-18 08:36:48 +000085 def testTempDirWhenDeleteTempDirNoLongerExist(self): # pylint: disable=invalid-name
86 """Test create a temp dir and dir no longer exists during deletion."""
87 self.Patch(os, "chmod")
88 self.Patch(tempfile, "mkdtemp", return_value="/tmp/tempdir")
89 expected_error = EnvironmentError()
90 expected_error.errno = errno.ENOENT
91 self.Patch(shutil, "rmtree", side_effect=expected_error)
Fang Deng26e4dc12018-03-04 19:01:59 -080092
cylan0d77ae12018-05-18 08:36:48 +000093 def _Call():
94 with utils.TempDir():
95 pass
Fang Deng26e4dc12018-03-04 19:01:59 -080096
cylan0d77ae12018-05-18 08:36:48 +000097 # Verify no exception should be raised when rmtree raises
98 # EnvironmentError with errno.ENOENT, i.e.
99 # directory no longer exists.
100 _Call()
101 tempfile.mkdtemp.assert_called_once() #pylint: disable=no-member
102 shutil.rmtree.assert_called_with("/tmp/tempdir") #pylint: disable=no-member
Fang Deng26e4dc12018-03-04 19:01:59 -0800103
cylan0d77ae12018-05-18 08:36:48 +0000104 def testTempDirWhenDeleteEncounterError(self):
105 """Test create a temp dir and encoutered error during deletion."""
106 self.Patch(os, "chmod")
107 self.Patch(tempfile, "mkdtemp", return_value="/tmp/tempdir")
108 expected_error = OSError("Expected OS Error")
109 self.Patch(shutil, "rmtree", side_effect=expected_error)
Fang Deng26e4dc12018-03-04 19:01:59 -0800110
cylan0d77ae12018-05-18 08:36:48 +0000111 def _Call():
112 with utils.TempDir():
113 pass
Fang Deng26e4dc12018-03-04 19:01:59 -0800114
cylan0d77ae12018-05-18 08:36:48 +0000115 # Verify OSError should be raised.
116 self.assertRaises(OSError, _Call)
117 tempfile.mkdtemp.assert_called_once() #pylint: disable=no-member
118 shutil.rmtree.assert_called_with("/tmp/tempdir") #pylint: disable=no-member
Fang Deng26e4dc12018-03-04 19:01:59 -0800119
cylan0d77ae12018-05-18 08:36:48 +0000120 def testTempDirOrininalErrorRaised(self):
121 """Test original error is raised even if tmp dir deletion failed."""
122 self.Patch(os, "chmod")
123 self.Patch(tempfile, "mkdtemp", return_value="/tmp/tempdir")
124 expected_error = OSError("Expected OS Error")
125 self.Patch(shutil, "rmtree", side_effect=expected_error)
Fang Deng69498c32017-03-02 14:29:30 -0800126
cylan0d77ae12018-05-18 08:36:48 +0000127 class ExpectedException(Exception):
128 """Expected exception."""
129 pass
Fang Deng69498c32017-03-02 14:29:30 -0800130
cylan0d77ae12018-05-18 08:36:48 +0000131 def _Call():
132 with utils.TempDir():
133 raise ExpectedException("Expected Exception")
Fang Dengf24be082018-02-10 10:09:55 -0800134
cylan0d77ae12018-05-18 08:36:48 +0000135 # Verify.
136 # ExpectedException should be raised, and OSError
137 # should not be raised.
138 self.assertRaises(ExpectedException, _Call)
139 tempfile.mkdtemp.assert_called_once() #pylint: disable=no-member
140 shutil.rmtree.assert_called_with("/tmp/tempdir") #pylint: disable=no-member
Fang Dengf24be082018-02-10 10:09:55 -0800141
cylan0d77ae12018-05-18 08:36:48 +0000142 def testCreateSshKeyPairKeyAlreadyExists(self): #pylint: disable=invalid-name
143 """Test when the key pair already exists."""
144 public_key = "/fake/public_key"
145 private_key = "/fake/private_key"
cylan4f73c1f2018-07-19 16:40:31 +0800146 self.Patch(os.path, "exists", side_effect=[True, True])
cylan0d77ae12018-05-18 08:36:48 +0000147 self.Patch(subprocess, "check_call")
cylan4f73c1f2018-07-19 16:40:31 +0800148 self.Patch(os, "makedirs", return_value=True)
cylan0d77ae12018-05-18 08:36:48 +0000149 utils.CreateSshKeyPairIfNotExist(private_key, public_key)
150 self.assertEqual(subprocess.check_call.call_count, 0) #pylint: disable=no-member
Fang Dengf24be082018-02-10 10:09:55 -0800151
cylan0d77ae12018-05-18 08:36:48 +0000152 def testCreateSshKeyPairKeyAreCreated(self):
153 """Test when the key pair created."""
154 public_key = "/fake/public_key"
155 private_key = "/fake/private_key"
156 self.Patch(os.path, "exists", return_value=False)
cylan4f73c1f2018-07-19 16:40:31 +0800157 self.Patch(os, "makedirs", return_value=True)
cylan0d77ae12018-05-18 08:36:48 +0000158 self.Patch(subprocess, "check_call")
159 self.Patch(os, "rename")
160 utils.CreateSshKeyPairIfNotExist(private_key, public_key)
161 self.assertEqual(subprocess.check_call.call_count, 1) #pylint: disable=no-member
162 subprocess.check_call.assert_called_with( #pylint: disable=no-member
163 utils.SSH_KEYGEN_CMD +
164 ["-C", getpass.getuser(), "-f", private_key],
165 stdout=mock.ANY,
166 stderr=mock.ANY)
Fang Dengf24be082018-02-10 10:09:55 -0800167
cylan4f73c1f2018-07-19 16:40:31 +0800168 def testCreatePublicKeyAreCreated(self):
169 """Test when the PublicKey created."""
170 public_key = "/fake/public_key"
171 private_key = "/fake/private_key"
172 self.Patch(os.path, "exists", side_effect=[False, True, True])
173 self.Patch(os, "makedirs", return_value=True)
174 mock_open = mock.mock_open(read_data=public_key)
cylan4f73c1f2018-07-19 16:40:31 +0800175 self.Patch(subprocess, "check_output")
176 self.Patch(os, "rename")
Kevin Chengda4f07a2018-06-26 10:25:05 -0700177 with mock.patch("__builtin__.open", mock_open):
178 utils.CreateSshKeyPairIfNotExist(private_key, public_key)
cylan4f73c1f2018-07-19 16:40:31 +0800179 self.assertEqual(subprocess.check_output.call_count, 1) #pylint: disable=no-member
180 subprocess.check_output.assert_called_with( #pylint: disable=no-member
181 utils.SSH_KEYGEN_PUB_CMD +["-f", private_key])
182
cylan0d77ae12018-05-18 08:36:48 +0000183 def TestRetryOnException(self):
184 """Test Retry."""
Fang Dengf24be082018-02-10 10:09:55 -0800185
cylan0d77ae12018-05-18 08:36:48 +0000186 def _IsValueError(exc):
187 return isinstance(exc, ValueError)
Fang Dengf24be082018-02-10 10:09:55 -0800188
cylan0d77ae12018-05-18 08:36:48 +0000189 num_retry = 5
Fang Dengf24be082018-02-10 10:09:55 -0800190
cylan0d77ae12018-05-18 08:36:48 +0000191 @utils.RetryOnException(_IsValueError, num_retry)
192 def _RaiseAndRetry(sentinel):
193 sentinel.alert()
194 raise ValueError("Fake error.")
195
196 sentinel = mock.MagicMock()
197 self.assertRaises(ValueError, _RaiseAndRetry, sentinel)
198 self.assertEqual(1 + num_retry, sentinel.alert.call_count)
199
200 def testRetryExceptionType(self):
201 """Test RetryExceptionType function."""
202
203 def _RaiseAndRetry(sentinel):
204 sentinel.alert()
205 raise ValueError("Fake error.")
206
207 num_retry = 5
208 sentinel = mock.MagicMock()
209 self.assertRaises(
210 ValueError,
211 utils.RetryExceptionType, (KeyError, ValueError),
212 num_retry,
213 _RaiseAndRetry,
Kevin Chengd25feee2018-05-24 10:15:20 -0700214 0, # sleep_multiplier
215 1, # retry_backoff_factor
cylan0d77ae12018-05-18 08:36:48 +0000216 sentinel=sentinel)
217 self.assertEqual(1 + num_retry, sentinel.alert.call_count)
218
219 def testRetry(self):
220 """Test Retry."""
Kevin Chengd25feee2018-05-24 10:15:20 -0700221 mock_sleep = self.Patch(time, "sleep")
cylan0d77ae12018-05-18 08:36:48 +0000222
223 def _RaiseAndRetry(sentinel):
224 sentinel.alert()
225 raise ValueError("Fake error.")
226
227 num_retry = 5
228 sentinel = mock.MagicMock()
229 self.assertRaises(
230 ValueError,
231 utils.RetryExceptionType, (ValueError, KeyError),
232 num_retry,
233 _RaiseAndRetry,
Kevin Chengd25feee2018-05-24 10:15:20 -0700234 1, # sleep_multiplier
235 2, # retry_backoff_factor
cylan0d77ae12018-05-18 08:36:48 +0000236 sentinel=sentinel)
237
238 self.assertEqual(1 + num_retry, sentinel.alert.call_count)
Kevin Chengd25feee2018-05-24 10:15:20 -0700239 mock_sleep.assert_has_calls(
cylan0d77ae12018-05-18 08:36:48 +0000240 [
241 mock.call(1),
242 mock.call(2),
243 mock.call(4),
244 mock.call(8),
245 mock.call(16)
246 ])
Fang Dengf24be082018-02-10 10:09:55 -0800247
Kevin Chengeb85e862018-10-09 15:35:13 -0700248 @mock.patch("__builtin__.raw_input")
249 def testGetAnswerFromList(self, mock_raw_input):
250 """Test GetAnswerFromList."""
251 answer_list = ["image1.zip", "image2.zip", "image3.zip"]
252 mock_raw_input.return_value = 0
253 with self.assertRaises(SystemExit):
254 utils.GetAnswerFromList(answer_list)
255 mock_raw_input.side_effect = [1, 2, 3, 1]
256 self.assertEqual(utils.GetAnswerFromList(answer_list),
257 ["image1.zip"])
258 self.assertEqual(utils.GetAnswerFromList(answer_list),
259 ["image2.zip"])
260 self.assertEqual(utils.GetAnswerFromList(answer_list),
261 ["image3.zip"])
262 self.assertEqual(utils.GetAnswerFromList(answer_list,
263 enable_choose_all=True),
264 answer_list)
265
Kevin Cheng358fb3e2018-11-13 14:05:54 -0800266 @mock.patch.object(Tkinter, "Tk")
267 def testCalculateVNCScreenRatio(self, mock_tk):
Sam Chiu7a477f52018-10-22 11:20:36 +0800268 """Test Calculating the scale ratio of VNC display."""
Sam Chiu7a477f52018-10-22 11:20:36 +0800269 # Get scale-down ratio if screen height is smaller than AVD height.
Kevin Cheng358fb3e2018-11-13 14:05:54 -0800270 mock_tk.return_value = FakeTkinter(height=800, width=1200)
Sam Chiu7a477f52018-10-22 11:20:36 +0800271 avd_h = 1920
272 avd_w = 1080
273 self.assertEqual(utils.CalculateVNCScreenRatio(avd_w, avd_h), 0.4)
274
275 # Get scale-down ratio if screen width is smaller than AVD width.
Kevin Cheng358fb3e2018-11-13 14:05:54 -0800276 mock_tk.return_value = FakeTkinter(height=800, width=1200)
Sam Chiu7a477f52018-10-22 11:20:36 +0800277 avd_h = 900
278 avd_w = 1920
279 self.assertEqual(utils.CalculateVNCScreenRatio(avd_w, avd_h), 0.6)
280
281 # Scale ratio = 1 if screen is larger than AVD.
Kevin Cheng358fb3e2018-11-13 14:05:54 -0800282 mock_tk.return_value = FakeTkinter(height=1080, width=1920)
Sam Chiu7a477f52018-10-22 11:20:36 +0800283 avd_h = 800
284 avd_w = 1280
285 self.assertEqual(utils.CalculateVNCScreenRatio(avd_w, avd_h), 1)
286
287 # Get the scale if ratio of width is smaller than the
288 # ratio of height.
Kevin Cheng358fb3e2018-11-13 14:05:54 -0800289 mock_tk.return_value = FakeTkinter(height=1200, width=800)
Sam Chiu7a477f52018-10-22 11:20:36 +0800290 avd_h = 1920
291 avd_w = 1080
292 self.assertEqual(utils.CalculateVNCScreenRatio(avd_w, avd_h), 0.6)
293
herbertxue07293a32018-11-05 20:40:11 +0800294 # pylint: disable=protected-access
295 def testCheckUserInGroups(self):
296 """Test CheckUserInGroups."""
297 self.Patch(os, "getgroups", return_value=[1, 2, 3])
298 gr1 = mock.MagicMock()
299 gr1.gr_name = "fake_gr_1"
300 gr2 = mock.MagicMock()
301 gr2.gr_name = "fake_gr_2"
302 gr3 = mock.MagicMock()
303 gr3.gr_name = "fake_gr_3"
304 self.Patch(grp, "getgrgid", side_effect=[gr1, gr2, gr3])
305
306 # User in all required groups should return true.
307 self.assertTrue(
308 utils.CheckUserInGroups(
309 ["fake_gr_1", "fake_gr_2"]))
310
311 # User not in all required groups should return False.
312 self.Patch(grp, "getgrgid", side_effect=[gr1, gr2, gr3])
313 self.assertFalse(
314 utils.CheckUserInGroups(
315 ["fake_gr_1", "fake_gr_4"]))
316
317 @mock.patch.object(utils, "CheckUserInGroups")
318 def testAddUserGroupsToCmd(self, mock_user_group):
319 """Test AddUserGroupsToCmd."""
320 command = "test_command"
321 groups = ["group1", "group2"]
322 # Don't add user group in command
323 mock_user_group.return_value = True
324 expected_value = "test_command"
325 self.assertEqual(expected_value, utils.AddUserGroupsToCmd(command,
326 groups))
327
328 # Add user group in command
329 mock_user_group.return_value = False
330 expected_value = "sg group1 <<EOF\nsg group2\ntest_command\nEOF"
331 self.assertEqual(expected_value, utils.AddUserGroupsToCmd(command,
332 groups))
333
Fang Deng69498c32017-03-02 14:29:30 -0800334
335if __name__ == "__main__":
336 unittest.main()