blob: 87bb41271d7ba880f2907100c65ffc19157ca803 [file] [log] [blame]
J. Richard Barnette266da2a2013-11-27 15:09:55 -08001# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import StringIO
6import json
7import mox
8import time
9import unittest
10import urllib2
11
12import common
13from autotest_lib.client.common_lib import global_config
14from autotest_lib.server import site_utils
15
J. Richard Barnetteabbe0962013-12-10 18:15:44 -080016_DEADBUILD = 'deadboard-release/R33-4966.0.0'
17_LIVEBUILD = 'liveboard-release/R32-4920.14.0'
J. Richard Barnette266da2a2013-11-27 15:09:55 -080018
J. Richard Barnette183e4292015-06-19 12:41:10 -070019_STATUS_TEMPLATE = '''
20 {
21 "username": "fizzbin@google.com",
22 "date": "2013-11-16 00:25:23.511208",
23 "message": "%s",
24 "can_commit_freely": %s,
25 "general_state": "%s"
26 }
27 '''
28
29
30def _make_status(message, can_commit, state):
31 return _STATUS_TEMPLATE % (message, can_commit, state)
32
33
34def _make_open_status(message, state):
35 return _make_status(message, 'true', state)
36
37
38def _make_closed_status(message):
39 return _make_status(message, 'false', 'closed')
40
41
42def _make_deadbuild_status(message):
43 return _make_status(message, 'false', 'open')
44
45
J. Richard Barnette266da2a2013-11-27 15:09:55 -080046_OPEN_STATUS_VALUES = [
J. Richard Barnette183e4292015-06-19 12:41:10 -070047 _make_open_status('Lab is up (cross your fingers)', 'open'),
48 _make_open_status('Lab is on fire', 'throttled'),
49 _make_open_status('Lab is up despite deadboard', 'open'),
J. Richard Barnette7f215d32015-06-19 12:44:38 -070050 _make_open_status('Lab is up despite .*/R33-4966.0.0', 'open'),
J. Richard Barnette266da2a2013-11-27 15:09:55 -080051]
52
53_CLOSED_STATUS_VALUES = [
J. Richard Barnette183e4292015-06-19 12:41:10 -070054 _make_closed_status('Lab is down for spite'),
55 _make_closed_status('Lab is down even for [%s]' % _LIVEBUILD),
56 _make_closed_status('Lab is down even for [%s]' % _DEADBUILD),
J. Richard Barnette266da2a2013-11-27 15:09:55 -080057]
58
J. Richard Barnetteabbe0962013-12-10 18:15:44 -080059_DEADBUILD_STATUS_VALUES = [
J. Richard Barnette183e4292015-06-19 12:41:10 -070060 _make_deadbuild_status('Lab is up except for [deadboard-]'),
J. Richard Barnette7f215d32015-06-19 12:44:38 -070061 _make_deadbuild_status('Lab is up except for [board- deadboard-]'),
62 _make_deadbuild_status('Lab is up except for [.*/R33-]'),
J. Richard Barnette183e4292015-06-19 12:41:10 -070063 _make_deadbuild_status('Lab is up except for [deadboard-.*/R33-]'),
64 _make_deadbuild_status('Lab is up except for [ deadboard-]'),
65 _make_deadbuild_status('Lab is up except for [deadboard- ]'),
J. Richard Barnette7f215d32015-06-19 12:44:38 -070066 _make_deadbuild_status('Lab is up [first .*/R33- last]'),
J. Richard Barnette183e4292015-06-19 12:41:10 -070067 _make_deadbuild_status('liveboard is good, but [deadboard-] is bad'),
68 _make_deadbuild_status('Lab is up [deadboard- otherboard-]'),
69 _make_deadbuild_status('Lab is up [otherboard- deadboard-]'),
J. Richard Barnette266da2a2013-11-27 15:09:55 -080070]
71
72
73_FAKE_URL = 'ignore://not.a.url'
74
75
76class _FakeURLResponse(object):
77
78 """Everything needed to pretend to be a response from urlopen().
79
80 Creates a StringIO instance to handle the File operations.
81
82 N.B. StringIO is lame: we can't inherit from it (super won't
83 work), and it doesn't implement __getattr__(), either. So, we
84 have to manually forward calls to the StringIO object. This
85 forwards only what empirical testing says is required; YMMV.
86
87 """
88
89 def __init__(self, code, buffer):
90 self._stringio = StringIO.StringIO(buffer)
91 self._code = code
92
93
94 def read(self, size=-1):
95 """Standard file-like read operation.
96
97 @param size size for read operation.
98 """
99 return self._stringio.read(size)
100
101
102 def getcode(self):
103 """Get URL HTTP response code."""
104 return self._code
105
106
107class GetStatusTest(mox.MoxTestBase):
108
109 """Test case for _get_lab_status().
110
111 We mock out dependencies on urllib2 and time.sleep(), and
112 confirm that the function returns the proper JSON representation
113 for a pre-defined response.
114
115 """
116
117 def setUp(self):
118 super(GetStatusTest, self).setUp()
119 self.mox.StubOutWithMock(urllib2, 'urlopen')
120 self.mox.StubOutWithMock(time, 'sleep')
121
122
123 def test_success(self):
124 """Test that successful calls to urlopen() succeed."""
125 json_string = _OPEN_STATUS_VALUES[0]
126 json_value = json.loads(json_string)
127 urllib2.urlopen(mox.IgnoreArg()).AndReturn(
128 _FakeURLResponse(200, json_string))
129 self.mox.ReplayAll()
130 result = site_utils._get_lab_status(_FAKE_URL)
131 self.mox.VerifyAll()
132 self.assertEqual(json_value, result)
133
134
135 def test_retry_ioerror(self):
136 """Test that an IOError retries at least once."""
137 json_string = _OPEN_STATUS_VALUES[0]
138 json_value = json.loads(json_string)
139 urllib2.urlopen(mox.IgnoreArg()).AndRaise(
140 IOError('Fake I/O error for a fake URL'))
141 time.sleep(mox.IgnoreArg()).AndReturn(None)
142 urllib2.urlopen(mox.IgnoreArg()).AndReturn(
143 _FakeURLResponse(200, json_string))
144 self.mox.ReplayAll()
145 result = site_utils._get_lab_status(_FAKE_URL)
146 self.mox.VerifyAll()
147 self.assertEqual(json_value, result)
148
149
150 def test_retry_http_internal_error(self):
151 """Test that an HTTP error retries at least once."""
152 json_string = _OPEN_STATUS_VALUES[0]
153 json_value = json.loads(json_string)
154 urllib2.urlopen(mox.IgnoreArg()).AndReturn(
155 _FakeURLResponse(500, ''))
156 time.sleep(mox.IgnoreArg()).AndReturn(None)
157 urllib2.urlopen(mox.IgnoreArg()).AndReturn(
158 _FakeURLResponse(200, json_string))
159 self.mox.ReplayAll()
160 result = site_utils._get_lab_status(_FAKE_URL)
161 self.mox.VerifyAll()
162 self.assertEqual(json_value, result)
163
164
165 def test_failure_ioerror(self):
166 """Test that there's a failure if urlopen() never succeeds."""
167 json_string = _OPEN_STATUS_VALUES[0]
168 json_value = json.loads(json_string)
169 for _ in range(site_utils._MAX_LAB_STATUS_ATTEMPTS):
170 urllib2.urlopen(mox.IgnoreArg()).AndRaise(
171 IOError('Fake I/O error for a fake URL'))
172 time.sleep(mox.IgnoreArg()).AndReturn(None)
173 self.mox.ReplayAll()
174 result = site_utils._get_lab_status(_FAKE_URL)
175 self.mox.VerifyAll()
176 self.assertEqual(None, result)
177
178
179 def test_failure_http_internal_error(self):
180 """Test that there's a failure for a permanent HTTP error."""
181 json_string = _OPEN_STATUS_VALUES[0]
182 json_value = json.loads(json_string)
183 for _ in range(site_utils._MAX_LAB_STATUS_ATTEMPTS):
184 urllib2.urlopen(mox.IgnoreArg()).AndReturn(
185 _FakeURLResponse(404, 'Not here, never gonna be'))
186 time.sleep(mox.IgnoreArg()).InAnyOrder().AndReturn(None)
187 self.mox.ReplayAll()
188 result = site_utils._get_lab_status(_FAKE_URL)
189 self.mox.VerifyAll()
190 self.assertEqual(None, result)
191
192
193class DecodeStatusTest(unittest.TestCase):
194
195 """Test case for _decode_lab_status().
196
197 Testing covers three distinct possible states:
198 1. Lab is up. All calls to _decode_lab_status() will
199 succeed without raising an exception.
200 2. Lab is down. All calls to _decode_lab_status() will
J. Richard Barnetteabbe0962013-12-10 18:15:44 -0800201 fail with TestLabException.
202 3. Build disabled. Calls to _decode_lab_status() will
203 succeed, except that board `_DEADBUILD` will raise
204 TestLabException.
J. Richard Barnette266da2a2013-11-27 15:09:55 -0800205
206 """
207
208 def _assert_lab_open(self, lab_status):
209 """Test that open status values are handled properly.
210
211 Test that _decode_lab_status() succeeds when the lab status
212 is up.
213
214 @param lab_status JSON value describing lab status.
215
216 """
J. Richard Barnetteabbe0962013-12-10 18:15:44 -0800217 site_utils._decode_lab_status(lab_status, _LIVEBUILD)
218 site_utils._decode_lab_status(lab_status, _DEADBUILD)
J. Richard Barnette266da2a2013-11-27 15:09:55 -0800219
220
221 def _assert_lab_closed(self, lab_status):
222 """Test that closed status values are handled properly.
223
J. Richard Barnetteabbe0962013-12-10 18:15:44 -0800224 Test that _decode_lab_status() raises TestLabException
J. Richard Barnette266da2a2013-11-27 15:09:55 -0800225 when the lab status is down.
226
227 @param lab_status JSON value describing lab status.
228
229 """
J. Richard Barnetteabbe0962013-12-10 18:15:44 -0800230 with self.assertRaises(site_utils.TestLabException):
231 site_utils._decode_lab_status(lab_status, _LIVEBUILD)
232 with self.assertRaises(site_utils.TestLabException):
233 site_utils._decode_lab_status(lab_status, _DEADBUILD)
J. Richard Barnette266da2a2013-11-27 15:09:55 -0800234
235
J. Richard Barnetteabbe0962013-12-10 18:15:44 -0800236 def _assert_lab_deadbuild(self, lab_status):
237 """Test that disabled builds are handled properly.
J. Richard Barnette266da2a2013-11-27 15:09:55 -0800238
J. Richard Barnetteabbe0962013-12-10 18:15:44 -0800239 Test that _decode_lab_status() raises TestLabException
240 for build `_DEADBUILD` and succeeds otherwise.
J. Richard Barnette266da2a2013-11-27 15:09:55 -0800241
242 @param lab_status JSON value describing lab status.
243
244 """
J. Richard Barnetteabbe0962013-12-10 18:15:44 -0800245 site_utils._decode_lab_status(lab_status, _LIVEBUILD)
246 with self.assertRaises(site_utils.TestLabException):
247 site_utils._decode_lab_status(lab_status, _DEADBUILD)
J. Richard Barnette266da2a2013-11-27 15:09:55 -0800248
249
250 def _assert_lab_status(self, test_values, checker):
251 """General purpose test for _decode_lab_status().
252
253 Decode each JSON string in `test_values`, and call the
254 `checker` function to test the corresponding status is
255 correctly handled.
256
257 @param test_values Array of JSON encoded strings representing
258 lab status.
259 @param checker Function to be called against each of the lab
260 status values in the `test_values` array.
261
262 """
263 for s in test_values:
264 lab_status = json.loads(s)
265 checker(lab_status)
266
267
268 def test_open_lab(self):
269 """Test that open lab status values are handled correctly."""
270 self._assert_lab_status(_OPEN_STATUS_VALUES,
271 self._assert_lab_open)
272
273
274 def test_closed_lab(self):
275 """Test that closed lab status values are handled correctly."""
276 self._assert_lab_status(_CLOSED_STATUS_VALUES,
277 self._assert_lab_closed)
278
279
J. Richard Barnetteabbe0962013-12-10 18:15:44 -0800280 def test_dead_build(self):
281 """Test that disabled builds are handled correctly."""
282 self._assert_lab_status(_DEADBUILD_STATUS_VALUES,
283 self._assert_lab_deadbuild)
J. Richard Barnette266da2a2013-11-27 15:09:55 -0800284
285
286class CheckStatusTest(mox.MoxTestBase):
287
288 """Test case for `check_lab_status()`.
289
290 We mock out dependencies on `global_config.global_config()`,
291 `_get_lab_status()` and confirm that the function succeeds or
292 fails as expected.
293
294 N.B. We don't mock `_decode_lab_status()`; if DecodeStatusTest
295 is failing, this test may fail, too.
296
297 """
298
299 def setUp(self):
300 super(CheckStatusTest, self).setUp()
301 self.mox.StubOutWithMock(global_config.global_config,
302 'get_config_value')
303 self.mox.StubOutWithMock(site_utils, '_get_lab_status')
304
305
306 def _setup_not_cautotest(self):
307 """Set up to mock the "we're not on cautotest" case."""
308 global_config.global_config.get_config_value(
309 'SERVER', 'hostname').AndReturn('not-cautotest')
310
311
312 def _setup_no_status(self):
313 """Set up to mock lab status as unavailable."""
314 global_config.global_config.get_config_value(
315 'SERVER', 'hostname').AndReturn('cautotest')
316 global_config.global_config.get_config_value(
317 'CROS', 'lab_status_url').AndReturn(_FAKE_URL)
318 site_utils._get_lab_status(_FAKE_URL).AndReturn(None)
319
320
321 def _setup_lab_status(self, json_string):
322 """Set up to mock a given lab status.
323
324 @param json_string JSON string for the JSON object to return
325 from `_get_lab_status()`.
326
327 """
328 global_config.global_config.get_config_value(
329 'SERVER', 'hostname').AndReturn('cautotest')
330 global_config.global_config.get_config_value(
331 'CROS', 'lab_status_url').AndReturn(_FAKE_URL)
332 json_value = json.loads(json_string)
333 site_utils._get_lab_status(_FAKE_URL).AndReturn(json_value)
334
335
J. Richard Barnetteabbe0962013-12-10 18:15:44 -0800336 def _try_check_status(self, build):
337 """Test calling check_lab_status() with `build`."""
J. Richard Barnette266da2a2013-11-27 15:09:55 -0800338 try:
339 self.mox.ReplayAll()
J. Richard Barnetteabbe0962013-12-10 18:15:44 -0800340 site_utils.check_lab_status(build)
J. Richard Barnette266da2a2013-11-27 15:09:55 -0800341 finally:
342 self.mox.VerifyAll()
343
344
J. Richard Barnetteabbe0962013-12-10 18:15:44 -0800345 def test_non_cautotest(self):
346 """Test a call with a build when the host isn't cautotest."""
J. Richard Barnette266da2a2013-11-27 15:09:55 -0800347 self._setup_not_cautotest()
J. Richard Barnetteabbe0962013-12-10 18:15:44 -0800348 self._try_check_status(_LIVEBUILD)
J. Richard Barnette266da2a2013-11-27 15:09:55 -0800349
350
J. Richard Barnetteabbe0962013-12-10 18:15:44 -0800351 def test_no_lab_status(self):
352 """Test with a build when `_get_lab_status()` returns `None`."""
J. Richard Barnette266da2a2013-11-27 15:09:55 -0800353 self._setup_no_status()
J. Richard Barnetteabbe0962013-12-10 18:15:44 -0800354 self._try_check_status(_LIVEBUILD)
J. Richard Barnette266da2a2013-11-27 15:09:55 -0800355
356
J. Richard Barnetteabbe0962013-12-10 18:15:44 -0800357 def test_lab_up_live_build(self):
358 """Test lab open with a build specified."""
J. Richard Barnette266da2a2013-11-27 15:09:55 -0800359 self._setup_lab_status(_OPEN_STATUS_VALUES[0])
J. Richard Barnetteabbe0962013-12-10 18:15:44 -0800360 self._try_check_status(_LIVEBUILD)
J. Richard Barnette266da2a2013-11-27 15:09:55 -0800361
362
J. Richard Barnetteabbe0962013-12-10 18:15:44 -0800363 def test_lab_down_live_build(self):
364 """Test lab closed with a build specified."""
J. Richard Barnette266da2a2013-11-27 15:09:55 -0800365 self._setup_lab_status(_CLOSED_STATUS_VALUES[0])
J. Richard Barnetteabbe0962013-12-10 18:15:44 -0800366 with self.assertRaises(site_utils.TestLabException):
367 self._try_check_status(_LIVEBUILD)
J. Richard Barnette266da2a2013-11-27 15:09:55 -0800368
369
J. Richard Barnetteabbe0962013-12-10 18:15:44 -0800370 def test_build_disabled_live_build(self):
371 """Test build disabled with a live build specified."""
372 self._setup_lab_status(_DEADBUILD_STATUS_VALUES[0])
373 self._try_check_status(_LIVEBUILD)
J. Richard Barnette266da2a2013-11-27 15:09:55 -0800374
375
J. Richard Barnetteabbe0962013-12-10 18:15:44 -0800376 def test_build_disabled_dead_build(self):
377 """Test build disabled with the disabled build specified."""
378 self._setup_lab_status(_DEADBUILD_STATUS_VALUES[0])
379 with self.assertRaises(site_utils.TestLabException):
380 self._try_check_status(_DEADBUILD)
J. Richard Barnette266da2a2013-11-27 15:09:55 -0800381
382
383if __name__ == '__main__':
384 unittest.main()