blob: 1299a66cbe552ede938c0d852f71cf5628568917 [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
19_OPEN_STATUS_VALUES = [
20 '''
21 {
22 "username": "fizzbin@google.com",
23 "date": "2013-11-16 00:25:23.511208",
24 "message": "Lab is up (cross your fingers)",
25 "can_commit_freely": true,
26 "general_state": "open"
27 }
28 ''',
29
30 '''
31 {
32 "username": "fizzbin@google.com",
33 "date": "2013-11-16 00:25:23.511208",
34 "message": "Lab is on fire",
35 "can_commit_freely": true,
36 "general_state": "throttled"
37 }
38 ''',
39
40 '''
41 {
42 "username": "fizzbin@google.com",
43 "date": "2013-11-16 00:25:23.511208",
44 "message": "Lab is up despite deadboard",
45 "can_commit_freely": true,
46 "general_state": "open"
47 }
48 ''',
J. Richard Barnetteabbe0962013-12-10 18:15:44 -080049
50 '''
51 {
52 "username": "fizzbin@google.com",
53 "date": "2013-11-16 00:25:23.511208",
54 "message": "Lab is up despite R33-4966.0.0",
55 "can_commit_freely": true,
56 "general_state": "open"
57 }
58 ''',
J. Richard Barnette266da2a2013-11-27 15:09:55 -080059]
60
61_CLOSED_STATUS_VALUES = [
62 '''
63 {
64 "username": "fizzbin@google.com",
65 "date": "2013-11-16 00:25:23.511208",
66 "message": "Lab is down for spite",
67 "can_commit_freely": false,
68 "general_state": "closed"
69 }
70 ''',
71
72 '''
73 {
74 "username": "fizzbin@google.com",
75 "date": "2013-11-16 00:25:23.511208",
J. Richard Barnetteabbe0962013-12-10 18:15:44 -080076 "message": "Lab is down even for [liveboard-release/R32-4920.14.0]",
J. Richard Barnette266da2a2013-11-27 15:09:55 -080077 "can_commit_freely": false,
78 "general_state": "closed"
79 }
80 ''',
81]
82
J. Richard Barnetteabbe0962013-12-10 18:15:44 -080083_DEADBUILD_STATUS_VALUES = [
J. Richard Barnette266da2a2013-11-27 15:09:55 -080084 '''
85 {
86 "username": "fizzbin@google.com",
87 "date": "2013-11-16 00:25:23.511208",
J. Richard Barnetteabbe0962013-12-10 18:15:44 -080088 "message": "Lab is up except for [deadboard-]",
J. Richard Barnette266da2a2013-11-27 15:09:55 -080089 "can_commit_freely": false,
90 "general_state": "open"
91 }
92 ''',
93
94 '''
95 {
96 "username": "fizzbin@google.com",
97 "date": "2013-11-16 00:25:23.511208",
J. Richard Barnetteabbe0962013-12-10 18:15:44 -080098 "message": "Lab is up except for [R33-]",
J. Richard Barnette266da2a2013-11-27 15:09:55 -080099 "can_commit_freely": false,
100 "general_state": "open"
101 }
102 ''',
103
104 '''
105 {
106 "username": "fizzbin@google.com",
107 "date": "2013-11-16 00:25:23.511208",
J. Richard Barnetteabbe0962013-12-10 18:15:44 -0800108 "message": "Lab is up except for [deadboard-.*/R33-]",
J. Richard Barnette266da2a2013-11-27 15:09:55 -0800109 "can_commit_freely": false,
110 "general_state": "open"
111 }
112 ''',
113
114 '''
115 {
116 "username": "fizzbin@google.com",
117 "date": "2013-11-16 00:25:23.511208",
J. Richard Barnetteabbe0962013-12-10 18:15:44 -0800118 "message": "Lab is up except for [ deadboard-]",
J. Richard Barnette266da2a2013-11-27 15:09:55 -0800119 "can_commit_freely": false,
120 "general_state": "open"
121 }
122 ''',
123
124 '''
125 {
126 "username": "fizzbin@google.com",
127 "date": "2013-11-16 00:25:23.511208",
J. Richard Barnetteabbe0962013-12-10 18:15:44 -0800128 "message": "Lab is up except for [deadboard- ]",
129 "can_commit_freely": false,
130 "general_state": "open"
131 }
132 ''',
133
134 '''
135 {
136 "username": "fizzbin@google.com",
137 "date": "2013-11-16 00:25:23.511208",
138 "message": "Lab is up [first R33- last]",
139 "can_commit_freely": false,
140 "general_state": "open"
141 }
142 ''',
143
144 '''
145 {
146 "username": "fizzbin@google.com",
147 "date": "2013-11-16 00:25:23.511208",
148 "message": "liveboard is good, but [deadboard-] is bad",
149 "can_commit_freely": false,
150 "general_state": "open"
151 }
152 ''',
153
154 '''
155 {
156 "username": "fizzbin@google.com",
157 "date": "2013-11-16 00:25:23.511208",
158 "message": "Lab is up [deadboard- otherboard-]",
159 "can_commit_freely": false,
160 "general_state": "open"
161 }
162 ''',
163
164 '''
165 {
166 "username": "fizzbin@google.com",
167 "date": "2013-11-16 00:25:23.511208",
168 "message": "Lab is up [otherboard- deadboard-]",
J. Richard Barnette266da2a2013-11-27 15:09:55 -0800169 "can_commit_freely": false,
170 "general_state": "open"
171 }
172 ''',
173]
174
175
176_FAKE_URL = 'ignore://not.a.url'
177
178
179class _FakeURLResponse(object):
180
181 """Everything needed to pretend to be a response from urlopen().
182
183 Creates a StringIO instance to handle the File operations.
184
185 N.B. StringIO is lame: we can't inherit from it (super won't
186 work), and it doesn't implement __getattr__(), either. So, we
187 have to manually forward calls to the StringIO object. This
188 forwards only what empirical testing says is required; YMMV.
189
190 """
191
192 def __init__(self, code, buffer):
193 self._stringio = StringIO.StringIO(buffer)
194 self._code = code
195
196
197 def read(self, size=-1):
198 """Standard file-like read operation.
199
200 @param size size for read operation.
201 """
202 return self._stringio.read(size)
203
204
205 def getcode(self):
206 """Get URL HTTP response code."""
207 return self._code
208
209
210class GetStatusTest(mox.MoxTestBase):
211
212 """Test case for _get_lab_status().
213
214 We mock out dependencies on urllib2 and time.sleep(), and
215 confirm that the function returns the proper JSON representation
216 for a pre-defined response.
217
218 """
219
220 def setUp(self):
221 super(GetStatusTest, self).setUp()
222 self.mox.StubOutWithMock(urllib2, 'urlopen')
223 self.mox.StubOutWithMock(time, 'sleep')
224
225
226 def test_success(self):
227 """Test that successful calls to urlopen() succeed."""
228 json_string = _OPEN_STATUS_VALUES[0]
229 json_value = json.loads(json_string)
230 urllib2.urlopen(mox.IgnoreArg()).AndReturn(
231 _FakeURLResponse(200, json_string))
232 self.mox.ReplayAll()
233 result = site_utils._get_lab_status(_FAKE_URL)
234 self.mox.VerifyAll()
235 self.assertEqual(json_value, result)
236
237
238 def test_retry_ioerror(self):
239 """Test that an IOError retries at least once."""
240 json_string = _OPEN_STATUS_VALUES[0]
241 json_value = json.loads(json_string)
242 urllib2.urlopen(mox.IgnoreArg()).AndRaise(
243 IOError('Fake I/O error for a fake URL'))
244 time.sleep(mox.IgnoreArg()).AndReturn(None)
245 urllib2.urlopen(mox.IgnoreArg()).AndReturn(
246 _FakeURLResponse(200, json_string))
247 self.mox.ReplayAll()
248 result = site_utils._get_lab_status(_FAKE_URL)
249 self.mox.VerifyAll()
250 self.assertEqual(json_value, result)
251
252
253 def test_retry_http_internal_error(self):
254 """Test that an HTTP error retries at least once."""
255 json_string = _OPEN_STATUS_VALUES[0]
256 json_value = json.loads(json_string)
257 urllib2.urlopen(mox.IgnoreArg()).AndReturn(
258 _FakeURLResponse(500, ''))
259 time.sleep(mox.IgnoreArg()).AndReturn(None)
260 urllib2.urlopen(mox.IgnoreArg()).AndReturn(
261 _FakeURLResponse(200, json_string))
262 self.mox.ReplayAll()
263 result = site_utils._get_lab_status(_FAKE_URL)
264 self.mox.VerifyAll()
265 self.assertEqual(json_value, result)
266
267
268 def test_failure_ioerror(self):
269 """Test that there's a failure if urlopen() never succeeds."""
270 json_string = _OPEN_STATUS_VALUES[0]
271 json_value = json.loads(json_string)
272 for _ in range(site_utils._MAX_LAB_STATUS_ATTEMPTS):
273 urllib2.urlopen(mox.IgnoreArg()).AndRaise(
274 IOError('Fake I/O error for a fake URL'))
275 time.sleep(mox.IgnoreArg()).AndReturn(None)
276 self.mox.ReplayAll()
277 result = site_utils._get_lab_status(_FAKE_URL)
278 self.mox.VerifyAll()
279 self.assertEqual(None, result)
280
281
282 def test_failure_http_internal_error(self):
283 """Test that there's a failure for a permanent HTTP error."""
284 json_string = _OPEN_STATUS_VALUES[0]
285 json_value = json.loads(json_string)
286 for _ in range(site_utils._MAX_LAB_STATUS_ATTEMPTS):
287 urllib2.urlopen(mox.IgnoreArg()).AndReturn(
288 _FakeURLResponse(404, 'Not here, never gonna be'))
289 time.sleep(mox.IgnoreArg()).InAnyOrder().AndReturn(None)
290 self.mox.ReplayAll()
291 result = site_utils._get_lab_status(_FAKE_URL)
292 self.mox.VerifyAll()
293 self.assertEqual(None, result)
294
295
296class DecodeStatusTest(unittest.TestCase):
297
298 """Test case for _decode_lab_status().
299
300 Testing covers three distinct possible states:
301 1. Lab is up. All calls to _decode_lab_status() will
302 succeed without raising an exception.
303 2. Lab is down. All calls to _decode_lab_status() will
J. Richard Barnetteabbe0962013-12-10 18:15:44 -0800304 fail with TestLabException.
305 3. Build disabled. Calls to _decode_lab_status() will
306 succeed, except that board `_DEADBUILD` will raise
307 TestLabException.
J. Richard Barnette266da2a2013-11-27 15:09:55 -0800308
309 """
310
311 def _assert_lab_open(self, lab_status):
312 """Test that open status values are handled properly.
313
314 Test that _decode_lab_status() succeeds when the lab status
315 is up.
316
317 @param lab_status JSON value describing lab status.
318
319 """
J. Richard Barnetteabbe0962013-12-10 18:15:44 -0800320 site_utils._decode_lab_status(lab_status, _LIVEBUILD)
321 site_utils._decode_lab_status(lab_status, _DEADBUILD)
J. Richard Barnette266da2a2013-11-27 15:09:55 -0800322
323
324 def _assert_lab_closed(self, lab_status):
325 """Test that closed status values are handled properly.
326
J. Richard Barnetteabbe0962013-12-10 18:15:44 -0800327 Test that _decode_lab_status() raises TestLabException
J. Richard Barnette266da2a2013-11-27 15:09:55 -0800328 when the lab status is down.
329
330 @param lab_status JSON value describing lab status.
331
332 """
J. Richard Barnetteabbe0962013-12-10 18:15:44 -0800333 with self.assertRaises(site_utils.TestLabException):
334 site_utils._decode_lab_status(lab_status, _LIVEBUILD)
335 with self.assertRaises(site_utils.TestLabException):
336 site_utils._decode_lab_status(lab_status, _DEADBUILD)
J. Richard Barnette266da2a2013-11-27 15:09:55 -0800337
338
J. Richard Barnetteabbe0962013-12-10 18:15:44 -0800339 def _assert_lab_deadbuild(self, lab_status):
340 """Test that disabled builds are handled properly.
J. Richard Barnette266da2a2013-11-27 15:09:55 -0800341
J. Richard Barnetteabbe0962013-12-10 18:15:44 -0800342 Test that _decode_lab_status() raises TestLabException
343 for build `_DEADBUILD` and succeeds otherwise.
J. Richard Barnette266da2a2013-11-27 15:09:55 -0800344
345 @param lab_status JSON value describing lab status.
346
347 """
J. Richard Barnetteabbe0962013-12-10 18:15:44 -0800348 site_utils._decode_lab_status(lab_status, _LIVEBUILD)
349 with self.assertRaises(site_utils.TestLabException):
350 site_utils._decode_lab_status(lab_status, _DEADBUILD)
J. Richard Barnette266da2a2013-11-27 15:09:55 -0800351
352
353 def _assert_lab_status(self, test_values, checker):
354 """General purpose test for _decode_lab_status().
355
356 Decode each JSON string in `test_values`, and call the
357 `checker` function to test the corresponding status is
358 correctly handled.
359
360 @param test_values Array of JSON encoded strings representing
361 lab status.
362 @param checker Function to be called against each of the lab
363 status values in the `test_values` array.
364
365 """
366 for s in test_values:
367 lab_status = json.loads(s)
368 checker(lab_status)
369
370
371 def test_open_lab(self):
372 """Test that open lab status values are handled correctly."""
373 self._assert_lab_status(_OPEN_STATUS_VALUES,
374 self._assert_lab_open)
375
376
377 def test_closed_lab(self):
378 """Test that closed lab status values are handled correctly."""
379 self._assert_lab_status(_CLOSED_STATUS_VALUES,
380 self._assert_lab_closed)
381
382
J. Richard Barnetteabbe0962013-12-10 18:15:44 -0800383 def test_dead_build(self):
384 """Test that disabled builds are handled correctly."""
385 self._assert_lab_status(_DEADBUILD_STATUS_VALUES,
386 self._assert_lab_deadbuild)
J. Richard Barnette266da2a2013-11-27 15:09:55 -0800387
388
389class CheckStatusTest(mox.MoxTestBase):
390
391 """Test case for `check_lab_status()`.
392
393 We mock out dependencies on `global_config.global_config()`,
394 `_get_lab_status()` and confirm that the function succeeds or
395 fails as expected.
396
397 N.B. We don't mock `_decode_lab_status()`; if DecodeStatusTest
398 is failing, this test may fail, too.
399
400 """
401
402 def setUp(self):
403 super(CheckStatusTest, self).setUp()
404 self.mox.StubOutWithMock(global_config.global_config,
405 'get_config_value')
406 self.mox.StubOutWithMock(site_utils, '_get_lab_status')
407
408
409 def _setup_not_cautotest(self):
410 """Set up to mock the "we're not on cautotest" case."""
411 global_config.global_config.get_config_value(
412 'SERVER', 'hostname').AndReturn('not-cautotest')
413
414
415 def _setup_no_status(self):
416 """Set up to mock lab status as unavailable."""
417 global_config.global_config.get_config_value(
418 'SERVER', 'hostname').AndReturn('cautotest')
419 global_config.global_config.get_config_value(
420 'CROS', 'lab_status_url').AndReturn(_FAKE_URL)
421 site_utils._get_lab_status(_FAKE_URL).AndReturn(None)
422
423
424 def _setup_lab_status(self, json_string):
425 """Set up to mock a given lab status.
426
427 @param json_string JSON string for the JSON object to return
428 from `_get_lab_status()`.
429
430 """
431 global_config.global_config.get_config_value(
432 'SERVER', 'hostname').AndReturn('cautotest')
433 global_config.global_config.get_config_value(
434 'CROS', 'lab_status_url').AndReturn(_FAKE_URL)
435 json_value = json.loads(json_string)
436 site_utils._get_lab_status(_FAKE_URL).AndReturn(json_value)
437
438
J. Richard Barnetteabbe0962013-12-10 18:15:44 -0800439 def _try_check_status(self, build):
440 """Test calling check_lab_status() with `build`."""
J. Richard Barnette266da2a2013-11-27 15:09:55 -0800441 try:
442 self.mox.ReplayAll()
J. Richard Barnetteabbe0962013-12-10 18:15:44 -0800443 site_utils.check_lab_status(build)
J. Richard Barnette266da2a2013-11-27 15:09:55 -0800444 finally:
445 self.mox.VerifyAll()
446
447
J. Richard Barnetteabbe0962013-12-10 18:15:44 -0800448 def test_non_cautotest(self):
449 """Test a call with a build when the host isn't cautotest."""
J. Richard Barnette266da2a2013-11-27 15:09:55 -0800450 self._setup_not_cautotest()
J. Richard Barnetteabbe0962013-12-10 18:15:44 -0800451 self._try_check_status(_LIVEBUILD)
J. Richard Barnette266da2a2013-11-27 15:09:55 -0800452
453
J. Richard Barnetteabbe0962013-12-10 18:15:44 -0800454 def test_no_lab_status(self):
455 """Test with a build when `_get_lab_status()` returns `None`."""
J. Richard Barnette266da2a2013-11-27 15:09:55 -0800456 self._setup_no_status()
J. Richard Barnetteabbe0962013-12-10 18:15:44 -0800457 self._try_check_status(_LIVEBUILD)
J. Richard Barnette266da2a2013-11-27 15:09:55 -0800458
459
J. Richard Barnetteabbe0962013-12-10 18:15:44 -0800460 def test_lab_up_live_build(self):
461 """Test lab open with a build specified."""
J. Richard Barnette266da2a2013-11-27 15:09:55 -0800462 self._setup_lab_status(_OPEN_STATUS_VALUES[0])
J. Richard Barnetteabbe0962013-12-10 18:15:44 -0800463 self._try_check_status(_LIVEBUILD)
J. Richard Barnette266da2a2013-11-27 15:09:55 -0800464
465
J. Richard Barnetteabbe0962013-12-10 18:15:44 -0800466 def test_lab_down_live_build(self):
467 """Test lab closed with a build specified."""
J. Richard Barnette266da2a2013-11-27 15:09:55 -0800468 self._setup_lab_status(_CLOSED_STATUS_VALUES[0])
J. Richard Barnetteabbe0962013-12-10 18:15:44 -0800469 with self.assertRaises(site_utils.TestLabException):
470 self._try_check_status(_LIVEBUILD)
J. Richard Barnette266da2a2013-11-27 15:09:55 -0800471
472
J. Richard Barnetteabbe0962013-12-10 18:15:44 -0800473 def test_build_disabled_live_build(self):
474 """Test build disabled with a live build specified."""
475 self._setup_lab_status(_DEADBUILD_STATUS_VALUES[0])
476 self._try_check_status(_LIVEBUILD)
J. Richard Barnette266da2a2013-11-27 15:09:55 -0800477
478
J. Richard Barnetteabbe0962013-12-10 18:15:44 -0800479 def test_build_disabled_dead_build(self):
480 """Test build disabled with the disabled build specified."""
481 self._setup_lab_status(_DEADBUILD_STATUS_VALUES[0])
482 with self.assertRaises(site_utils.TestLabException):
483 self._try_check_status(_DEADBUILD)
J. Richard Barnette266da2a2013-11-27 15:09:55 -0800484
485
486if __name__ == '__main__':
487 unittest.main()