blob: 02cf8aa9bdf6c63b1c58282941771a31896b264d [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
Fang Deng69498c32017-03-02 14:29:30 -080025
cylan0d77ae12018-05-18 08:36:48 +000026import unittest
Fang Deng69498c32017-03-02 14:29:30 -080027import mock
28
Fang Deng69498c32017-03-02 14:29:30 -080029from acloud.internal.lib import driver_test_lib
30from acloud.internal.lib import utils
31
32
33class UtilsTest(driver_test_lib.BaseDriverTest):
cylan0d77ae12018-05-18 08:36:48 +000034 """Test Utils."""
Fang Deng69498c32017-03-02 14:29:30 -080035
cylan0d77ae12018-05-18 08:36:48 +000036 def TestTempDirSuccess(self):
37 """Test create a temp dir."""
38 self.Patch(os, "chmod")
39 self.Patch(tempfile, "mkdtemp", return_value="/tmp/tempdir")
40 self.Patch(shutil, "rmtree")
41 with utils.TempDir():
42 pass
43 # Verify.
44 tempfile.mkdtemp.assert_called_once() # pylint: disable=no-member
45 shutil.rmtree.assert_called_with("/tmp/tempdir") # pylint: disable=no-member
Fang Deng26e4dc12018-03-04 19:01:59 -080046
cylan0d77ae12018-05-18 08:36:48 +000047 def TestTempDirExceptionRaised(self):
48 """Test create a temp dir and exception is raised within with-clause."""
49 self.Patch(os, "chmod")
50 self.Patch(tempfile, "mkdtemp", return_value="/tmp/tempdir")
51 self.Patch(shutil, "rmtree")
Fang Deng26e4dc12018-03-04 19:01:59 -080052
cylan0d77ae12018-05-18 08:36:48 +000053 class ExpectedException(Exception):
54 """Expected exception."""
55 pass
Fang Deng26e4dc12018-03-04 19:01:59 -080056
cylan0d77ae12018-05-18 08:36:48 +000057 def _Call():
58 with utils.TempDir():
59 raise ExpectedException("Expected exception.")
Fang Deng26e4dc12018-03-04 19:01:59 -080060
cylan0d77ae12018-05-18 08:36:48 +000061 # Verify. ExpectedException should be raised.
62 self.assertRaises(ExpectedException, _Call)
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 testTempDirWhenDeleteTempDirNoLongerExist(self): # pylint: disable=invalid-name
67 """Test create a temp dir and dir no longer exists during deletion."""
68 self.Patch(os, "chmod")
69 self.Patch(tempfile, "mkdtemp", return_value="/tmp/tempdir")
70 expected_error = EnvironmentError()
71 expected_error.errno = errno.ENOENT
72 self.Patch(shutil, "rmtree", side_effect=expected_error)
Fang Deng26e4dc12018-03-04 19:01:59 -080073
cylan0d77ae12018-05-18 08:36:48 +000074 def _Call():
75 with utils.TempDir():
76 pass
Fang Deng26e4dc12018-03-04 19:01:59 -080077
cylan0d77ae12018-05-18 08:36:48 +000078 # Verify no exception should be raised when rmtree raises
79 # EnvironmentError with errno.ENOENT, i.e.
80 # directory no longer exists.
81 _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 testTempDirWhenDeleteEncounterError(self):
86 """Test create a temp dir and encoutered error during deletion."""
87 self.Patch(os, "chmod")
88 self.Patch(tempfile, "mkdtemp", return_value="/tmp/tempdir")
89 expected_error = OSError("Expected OS Error")
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 OSError should be raised.
97 self.assertRaises(OSError, _Call)
98 tempfile.mkdtemp.assert_called_once() #pylint: disable=no-member
99 shutil.rmtree.assert_called_with("/tmp/tempdir") #pylint: disable=no-member
Fang Deng26e4dc12018-03-04 19:01:59 -0800100
cylan0d77ae12018-05-18 08:36:48 +0000101 def testTempDirOrininalErrorRaised(self):
102 """Test original error is raised even if tmp dir deletion failed."""
103 self.Patch(os, "chmod")
104 self.Patch(tempfile, "mkdtemp", return_value="/tmp/tempdir")
105 expected_error = OSError("Expected OS Error")
106 self.Patch(shutil, "rmtree", side_effect=expected_error)
Fang Deng69498c32017-03-02 14:29:30 -0800107
cylan0d77ae12018-05-18 08:36:48 +0000108 class ExpectedException(Exception):
109 """Expected exception."""
110 pass
Fang Deng69498c32017-03-02 14:29:30 -0800111
cylan0d77ae12018-05-18 08:36:48 +0000112 def _Call():
113 with utils.TempDir():
114 raise ExpectedException("Expected Exception")
Fang Dengf24be082018-02-10 10:09:55 -0800115
cylan0d77ae12018-05-18 08:36:48 +0000116 # Verify.
117 # ExpectedException should be raised, and OSError
118 # should not be raised.
119 self.assertRaises(ExpectedException, _Call)
120 tempfile.mkdtemp.assert_called_once() #pylint: disable=no-member
121 shutil.rmtree.assert_called_with("/tmp/tempdir") #pylint: disable=no-member
Fang Dengf24be082018-02-10 10:09:55 -0800122
cylan0d77ae12018-05-18 08:36:48 +0000123 def testCreateSshKeyPairKeyAlreadyExists(self): #pylint: disable=invalid-name
124 """Test when the key pair already exists."""
125 public_key = "/fake/public_key"
126 private_key = "/fake/private_key"
cylan4f73c1f2018-07-19 16:40:31 +0800127 self.Patch(os.path, "exists", side_effect=[True, True])
cylan0d77ae12018-05-18 08:36:48 +0000128 self.Patch(subprocess, "check_call")
cylan4f73c1f2018-07-19 16:40:31 +0800129 self.Patch(os, "makedirs", return_value=True)
cylan0d77ae12018-05-18 08:36:48 +0000130 utils.CreateSshKeyPairIfNotExist(private_key, public_key)
131 self.assertEqual(subprocess.check_call.call_count, 0) #pylint: disable=no-member
Fang Dengf24be082018-02-10 10:09:55 -0800132
cylan0d77ae12018-05-18 08:36:48 +0000133 def testCreateSshKeyPairKeyAreCreated(self):
134 """Test when the key pair created."""
135 public_key = "/fake/public_key"
136 private_key = "/fake/private_key"
137 self.Patch(os.path, "exists", return_value=False)
cylan4f73c1f2018-07-19 16:40:31 +0800138 self.Patch(os, "makedirs", return_value=True)
cylan0d77ae12018-05-18 08:36:48 +0000139 self.Patch(subprocess, "check_call")
140 self.Patch(os, "rename")
141 utils.CreateSshKeyPairIfNotExist(private_key, public_key)
142 self.assertEqual(subprocess.check_call.call_count, 1) #pylint: disable=no-member
143 subprocess.check_call.assert_called_with( #pylint: disable=no-member
144 utils.SSH_KEYGEN_CMD +
145 ["-C", getpass.getuser(), "-f", private_key],
146 stdout=mock.ANY,
147 stderr=mock.ANY)
Fang Dengf24be082018-02-10 10:09:55 -0800148
cylan4f73c1f2018-07-19 16:40:31 +0800149 def testCreatePublicKeyAreCreated(self):
150 """Test when the PublicKey created."""
151 public_key = "/fake/public_key"
152 private_key = "/fake/private_key"
153 self.Patch(os.path, "exists", side_effect=[False, True, True])
154 self.Patch(os, "makedirs", return_value=True)
155 mock_open = mock.mock_open(read_data=public_key)
156 self.Patch(__builtins__, "open", mock_open, create=True)
157 self.Patch(subprocess, "check_output")
158 self.Patch(os, "rename")
159 utils.CreateSshKeyPairIfNotExist(private_key, public_key)
160 self.assertEqual(subprocess.check_output.call_count, 1) #pylint: disable=no-member
161 subprocess.check_output.assert_called_with( #pylint: disable=no-member
162 utils.SSH_KEYGEN_PUB_CMD +["-f", private_key])
163
cylan0d77ae12018-05-18 08:36:48 +0000164 def TestRetryOnException(self):
165 """Test Retry."""
Fang Dengf24be082018-02-10 10:09:55 -0800166
cylan0d77ae12018-05-18 08:36:48 +0000167 def _IsValueError(exc):
168 return isinstance(exc, ValueError)
Fang Dengf24be082018-02-10 10:09:55 -0800169
cylan0d77ae12018-05-18 08:36:48 +0000170 num_retry = 5
Fang Dengf24be082018-02-10 10:09:55 -0800171
cylan0d77ae12018-05-18 08:36:48 +0000172 @utils.RetryOnException(_IsValueError, num_retry)
173 def _RaiseAndRetry(sentinel):
174 sentinel.alert()
175 raise ValueError("Fake error.")
176
177 sentinel = mock.MagicMock()
178 self.assertRaises(ValueError, _RaiseAndRetry, sentinel)
179 self.assertEqual(1 + num_retry, sentinel.alert.call_count)
180
181 def testRetryExceptionType(self):
182 """Test RetryExceptionType function."""
183
184 def _RaiseAndRetry(sentinel):
185 sentinel.alert()
186 raise ValueError("Fake error.")
187
188 num_retry = 5
189 sentinel = mock.MagicMock()
190 self.assertRaises(
191 ValueError,
192 utils.RetryExceptionType, (KeyError, ValueError),
193 num_retry,
194 _RaiseAndRetry,
Kevin Chengd25feee2018-05-24 10:15:20 -0700195 0, # sleep_multiplier
196 1, # retry_backoff_factor
cylan0d77ae12018-05-18 08:36:48 +0000197 sentinel=sentinel)
198 self.assertEqual(1 + num_retry, sentinel.alert.call_count)
199
200 def testRetry(self):
201 """Test Retry."""
Kevin Chengd25feee2018-05-24 10:15:20 -0700202 mock_sleep = self.Patch(time, "sleep")
cylan0d77ae12018-05-18 08:36:48 +0000203
204 def _RaiseAndRetry(sentinel):
205 sentinel.alert()
206 raise ValueError("Fake error.")
207
208 num_retry = 5
209 sentinel = mock.MagicMock()
210 self.assertRaises(
211 ValueError,
212 utils.RetryExceptionType, (ValueError, KeyError),
213 num_retry,
214 _RaiseAndRetry,
Kevin Chengd25feee2018-05-24 10:15:20 -0700215 1, # sleep_multiplier
216 2, # retry_backoff_factor
cylan0d77ae12018-05-18 08:36:48 +0000217 sentinel=sentinel)
218
219 self.assertEqual(1 + num_retry, sentinel.alert.call_count)
Kevin Chengd25feee2018-05-24 10:15:20 -0700220 mock_sleep.assert_has_calls(
cylan0d77ae12018-05-18 08:36:48 +0000221 [
222 mock.call(1),
223 mock.call(2),
224 mock.call(4),
225 mock.call(8),
226 mock.call(16)
227 ])
Fang Dengf24be082018-02-10 10:09:55 -0800228
Fang Deng69498c32017-03-02 14:29:30 -0800229
230if __name__ == "__main__":
231 unittest.main()