Don Garrett | d032172 | 2014-11-18 16:03:33 -0800 | [diff] [blame] | 1 | #!/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 | |
| 8 | from __future__ import print_function |
| 9 | |
| 10 | import mock |
Don Garrett | fa2c1c4 | 2014-12-11 12:11:49 -0800 | [diff] [blame^] | 11 | import subprocess |
Don Garrett | d032172 | 2014-11-18 16:03:33 -0800 | [diff] [blame] | 12 | import unittest |
| 13 | |
| 14 | import deploy_production_local as dpl |
| 15 | |
| 16 | |
| 17 | class 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 Garrett | 699b4b3 | 2014-12-11 13:10:15 -0800 | [diff] [blame] | 36 | 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 Garrett | d032172 | 2014-11-18 16:03:33 -0800 | [diff] [blame] | 46 | 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 Garrett | 699b4b3 | 2014-12-11 13:10:15 -0800 | [diff] [blame] | 53 | # 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 Garrett | d032172 | 2014-11-18 16:03:33 -0800 | [diff] [blame] | 58 | @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 Garrett | fa2c1c4 | 2014-12-11 12:11:49 -0800 | [diff] [blame^] | 64 | output = """project autotest/ |
| 65 | /usr/local/autotest |
| 66 | 5897108 |
Don Garrett | d032172 | 2014-11-18 16:03:33 -0800 | [diff] [blame] | 67 | |
Don Garrett | fa2c1c4 | 2014-12-11 12:11:49 -0800 | [diff] [blame^] | 68 | project autotest/site_utils/autotest_private/ |
| 69 | /usr/local/autotest/site_utils/autotest_private |
| 70 | 78b9626 |
| 71 | |
| 72 | project autotest/site_utils/autotest_tools/ |
| 73 | /usr/local/autotest/site_utils/autotest_tools |
| 74 | a1598f7 |
| 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 Garrett | d032172 | 2014-11-18 16:03:33 -0800 | [diff] [blame] | 87 | result = dpl.repo_versions() |
| 88 | self.assertEquals(result, expected) |
| 89 | |
| 90 | run_cmd.assert_called_with( |
Don Garrett | fa2c1c4 | 2014-12-11 12:11:49 -0800 | [diff] [blame^] | 91 | ['repo', 'forall', '-p', '-c', |
| 92 | 'pwd && git log -1 --format=%h']) |
Don Garrett | d032172 | 2014-11-18 16:03:33 -0800 | [diff] [blame] | 93 | |
| 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 Garrett | fa2c1c4 | 2014-12-11 12:11:49 -0800 | [diff] [blame^] | 190 | @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: |
| 214 | hash1 Fix change. |
| 215 | hash2 Bad change. |
| 216 | |
| 217 | autotest_private: |
| 218 | Removed. |
| 219 | |
| 220 | autotest_tools: |
| 221 | Added. |
| 222 | |
| 223 | other: |
| 224 | No 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 Garrett | 03432d6 | 2014-11-19 18:18:35 -0800 | [diff] [blame] | 231 | def test_parse_arguments(self): |
| 232 | """Test deploy_production_local.parse_arguments.""" |
| 233 | # No arguments. |
| 234 | results = dpl.parse_arguments([]) |
Don Garrett | e371891 | 2014-12-05 13:11:44 -0800 | [diff] [blame] | 235 | self.assertDictContainsSubset( |
Don Garrett | 03432d6 | 2014-11-19 18:18:35 -0800 | [diff] [blame] | 236 | {'verify': True, 'update': True, 'actions': True, |
Don Garrett | e371891 | 2014-12-05 13:11:44 -0800 | [diff] [blame] | 237 | 'report': True, 'dryrun': False}, |
| 238 | vars(results)) |
Don Garrett | 03432d6 | 2014-11-19 18:18:35 -0800 | [diff] [blame] | 239 | |
| 240 | # Dryrun. |
| 241 | results = dpl.parse_arguments(['--dryrun']) |
Don Garrett | e371891 | 2014-12-05 13:11:44 -0800 | [diff] [blame] | 242 | self.assertDictContainsSubset( |
Don Garrett | 03432d6 | 2014-11-19 18:18:35 -0800 | [diff] [blame] | 243 | {'verify': False, 'update': False, 'actions': True, |
Don Garrett | e371891 | 2014-12-05 13:11:44 -0800 | [diff] [blame] | 244 | '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 Garrett | 03432d6 | 2014-11-19 18:18:35 -0800 | [diff] [blame] | 253 | |
| 254 | # All skip arguments. |
| 255 | results = dpl.parse_arguments(['--skip-verify', '--skip-update', |
| 256 | '--skip-actions', '--skip-report']) |
Don Garrett | e371891 | 2014-12-05 13:11:44 -0800 | [diff] [blame] | 257 | self.assertDictContainsSubset( |
Don Garrett | 03432d6 | 2014-11-19 18:18:35 -0800 | [diff] [blame] | 258 | {'verify': False, 'update': False, 'actions': False, |
Don Garrett | e371891 | 2014-12-05 13:11:44 -0800 | [diff] [blame] | 259 | 'report': False, 'dryrun': False}, |
| 260 | vars(results)) |
Don Garrett | 03432d6 | 2014-11-19 18:18:35 -0800 | [diff] [blame] | 261 | |
| 262 | # All arguments. |
| 263 | results = dpl.parse_arguments(['--skip-verify', '--skip-update', |
| 264 | '--skip-actions', '--skip-report', |
Don Garrett | e371891 | 2014-12-05 13:11:44 -0800 | [diff] [blame] | 265 | '--actions-only', '--dryrun']) |
| 266 | self.assertDictContainsSubset( |
Don Garrett | 03432d6 | 2014-11-19 18:18:35 -0800 | [diff] [blame] | 267 | {'verify': False, 'update': False, 'actions': False, |
Don Garrett | e371891 | 2014-12-05 13:11:44 -0800 | [diff] [blame] | 268 | 'report': False, 'dryrun': True}, |
| 269 | vars(results)) |
Don Garrett | 03432d6 | 2014-11-19 18:18:35 -0800 | [diff] [blame] | 270 | |
Don Garrett | d032172 | 2014-11-18 16:03:33 -0800 | [diff] [blame] | 271 | |
| 272 | if __name__ == '__main__': |
| 273 | unittest.main() |