blob: 60bd34d8f51171e7c5274ea2b7a40662dd9633cc [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
20import os
Fang Deng26e4dc12018-03-04 19:01:59 -080021import shutil
Fang Deng69498c32017-03-02 14:29:30 -080022import subprocess
Fang Deng26e4dc12018-03-04 19:01:59 -080023import tempfile
Fang Dengf24be082018-02-10 10:09:55 -080024import time
Sam Chiu7a477f52018-10-22 11:20:36 +080025import Tkinter
Fang Deng69498c32017-03-02 14:29:30 -080026
cylan0d77ae12018-05-18 08:36:48 +000027import unittest
Fang Deng69498c32017-03-02 14:29:30 -080028import mock
29
Fang Deng69498c32017-03-02 14:29:30 -080030from acloud.internal.lib import driver_test_lib
31from acloud.internal.lib import utils
32
Kevin Cheng358fb3e2018-11-13 14:05:54 -080033class FakeTkinter(object):
34 """Fake implementation of Tkinter.Tk()"""
35
36 def __init__(self, width=None, height=None):
37 self.width = width
38 self.height = height
39
40 # pylint: disable=invalid-name
41 def winfo_screenheight(self):
42 """Return the screen height."""
43 return self.height
44
45 # pylint: disable=invalid-name
46 def winfo_screenwidth(self):
47 """Return the screen width."""
48 return self.width
49
Fang Deng69498c32017-03-02 14:29:30 -080050
51class UtilsTest(driver_test_lib.BaseDriverTest):
cylan0d77ae12018-05-18 08:36:48 +000052 """Test Utils."""
Fang Deng69498c32017-03-02 14:29:30 -080053
cylan0d77ae12018-05-18 08:36:48 +000054 def TestTempDirSuccess(self):
55 """Test create a temp dir."""
56 self.Patch(os, "chmod")
57 self.Patch(tempfile, "mkdtemp", return_value="/tmp/tempdir")
58 self.Patch(shutil, "rmtree")
59 with utils.TempDir():
60 pass
61 # Verify.
62 tempfile.mkdtemp.assert_called_once() # pylint: disable=no-member
63 shutil.rmtree.assert_called_with("/tmp/tempdir") # pylint: disable=no-member
Fang Deng26e4dc12018-03-04 19:01:59 -080064
cylan0d77ae12018-05-18 08:36:48 +000065 def TestTempDirExceptionRaised(self):
66 """Test create a temp dir and exception is raised within with-clause."""
67 self.Patch(os, "chmod")
68 self.Patch(tempfile, "mkdtemp", return_value="/tmp/tempdir")
69 self.Patch(shutil, "rmtree")
Fang Deng26e4dc12018-03-04 19:01:59 -080070
cylan0d77ae12018-05-18 08:36:48 +000071 class ExpectedException(Exception):
72 """Expected exception."""
73 pass
Fang Deng26e4dc12018-03-04 19:01:59 -080074
cylan0d77ae12018-05-18 08:36:48 +000075 def _Call():
76 with utils.TempDir():
77 raise ExpectedException("Expected exception.")
Fang Deng26e4dc12018-03-04 19:01:59 -080078
cylan0d77ae12018-05-18 08:36:48 +000079 # Verify. ExpectedException should be raised.
80 self.assertRaises(ExpectedException, _Call)
81 tempfile.mkdtemp.assert_called_once() # pylint: disable=no-member
82 shutil.rmtree.assert_called_with("/tmp/tempdir") #pylint: disable=no-member
Fang Deng26e4dc12018-03-04 19:01:59 -080083
cylan0d77ae12018-05-18 08:36:48 +000084 def testTempDirWhenDeleteTempDirNoLongerExist(self): # pylint: disable=invalid-name
85 """Test create a temp dir and dir no longer exists during deletion."""
86 self.Patch(os, "chmod")
87 self.Patch(tempfile, "mkdtemp", return_value="/tmp/tempdir")
88 expected_error = EnvironmentError()
89 expected_error.errno = errno.ENOENT
90 self.Patch(shutil, "rmtree", side_effect=expected_error)
Fang Deng26e4dc12018-03-04 19:01:59 -080091
cylan0d77ae12018-05-18 08:36:48 +000092 def _Call():
93 with utils.TempDir():
94 pass
Fang Deng26e4dc12018-03-04 19:01:59 -080095
cylan0d77ae12018-05-18 08:36:48 +000096 # Verify no exception should be raised when rmtree raises
97 # EnvironmentError with errno.ENOENT, i.e.
98 # directory no longer exists.
99 _Call()
100 tempfile.mkdtemp.assert_called_once() #pylint: disable=no-member
101 shutil.rmtree.assert_called_with("/tmp/tempdir") #pylint: disable=no-member
Fang Deng26e4dc12018-03-04 19:01:59 -0800102
cylan0d77ae12018-05-18 08:36:48 +0000103 def testTempDirWhenDeleteEncounterError(self):
104 """Test create a temp dir and encoutered error during deletion."""
105 self.Patch(os, "chmod")
106 self.Patch(tempfile, "mkdtemp", return_value="/tmp/tempdir")
107 expected_error = OSError("Expected OS Error")
108 self.Patch(shutil, "rmtree", side_effect=expected_error)
Fang Deng26e4dc12018-03-04 19:01:59 -0800109
cylan0d77ae12018-05-18 08:36:48 +0000110 def _Call():
111 with utils.TempDir():
112 pass
Fang Deng26e4dc12018-03-04 19:01:59 -0800113
cylan0d77ae12018-05-18 08:36:48 +0000114 # Verify OSError should be raised.
115 self.assertRaises(OSError, _Call)
116 tempfile.mkdtemp.assert_called_once() #pylint: disable=no-member
117 shutil.rmtree.assert_called_with("/tmp/tempdir") #pylint: disable=no-member
Fang Deng26e4dc12018-03-04 19:01:59 -0800118
cylan0d77ae12018-05-18 08:36:48 +0000119 def testTempDirOrininalErrorRaised(self):
120 """Test original error is raised even if tmp dir deletion failed."""
121 self.Patch(os, "chmod")
122 self.Patch(tempfile, "mkdtemp", return_value="/tmp/tempdir")
123 expected_error = OSError("Expected OS Error")
124 self.Patch(shutil, "rmtree", side_effect=expected_error)
Fang Deng69498c32017-03-02 14:29:30 -0800125
cylan0d77ae12018-05-18 08:36:48 +0000126 class ExpectedException(Exception):
127 """Expected exception."""
128 pass
Fang Deng69498c32017-03-02 14:29:30 -0800129
cylan0d77ae12018-05-18 08:36:48 +0000130 def _Call():
131 with utils.TempDir():
132 raise ExpectedException("Expected Exception")
Fang Dengf24be082018-02-10 10:09:55 -0800133
cylan0d77ae12018-05-18 08:36:48 +0000134 # Verify.
135 # ExpectedException should be raised, and OSError
136 # should not be raised.
137 self.assertRaises(ExpectedException, _Call)
138 tempfile.mkdtemp.assert_called_once() #pylint: disable=no-member
139 shutil.rmtree.assert_called_with("/tmp/tempdir") #pylint: disable=no-member
Fang Dengf24be082018-02-10 10:09:55 -0800140
cylan0d77ae12018-05-18 08:36:48 +0000141 def testCreateSshKeyPairKeyAlreadyExists(self): #pylint: disable=invalid-name
142 """Test when the key pair already exists."""
143 public_key = "/fake/public_key"
144 private_key = "/fake/private_key"
cylan4f73c1f2018-07-19 16:40:31 +0800145 self.Patch(os.path, "exists", side_effect=[True, True])
cylan0d77ae12018-05-18 08:36:48 +0000146 self.Patch(subprocess, "check_call")
cylan4f73c1f2018-07-19 16:40:31 +0800147 self.Patch(os, "makedirs", return_value=True)
cylan0d77ae12018-05-18 08:36:48 +0000148 utils.CreateSshKeyPairIfNotExist(private_key, public_key)
149 self.assertEqual(subprocess.check_call.call_count, 0) #pylint: disable=no-member
Fang Dengf24be082018-02-10 10:09:55 -0800150
cylan0d77ae12018-05-18 08:36:48 +0000151 def testCreateSshKeyPairKeyAreCreated(self):
152 """Test when the key pair created."""
153 public_key = "/fake/public_key"
154 private_key = "/fake/private_key"
155 self.Patch(os.path, "exists", return_value=False)
cylan4f73c1f2018-07-19 16:40:31 +0800156 self.Patch(os, "makedirs", return_value=True)
cylan0d77ae12018-05-18 08:36:48 +0000157 self.Patch(subprocess, "check_call")
158 self.Patch(os, "rename")
159 utils.CreateSshKeyPairIfNotExist(private_key, public_key)
160 self.assertEqual(subprocess.check_call.call_count, 1) #pylint: disable=no-member
161 subprocess.check_call.assert_called_with( #pylint: disable=no-member
162 utils.SSH_KEYGEN_CMD +
163 ["-C", getpass.getuser(), "-f", private_key],
164 stdout=mock.ANY,
165 stderr=mock.ANY)
Fang Dengf24be082018-02-10 10:09:55 -0800166
cylan4f73c1f2018-07-19 16:40:31 +0800167 def testCreatePublicKeyAreCreated(self):
168 """Test when the PublicKey created."""
169 public_key = "/fake/public_key"
170 private_key = "/fake/private_key"
171 self.Patch(os.path, "exists", side_effect=[False, True, True])
172 self.Patch(os, "makedirs", return_value=True)
173 mock_open = mock.mock_open(read_data=public_key)
cylan4f73c1f2018-07-19 16:40:31 +0800174 self.Patch(subprocess, "check_output")
175 self.Patch(os, "rename")
Kevin Chengda4f07a2018-06-26 10:25:05 -0700176 with mock.patch("__builtin__.open", mock_open):
177 utils.CreateSshKeyPairIfNotExist(private_key, public_key)
cylan4f73c1f2018-07-19 16:40:31 +0800178 self.assertEqual(subprocess.check_output.call_count, 1) #pylint: disable=no-member
179 subprocess.check_output.assert_called_with( #pylint: disable=no-member
180 utils.SSH_KEYGEN_PUB_CMD +["-f", private_key])
181
cylan0d77ae12018-05-18 08:36:48 +0000182 def TestRetryOnException(self):
183 """Test Retry."""
Fang Dengf24be082018-02-10 10:09:55 -0800184
cylan0d77ae12018-05-18 08:36:48 +0000185 def _IsValueError(exc):
186 return isinstance(exc, ValueError)
Fang Dengf24be082018-02-10 10:09:55 -0800187
cylan0d77ae12018-05-18 08:36:48 +0000188 num_retry = 5
Fang Dengf24be082018-02-10 10:09:55 -0800189
cylan0d77ae12018-05-18 08:36:48 +0000190 @utils.RetryOnException(_IsValueError, num_retry)
191 def _RaiseAndRetry(sentinel):
192 sentinel.alert()
193 raise ValueError("Fake error.")
194
195 sentinel = mock.MagicMock()
196 self.assertRaises(ValueError, _RaiseAndRetry, sentinel)
197 self.assertEqual(1 + num_retry, sentinel.alert.call_count)
198
199 def testRetryExceptionType(self):
200 """Test RetryExceptionType function."""
201
202 def _RaiseAndRetry(sentinel):
203 sentinel.alert()
204 raise ValueError("Fake error.")
205
206 num_retry = 5
207 sentinel = mock.MagicMock()
208 self.assertRaises(
209 ValueError,
210 utils.RetryExceptionType, (KeyError, ValueError),
211 num_retry,
212 _RaiseAndRetry,
Kevin Chengd25feee2018-05-24 10:15:20 -0700213 0, # sleep_multiplier
214 1, # retry_backoff_factor
cylan0d77ae12018-05-18 08:36:48 +0000215 sentinel=sentinel)
216 self.assertEqual(1 + num_retry, sentinel.alert.call_count)
217
218 def testRetry(self):
219 """Test Retry."""
Kevin Chengd25feee2018-05-24 10:15:20 -0700220 mock_sleep = self.Patch(time, "sleep")
cylan0d77ae12018-05-18 08:36:48 +0000221
222 def _RaiseAndRetry(sentinel):
223 sentinel.alert()
224 raise ValueError("Fake error.")
225
226 num_retry = 5
227 sentinel = mock.MagicMock()
228 self.assertRaises(
229 ValueError,
230 utils.RetryExceptionType, (ValueError, KeyError),
231 num_retry,
232 _RaiseAndRetry,
Kevin Chengd25feee2018-05-24 10:15:20 -0700233 1, # sleep_multiplier
234 2, # retry_backoff_factor
cylan0d77ae12018-05-18 08:36:48 +0000235 sentinel=sentinel)
236
237 self.assertEqual(1 + num_retry, sentinel.alert.call_count)
Kevin Chengd25feee2018-05-24 10:15:20 -0700238 mock_sleep.assert_has_calls(
cylan0d77ae12018-05-18 08:36:48 +0000239 [
240 mock.call(1),
241 mock.call(2),
242 mock.call(4),
243 mock.call(8),
244 mock.call(16)
245 ])
Fang Dengf24be082018-02-10 10:09:55 -0800246
Kevin Chengeb85e862018-10-09 15:35:13 -0700247 @mock.patch("__builtin__.raw_input")
248 def testGetAnswerFromList(self, mock_raw_input):
249 """Test GetAnswerFromList."""
250 answer_list = ["image1.zip", "image2.zip", "image3.zip"]
251 mock_raw_input.return_value = 0
252 with self.assertRaises(SystemExit):
253 utils.GetAnswerFromList(answer_list)
254 mock_raw_input.side_effect = [1, 2, 3, 1]
255 self.assertEqual(utils.GetAnswerFromList(answer_list),
256 ["image1.zip"])
257 self.assertEqual(utils.GetAnswerFromList(answer_list),
258 ["image2.zip"])
259 self.assertEqual(utils.GetAnswerFromList(answer_list),
260 ["image3.zip"])
261 self.assertEqual(utils.GetAnswerFromList(answer_list,
262 enable_choose_all=True),
263 answer_list)
264
Kevin Cheng358fb3e2018-11-13 14:05:54 -0800265 @mock.patch.object(Tkinter, "Tk")
266 def testCalculateVNCScreenRatio(self, mock_tk):
Sam Chiu7a477f52018-10-22 11:20:36 +0800267 """Test Calculating the scale ratio of VNC display."""
Sam Chiu7a477f52018-10-22 11:20:36 +0800268 # Get scale-down ratio if screen height is smaller than AVD height.
Kevin Cheng358fb3e2018-11-13 14:05:54 -0800269 mock_tk.return_value = FakeTkinter(height=800, width=1200)
Sam Chiu7a477f52018-10-22 11:20:36 +0800270 avd_h = 1920
271 avd_w = 1080
272 self.assertEqual(utils.CalculateVNCScreenRatio(avd_w, avd_h), 0.4)
273
274 # Get scale-down ratio if screen width is smaller than AVD width.
Kevin Cheng358fb3e2018-11-13 14:05:54 -0800275 mock_tk.return_value = FakeTkinter(height=800, width=1200)
Sam Chiu7a477f52018-10-22 11:20:36 +0800276 avd_h = 900
277 avd_w = 1920
278 self.assertEqual(utils.CalculateVNCScreenRatio(avd_w, avd_h), 0.6)
279
280 # Scale ratio = 1 if screen is larger than AVD.
Kevin Cheng358fb3e2018-11-13 14:05:54 -0800281 mock_tk.return_value = FakeTkinter(height=1080, width=1920)
Sam Chiu7a477f52018-10-22 11:20:36 +0800282 avd_h = 800
283 avd_w = 1280
284 self.assertEqual(utils.CalculateVNCScreenRatio(avd_w, avd_h), 1)
285
286 # Get the scale if ratio of width is smaller than the
287 # ratio of height.
Kevin Cheng358fb3e2018-11-13 14:05:54 -0800288 mock_tk.return_value = FakeTkinter(height=1200, width=800)
Sam Chiu7a477f52018-10-22 11:20:36 +0800289 avd_h = 1920
290 avd_w = 1080
291 self.assertEqual(utils.CalculateVNCScreenRatio(avd_w, avd_h), 0.6)
292
Fang Deng69498c32017-03-02 14:29:30 -0800293
294if __name__ == "__main__":
295 unittest.main()