| OLD | NEW |
| (Empty) |
| 1 #!/usr/bin/env python | |
| 2 # Copyright (c) 2012 The Chromium 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 """Unit tests for buildbot_json.py.""" | |
| 7 | |
| 8 import json | |
| 9 import logging | |
| 10 import os | |
| 11 import cStringIO | |
| 12 import StringIO | |
| 13 import sys | |
| 14 import unittest | |
| 15 import urllib | |
| 16 | |
| 17 ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) | |
| 18 sys.path.insert(0, os.path.join(ROOT_DIR, '..')) | |
| 19 | |
| 20 import find_depot_tools # pylint: disable=W0611 | |
| 21 from testing_support import auto_stub | |
| 22 | |
| 23 # in tests/ | |
| 24 import reduce_test_data # pylint: disable=F0401 | |
| 25 | |
| 26 # In root | |
| 27 import buildbot_json | |
| 28 | |
| 29 | |
| 30 class BuildbotJsonTest(auto_stub.TestCase): | |
| 31 def setUp(self): | |
| 32 super(BuildbotJsonTest, self).setUp() | |
| 33 # Default mock. | |
| 34 self.old_urlopen = self.mock(urllib, 'urlopen', self.mockurlopen) | |
| 35 self.mock(sys, 'stderr', cStringIO.StringIO()) | |
| 36 self.mock(sys, 'stdout', cStringIO.StringIO()) | |
| 37 self.mock(buildbot_json.time, 'time', lambda: 1325394000.01) | |
| 38 self.url = 'http://build.chromium.org/p/tryserver.chromium' | |
| 39 self.datadir = os.path.join(ROOT_DIR, 'data') | |
| 40 if not os.path.isdir(self.datadir): | |
| 41 os.mkdir(self.datadir) | |
| 42 self.test_id = self.id().split('BuildbotJsonTest.', 1)[1] | |
| 43 self.filepath = os.path.join(self.datadir, self.test_id) + '.json' | |
| 44 self.queue = [] | |
| 45 self.training = False | |
| 46 if os.path.isfile(self.filepath): | |
| 47 self.queue = json.load(open(self.filepath)) | |
| 48 # Auto upgrade old data. | |
| 49 for i in xrange(len(self.queue)): | |
| 50 url = self.queue[i][0] | |
| 51 if not url.endswith('filter=1'): | |
| 52 if '?' in url: | |
| 53 url += '&filter=1' | |
| 54 else: | |
| 55 url += '?filter=1' | |
| 56 self.queue[i][0] = url | |
| 57 logging.warn('Auto-convert to training because missing filter=1.') | |
| 58 self.training = True | |
| 59 self.queue_index = 0 | |
| 60 self.reducer = reduce_test_data.Filterer() | |
| 61 | |
| 62 def tearDown(self): | |
| 63 try: | |
| 64 if not self.has_failed(): | |
| 65 if self.queue_index < len(self.queue): | |
| 66 self.queue = self.queue[:self.queue_index] | |
| 67 logging.warning('Auto-convert to training because of queue overflow') | |
| 68 self.training = True | |
| 69 if self.training: | |
| 70 json.dump(self.queue, open(self.filepath, 'w'), separators=(',',':')) | |
| 71 self.assertEqual(self.queue_index, len(self.queue)) | |
| 72 self.assertOut('stderr', '') | |
| 73 self.assertOut('stdout', '') | |
| 74 else: | |
| 75 if self.training: | |
| 76 logging.error('Not saving data even if in training mode.') | |
| 77 finally: | |
| 78 # Make sure the super class tearDown() function is called so stubs are | |
| 79 # removed. | |
| 80 super(BuildbotJsonTest, self).tearDown() | |
| 81 if self.training: | |
| 82 self.fail( | |
| 83 'Don\'t worry, it\'s just updating internal files. Please run ' | |
| 84 'again.\n%s' % '\n'.join(q[0] for q in self.queue)) | |
| 85 | |
| 86 def assertOut(self, out, expected): | |
| 87 """Check stderr/stdout and resets it.""" | |
| 88 self.assertEqual(str(expected), str(getattr(sys, out).getvalue())) | |
| 89 self.mock(sys, out, cStringIO.StringIO()) | |
| 90 | |
| 91 def mockurlopen(self, url): | |
| 92 self.assertTrue(self.queue_index <= len(self.queue)) | |
| 93 if self.queue_index != len(self.queue): | |
| 94 expected_url, data = self.queue[self.queue_index] | |
| 95 if url != expected_url: | |
| 96 logging.warn( | |
| 97 'Auto-convert to training because %s != %s.' % (url, expected_url)) | |
| 98 self.training = True | |
| 99 # Delete the remainder of the queue. | |
| 100 self.queue = self.queue[:self.queue_index] | |
| 101 | |
| 102 if self.queue_index == len(self.queue): | |
| 103 data = self.old_urlopen(url).read() | |
| 104 self.training = True | |
| 105 | |
| 106 # Re-filter it. | |
| 107 try: | |
| 108 data = json.loads(data) | |
| 109 except ValueError: | |
| 110 self.fail('Failed to decode %s' % url) | |
| 111 expected_url, new_data = self.reducer.filter_response(url, data) | |
| 112 assert new_data | |
| 113 new_data_json = json.dumps(new_data, separators=(',',':')) | |
| 114 | |
| 115 if self.queue_index == len(self.queue): | |
| 116 self.queue.append((url, new_data_json)) | |
| 117 elif new_data != data: | |
| 118 logging.warn( | |
| 119 'Auto-convert to training because url %s\n%s != %s.' % ( | |
| 120 url, data, new_data)) | |
| 121 self.queue[self.queue_index] = [url, new_data_json] | |
| 122 self.training = True | |
| 123 channel = StringIO.StringIO(new_data_json) | |
| 124 channel.headers = '<mocked headers>' | |
| 125 self.queue_index += 1 | |
| 126 return channel | |
| 127 | |
| 128 def testCommands(self): | |
| 129 # Assert no new command was added, otherwise a test needs to be written. | |
| 130 expected = [ | |
| 131 'busy', | |
| 132 'builds', | |
| 133 'count', | |
| 134 'current', | |
| 135 'disconnected', | |
| 136 'help', | |
| 137 'idle', | |
| 138 'interactive', | |
| 139 'last_failure', | |
| 140 'pending', | |
| 141 'run', | |
| 142 ] | |
| 143 actual = [i[3:] for i in dir(buildbot_json) if i.startswith('CMD')] | |
| 144 self.assertEqual(sorted(expected), sorted(actual)) | |
| 145 for i in actual: | |
| 146 self.assertTrue(hasattr(self, 'testCMD' + i)) | |
| 147 | |
| 148 def testCMDbusy(self): | |
| 149 parser = buildbot_json.gen_parser() | |
| 150 self.assertEqual( | |
| 151 0, | |
| 152 buildbot_json.CMDbusy(parser, [self.url, '-b', 'linux'])) | |
| 153 filepath = os.path.join(self.datadir, self.test_id) + '_expected.txt' | |
| 154 if self.training or not os.path.isfile(filepath): | |
| 155 # pylint: disable=E1101 | |
| 156 json.dump(sys.stdout.getvalue(), open(filepath, 'w')) | |
| 157 expected = json.load(open(filepath)) | |
| 158 self.assertOut('stdout', expected) | |
| 159 | |
| 160 def testCMDbuilds(self): | |
| 161 parser = buildbot_json.gen_parser() | |
| 162 self.assertEqual( | |
| 163 0, | |
| 164 buildbot_json.CMDbuilds( | |
| 165 parser, [self.url, '-b', 'linux', '-s', 'vm146-m4', '-q'])) | |
| 166 filepath = os.path.join(self.datadir, self.test_id) + '_expected.txt' | |
| 167 if self.training or not os.path.isfile(filepath): | |
| 168 # pylint: disable=E1101 | |
| 169 json.dump(sys.stdout.getvalue(), open(filepath, 'w')) | |
| 170 expected = json.load(open(filepath)) | |
| 171 self.assertOut('stdout', expected) | |
| 172 | |
| 173 def testCMDcount(self): | |
| 174 self.mock(buildbot_json.time, 'time', lambda: 1348166285.56) | |
| 175 parser = buildbot_json.gen_parser() | |
| 176 self.assertEqual( | |
| 177 0, | |
| 178 buildbot_json.CMDcount( | |
| 179 parser, [self.url, '-b', 'linux', '-o' '360'])) | |
| 180 filepath = os.path.join(self.datadir, self.test_id) + '_expected.txt' | |
| 181 if self.training or not os.path.isfile(filepath): | |
| 182 # pylint: disable=E1101 | |
| 183 json.dump(sys.stdout.getvalue(), open(filepath, 'w')) | |
| 184 expected = json.load(open(filepath)) | |
| 185 self.assertOut('stdout', expected) | |
| 186 | |
| 187 def testCMDdisconnected(self): | |
| 188 parser = buildbot_json.gen_parser() | |
| 189 self.assertEqual( | |
| 190 0, | |
| 191 buildbot_json.CMDdisconnected(parser, [self.url])) | |
| 192 self.assertOut( | |
| 193 'stdout', | |
| 194 'vm112-m4\nvm122-m4\nvm124-m4\nvm131-m4\nvm134-m4\nvm139-m4\nvm143-m4\n' | |
| 195 'vm146-m4\nvm157-m4\nvm162-m4\nvm165-m4\nvm60-m4\nvm62-m4\nvm64-m4\n') | |
| 196 | |
| 197 def testCMDhelp(self): | |
| 198 parser = buildbot_json.gen_parser() | |
| 199 self.assertEqual(0, buildbot_json.CMDhelp(parser, [])) | |
| 200 # No need to check exact output here. | |
| 201 # pylint: disable=E1101 | |
| 202 self.assertTrue( | |
| 203 'show program\'s version number and exit\n' in sys.stdout.getvalue()) | |
| 204 self.mock(sys, 'stdout', cStringIO.StringIO()) | |
| 205 | |
| 206 def testCMDidle(self): | |
| 207 parser = buildbot_json.gen_parser() | |
| 208 self.assertEqual( | |
| 209 0, | |
| 210 buildbot_json.CMDidle(parser, [self.url, '--builder', 'linux_clang'])) | |
| 211 self.assertOut( | |
| 212 'stdout', 'Builder linux_clang: vm104-m4, vm113-m4, vm165-m4\n') | |
| 213 | |
| 214 def testCMDinteractive(self): | |
| 215 self.mock(sys, 'stdin', cStringIO.StringIO('exit()')) | |
| 216 parser = buildbot_json.gen_parser() | |
| 217 try: | |
| 218 # TODO(maruel): Real testing. | |
| 219 buildbot_json.CMDinteractive(parser, [self.url]) | |
| 220 self.fail() | |
| 221 except SystemExit: | |
| 222 pass | |
| 223 self.assertOut( | |
| 224 'stderr', | |
| 225 'Buildbot interactive console for "http://build.chromium.org' | |
| 226 '/p/tryserver.chromium".\nHint: Start with typing: ' | |
| 227 '\'buildbot.printable_attributes\' or \'print str(buildbot)\' to ' | |
| 228 'explore.\n') | |
| 229 self.assertOut('stdout', '>>> ') | |
| 230 | |
| 231 def testCMDlast_failure(self): | |
| 232 parser = buildbot_json.gen_parser() | |
| 233 self.assertEqual( | |
| 234 0, | |
| 235 buildbot_json.CMDlast_failure( | |
| 236 parser, [self.url, '-b', 'linux', '--step', 'compile'])) | |
| 237 self.assertOut( | |
| 238 'stdout', | |
| 239 '27369 on vm136-m4: blame:jam@chromium.org\n' | |
| 240 '27367 on vm158-m4: blame:jam@chromium.org\n') | |
| 241 | |
| 242 def testCMDpending(self): | |
| 243 parser = buildbot_json.gen_parser() | |
| 244 self.assertEqual(0, buildbot_json.CMDpending(parser, [self.url])) | |
| 245 self.assertOut('stdout', | |
| 246 "Builder linux_touch: 2\n" | |
| 247 " revision: HEAD\n change:\n comment: u''\n" | |
| 248 " who: saintlou@google.com\n revision: HEAD\n change:\n" | |
| 249 " comment: u''\n who: saintlou@google.com\n") | |
| 250 | |
| 251 def testCMDcurrent(self): | |
| 252 parser = buildbot_json.gen_parser() | |
| 253 self.assertEqual(0, buildbot_json.CMDcurrent(parser, [self.url])) | |
| 254 filepath = os.path.join(self.datadir, self.test_id) + '_expected.txt' | |
| 255 if self.training or not os.path.isfile(filepath): | |
| 256 # pylint: disable=E1101 | |
| 257 json.dump(sys.stdout.getvalue(), open(filepath, 'w')) | |
| 258 expected = json.load(open(filepath)) | |
| 259 self.assertOut('stdout', expected) | |
| 260 | |
| 261 def testCMDrun(self): | |
| 262 parser = buildbot_json.gen_parser() | |
| 263 self.assertEqual( | |
| 264 0, | |
| 265 buildbot_json.CMDrun( | |
| 266 parser, [self.url, "print '\\n'.join(buildbot.builders.keys)"])) | |
| 267 self.assertOut('stdout', 'linux\nlinux_clang\nlinux_touch\n') | |
| 268 | |
| 269 def testCurrentBuilds(self): | |
| 270 b = buildbot_json.Buildbot('http://build.chromium.org/p/tryserver.chromium') | |
| 271 actual = [] | |
| 272 for builder in b.builders: | |
| 273 self.assertEqual([], list(builder.current_builds.cached_children)) | |
| 274 i = 0 | |
| 275 last_build = None | |
| 276 for c in builder.current_builds: | |
| 277 self.assertEqual(builder, c.builder) | |
| 278 actual.append(str(c)) | |
| 279 i += 1 | |
| 280 last_build = c | |
| 281 if i: | |
| 282 self.assertEqual(last_build.number, builder.builds[-1].number) | |
| 283 self.assertEqual(i, len(list(builder.current_builds.cached_children))) | |
| 284 builder.current_builds.discard() | |
| 285 self.assertEqual([], list(builder.current_builds.cached_children)) | |
| 286 | |
| 287 filepath = os.path.join(self.datadir, self.test_id) + '_expected.json' | |
| 288 if self.training or not os.path.isfile(filepath): | |
| 289 json.dump(actual, open(filepath, 'w')) | |
| 290 expected = json.load(open(filepath)) | |
| 291 self.assertEqual(expected, actual) | |
| 292 | |
| 293 def test_builds_reverse(self): | |
| 294 # Check the 2 last builds from 'linux' using iterall() instead of | |
| 295 # __iter__(). The test also confirms that the build object itself is not | |
| 296 # loaded. | |
| 297 b = buildbot_json.Buildbot('http://build.chromium.org/p/tryserver.chromium') | |
| 298 actual = [] | |
| 299 for b in b.builders['linux'].builds.iterall(): | |
| 300 actual.append(b.number) | |
| 301 # When using iterall() the Build data is delay loaded: | |
| 302 assert b._data is None # pylint: disable=W0212 | |
| 303 if len(actual) == 2: | |
| 304 break | |
| 305 | |
| 306 filepath = os.path.join(self.datadir, self.test_id) + '_expected.json' | |
| 307 if self.training or not os.path.isfile(filepath): | |
| 308 json.dump(actual, open(filepath, 'w')) | |
| 309 expected = json.load(open(filepath)) | |
| 310 self.assertEqual(expected, actual) | |
| 311 | |
| 312 def test_build_results(self): | |
| 313 b = buildbot_json.Buildbot('http://build.chromium.org/p/tryserver.chromium') | |
| 314 # builds.data['results'] is not present. | |
| 315 self.assertEqual( | |
| 316 buildbot_json.SUCCESS, b.builders['linux_clang'].builds[1638].result) | |
| 317 self.assertEqual( | |
| 318 buildbot_json.SUCCESS, | |
| 319 b.builders['linux_clang'].builds[1638].steps[0].result) | |
| 320 | |
| 321 def test_build_steps_keys(self): | |
| 322 b = buildbot_json.Buildbot('http://build.chromium.org/p/tryserver.chromium') | |
| 323 build = b.builders['linux_clang'].builds[1638] | |
| 324 #self.assertEqual([0, 1, 2, 3], build.steps.keys) | |
| 325 | |
| 326 # Grab cached version. There is none. | |
| 327 actual = [step for step in build.steps.cached_children] | |
| 328 self.assertEqual([], actual) | |
| 329 | |
| 330 # Force load. | |
| 331 actual = [step for step in build.steps] | |
| 332 self.assertEqual( | |
| 333 [buildbot_json.SUCCESS] * 4, [step.result for step in actual]) | |
| 334 self.assertEqual( | |
| 335 [True] * 4, [step.simplified_result for step in actual]) | |
| 336 self.assertEqual(4, len(actual)) | |
| 337 | |
| 338 # Grab cached version. | |
| 339 actual = [step for step in build.steps.cached_children] | |
| 340 self.assertEqual( | |
| 341 [buildbot_json.SUCCESS] * 4, [step.result for step in actual]) | |
| 342 self.assertEqual(4, len(actual)) | |
| 343 | |
| 344 def test_repr(self): | |
| 345 b = buildbot_json.Buildbot('http://build.chromium.org/p/tryserver.chromium') | |
| 346 self.assertEqual('<Builder key=linux>', repr(b.builders['linux'])) | |
| 347 self.assertEqual("<Builders keys=['linux']>", repr(b.builders)) | |
| 348 | |
| 349 def test_refresh(self): | |
| 350 b = buildbot_json.Buildbot('http://build.chromium.org/p/tryserver.chromium') | |
| 351 self.assertEqual(True, b.refresh()) | |
| 352 | |
| 353 def test_build_step_cached_data(self): | |
| 354 b = buildbot_json.Buildbot('http://build.chromium.org/p/tryserver.chromium') | |
| 355 build = 30157 | |
| 356 self.assertEqual( | |
| 357 None, b.builders['linux'].current_builds[build].steps[0].cached_data) | |
| 358 b.builders['linux'].current_builds[build].steps[0].cache() | |
| 359 self.assertEqual( | |
| 360 'update_scripts', | |
| 361 b.builders['linux'].current_builds[build].steps[0].name) | |
| 362 self.assertEqual( | |
| 363 ['browser_tests', 'ui_tests'], | |
| 364 b.builders['linux'].current_builds[build].steps.failed) | |
| 365 self.assertEqual( | |
| 366 2, | |
| 367 b.builders['linux'].current_builds[build].steps[2 | |
| 368 ].cached_data['step_number']) | |
| 369 b.refresh() | |
| 370 # cache_keys() does the same thing as cache(). | |
| 371 b.builders['linux'].current_builds[build].steps.cache_keys() | |
| 372 | |
| 373 def test_contains(self): | |
| 374 b = buildbot_json.Buildbot('http://build.chromium.org/p/tryserver.chromium') | |
| 375 self.assertTrue('linux' in b.builders) | |
| 376 self.assertEqual(3, len(list(b.builders.cached_children))) | |
| 377 try: | |
| 378 # The dereference of an invalid key when keys are cached will throw an | |
| 379 # exception. | |
| 380 # pylint: disable=W0104 | |
| 381 b.builders['non_existent'] | |
| 382 self.fail() | |
| 383 except KeyError: | |
| 384 pass | |
| 385 | |
| 386 def test_slaves(self): | |
| 387 b = buildbot_json.Buildbot('http://build.chromium.org/p/tryserver.chromium') | |
| 388 self.assertEqual(11, len(b.slaves.names)) | |
| 389 self.assertEqual(False, b.slaves['mini34-m4'].connected) | |
| 390 | |
| 391 def test_build_revision(self): | |
| 392 class Root(object): | |
| 393 @staticmethod | |
| 394 def read(_): | |
| 395 return {'sourceStamp': {'revision': 321}} | |
| 396 build = buildbot_json.Build(Root(), '123', None) | |
| 397 self.assertEqual(321, build.revision) | |
| 398 | |
| 399 def test_build_revision_none(self): | |
| 400 class Root(object): | |
| 401 @staticmethod | |
| 402 def read(_): | |
| 403 return {} | |
| 404 build = buildbot_json.Build(Root(), '123', None) | |
| 405 self.assertEqual(None, build.revision) | |
| 406 | |
| 407 def test_build_duration(self): | |
| 408 class Root(object): | |
| 409 @staticmethod | |
| 410 def read(_): | |
| 411 return {'times': [3, 15]} | |
| 412 build = buildbot_json.Build(Root(), '123', None) | |
| 413 self.assertEqual(12, build.duration) | |
| 414 self.assertEqual(3, build.start_time) | |
| 415 self.assertEqual(15, build.end_time) | |
| 416 | |
| 417 def test_build_duration_none(self): | |
| 418 class Root(object): | |
| 419 @staticmethod | |
| 420 def read(_): | |
| 421 return {} | |
| 422 build = buildbot_json.Build(Root(), '123', None) | |
| 423 self.assertEqual(None, build.duration) | |
| 424 self.assertEqual(None, build.start_time) | |
| 425 self.assertEqual(None, build.end_time) | |
| 426 | |
| 427 def test_build_steps_names(self): | |
| 428 class Root(object): | |
| 429 @staticmethod | |
| 430 def read(url): # pylint: disable=E0213 | |
| 431 self.assertEqual('123', url) | |
| 432 return {'steps': [{'name': 'a'}, {'name': 'b'}]} | |
| 433 build = buildbot_json.Build(Root(), '123', None) | |
| 434 self.assertEqual(['a', 'b'], build.steps.keys) | |
| 435 | |
| 436 def test_build_step_duration(self): | |
| 437 class Root(object): | |
| 438 @staticmethod | |
| 439 def read(_): | |
| 440 return {'steps': [{'times': [3, 15], 'isStarted': True}]} | |
| 441 build = buildbot_json.Build(Root(), '123', None) | |
| 442 build_step = buildbot_json.BuildStep(buildbot_json.BuildSteps(build), 0) | |
| 443 self.assertEqual(12, build_step.duration) | |
| 444 self.assertEqual(True, build_step.is_running) | |
| 445 self.assertEqual(True, build_step.is_started) | |
| 446 self.assertEqual(False, build_step.is_finished) | |
| 447 | |
| 448 def test_build_step_duration_none(self): | |
| 449 class Root(object): | |
| 450 @staticmethod | |
| 451 def read(_): | |
| 452 return {'steps': [{}]} | |
| 453 build = buildbot_json.Build(Root(), '123', None) | |
| 454 build_step = buildbot_json.BuildStep(buildbot_json.BuildSteps(build), 0) | |
| 455 self.assertEqual(None, build_step.duration) | |
| 456 | |
| 457 | |
| 458 if __name__ == '__main__': | |
| 459 logging.basicConfig(level= | |
| 460 [logging.WARN, logging.INFO, logging.DEBUG][min(2, sys.argv.count('-v'))]) | |
| 461 unittest.main() | |
| OLD | NEW |