blob: 017af2db40c84368c623e8499fba06feda7d73d2 [file] [log] [blame]
Don Garrettd0321722014-11-18 16:03:33 -08001#!/usr/bin/python
2# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Unittests for deploy_production_local.py."""
7
8from __future__ import print_function
9
10import mock
Don Garrettfa2c1c42014-12-11 12:11:49 -080011import subprocess
Don Garrettd0321722014-11-18 16:03:33 -080012import unittest
13
14import deploy_production_local as dpl
15
16
17class TestDeployProductionLocal(unittest.TestCase):
18 """Test deploy_production_local with commands mocked out."""
19
20 orig_timer = dpl.SERVICE_STABILITY_TIMER
21
22 def setUp(self):
23 dpl.SERVICE_STABILITY_TIMER = 0.01
24
25 def tearDown(self):
26 dpl.SERVICE_STABILITY_TIMER = self.orig_timer
27
28
29 @mock.patch('subprocess.check_output', autospec=True)
30 def test_verify_repo_clean(self, run_cmd):
31 """Test deploy_production_local.verify_repo_clean.
32
33 @param run_cmd: Mock of subprocess call used.
34 """
35 # If repo returns what we expect, exit cleanly.
Don Garrett699b4b32014-12-11 13:10:15 -080036 run_cmd.return_value = 'nothing to commit (working directory clean)\n'
37 dpl.verify_repo_clean()
38
39 PROD_BRANCH = (
40 '^[[1mproject autotest/ '
41 ' ^[[m^[[1mbranch prod^[[m\n')
42
43 # We allow a single branch named 'prod' in the autotest directory.
44 # repo uses bold highlights when reporting it.
45 run_cmd.return_value = PROD_BRANCH
Don Garrettd0321722014-11-18 16:03:33 -080046 dpl.verify_repo_clean()
47
48 # If repo doesn't return what we expect, raise.
49 run_cmd.return_value = "That's a very dirty repo you've got."
50 with self.assertRaises(dpl.DirtyTreeException):
51 dpl.verify_repo_clean()
52
Don Garrett699b4b32014-12-11 13:10:15 -080053 # Dirty tree with 'prod' branch.
54 run_cmd.return_value = PROD_BRANCH + 'other stuff is dirty.\n'
55 with self.assertRaises(dpl.DirtyTreeException):
56 dpl.verify_repo_clean()
57
Don Garrettd0321722014-11-18 16:03:33 -080058 @mock.patch('subprocess.check_output', autospec=True)
59 def test_repo_versions(self, run_cmd):
60 """Test deploy_production_local.repo_versions.
61
62 @param run_cmd: Mock of subprocess call used.
63 """
Don Garrettfa2c1c42014-12-11 12:11:49 -080064 output = """project autotest/
65/usr/local/autotest
665897108
Don Garrettd0321722014-11-18 16:03:33 -080067
Don Garrettfa2c1c42014-12-11 12:11:49 -080068project autotest/site_utils/autotest_private/
69/usr/local/autotest/site_utils/autotest_private
7078b9626
71
72project autotest/site_utils/autotest_tools/
73/usr/local/autotest/site_utils/autotest_tools
74a1598f7
75"""
76
77 expected = {
78 'autotest':
79 ('/usr/local/autotest', '5897108'),
80 'autotest/site_utils/autotest_private':
81 ('/usr/local/autotest/site_utils/autotest_private', '78b9626'),
82 'autotest/site_utils/autotest_tools':
83 ('/usr/local/autotest/site_utils/autotest_tools', 'a1598f7'),
84 }
85
86 run_cmd.return_value = output
Don Garrettd0321722014-11-18 16:03:33 -080087 result = dpl.repo_versions()
88 self.assertEquals(result, expected)
89
90 run_cmd.assert_called_with(
Don Garrettfa2c1c42014-12-11 12:11:49 -080091 ['repo', 'forall', '-p', '-c',
92 'pwd && git log -1 --format=%h'])
Don Garrettd0321722014-11-18 16:03:33 -080093
94 @mock.patch('subprocess.check_output', autospec=True)
95 def test_repo_sync(self, run_cmd):
96 """Test deploy_production_local.repo_sync.
97
98 @param run_cmd: Mock of subprocess call used.
99 """
100 dpl.repo_sync()
101 run_cmd.assert_called_with(['repo', 'sync'])
102
103 def test_discover_commands_and_services(self):
104 """Test deploy_production_local.discover_update_commands and
105 discover_restart_services."""
106 # It should always be a list, and should always be callable in
107 # any local environment, though the result will vary.
108 result = dpl.discover_update_commands()
109 self.assertIsInstance(result, list)
110
111 result = dpl.discover_restart_services()
112 self.assertIsInstance(result, list)
113
114 @mock.patch('subprocess.check_call', autospec=True)
115 def test_update_command(self, run_cmd):
116 """Test deploy_production_local.update_command.
117
118 @param run_cmd: Mock of subprocess call used.
119 """
120 # Call with a bad command name.
121 with self.assertRaises(dpl.UnknownCommandException):
122 dpl.update_command('Unknown Command')
123 self.assertFalse(run_cmd.called)
124
125 # Call with a valid command name.
126 dpl.update_command('apache')
127 run_cmd.assert_called_with('sudo service apache2 reload', shell=True)
128
129 # Call with a valid command name that uses AUTOTEST_REPO expansion.
130 dpl.update_command('build_externals')
131 expanded_cmd = dpl.common.autotest_dir+'/utils/build_externals.py'
132 run_cmd.assert_called_with(expanded_cmd, shell=True)
133
134 @mock.patch('subprocess.check_call', autospec=True)
135 def test_restart_service(self, run_cmd):
136 """Test deploy_production_local.restart_service.
137
138 @param run_cmd: Mock of subprocess call used.
139 """
140 # Standard call.
141 dpl.restart_service('foobar')
142 run_cmd.assert_called_with(['sudo', 'service', 'foobar', 'restart'])
143
144 @mock.patch('subprocess.check_output', autospec=True)
145 def test_restart_status(self, run_cmd):
146 """Test deploy_production_local.service_status.
147
148 @param run_cmd: Mock of subprocess call used.
149 """
150 # Standard call.
151 dpl.service_status('foobar')
152 run_cmd.assert_called_with(['sudo', 'status', 'foobar'])
153
154 @mock.patch.object(dpl, 'restart_service', autospec=True)
155 def _test_restart_services(self, service_results, _restart):
156 """Helper for testing restart_services.
157
158 @param service_results: {'service_name': ['status_1', 'status_2']}
159 """
160 # each call to service_status should return the next status value for
161 # that service.
162 with mock.patch.object(dpl, 'service_status', autospec=True,
163 side_effect=lambda n: service_results[n].pop(0)):
164 dpl.restart_services(service_results.keys())
165
166 def test_restart_services(self):
167 """Test deploy_production_local.restart_services."""
168 single_stable = {'foo': ['status_ok', 'status_ok']}
169 double_stable = {'foo': ['status_a', 'status_a'],
170 'bar': ['status_b', 'status_b']}
171
172 # Verify we can handle stable services.
173 self._test_restart_services(single_stable)
174 self._test_restart_services(double_stable)
175
176 single_unstable = {'foo': ['status_ok', 'status_not_ok']}
177 triple_unstable = {'foo': ['status_a', 'status_a'],
178 'bar': ['status_b', 'status_b_not_ok'],
179 'joe': ['status_c', 'status_c_not_ok']}
180
181 # Verify we can handle unstable services and report the right failures.
182 with self.assertRaises(dpl.UnstableServices) as unstable:
183 self._test_restart_services(single_unstable)
184 self.assertEqual(unstable.exception.args[0], ['foo'])
185
186 with self.assertRaises(dpl.UnstableServices) as unstable:
187 self._test_restart_services(triple_unstable)
188 self.assertEqual(unstable.exception.args[0], ['bar', 'joe'])
189
Don Garrettfa2c1c42014-12-11 12:11:49 -0800190 @mock.patch('subprocess.check_output', autospec=True)
191 def test_report_changes(self, run_cmd):
192 """Test deploy_production_local.report_changes.
193
194 @param run_cmd: Mock of subprocess call used.
195 """
196
197 before = {
198 'autotest': ('/usr/local/autotest', 'auto_before'),
199 'autotest_private': ('/dir/autotest_private', '78b9626'),
200 'other': ('/fake/unchanged', 'constant_hash'),
201 }
202
203 after = {
204 'autotest': ('/usr/local/autotest', 'auto_after'),
205 'autotest_tools': ('/dir/autotest_tools', 'a1598f7'),
206 'other': ('/fake/unchanged', 'constant_hash'),
207 }
208
209 run_cmd.return_value = 'hash1 Fix change.\nhash2 Bad change.\n'
210
211 result = dpl.report_changes(before, after)
212
213 self.assertEqual(result, """autotest:
214hash1 Fix change.
215hash2 Bad change.
216
217autotest_private:
218Removed.
219
220autotest_tools:
221Added.
222
223other:
224No Change.
225""")
226
227 run_cmd.assert_called_with(
228 ['git', 'log', 'auto_before..auto_after', '--oneline'],
229 cwd='/usr/local/autotest', stderr=subprocess.STDOUT)
230
Don Garrett03432d62014-11-19 18:18:35 -0800231 def test_parse_arguments(self):
232 """Test deploy_production_local.parse_arguments."""
233 # No arguments.
234 results = dpl.parse_arguments([])
Don Garrette3718912014-12-05 13:11:44 -0800235 self.assertDictContainsSubset(
Don Garrett03432d62014-11-19 18:18:35 -0800236 {'verify': True, 'update': True, 'actions': True,
Don Garrette3718912014-12-05 13:11:44 -0800237 'report': True, 'dryrun': False},
238 vars(results))
Don Garrett03432d62014-11-19 18:18:35 -0800239
240 # Dryrun.
241 results = dpl.parse_arguments(['--dryrun'])
Don Garrette3718912014-12-05 13:11:44 -0800242 self.assertDictContainsSubset(
Don Garrett03432d62014-11-19 18:18:35 -0800243 {'verify': False, 'update': False, 'actions': True,
Don Garrette3718912014-12-05 13:11:44 -0800244 'report': True, 'dryrun': True},
245 vars(results))
246
247 # Restart only.
248 results = dpl.parse_arguments(['--actions-only'])
249 self.assertDictContainsSubset(
250 {'verify': False, 'update': False, 'actions': True,
251 'report': False, 'dryrun': False},
252 vars(results))
Don Garrett03432d62014-11-19 18:18:35 -0800253
254 # All skip arguments.
255 results = dpl.parse_arguments(['--skip-verify', '--skip-update',
256 '--skip-actions', '--skip-report'])
Don Garrette3718912014-12-05 13:11:44 -0800257 self.assertDictContainsSubset(
Don Garrett03432d62014-11-19 18:18:35 -0800258 {'verify': False, 'update': False, 'actions': False,
Don Garrette3718912014-12-05 13:11:44 -0800259 'report': False, 'dryrun': False},
260 vars(results))
Don Garrett03432d62014-11-19 18:18:35 -0800261
262 # All arguments.
263 results = dpl.parse_arguments(['--skip-verify', '--skip-update',
264 '--skip-actions', '--skip-report',
Don Garrette3718912014-12-05 13:11:44 -0800265 '--actions-only', '--dryrun'])
266 self.assertDictContainsSubset(
Don Garrett03432d62014-11-19 18:18:35 -0800267 {'verify': False, 'update': False, 'actions': False,
Don Garrette3718912014-12-05 13:11:44 -0800268 'report': False, 'dryrun': True},
269 vars(results))
Don Garrett03432d62014-11-19 18:18:35 -0800270
Don Garrettd0321722014-11-18 16:03:33 -0800271
272if __name__ == '__main__':
273 unittest.main()